Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
msg555 committed Oct 7, 2011
0 parents commit dd56456
Show file tree
Hide file tree
Showing 111 changed files with 11,141 additions and 0 deletions.
95 changes: 95 additions & 0 deletions AndroidManifest.xml
@@ -0,0 +1,95 @@
<?xml version="1.1" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="edu.umich.PowerTutor"
android:versionCode="13" android:versionName="1.4">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".ui.UMLogger"
android:label="@string/app_name"
android:screenOrientation="portrait" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".ui.Help" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
<activity android:name=".ui.PowerViewer" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
<activity android:name=".ui.PowerTop"
android:label="Power Top" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
<activity android:name=".ui.PowerPie" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
<activity android:name=".ui.MiscView" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
<activity android:name=".ui.PowerTabs"
android:theme="@android:style/Theme.NoTitleBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
<activity android:name=".ui.EditPreferences"
android:label="PowerTutor Options">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
<activity android:name=".ui.ViewerPreferences"
android:label="PowerTutor History Options">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
<activity android:name=".widget.Configure"
android:label="Configure PowerTutor Widget" >
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
</intent-filter>
</activity>
<activity android:name=".widget.DataSourceConfigure"
android:label="Configure Data Source">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
<receiver android:name=".ui.StartupReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<category android:name="android.intent.category.HOME" />
</intent-filter>
</receiver>
<service android:name=".service.UMLoggerService"></service>
<receiver android:name=".widget.PowerWidget" >
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="@xml/widget_info" />
</receiver>
</application>



<uses-sdk android:minSdkVersion="3" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
</manifest>
16 changes: 16 additions & 0 deletions LICENCE
@@ -0,0 +1,16 @@
Copyright (C) 2011 The University of Michigan

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

Please send inquiries to powertutor@umich.edu
224 changes: 224 additions & 0 deletions LogUploaderUmich.java
@@ -0,0 +1,224 @@
package edu.umich.PowerTutor.service;

import edu.umich.PowerTutor.ui.UMLogger;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Build;
import android.os.PowerManager;
import android.telephony.TelephonyManager;
import android.util.Log;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketTimeoutException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.zip.DeflaterOutputStream;

/* This class is responsible for all of the policy decisions on when to actually
* send log information back to our log collecting servers and is also
* responsible for actually sending the data should it decide that it is
* appropriate.
*/
public class LogUploader {
private static final String TAG = "LogUploader";

public static final String UPLOAD_FILE = "PowerTrace_Upload.log";

private static final long NONE_LOG_LENGTH = 1 << 20; // 1 MiB
private static final long WIFI_LOG_LENGTH = 1 << 17; // 128 KiB
private static final long THREEG_LOG_LENGTH = 1 << 19; // 512 KiB

private static final int CONNECTION_NONE = 0;
private static final int CONNECTION_WIFI = 1;
private static final int CONNECTION_3G = 2;

private boolean plugged;

private File logFile;
private ConnectivityManager connectivityManager;
private TelephonyManager telephonyManager;

private Thread uploadThread;

public LogUploader(Context context) {
telephonyManager = (TelephonyManager)context.getSystemService(
Context.TELEPHONY_SERVICE);
connectivityManager = (ConnectivityManager)context.getSystemService(
Context.CONNECTIVITY_SERVICE);
logFile = context.getFileStreamPath(UPLOAD_FILE);
}

public synchronized boolean shouldUpload() {
switch(connectionAvailable()) {
case CONNECTION_WIFI:
return plugged && logFile.length() > WIFI_LOG_LENGTH;
case CONNECTION_3G:
return plugged && logFile.length() > THREEG_LOG_LENGTH;
default: // CONNECTION_NONE
return logFile.length() > NONE_LOG_LENGTH;
}
}

public synchronized void plug(boolean plugged) {
this.plugged = plugged;
}

private int connectionAvailable() {
/* TODO: Maybe we should only send data when the device is plugged in.
*/
NetworkInfo info = connectivityManager.getActiveNetworkInfo();
if(info == null || !connectivityManager.getBackgroundDataSetting()) {
return CONNECTION_NONE;
}
int netType = info.getType();
int netSubtype = info.getSubtype();
if (netType == ConnectivityManager.TYPE_WIFI) {
return info.isConnected() ? CONNECTION_WIFI : CONNECTION_NONE;
} else if (netType == ConnectivityManager.TYPE_MOBILE
&& netSubtype == TelephonyManager.NETWORK_TYPE_UMTS
&& !telephonyManager.isNetworkRoaming()) {
return info.isConnected() ? CONNECTION_3G : CONNECTION_NONE;
}
return CONNECTION_NONE;
}

public void upload(String origFile) {
if(new File(origFile).renameTo(logFile)) {
interrupt();
uploadThread = new Thread() {
public void run() {
long runID = System.currentTimeMillis();
for(int iter = 1; !interrupted(); iter++) {
if(send(runID)) {
break;
}
if(iter > 12) iter = 12; // The max wait is a little over 1 hour.
Log.i(TAG, "Failed to send log. Will try again in " + (1 << iter) +
" seconds");
try {
do {
sleep(1000 * (1 << iter)); // Sleep for 2^iter seconds.
} while(connectionAvailable() == CONNECTION_NONE);
} catch(InterruptedException e) {
break;
}
}
}
};
uploadThread.start();
} else {
Log.w(TAG, "Failed to move log file before sending");
}
}

public boolean isUploading() {
return uploadThread != null && uploadThread.isAlive();
}

public void interrupt() {
if(uploadThread != null) {
uploadThread.interrupt();
}
}

public void join() throws InterruptedException {
if(uploadThread != null) {
uploadThread.join();
}
}

public boolean send(long runID) {
Log.i(TAG, "Sending log data");
Socket s = new Socket();
try {
s.setSoTimeout(4000);
s.connect(new InetSocketAddress(UMLogger.SERVER_IP, UMLogger.SERVER_PORT),
15000);
} catch(IOException e) {
/* Failed to connect to server. Try again later.
*/
return false;
}

try {
BufferedInputStream in = new BufferedInputStream(
new FileInputStream(logFile), 1024);
BufferedOutputStream sockOut = new BufferedOutputStream(
s.getOutputStream(), 1024);

/* Write the prefix string to the server. */
sockOut.write(getPrefix(runID, logFile.length()));
sockOut.write(0);

/* Write the log file to the server. */
byte[] buf = new byte[1024];
while(true) {
int sz = in.read(buf, 0, buf.length);
if(sz == -1) break;
sockOut.write(buf, 0, sz);
}
sockOut.flush();
int response = s.getInputStream().read();
in.close();
s.close();

if(response != 0) {
Log.w(TAG, "Log data not accepted by server");
}
} catch(SocketTimeoutException e) {
/* Connection trouble with server. Try again later.
*/
return false;
} catch(IOException e) {
Log.w(TAG, "Unexpected exception sending log. Dropping log data");
e.printStackTrace();
}
logFile.delete();
return true;
}

private byte[] getPrefix(long runID, long payloadLength) {
String deviceID = telephonyManager.getDeviceId();
return (UMLogger.CURRENT_VERSION + '|' + sanatize(Build.DEVICE) + '|' +
getMD5(deviceID) + "|" + payloadLength).getBytes();
}

/* Just strip out any | characters present. Normal DEVICE strings shouldn't
* have a | but this string can be set by anyone so we should treat it as
* adversarial.
*/
private String sanatize(String s) {
StringBuffer buf = new StringBuffer();
for(int i = 0; i < s.length(); i++) {
if(s.charAt(i) != '|') {
buf.append(s.charAt(i));
}
}
return buf.toString();
}

private String getMD5(String s){
MessageDigest m = null;
try {
m = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
// Well this sucks...
e.printStackTrace();
return "nohash";
}
m.update(s.getBytes(), 0, s.length());
return new BigInteger(1, m.digest()).toString(16);
}
}
39 changes: 39 additions & 0 deletions Makefile
@@ -0,0 +1,39 @@
all: package

ANDROID_LIB=android-9.jar
CLASSPATH=$(ANDROID_LIB):libs/achartengine-0.7.0.jar:libs/com.artfulbits.aiCharts.jar

genres:
mkdir -p gen bin
aapt package -m -J gen -M AndroidManifest.xml -S res -I $(ANDROID_LIB)

aidl:
find src/ -type f | \
grep '\.aidl$$' | \
xargs -n 1 aidl -Isrc -I$(ANDROID_LIB) -ogen

gen: genres aidl

compile: gen
mkdir -p bin
find src/ gen/ -type f | \
grep '\.java$$' | \
xargs javac -cp $(CLASSPATH) -d bin
ndk-build

dex: compile
dx --dex --output=bin/classes.dex bin/ libs/

package: dex
aapt package -M AndroidManifest.xml -A assets -S res \
-F bin/PowerTutor.apk -I $(ANDROID_LIB)
cd bin; zip PowerTutor.apk classes.dex
zip bin/PowerTutor.apk -r libs -i \*.so
jarsigner -storepass android -keystore debug.keystore \
bin/PowerTutor.apk androiddebugkey

install: package
adb install bin/PowerTutor.apk

clean:
rm -rf bin/ gen/
Binary file added android-9.jar
Binary file not shown.
Binary file added debug.keystore
Binary file not shown.
11 changes: 11 additions & 0 deletions default.properties
@@ -0,0 +1,11 @@
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must be checked in Version Control Systems.
#
# To customize properties used by the Ant build system use,
# "build.properties", and override values to adapt the script to your
# project structure.

# Project target.
target=android-3
9 changes: 9 additions & 0 deletions jni/Android.mk
@@ -0,0 +1,9 @@
LOCAL_PATH := $(call my-dir)

include $(CLEAN_VARS)

LOCAL_MODULE_FILENAME := bindings
LOCAL_MODULE := bindings
LOCAL_SRC_FILES := bindings.cpp

include $(BUILD_SHARED_LIBRARY)

0 comments on commit dd56456

Please sign in to comment.