diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..5c98b42
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,2 @@
+# Default ignored files
+/workspace.xml
\ No newline at end of file
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
new file mode 100644
index 0000000..681f41a
--- /dev/null
+++ b/.idea/codeStyles/Project.xml
@@ -0,0 +1,116 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ xmlns:android
+
+ ^$
+
+
+
+
+
+
+
+
+ xmlns:.*
+
+ ^$
+
+
+ BY_NAME
+
+
+
+
+
+
+ .*:id
+
+ http://schemas.android.com/apk/res/android
+
+
+
+
+
+
+
+
+ .*:name
+
+ http://schemas.android.com/apk/res/android
+
+
+
+
+
+
+
+
+ name
+
+ ^$
+
+
+
+
+
+
+
+
+ style
+
+ ^$
+
+
+
+
+
+
+
+
+ .*
+
+ ^$
+
+
+ BY_NAME
+
+
+
+
+
+
+ .*
+
+ http://schemas.android.com/apk/res/android
+
+
+ ANDROID_ATTRIBUTE_ORDER
+
+
+
+
+
+
+ .*
+
+ .*
+
+
+ BY_NAME
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
new file mode 100644
index 0000000..9934962
--- /dev/null
+++ b/.idea/gradle.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
new file mode 100644
index 0000000..a5f05cd
--- /dev/null
+++ b/.idea/jarRepositories.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Gradle____PowerTutor2_libs_achartengine_1_0_0_jar.xml b/.idea/libraries/Gradle____PowerTutor2_libs_achartengine_1_0_0_jar.xml
new file mode 100644
index 0000000..b853066
--- /dev/null
+++ b/.idea/libraries/Gradle____PowerTutor2_libs_achartengine_1_0_0_jar.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Gradle__androidx_activity_activity_1_0_0_aar.xml b/.idea/libraries/Gradle__androidx_activity_activity_1_0_0_aar.xml
new file mode 100644
index 0000000..4e8e425
--- /dev/null
+++ b/.idea/libraries/Gradle__androidx_activity_activity_1_0_0_aar.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Gradle__androidx_annotation_annotation_1_1_0_jar.xml b/.idea/libraries/Gradle__androidx_annotation_annotation_1_1_0_jar.xml
new file mode 100644
index 0000000..5b17db6
--- /dev/null
+++ b/.idea/libraries/Gradle__androidx_annotation_annotation_1_1_0_jar.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Gradle__androidx_appcompat_appcompat_1_1_0_aar.xml b/.idea/libraries/Gradle__androidx_appcompat_appcompat_1_1_0_aar.xml
new file mode 100644
index 0000000..c618869
--- /dev/null
+++ b/.idea/libraries/Gradle__androidx_appcompat_appcompat_1_1_0_aar.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Gradle__androidx_appcompat_appcompat_resources_1_1_0_aar.xml b/.idea/libraries/Gradle__androidx_appcompat_appcompat_resources_1_1_0_aar.xml
new file mode 100644
index 0000000..1ea74dd
--- /dev/null
+++ b/.idea/libraries/Gradle__androidx_appcompat_appcompat_resources_1_1_0_aar.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Gradle__androidx_arch_core_core_common_2_1_0_jar.xml b/.idea/libraries/Gradle__androidx_arch_core_core_common_2_1_0_jar.xml
new file mode 100644
index 0000000..a7f501b
--- /dev/null
+++ b/.idea/libraries/Gradle__androidx_arch_core_core_common_2_1_0_jar.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Gradle__androidx_arch_core_core_runtime_2_0_0_aar.xml b/.idea/libraries/Gradle__androidx_arch_core_core_runtime_2_0_0_aar.xml
new file mode 100644
index 0000000..8b6395d
--- /dev/null
+++ b/.idea/libraries/Gradle__androidx_arch_core_core_runtime_2_0_0_aar.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Gradle__androidx_collection_collection_1_1_0_jar.xml b/.idea/libraries/Gradle__androidx_collection_collection_1_1_0_jar.xml
new file mode 100644
index 0000000..ecb16c3
--- /dev/null
+++ b/.idea/libraries/Gradle__androidx_collection_collection_1_1_0_jar.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Gradle__androidx_core_core_1_1_0_aar.xml b/.idea/libraries/Gradle__androidx_core_core_1_1_0_aar.xml
new file mode 100644
index 0000000..1d18987
--- /dev/null
+++ b/.idea/libraries/Gradle__androidx_core_core_1_1_0_aar.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Gradle__androidx_cursoradapter_cursoradapter_1_0_0_aar.xml b/.idea/libraries/Gradle__androidx_cursoradapter_cursoradapter_1_0_0_aar.xml
new file mode 100644
index 0000000..e06ff7f
--- /dev/null
+++ b/.idea/libraries/Gradle__androidx_cursoradapter_cursoradapter_1_0_0_aar.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Gradle__androidx_customview_customview_1_0_0_aar.xml b/.idea/libraries/Gradle__androidx_customview_customview_1_0_0_aar.xml
new file mode 100644
index 0000000..e770802
--- /dev/null
+++ b/.idea/libraries/Gradle__androidx_customview_customview_1_0_0_aar.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Gradle__androidx_drawerlayout_drawerlayout_1_0_0_aar.xml b/.idea/libraries/Gradle__androidx_drawerlayout_drawerlayout_1_0_0_aar.xml
new file mode 100644
index 0000000..485fc9d
--- /dev/null
+++ b/.idea/libraries/Gradle__androidx_drawerlayout_drawerlayout_1_0_0_aar.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Gradle__androidx_fragment_fragment_1_1_0_aar.xml b/.idea/libraries/Gradle__androidx_fragment_fragment_1_1_0_aar.xml
new file mode 100644
index 0000000..709e94a
--- /dev/null
+++ b/.idea/libraries/Gradle__androidx_fragment_fragment_1_1_0_aar.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Gradle__androidx_interpolator_interpolator_1_0_0_aar.xml b/.idea/libraries/Gradle__androidx_interpolator_interpolator_1_0_0_aar.xml
new file mode 100644
index 0000000..3a464a2
--- /dev/null
+++ b/.idea/libraries/Gradle__androidx_interpolator_interpolator_1_0_0_aar.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_common_2_1_0_jar.xml b/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_common_2_1_0_jar.xml
new file mode 100644
index 0000000..8c4384c
--- /dev/null
+++ b/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_common_2_1_0_jar.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_livedata_2_0_0_aar.xml b/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_livedata_2_0_0_aar.xml
new file mode 100644
index 0000000..9381a45
--- /dev/null
+++ b/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_livedata_2_0_0_aar.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_livedata_core_2_0_0_aar.xml b/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_livedata_core_2_0_0_aar.xml
new file mode 100644
index 0000000..e8f4dbd
--- /dev/null
+++ b/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_livedata_core_2_0_0_aar.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_runtime_2_1_0_aar.xml b/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_runtime_2_1_0_aar.xml
new file mode 100644
index 0000000..321d8e4
--- /dev/null
+++ b/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_runtime_2_1_0_aar.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_viewmodel_2_1_0_aar.xml b/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_viewmodel_2_1_0_aar.xml
new file mode 100644
index 0000000..02ec159
--- /dev/null
+++ b/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_viewmodel_2_1_0_aar.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Gradle__androidx_loader_loader_1_0_0_aar.xml b/.idea/libraries/Gradle__androidx_loader_loader_1_0_0_aar.xml
new file mode 100644
index 0000000..229b0de
--- /dev/null
+++ b/.idea/libraries/Gradle__androidx_loader_loader_1_0_0_aar.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Gradle__androidx_savedstate_savedstate_1_0_0_aar.xml b/.idea/libraries/Gradle__androidx_savedstate_savedstate_1_0_0_aar.xml
new file mode 100644
index 0000000..6531f10
--- /dev/null
+++ b/.idea/libraries/Gradle__androidx_savedstate_savedstate_1_0_0_aar.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Gradle__androidx_vectordrawable_vectordrawable_1_1_0_aar.xml b/.idea/libraries/Gradle__androidx_vectordrawable_vectordrawable_1_1_0_aar.xml
new file mode 100644
index 0000000..260dd80
--- /dev/null
+++ b/.idea/libraries/Gradle__androidx_vectordrawable_vectordrawable_1_1_0_aar.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Gradle__androidx_vectordrawable_vectordrawable_animated_1_1_0_aar.xml b/.idea/libraries/Gradle__androidx_vectordrawable_vectordrawable_animated_1_1_0_aar.xml
new file mode 100644
index 0000000..007480e
--- /dev/null
+++ b/.idea/libraries/Gradle__androidx_vectordrawable_vectordrawable_animated_1_1_0_aar.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Gradle__androidx_versionedparcelable_versionedparcelable_1_1_0_aar.xml b/.idea/libraries/Gradle__androidx_versionedparcelable_versionedparcelable_1_1_0_aar.xml
new file mode 100644
index 0000000..b301bcc
--- /dev/null
+++ b/.idea/libraries/Gradle__androidx_versionedparcelable_versionedparcelable_1_1_0_aar.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Gradle__androidx_viewpager_viewpager_1_0_0_aar.xml b/.idea/libraries/Gradle__androidx_viewpager_viewpager_1_0_0_aar.xml
new file mode 100644
index 0000000..918d22e
--- /dev/null
+++ b/.idea/libraries/Gradle__androidx_viewpager_viewpager_1_0_0_aar.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..37a7509
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..5d466ca
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules/PowerTutor2/PowerTutorII-PowerTutor2.iml b/.idea/modules/PowerTutor2/PowerTutorII-PowerTutor2.iml
new file mode 100644
index 0000000..5f350fb
--- /dev/null
+++ b/.idea/modules/PowerTutor2/PowerTutorII-PowerTutor2.iml
@@ -0,0 +1,116 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ generateDebugSources
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules/PowerTutorII.iml b/.idea/modules/PowerTutorII.iml
new file mode 100644
index 0000000..659a1b4
--- /dev/null
+++ b/.idea/modules/PowerTutorII.iml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml
new file mode 100644
index 0000000..7f68460
--- /dev/null
+++ b/.idea/runConfigurations.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/LICENCE b/LICENCE
deleted file mode 100644
index d3bb00c..0000000
--- a/LICENCE
+++ /dev/null
@@ -1,16 +0,0 @@
-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 .
-
-Please send inquiries to powertutor@umich.edu
diff --git a/Makefile b/Makefile
deleted file mode 100644
index bce0d17..0000000
--- a/Makefile
+++ /dev/null
@@ -1,39 +0,0 @@
-all: package
-
-ANDROID_LIB=android-9.jar
-CLASSPATH=$(ANDROID_LIB):libs/achartengine-1.0.0.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 -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/
diff --git a/PowerTutor2/.gitignore b/PowerTutor2/.gitignore
new file mode 100644
index 0000000..705b8a2
--- /dev/null
+++ b/PowerTutor2/.gitignore
@@ -0,0 +1,38 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
+
+# Flutter/Dart/Pub related
+**/doc/api/
+.dart_tool/
+.flutter-plugins
+.flutter-plugins-dependencies
+.packages
+.pub-cache/
+.pub/
+/build/
+.gradle/
+
+# Web related
+lib/generated_plugin_registrant.dart
+
+# Exceptions to above rules.
+!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
diff --git a/PowerTutor2/build.gradle b/PowerTutor2/build.gradle
new file mode 100644
index 0000000..273ca07
--- /dev/null
+++ b/PowerTutor2/build.gradle
@@ -0,0 +1,30 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 29
+ buildToolsVersion "29.0.3"
+
+ defaultConfig {
+ applicationId "edu.umich.PowerTutor"
+ minSdkVersion 14
+ targetSdkVersion 29
+
+ ndk {
+ moduleName "bindings"
+ }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
+ }
+ }
+}
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ //compile files('libs/achartengine-1.0.0.jar')
+
+ implementation 'androidx.appcompat:appcompat:1.1.0'
+}
diff --git a/PowerTutor2/gradle.properties b/PowerTutor2/gradle.properties
new file mode 100644
index 0000000..c73d239
--- /dev/null
+++ b/PowerTutor2/gradle.properties
@@ -0,0 +1,19 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app's APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Automatically convert third-party libraries to use AndroidX
+android.enableJetifier=true
diff --git a/libs/achartengine-1.0.0.jar b/PowerTutor2/libs/achartengine-1.0.0.jar
similarity index 100%
rename from libs/achartengine-1.0.0.jar
rename to PowerTutor2/libs/achartengine-1.0.0.jar
diff --git a/AndroidManifest.xml b/PowerTutor2/src/main/AndroidManifest.xml
similarity index 95%
rename from AndroidManifest.xml
rename to PowerTutor2/src/main/AndroidManifest.xml
index 74956b0..da0ce76 100644
--- a/AndroidManifest.xml
+++ b/PowerTutor2/src/main/AndroidManifest.xml
@@ -1,7 +1,16 @@
-
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
@@ -82,12 +94,4 @@
android:resource="@xml/widget_info" />
-
-
-
-
-
-
-
-
diff --git a/src/edu/umich/PowerTutor/PowerNotifications.aidl b/PowerTutor2/src/main/aidl/edu/umich/PowerTutor/PowerNotifications.aidl
similarity index 100%
rename from src/edu/umich/PowerTutor/PowerNotifications.aidl
rename to PowerTutor2/src/main/aidl/edu/umich/PowerTutor/PowerNotifications.aidl
diff --git a/src/edu/umich/PowerTutor/service/ICounterService.aidl b/PowerTutor2/src/main/aidl/edu/umich/PowerTutor/service/ICounterService.aidl
similarity index 97%
rename from src/edu/umich/PowerTutor/service/ICounterService.aidl
rename to PowerTutor2/src/main/aidl/edu/umich/PowerTutor/service/ICounterService.aidl
index 96e5b38..531ab56 100644
--- a/src/edu/umich/PowerTutor/service/ICounterService.aidl
+++ b/PowerTutor2/src/main/aidl/edu/umich/PowerTutor/service/ICounterService.aidl
@@ -1,79 +1,79 @@
-/*
-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 .
-
-Please send inquiries to powertutor@umich.edu
-*/
-
-package edu.umich.PowerTutor.service;
-
-interface ICounterService {
- // Returns the name of the components that are being logged.
- String[] getComponents();
-
- // Returns the maximum power usage for each of the components being logged.
- int[] getComponentsMaxPower();
-
- // Returns a bit mask with a 1 in the ith bit if component i doesn't have
- // uid specific information.
- int getNoUidMask();
-
- // Returns the power consumption in mW for component componentId for the last
- // count iterations. uid can be specified to make this data only include a
- // specific user id or you can provide SystemInfo.AID_ALL to request
- // global power state information.
- int[] getComponentHistory(int count, int componentId, int uid);
-
- // Returns the total energy consumption for each component in the same order
- // that the components were returned in getComponents() and in the same order
- // that the components are populated by PhoneSelector.generateComponents().
- //
- // uid can be specified to make the information specific to a single user
- // id or SystemInfo.AID_ALL can be specified to request global information.
- //
- // windowType should be one of Counter.WINDOW_MINUTE, Counter.WINDOW_HOUR,
- // Counter.WINDOW_DAY, Counter.WINDOW_TOTAL to request the window that the
- // energy usage will be calculated over.
- //
- // The returned result is given in mJ.
- long[] getTotals(int uid, int windowType);
-
- // Like getTotals() except that each entry is divided by how long the given
- // uid was running. If SystemInfo.AID_ALL is provided this is effectively
- // like dividing each entry by the window size. (unless PowerTutor hasn't
- // been running that long).
- long[] getMeans(int uid, int windowType);
-
- // Gets the total time that this uid has been running in seconds.
- long getRuntime(int uid, int windowType);
-
- // Returns a byte array representing a serialized array of UidInfo structures.
- // See UidInfo.java for what information is given. Note that members marked
- // as transient are not filled in.
- // Power contributions from component i will be dropped if the ith bit is set
- // in ignoreMask. Providing 0 for ignoreMask will give results for all
- // components.
- //
- // Example Usage:
- // byte[] rawUidInfo = counterService.getUidInfo(windowType);
- // UidInfo[] uidInfos = (UidInfo[])new ObjectInputStream(
- // new ByteArrayInputStream(rawUidInfo)).readObject();
- byte[] getUidInfo(int windowType, int ignoreMask);
-
- // Return miscellaneous data point for the passed uid.
- // Current extras included:
- // OLEDSCORE
- long getUidExtra(String name, int uid);
-}
+/*
+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 .
+
+Please send inquiries to powertutor@umich.edu
+*/
+
+package edu.umich.PowerTutor.service;
+
+interface ICounterService {
+ // Returns the name of the components that are being logged.
+ String[] getComponents();
+
+ // Returns the maximum power usage for each of the components being logged.
+ int[] getComponentsMaxPower();
+
+ // Returns a bit mask with a 1 in the ith bit if component i doesn't have
+ // uid specific information.
+ int getNoUidMask();
+
+ // Returns the power consumption in mW for component componentId for the last
+ // count iterations. uid can be specified to make this data only include a
+ // specific user id or you can provide SystemInfo.AID_ALL to request
+ // global power state information.
+ int[] getComponentHistory(int count, int componentId, int uid);
+
+ // Returns the total energy consumption for each component in the same order
+ // that the components were returned in getComponents() and in the same order
+ // that the components are populated by PhoneSelector.generateComponents().
+ //
+ // uid can be specified to make the information specific to a single user
+ // id or SystemInfo.AID_ALL can be specified to request global information.
+ //
+ // windowType should be one of Counter.WINDOW_MINUTE, Counter.WINDOW_HOUR,
+ // Counter.WINDOW_DAY, Counter.WINDOW_TOTAL to request the window that the
+ // energy usage will be calculated over.
+ //
+ // The returned result is given in mJ.
+ long[] getTotals(int uid, int windowType);
+
+ // Like getTotals() except that each entry is divided by how long the given
+ // uid was running. If SystemInfo.AID_ALL is provided this is effectively
+ // like dividing each entry by the window size. (unless PowerTutor hasn't
+ // been running that long).
+ long[] getMeans(int uid, int windowType);
+
+ // Gets the total time that this uid has been running in seconds.
+ long getRuntime(int uid, int windowType);
+
+ // Returns a byte array representing a serialized array of UidInfo structures.
+ // See UidInfo.java for what information is given. Note that members marked
+ // as transient are not filled in.
+ // Power contributions from component i will be dropped if the ith bit is set
+ // in ignoreMask. Providing 0 for ignoreMask will give results for all
+ // components.
+ //
+ // Example Usage:
+ // byte[] rawUidInfo = counterService.getUidInfo(windowType);
+ // UidInfo[] uidInfos = (UidInfo[])new ObjectInputStream(
+ // new ByteArrayInputStream(rawUidInfo)).readObject();
+ byte[] getUidInfo(int windowType, int ignoreMask);
+
+ // Return miscellaneous data point for the passed uid.
+ // Current extras included:
+ // OLEDSCORE
+ long getUidExtra(String name, int uid);
+}
diff --git a/src/edu/umich/PowerTutor/components/Audio.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/components/Audio.java
similarity index 96%
rename from src/edu/umich/PowerTutor/components/Audio.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/components/Audio.java
index b370b83..0057aef 100644
--- a/src/edu/umich/PowerTutor/components/Audio.java
+++ b/PowerTutor2/src/main/java/edu/umich/PowerTutor/components/Audio.java
@@ -1,188 +1,188 @@
-/*
-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 .
-
-Please send inquiries to powertutor@umich.edu
-*/
-
-package edu.umich.PowerTutor.components;
-
-import edu.umich.PowerTutor.PowerNotifications;
-import edu.umich.PowerTutor.service.IterationData;
-import edu.umich.PowerTutor.service.PowerData;
-import edu.umich.PowerTutor.util.NotificationService;
-import edu.umich.PowerTutor.util.Recycler;
-
-import android.content.Context;
-import android.media.AudioManager;
-
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.util.TreeSet;
-
-/**This class aims to log the audio device status once per log interval*/
-public class Audio extends PowerComponent {
- /**This class is the logger data file corresponding to Audio*/
- public static class AudioData extends PowerData {
- private static Recycler recycler = new Recycler();
-
- public static AudioData obtain() {
- AudioData result = recycler.obtain();
- if(result != null) return result;
- return new AudioData();
- }
-
- @Override
- public void recycle() {
- recycler.recycle(this);
- }
-
- public boolean musicOn;
-
- private AudioData() {
- }
-
- public void init(boolean musicOn) {
- this.musicOn = musicOn;
- }
-
- public void writeLogDataInfo(OutputStreamWriter out) throws IOException {
- out.write("Audio-on " + musicOn + "\n");
- }
- }
-
- private static class MediaData implements Comparable {
- private static Recycler recycler = new Recycler();
-
- public static MediaData obtain() {
- MediaData result = recycler.obtain();
- if(result != null) return result;
- return new MediaData();
- }
-
- public void recycle() {
- recycler.recycle(this);
- }
-
- public int uid;
- public int id;
- public int assignUid;
-
- public int compareTo(Object obj) {
- MediaData x = (MediaData)obj;
- if(uid < x.uid) return -1;
- if(uid > x.uid) return 1;
- if(id < x.id) return -1;
- if(id > x.id) return 1;
- return 0;
- }
-
- public boolean equals(Object obj) {
- MediaData x = (MediaData)obj;
- return uid == x.uid && id == x.id;
- }
- }
-
- private AudioManager audioManager;
- private PowerNotifications audioNotif;
- private TreeSet uidData;
-
- public Audio(Context context) {
- if(NotificationService.available()) {
- uidData = new TreeSet();
- audioNotif = new NotificationService.DefaultReceiver() {
- private int sysUid = -1;
-
- @Override
- public void noteSystemMediaCall(int uid) {
- sysUid = uid;
- }
-
- @Override
- public void noteStartMedia(int uid, int id) {
- MediaData data = MediaData.obtain();
- data.uid = uid;
- data.id = id;
- if(uid == 1000 && sysUid != -1) {
- data.assignUid = sysUid;
- sysUid = -1;
- } else {
- data.assignUid = uid;
- }
- synchronized(uidData) {
- if(!uidData.add(data)) {
- data.recycle();
- }
- }
- }
-
- @Override
- public void noteStopMedia(int uid, int id) {
- MediaData data = MediaData.obtain();
- data.uid = uid;
- data.id = id;
- synchronized(uidData) {
- uidData.remove(data);
- }
- data.recycle();
- }
- };
- NotificationService.addHook(audioNotif);
- }
-
- audioManager = (AudioManager)context.getSystemService(
- Context.AUDIO_SERVICE);
- }
-
- @Override
- protected void onExit() {
- if(audioNotif != null) {
- NotificationService.removeHook(audioNotif);
- }
- }
-
- @Override
- public IterationData calculateIteration(long iteration) {
- IterationData result = IterationData.obtain();
- AudioData data = AudioData.obtain();
- data.init(uidData != null && !uidData.isEmpty() ||
- audioManager.isMusicActive());
- result.setPowerData(data);
-
- if(uidData != null) synchronized(uidData) {
- int last_uid = -1;
- for(MediaData dat : uidData) {
- if(dat.uid != last_uid) {
- AudioData audioPower = AudioData.obtain();
- audioPower.init(true);
- result.addUidPowerData(dat.assignUid, audioPower);
- }
- last_uid = dat.uid;
- }
- }
-
- return result;
- }
-
- @Override
- public boolean hasUidInformation() {
- return audioNotif != null;
- }
-
- @Override
- public String getComponentName() {
- return "Audio";
- }
-}
+/*
+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 .
+
+Please send inquiries to powertutor@umich.edu
+*/
+
+package edu.umich.PowerTutor.components;
+
+import edu.umich.PowerTutor.PowerNotifications;
+import edu.umich.PowerTutor.service.IterationData;
+import edu.umich.PowerTutor.service.PowerData;
+import edu.umich.PowerTutor.util.NotificationService;
+import edu.umich.PowerTutor.util.Recycler;
+
+import android.content.Context;
+import android.media.AudioManager;
+
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.util.TreeSet;
+
+/**This class aims to log the audio device status once per log interval*/
+public class Audio extends PowerComponent {
+ /**This class is the logger data file corresponding to Audio*/
+ public static class AudioData extends PowerData {
+ private static Recycler recycler = new Recycler();
+
+ public static AudioData obtain() {
+ AudioData result = recycler.obtain();
+ if(result != null) return result;
+ return new AudioData();
+ }
+
+ @Override
+ public void recycle() {
+ recycler.recycle(this);
+ }
+
+ public boolean musicOn;
+
+ private AudioData() {
+ }
+
+ public void init(boolean musicOn) {
+ this.musicOn = musicOn;
+ }
+
+ public void writeLogDataInfo(OutputStreamWriter out) throws IOException {
+ out.write("Audio-on " + musicOn + "\n");
+ }
+ }
+
+ private static class MediaData implements Comparable {
+ private static Recycler recycler = new Recycler();
+
+ public static MediaData obtain() {
+ MediaData result = recycler.obtain();
+ if(result != null) return result;
+ return new MediaData();
+ }
+
+ public void recycle() {
+ recycler.recycle(this);
+ }
+
+ public int uid;
+ public int id;
+ public int assignUid;
+
+ public int compareTo(Object obj) {
+ MediaData x = (MediaData)obj;
+ if(uid < x.uid) return -1;
+ if(uid > x.uid) return 1;
+ if(id < x.id) return -1;
+ if(id > x.id) return 1;
+ return 0;
+ }
+
+ public boolean equals(Object obj) {
+ MediaData x = (MediaData)obj;
+ return uid == x.uid && id == x.id;
+ }
+ }
+
+ private AudioManager audioManager;
+ private PowerNotifications audioNotif;
+ private TreeSet uidData;
+
+ public Audio(Context context) {
+ if(NotificationService.available()) {
+ uidData = new TreeSet();
+ audioNotif = new NotificationService.DefaultReceiver() {
+ private int sysUid = -1;
+
+ @Override
+ public void noteSystemMediaCall(int uid) {
+ sysUid = uid;
+ }
+
+ @Override
+ public void noteStartMedia(int uid, int id) {
+ MediaData data = MediaData.obtain();
+ data.uid = uid;
+ data.id = id;
+ if(uid == 1000 && sysUid != -1) {
+ data.assignUid = sysUid;
+ sysUid = -1;
+ } else {
+ data.assignUid = uid;
+ }
+ synchronized(uidData) {
+ if(!uidData.add(data)) {
+ data.recycle();
+ }
+ }
+ }
+
+ @Override
+ public void noteStopMedia(int uid, int id) {
+ MediaData data = MediaData.obtain();
+ data.uid = uid;
+ data.id = id;
+ synchronized(uidData) {
+ uidData.remove(data);
+ }
+ data.recycle();
+ }
+ };
+ NotificationService.addHook(audioNotif);
+ }
+
+ audioManager = (AudioManager)context.getSystemService(
+ Context.AUDIO_SERVICE);
+ }
+
+ @Override
+ protected void onExit() {
+ if(audioNotif != null) {
+ NotificationService.removeHook(audioNotif);
+ }
+ }
+
+ @Override
+ public IterationData calculateIteration(long iteration) {
+ IterationData result = IterationData.obtain();
+ AudioData data = AudioData.obtain();
+ data.init(uidData != null && !uidData.isEmpty() ||
+ audioManager.isMusicActive());
+ result.setPowerData(data);
+
+ if(uidData != null) synchronized(uidData) {
+ int last_uid = -1;
+ for(MediaData dat : uidData) {
+ if(dat.uid != last_uid) {
+ AudioData audioPower = AudioData.obtain();
+ audioPower.init(true);
+ result.addUidPowerData(dat.assignUid, audioPower);
+ }
+ last_uid = dat.uid;
+ }
+ }
+
+ return result;
+ }
+
+ @Override
+ public boolean hasUidInformation() {
+ return audioNotif != null;
+ }
+
+ @Override
+ public String getComponentName() {
+ return "Audio";
+ }
+}
diff --git a/src/edu/umich/PowerTutor/components/CPU.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/components/CPU.java
similarity index 96%
rename from src/edu/umich/PowerTutor/components/CPU.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/components/CPU.java
index 283e991..6ed1eb8 100644
--- a/src/edu/umich/PowerTutor/components/CPU.java
+++ b/PowerTutor2/src/main/java/edu/umich/PowerTutor/components/CPU.java
@@ -1,358 +1,358 @@
-/*
-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 .
-
-Please send inquiries to powertutor@umich.edu
-*/
-
-package edu.umich.PowerTutor.components;
-
-import edu.umich.PowerTutor.phone.PhoneConstants;
-import edu.umich.PowerTutor.service.IterationData;
-import edu.umich.PowerTutor.service.PowerData;
-import edu.umich.PowerTutor.util.Recycler;
-import edu.umich.PowerTutor.util.SystemInfo;
-
-import android.util.Log;
-import android.os.Process;
-import android.os.SystemClock;
-import android.util.SparseArray;
-
-import java.io.BufferedReader;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.lang.reflect.Method;
-import java.lang.reflect.InvocationTargetException;
-
-public class CPU extends PowerComponent {
- public static class CpuData extends PowerData {
- private static Recycler recycler = new Recycler();
-
- public static CpuData obtain() {
- CpuData result = recycler.obtain();
- if(result != null) return result;
- return new CpuData();
- }
-
- @Override
- public void recycle() {
- recycler.recycle(this);
- }
-
- public double sysPerc;
- public double usrPerc;
- public double freq;
-
- private CpuData() {
- }
-
- public void init(double sysPerc, double usrPerc, double freq) {
- this.sysPerc = sysPerc;
- this.usrPerc = usrPerc;
- this.freq = freq;
- }
-
- public void writeLogDataInfo(OutputStreamWriter out) throws IOException {
- StringBuilder res = new StringBuilder();
- res.append("CPU-sys ").append((long)Math.round(sysPerc))
- .append("\nCPU-usr ").append((long)Math.round(usrPerc))
- .append("\nCPU-freq ").append(freq)
- .append("\n");
- out.write(res.toString());
- }
- }
-
- private static final String TAG = "CPU";
- private static final String CPU_FREQ_FILE = "/proc/cpuinfo";
- private static final String STAT_FILE = "/proc/stat";
-
- private CpuStateKeeper cpuState;
- private SparseArray pidStates;
- private SparseArray uidLinks;
-
- private int[] pids;
- private long[] statsBuf;
-
- private PhoneConstants constants;
-
- public CPU(PhoneConstants constants) {
- this.constants = constants;
- cpuState = new CpuStateKeeper(SystemInfo.AID_ALL);
- pidStates = new SparseArray();
- uidLinks = new SparseArray();
- statsBuf = new long[7];
- }
-
- @Override
- public IterationData calculateIteration(long iteration) {
- IterationData result = IterationData.obtain();
-
- SystemInfo sysInfo = SystemInfo.getInstance();
- double freq = readCpuFreq(sysInfo);
- if(freq < 0) {
- Log.w(TAG, "Failed to read cpu frequency");
- return result;
- }
-
- if(!sysInfo.getUsrSysTotalTime(statsBuf)) {
- Log.w(TAG, "Failed to read cpu times");
- return result;
- }
-
- long usrTime = statsBuf[SystemInfo.INDEX_USER_TIME];
- long sysTime = statsBuf[SystemInfo.INDEX_SYS_TIME];
- long totalTime = statsBuf[SystemInfo.INDEX_TOTAL_TIME];
-
- boolean init = cpuState.isInitialized();
- cpuState.updateState(usrTime, sysTime, totalTime, iteration);
-
- if(init) {
- CpuData data = CpuData.obtain();
- data.init(cpuState.getUsrPerc(), cpuState.getSysPerc(), freq);
- result.setPowerData(data);
- }
-
- uidLinks.clear();
- pids = sysInfo.getPids(pids);
- int pidInd = 0;
- if(pids != null) for(int pid : pids) {
- if(pid < 0) {
- break;
- }
-
- CpuStateKeeper pidState;
- if(pidInd < pidStates.size() && pidStates.keyAt(pidInd) == pid) {
- pidState = pidStates.valueAt(pidInd);
- } else {
- int uid = sysInfo.getUidForPid(pid);
- if(uid >= 0) {
- pidState = new CpuStateKeeper(uid);
- pidStates.put(pid, pidState);
- } else {
- /* Assume that this process no longer exists. */
- continue;
- }
- }
- pidInd++;
-
- if(!pidState.isStale(iteration)) {
- /* Nothing much is going on with this pid recently. We'll just
- * assume that it's not using any of the cpu for this iteration.
- */
- pidState.updateIteration(iteration, totalTime);
- } else if(sysInfo.getPidUsrSysTime(pid, statsBuf)) {
- usrTime = statsBuf[SystemInfo.INDEX_USER_TIME];
- sysTime = statsBuf[SystemInfo.INDEX_SYS_TIME];
-
- init = pidState.isInitialized();
- pidState.updateState(usrTime, sysTime, totalTime, iteration);
-
- if(!init) {
- continue;
- }
- }
-
- CpuStateKeeper linkState = uidLinks.get(pidState.getUid());
- if(linkState == null) {
- uidLinks.put(pidState.getUid(), pidState);
- } else {
- linkState.absorb(pidState);
- }
- }
-
- /* Remove processes that are no longer active. */
- for(int i = 0; i < pidStates.size(); i++) {
- if(!pidStates.valueAt(i).isAlive(iteration)) {
- pidStates.remove(pidStates.keyAt(i--));
- }
- }
-
- /* Collect the summed uid information. */
- for(int i = 0; i < uidLinks.size(); i++) {
- int uid = uidLinks.keyAt(i);
- CpuStateKeeper linkState = uidLinks.valueAt(i);
-
- CpuData uidData = CpuData.obtain();
- predictAppUidState(uidData, linkState.getUsrPerc(),
- linkState.getSysPerc(), freq);
- result.addUidPowerData(uid, uidData);
- }
-
- return result;
- }
-
- /* This is the function that is responsible for predicting the cpu frequency
- * state of the individual uid as though it were the only thing running. It
- * simply is finding the lowest frequency that keeps the cpu usage under
- * 70% assuming there is a linear relationship to the cpu utilization at
- * different frequencies.
- */
- private void predictAppUidState(CpuData uidData, double usrPerc,
- double sysPerc, double freq) {
- double[] freqs = constants.cpuFreqs();
- if(usrPerc + sysPerc < 1e-6) {
- /* Don't waste time with the binary search if there is no utilization
- * which will be the case a lot.
- */
- uidData.init(sysPerc, usrPerc, freqs[0]);
- return;
- }
- int lo = 0;
- int hi = freqs.length - 1;
- double perc = sysPerc + usrPerc;
- while(lo < hi) {
- int mid = (lo + hi) / 2;
- double nperc = perc * freq / freqs[mid];
- if(nperc < 70) {
- hi = mid;
- } else {
- lo = mid + 1;
- }
- }
- uidData.init(sysPerc * freq / freqs[lo], usrPerc * freq / freqs[lo],
- freqs[lo]);
- }
-
- private static class CpuStateKeeper {
- private int uid;
- private long iteration;
- private long lastUpdateIteration;
- private long inactiveIterations;
-
- private long lastUsr;
- private long lastSys;
- private long lastTotal;
-
- private long sumUsr;
- private long sumSys;
- private long deltaTotal;
-
- private CpuStateKeeper(int uid) {
- this.uid = uid;
- lastUsr = lastSys = -1;
- lastUpdateIteration = iteration = -1;
- inactiveIterations = 0;
- }
-
- public boolean isInitialized() {
- return lastUsr != -1;
- }
-
- public void updateIteration(long iteration, long totalTime) {
- /* Process is still running but actually reading the cpu utilization has
- * been skipped this iteration to avoid wasting cpu cycles as this process
- * has not been very active recently. */
- sumUsr = 0;
- sumSys = 0;
- deltaTotal = totalTime - lastTotal;
- if(deltaTotal < 1) deltaTotal = 1;
- lastTotal = totalTime;
- this.iteration = iteration;
- }
-
- public void updateState(long usrTime, long sysTime, long totalTime,
- long iteration) {
- sumUsr = usrTime - lastUsr;
- sumSys = sysTime - lastSys;
- deltaTotal = totalTime - lastTotal;
- if(deltaTotal < 1) deltaTotal = 1;
- lastUsr = usrTime;
- lastSys = sysTime;
- lastTotal = totalTime;
- lastUpdateIteration = this.iteration = iteration;
-
- if(getUsrPerc() + getSysPerc() < 0.1) {
- inactiveIterations++;
- } else {
- inactiveIterations = 0;
- }
- }
-
- public int getUid() {
- return uid;
- }
-
- public void absorb(CpuStateKeeper s) {
- sumUsr += s.sumUsr;
- sumSys += s.sumSys;
- }
-
- public double getUsrPerc() {
- return 100.0 * sumUsr / Math.max(sumUsr + sumSys, deltaTotal);
- }
-
- public double getSysPerc() {
- return 100.0 * sumSys / Math.max(sumUsr + sumSys, deltaTotal);
- }
-
- public boolean isAlive(long iteration) {
- return this.iteration == iteration;
- }
-
- public boolean isStale(long iteration) {
- return 1L << (iteration - lastUpdateIteration) >
- inactiveIterations * inactiveIterations;
- }
- }
-
- @Override
- public boolean hasUidInformation() {
- return true;
- }
-
- @Override
- public String getComponentName() {
- return "CPU";
- }
-
- /* Returns the frequency of the processor in Mhz. If the frequency cannot
- * be determined returns a negative value instead.
- */
- private double readCpuFreq(SystemInfo sysInfo) {
- /* Try to read from the /sys/devices file first. If that doesn't work
- * try manually inspecting the /proc/cpuinfo file.
- */
- long cpuFreqKhz = sysInfo.readLongFromFile(
- "/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq");
- if(cpuFreqKhz != -1) {
- return cpuFreqKhz / 1000.0;
- }
-
- FileReader fstream;
- try {
- fstream = new FileReader(CPU_FREQ_FILE);
- } catch (FileNotFoundException e) {
- Log.w(TAG, "Could not read cpu frequency file");
- return -1;
- }
- BufferedReader in = new BufferedReader(fstream, 500);
- String line;
- try {
- while((line = in.readLine()) != null) {
- if(line.startsWith("BogoMIPS")) {
- return Double.parseDouble(line.trim().split("[ :]+")[1]);
- }
- }
- } catch(IOException e) {
- /* Failed to read from the cpu freq file. */
- } catch(NumberFormatException e) {
- /* Frequency not formatted properly as a double. */
- }
- Log.w(TAG, "Failed to read cpu frequency");
- return -1;
- }
-}
+/*
+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 .
+
+Please send inquiries to powertutor@umich.edu
+*/
+
+package edu.umich.PowerTutor.components;
+
+import edu.umich.PowerTutor.phone.PhoneConstants;
+import edu.umich.PowerTutor.service.IterationData;
+import edu.umich.PowerTutor.service.PowerData;
+import edu.umich.PowerTutor.util.Recycler;
+import edu.umich.PowerTutor.util.SystemInfo;
+
+import android.util.Log;
+import android.os.Process;
+import android.os.SystemClock;
+import android.util.SparseArray;
+
+import java.io.BufferedReader;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+
+public class CPU extends PowerComponent {
+ public static class CpuData extends PowerData {
+ private static Recycler recycler = new Recycler();
+
+ public static CpuData obtain() {
+ CpuData result = recycler.obtain();
+ if(result != null) return result;
+ return new CpuData();
+ }
+
+ @Override
+ public void recycle() {
+ recycler.recycle(this);
+ }
+
+ public double sysPerc;
+ public double usrPerc;
+ public double freq;
+
+ private CpuData() {
+ }
+
+ public void init(double sysPerc, double usrPerc, double freq) {
+ this.sysPerc = sysPerc;
+ this.usrPerc = usrPerc;
+ this.freq = freq;
+ }
+
+ public void writeLogDataInfo(OutputStreamWriter out) throws IOException {
+ StringBuilder res = new StringBuilder();
+ res.append("CPU-sys ").append((long)Math.round(sysPerc))
+ .append("\nCPU-usr ").append((long)Math.round(usrPerc))
+ .append("\nCPU-freq ").append(freq)
+ .append("\n");
+ out.write(res.toString());
+ }
+ }
+
+ private static final String TAG = "CPU";
+ private static final String CPU_FREQ_FILE = "/proc/cpuinfo";
+ private static final String STAT_FILE = "/proc/stat";
+
+ private CpuStateKeeper cpuState;
+ private SparseArray pidStates;
+ private SparseArray uidLinks;
+
+ private int[] pids;
+ private long[] statsBuf;
+
+ private PhoneConstants constants;
+
+ public CPU(PhoneConstants constants) {
+ this.constants = constants;
+ cpuState = new CpuStateKeeper(SystemInfo.AID_ALL);
+ pidStates = new SparseArray();
+ uidLinks = new SparseArray();
+ statsBuf = new long[7];
+ }
+
+ @Override
+ public IterationData calculateIteration(long iteration) {
+ IterationData result = IterationData.obtain();
+
+ SystemInfo sysInfo = SystemInfo.getInstance();
+ double freq = readCpuFreq(sysInfo);
+ if(freq < 0) {
+ Log.w(TAG, "Failed to read cpu frequency");
+ return result;
+ }
+
+ if(!sysInfo.getUsrSysTotalTime(statsBuf)) {
+ Log.w(TAG, "Failed to read cpu times");
+ return result;
+ }
+
+ long usrTime = statsBuf[SystemInfo.INDEX_USER_TIME];
+ long sysTime = statsBuf[SystemInfo.INDEX_SYS_TIME];
+ long totalTime = statsBuf[SystemInfo.INDEX_TOTAL_TIME];
+
+ boolean init = cpuState.isInitialized();
+ cpuState.updateState(usrTime, sysTime, totalTime, iteration);
+
+ if(init) {
+ CpuData data = CpuData.obtain();
+ data.init(cpuState.getUsrPerc(), cpuState.getSysPerc(), freq);
+ result.setPowerData(data);
+ }
+
+ uidLinks.clear();
+ pids = sysInfo.getPids(pids);
+ int pidInd = 0;
+ if(pids != null) for(int pid : pids) {
+ if(pid < 0) {
+ break;
+ }
+
+ CpuStateKeeper pidState;
+ if(pidInd < pidStates.size() && pidStates.keyAt(pidInd) == pid) {
+ pidState = pidStates.valueAt(pidInd);
+ } else {
+ int uid = sysInfo.getUidForPid(pid);
+ if(uid >= 0) {
+ pidState = new CpuStateKeeper(uid);
+ pidStates.put(pid, pidState);
+ } else {
+ /* Assume that this process no longer exists. */
+ continue;
+ }
+ }
+ pidInd++;
+
+ if(!pidState.isStale(iteration)) {
+ /* Nothing much is going on with this pid recently. We'll just
+ * assume that it's not using any of the cpu for this iteration.
+ */
+ pidState.updateIteration(iteration, totalTime);
+ } else if(sysInfo.getPidUsrSysTime(pid, statsBuf)) {
+ usrTime = statsBuf[SystemInfo.INDEX_USER_TIME];
+ sysTime = statsBuf[SystemInfo.INDEX_SYS_TIME];
+
+ init = pidState.isInitialized();
+ pidState.updateState(usrTime, sysTime, totalTime, iteration);
+
+ if(!init) {
+ continue;
+ }
+ }
+
+ CpuStateKeeper linkState = uidLinks.get(pidState.getUid());
+ if(linkState == null) {
+ uidLinks.put(pidState.getUid(), pidState);
+ } else {
+ linkState.absorb(pidState);
+ }
+ }
+
+ /* Remove processes that are no longer active. */
+ for(int i = 0; i < pidStates.size(); i++) {
+ if(!pidStates.valueAt(i).isAlive(iteration)) {
+ pidStates.remove(pidStates.keyAt(i--));
+ }
+ }
+
+ /* Collect the summed uid information. */
+ for(int i = 0; i < uidLinks.size(); i++) {
+ int uid = uidLinks.keyAt(i);
+ CpuStateKeeper linkState = uidLinks.valueAt(i);
+
+ CpuData uidData = CpuData.obtain();
+ predictAppUidState(uidData, linkState.getUsrPerc(),
+ linkState.getSysPerc(), freq);
+ result.addUidPowerData(uid, uidData);
+ }
+
+ return result;
+ }
+
+ /* This is the function that is responsible for predicting the cpu frequency
+ * state of the individual uid as though it were the only thing running. It
+ * simply is finding the lowest frequency that keeps the cpu usage under
+ * 70% assuming there is a linear relationship to the cpu utilization at
+ * different frequencies.
+ */
+ private void predictAppUidState(CpuData uidData, double usrPerc,
+ double sysPerc, double freq) {
+ double[] freqs = constants.cpuFreqs();
+ if(usrPerc + sysPerc < 1e-6) {
+ /* Don't waste time with the binary search if there is no utilization
+ * which will be the case a lot.
+ */
+ uidData.init(sysPerc, usrPerc, freqs[0]);
+ return;
+ }
+ int lo = 0;
+ int hi = freqs.length - 1;
+ double perc = sysPerc + usrPerc;
+ while(lo < hi) {
+ int mid = (lo + hi) / 2;
+ double nperc = perc * freq / freqs[mid];
+ if(nperc < 70) {
+ hi = mid;
+ } else {
+ lo = mid + 1;
+ }
+ }
+ uidData.init(sysPerc * freq / freqs[lo], usrPerc * freq / freqs[lo],
+ freqs[lo]);
+ }
+
+ private static class CpuStateKeeper {
+ private int uid;
+ private long iteration;
+ private long lastUpdateIteration;
+ private long inactiveIterations;
+
+ private long lastUsr;
+ private long lastSys;
+ private long lastTotal;
+
+ private long sumUsr;
+ private long sumSys;
+ private long deltaTotal;
+
+ private CpuStateKeeper(int uid) {
+ this.uid = uid;
+ lastUsr = lastSys = -1;
+ lastUpdateIteration = iteration = -1;
+ inactiveIterations = 0;
+ }
+
+ public boolean isInitialized() {
+ return lastUsr != -1;
+ }
+
+ public void updateIteration(long iteration, long totalTime) {
+ /* Process is still running but actually reading the cpu utilization has
+ * been skipped this iteration to avoid wasting cpu cycles as this process
+ * has not been very active recently. */
+ sumUsr = 0;
+ sumSys = 0;
+ deltaTotal = totalTime - lastTotal;
+ if(deltaTotal < 1) deltaTotal = 1;
+ lastTotal = totalTime;
+ this.iteration = iteration;
+ }
+
+ public void updateState(long usrTime, long sysTime, long totalTime,
+ long iteration) {
+ sumUsr = usrTime - lastUsr;
+ sumSys = sysTime - lastSys;
+ deltaTotal = totalTime - lastTotal;
+ if(deltaTotal < 1) deltaTotal = 1;
+ lastUsr = usrTime;
+ lastSys = sysTime;
+ lastTotal = totalTime;
+ lastUpdateIteration = this.iteration = iteration;
+
+ if(getUsrPerc() + getSysPerc() < 0.1) {
+ inactiveIterations++;
+ } else {
+ inactiveIterations = 0;
+ }
+ }
+
+ public int getUid() {
+ return uid;
+ }
+
+ public void absorb(CpuStateKeeper s) {
+ sumUsr += s.sumUsr;
+ sumSys += s.sumSys;
+ }
+
+ public double getUsrPerc() {
+ return 100.0 * sumUsr / Math.max(sumUsr + sumSys, deltaTotal);
+ }
+
+ public double getSysPerc() {
+ return 100.0 * sumSys / Math.max(sumUsr + sumSys, deltaTotal);
+ }
+
+ public boolean isAlive(long iteration) {
+ return this.iteration == iteration;
+ }
+
+ public boolean isStale(long iteration) {
+ return 1L << (iteration - lastUpdateIteration) >
+ inactiveIterations * inactiveIterations;
+ }
+ }
+
+ @Override
+ public boolean hasUidInformation() {
+ return true;
+ }
+
+ @Override
+ public String getComponentName() {
+ return "CPU";
+ }
+
+ /* Returns the frequency of the processor in Mhz. If the frequency cannot
+ * be determined returns a negative value instead.
+ */
+ private double readCpuFreq(SystemInfo sysInfo) {
+ /* Try to read from the /sys/devices file first. If that doesn't work
+ * try manually inspecting the /proc/cpuinfo file.
+ */
+ long cpuFreqKhz = sysInfo.readLongFromFile(
+ "/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq");
+ if(cpuFreqKhz != -1) {
+ return cpuFreqKhz / 1000.0;
+ }
+
+ FileReader fstream;
+ try {
+ fstream = new FileReader(CPU_FREQ_FILE);
+ } catch (FileNotFoundException e) {
+ Log.w(TAG, "Could not read cpu frequency file");
+ return -1;
+ }
+ BufferedReader in = new BufferedReader(fstream, 500);
+ String line;
+ try {
+ while((line = in.readLine()) != null) {
+ if(line.startsWith("BogoMIPS")) {
+ return Double.parseDouble(line.trim().split("[ :]+")[1]);
+ }
+ }
+ } catch(IOException e) {
+ /* Failed to read from the cpu freq file. */
+ } catch(NumberFormatException e) {
+ /* Frequency not formatted properly as a double. */
+ }
+ Log.w(TAG, "Failed to read cpu frequency");
+ return -1;
+ }
+}
diff --git a/src/edu/umich/PowerTutor/components/GPS.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/components/GPS.java
similarity index 97%
rename from src/edu/umich/PowerTutor/components/GPS.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/components/GPS.java
index c350819..3f8882b 100644
--- a/src/edu/umich/PowerTutor/components/GPS.java
+++ b/PowerTutor2/src/main/java/edu/umich/PowerTutor/components/GPS.java
@@ -1,456 +1,456 @@
-/*
-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 .
-
-Please send inquiries to powertutor@umich.edu
-*/
-
-package edu.umich.PowerTutor.components;
-
-import edu.umich.PowerTutor.PowerNotifications;
-import edu.umich.PowerTutor.phone.PhoneConstants;
-import edu.umich.PowerTutor.service.IterationData;
-import edu.umich.PowerTutor.service.PowerData;
-import edu.umich.PowerTutor.util.NotificationService;
-import edu.umich.PowerTutor.util.Recycler;
-import edu.umich.PowerTutor.util.SystemInfo;
-
-import android.content.Context;
-import android.location.GpsSatellite;
-import android.location.GpsStatus;
-import android.location.LocationManager;
-import android.os.Build;
-import android.os.SystemClock;
-import android.util.Log;
-import android.util.SparseArray;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Type;
-import java.util.Map;
-
-public class GPS extends PowerComponent {
- public static class GpsData extends PowerData {
- private static Recycler recycler = new Recycler();
-
- public static GpsData obtain() {
- GpsData result = recycler.obtain();
- if(result != null) return result;
- return new GpsData();
- }
-
- /* The time in seconds since the last iteration of data. */
- public double[] stateTimes;
- /* The number of satellites. This number is only available while the GPS is
- * in the on state. Otherwise it is 0.
- */
- public int satellites;
-
- private GpsData() {
- stateTimes = new double[GPS.POWER_STATES];
- }
-
- public void init(double[] stateTimes, int satellites) {
- for(int i = 0; i < GPS.POWER_STATES; i++) {
- this.stateTimes[i] = stateTimes[i];
- }
- this.satellites = satellites;
- }
-
- @Override
- public void recycle() {
- recycler.recycle(this);
- }
-
- @Override
- public void writeLogDataInfo(OutputStreamWriter out) throws IOException {
- StringBuilder res = new StringBuilder();
- res.append("GPS-state-times");
- for(int i = 0; i < GPS.POWER_STATES; i++) {
- res.append(" ").append(stateTimes[i]);
- }
- res.append("\nGPS-sattelites ").append(satellites).append("\n");
- out.write(res.toString());
- }
- }
-
- public static final int POWER_STATES = 3;
- public static final int POWER_STATE_OFF = 0;
- public static final int POWER_STATE_SLEEP = 1;
- public static final int POWER_STATE_ON = 2;
- public static final String[] POWER_STATE_NAMES = {"OFF", "SLEEP", "ON"};
-
- private static final String TAG = "GPS";
-
- private static final int HOOK_LIBGPS = 1;
- private static final int HOOK_GPS_STATUS_LISTENER = 2;
- private static final int HOOK_NOTIFICATIONS = 4;
- private static final int HOOK_TIMER = 8;
-
- /* A named pipe written to by the hacked libgps library. */
- private static String HOOK_GPS_STATUS_FILE = "/data/misc/gps.status";
-
- private GpsStatus.Listener gpsListener;
- private Thread statusThread;
- private PowerNotifications notificationReceiver;
-
- private Context context;
- private LocationManager locationManager;
- private GpsStatus lastStatus;
- private boolean hasUidInfo;
- private long sleepTime;
- private long lastTime;
-
- private GpsStateKeeper gpsState;
- private SparseArray uidStates;
-
- private static final int GPS_STATUS_SESSION_BEGIN = 1;
- private static final int GPS_STATUS_SESSION_END = 2;
- private static final int GPS_STATUS_ENGINE_ON = 3;
- private static final int GPS_STATUS_ENGINE_OFF = 4;
-
- public GPS(Context context, PhoneConstants constants) {
- this.context = context;
- uidStates = new SparseArray();
- sleepTime = (long)Math.round(1000.0 * constants.gpsSleepTime());
-
- hasUidInfo = NotificationService.available();
-
- int hookMethod = 0;
- final File gpsStatusFile = new File(HOOK_GPS_STATUS_FILE);
- if(gpsStatusFile.exists()) {
- /* The libgps hack appears to be available. Let's use this to gather
- * our status updates from the GPS.
- */
- hookMethod = HOOK_LIBGPS;
- } else {
- /* We can always use the status listener hook and perhaps the notification
- * hook if we are running eclaire or higher and the notification hook
- * is installed. We can only do this on eclaire or higher because it
- * wasn't until eclaire that they fixed a bug where they didn't maintain
- * a wakelock while the gps engine was on.
- */
- hookMethod = HOOK_GPS_STATUS_LISTENER;
- try {
- if(NotificationService.available() &&
- Integer.parseInt(Build.VERSION.SDK) >= 5 /* eclaire or higher */) {
- hookMethod |= HOOK_NOTIFICATIONS;
- }
- } catch(NumberFormatException e) {
- Log.w(TAG, "Could not parse sdk version: " + Build.VERSION.SDK);
- }
- }
- /* If we don't have a way of getting the off<->sleep transitions through
- * notifications let's just use a timer and simulat the state of the gps
- * instead.
- */
- if((hookMethod & (HOOK_LIBGPS | HOOK_NOTIFICATIONS)) == 0) {
- hookMethod |= HOOK_TIMER;
- }
-
- /* Create the object that keeps track of the physical GPS state. */
- gpsState = new GpsStateKeeper(hookMethod, sleepTime);
-
- /* No matter what we are going to register a GpsStatus listener so that we
- * can get the satellite count. Also if anything goes wrong with the
- * libgps hook we will revert to using this.
- */
- locationManager = (LocationManager)
- context.getSystemService(Context.LOCATION_SERVICE);
- gpsListener = new GpsStatus.Listener() {
- public void onGpsStatusChanged(int event){
- if(event == GpsStatus.GPS_EVENT_STARTED) {
- gpsState.updateEvent(GPS_STATUS_SESSION_BEGIN,
- HOOK_GPS_STATUS_LISTENER);
- } else if(event == GpsStatus.GPS_EVENT_STOPPED) {
- gpsState.updateEvent(GPS_STATUS_SESSION_END,
- HOOK_GPS_STATUS_LISTENER);
- }
- synchronized(GPS.this) {
- lastStatus = locationManager.getGpsStatus(lastStatus);
- }
- }
- };
- locationManager.addGpsStatusListener(gpsListener);
-
- /* No matter what we register a notification service listener as well so
- * that we can get uid information if it's available.
- */
- if(hasUidInfo) {
- notificationReceiver = new NotificationService.DefaultReceiver() {
- public void noteStartWakelock(int uid, String name, int type) {
- if(uid == SystemInfo.AID_SYSTEM &&
- "GpsLocationProvider".equals(name)) {
- gpsState.updateEvent(GPS_STATUS_ENGINE_ON, HOOK_NOTIFICATIONS);
- }
- }
-
- public void noteStopWakelock(int uid, String name, int type) {
- if(uid == SystemInfo.AID_SYSTEM &&
- "GpsLocationProvider".equals(name)) {
- gpsState.updateEvent(GPS_STATUS_ENGINE_OFF, HOOK_NOTIFICATIONS);
- }
- }
-
- public void noteStartGps(int uid) {
- updateUidEvent(uid, GPS_STATUS_SESSION_BEGIN, HOOK_NOTIFICATIONS);
- }
-
- public void noteStopGps(int uid) {
- updateUidEvent(uid, GPS_STATUS_SESSION_END, HOOK_NOTIFICATIONS);
- }
- };
- NotificationService.addHook(notificationReceiver);
- }
-
- if(gpsStatusFile.exists()) {
- /* Start a thread to read from the named pipe and feed us status updates.
- */
- statusThread = new Thread() {
- public void run() {
- try {
- java.io.FileInputStream fin =
- new java.io.FileInputStream(gpsStatusFile);
- for(int event = fin.read(); !interrupted() && event != -1;
- event = fin.read()) {
- gpsState.updateEvent(event, HOOK_LIBGPS);
- }
- } catch(IOException e) {
- e.printStackTrace();
- }
- if(!interrupted()) {
- // TODO: Have this instead just switch to use different hooks.
- Log.w(TAG, "GPS status thread exited. " +
- "No longer gathering gps data.");
- }
- }
- };
- statusThread.start();
- }
- }
-
- private void updateUidEvent(int uid, int event, int source) {
- synchronized(uidStates) {
- GpsStateKeeper state = uidStates.get(uid);
- if(state == null) {
- state = new GpsStateKeeper(HOOK_NOTIFICATIONS | HOOK_TIMER, sleepTime,
- lastTime);
- uidStates.put(uid, state);
- }
- state.updateEvent(event, source);
- }
- }
-
- @Override
- protected void onExit() {
- if(gpsListener != null) {
- locationManager.removeGpsStatusListener(gpsListener);
- }
- if(statusThread != null) {
- statusThread.interrupt();
- }
- if(notificationReceiver != null) {
- NotificationService.removeHook(notificationReceiver);
- }
- super.onExit();
- }
-
- @Override
- public IterationData calculateIteration(long iteration) {
- IterationData result = IterationData.obtain();
-
- /* Get the number of satellites that were available in the last update. */
- int satellites = 0;
- synchronized(this) {
- if(lastStatus != null) {
- for(GpsSatellite satellite : lastStatus.getSatellites()) {
- satellites++;
- }
- }
- }
-
- /* Get the power data for the physical gps device. */
- GpsData power = GpsData.obtain();
- synchronized(gpsState) {
- double[] stateTimes = gpsState.getStateTimesLocked();
- int curState = gpsState.getCurrentStateLocked();
- power.init(stateTimes, curState == POWER_STATE_ON ? satellites : 0);
- gpsState.resetTimesLocked();
- }
- result.setPowerData(power);
-
- /* Get the power data for each uid if we have information on it. */
- if(hasUidInfo) synchronized(uidStates) {
- lastTime = beginTime + iterationInterval * iteration;
- for(int i = 0; i < uidStates.size(); i++) {
- int uid = uidStates.keyAt(i);
- GpsStateKeeper state = uidStates.valueAt(i);
-
- double[] stateTimes = state.getStateTimesLocked();
- int curState = state.getCurrentStateLocked();
- GpsData uidPower = GpsData.obtain();
- uidPower.init(stateTimes, curState == POWER_STATE_ON ? satellites : 0);
- state.resetTimesLocked();
-
- result.addUidPowerData(uid, uidPower);
-
- /* Remove state information for uids no longer using the gps. */
- if(curState == POWER_STATE_OFF) {
- uidStates.remove(uid);
- i--;
- }
- }
- }
-
- return result;
- }
-
- @Override
- public boolean hasUidInformation() {
- return hasUidInfo;
- }
-
- /* This class is used to maintain the actual GPS state in addition to
- * simulating individual uid states.
- */
- private static class GpsStateKeeper {
- private double[] stateTimes;
- private long lastTime;
- private int curState;
-
- /* The sum of whatever hook sources are valid. See the HOOK_ constants. */
- private int hookMask;
- /* The time that the GPS hardware should turn off. This is only used
- * if HOOK_TIMER is in the hookMask.
- */
- private long offTime;
- /* Gives the time that the GPS stays in the sleep state after the session
- * has ended in milliseconds.
- */
- private long sleepTime;
-
- public GpsStateKeeper(int hookMask, long sleepTime) {
- this(hookMask, sleepTime, SystemClock.elapsedRealtime());
- }
-
- public GpsStateKeeper(int hookMask, long sleepTime, long lastTime) {
- this.hookMask = hookMask;
- this.sleepTime = sleepTime; /* This isn't required if HOOK_TIEMR is not
- * set. */
- this.lastTime = lastTime;
- stateTimes = new double[POWER_STATES];
- curState = POWER_STATE_OFF;
- offTime = -1;
- }
-
- /* Make sure that you have a lock on this before calling. */
- public double[] getStateTimesLocked() {
- updateTimesLocked();
-
- /* Let's normalize the times so that power measurements are consistent. */
- double total = 0;
- for(int i = 0; i < POWER_STATES; i++) {
- total += stateTimes[i];
- }
- if(total == 0) total = 1;
- for(int i = 0; i < POWER_STATES; i++) {
- stateTimes[i] /= total;
- }
-
- return stateTimes;
- }
-
- public void resetTimesLocked() {
- for(int i = 0; i < POWER_STATES; i++) {
- stateTimes[i] = 0;
- }
- }
-
- public int getCurrentStateLocked() {
- return curState;
- }
-
- /* Make sure that you have a lock on this before calling. */
- private void updateTimesLocked() {
- /* Update the time we were in the previous state. */
- long curTime = SystemClock.elapsedRealtime();
-
- /* Check if the GPS has gone to sleep as a result of a timer. */
- if((hookMask & HOOK_TIMER) != 0 && offTime != -1 &&
- offTime < curTime) {
- stateTimes[curState] += (offTime - lastTime) / 1000.0;
- curState = POWER_STATE_OFF;
- offTime = -1;
- }
-
- /* Update the amount of time that we've been in the current state. */
- stateTimes[curState] += (curTime - lastTime) / 1000.0;
- lastTime = curTime;
- }
-
- /* When a hook source gets an event it should report it to updateEvent.
- * The only exception is HOOK_TIMER which is handled within this class
- * itself.
- */
- public void updateEvent(int event, int source) {
- synchronized(this) {
- if((hookMask & source) == 0) {
- /* We are not using this hook source, ignore. */
- return;
- }
-
- updateTimesLocked();
- int oldState = curState;
- switch(event) {
- case GPS_STATUS_SESSION_BEGIN:
- curState = POWER_STATE_ON;
- break;
- case GPS_STATUS_SESSION_END:
- if(curState == POWER_STATE_ON) {
- curState = POWER_STATE_SLEEP;
- }
- break;
- case GPS_STATUS_ENGINE_ON:
- if(curState == POWER_STATE_OFF) {
- curState = POWER_STATE_SLEEP;
- }
- break;
- case GPS_STATUS_ENGINE_OFF:
- curState = POWER_STATE_OFF;
- break;
- default:
- Log.w(TAG, "Unknown GPS event captured");
- }
- if(curState != oldState) {
- if(oldState == POWER_STATE_ON && curState == POWER_STATE_SLEEP) {
- offTime = SystemClock.elapsedRealtime() + sleepTime;
- } else {
- /* Any other state transition should reset the off timer. */
- offTime = -1;
- }
- }
- }
- }
- }
-
- @Override
- public String getComponentName() {
- return "GPS";
- }
-}
-
+/*
+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 .
+
+Please send inquiries to powertutor@umich.edu
+*/
+
+package edu.umich.PowerTutor.components;
+
+import edu.umich.PowerTutor.PowerNotifications;
+import edu.umich.PowerTutor.phone.PhoneConstants;
+import edu.umich.PowerTutor.service.IterationData;
+import edu.umich.PowerTutor.service.PowerData;
+import edu.umich.PowerTutor.util.NotificationService;
+import edu.umich.PowerTutor.util.Recycler;
+import edu.umich.PowerTutor.util.SystemInfo;
+
+import android.content.Context;
+import android.location.GpsSatellite;
+import android.location.GpsStatus;
+import android.location.LocationManager;
+import android.os.Build;
+import android.os.SystemClock;
+import android.util.Log;
+import android.util.SparseArray;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.util.Map;
+
+public class GPS extends PowerComponent {
+ public static class GpsData extends PowerData {
+ private static Recycler recycler = new Recycler();
+
+ public static GpsData obtain() {
+ GpsData result = recycler.obtain();
+ if(result != null) return result;
+ return new GpsData();
+ }
+
+ /* The time in seconds since the last iteration of data. */
+ public double[] stateTimes;
+ /* The number of satellites. This number is only available while the GPS is
+ * in the on state. Otherwise it is 0.
+ */
+ public int satellites;
+
+ private GpsData() {
+ stateTimes = new double[GPS.POWER_STATES];
+ }
+
+ public void init(double[] stateTimes, int satellites) {
+ for(int i = 0; i < GPS.POWER_STATES; i++) {
+ this.stateTimes[i] = stateTimes[i];
+ }
+ this.satellites = satellites;
+ }
+
+ @Override
+ public void recycle() {
+ recycler.recycle(this);
+ }
+
+ @Override
+ public void writeLogDataInfo(OutputStreamWriter out) throws IOException {
+ StringBuilder res = new StringBuilder();
+ res.append("GPS-state-times");
+ for(int i = 0; i < GPS.POWER_STATES; i++) {
+ res.append(" ").append(stateTimes[i]);
+ }
+ res.append("\nGPS-sattelites ").append(satellites).append("\n");
+ out.write(res.toString());
+ }
+ }
+
+ public static final int POWER_STATES = 3;
+ public static final int POWER_STATE_OFF = 0;
+ public static final int POWER_STATE_SLEEP = 1;
+ public static final int POWER_STATE_ON = 2;
+ public static final String[] POWER_STATE_NAMES = {"OFF", "SLEEP", "ON"};
+
+ private static final String TAG = "GPS";
+
+ private static final int HOOK_LIBGPS = 1;
+ private static final int HOOK_GPS_STATUS_LISTENER = 2;
+ private static final int HOOK_NOTIFICATIONS = 4;
+ private static final int HOOK_TIMER = 8;
+
+ /* A named pipe written to by the hacked libgps library. */
+ private static String HOOK_GPS_STATUS_FILE = "/data/misc/gps.status";
+
+ private GpsStatus.Listener gpsListener;
+ private Thread statusThread;
+ private PowerNotifications notificationReceiver;
+
+ private Context context;
+ private LocationManager locationManager;
+ private GpsStatus lastStatus;
+ private boolean hasUidInfo;
+ private long sleepTime;
+ private long lastTime;
+
+ private GpsStateKeeper gpsState;
+ private SparseArray uidStates;
+
+ private static final int GPS_STATUS_SESSION_BEGIN = 1;
+ private static final int GPS_STATUS_SESSION_END = 2;
+ private static final int GPS_STATUS_ENGINE_ON = 3;
+ private static final int GPS_STATUS_ENGINE_OFF = 4;
+
+ public GPS(Context context, PhoneConstants constants) {
+ this.context = context;
+ uidStates = new SparseArray();
+ sleepTime = (long)Math.round(1000.0 * constants.gpsSleepTime());
+
+ hasUidInfo = NotificationService.available();
+
+ int hookMethod = 0;
+ final File gpsStatusFile = new File(HOOK_GPS_STATUS_FILE);
+ if(gpsStatusFile.exists()) {
+ /* The libgps hack appears to be available. Let's use this to gather
+ * our status updates from the GPS.
+ */
+ hookMethod = HOOK_LIBGPS;
+ } else {
+ /* We can always use the status listener hook and perhaps the notification
+ * hook if we are running eclaire or higher and the notification hook
+ * is installed. We can only do this on eclaire or higher because it
+ * wasn't until eclaire that they fixed a bug where they didn't maintain
+ * a wakelock while the gps engine was on.
+ */
+ hookMethod = HOOK_GPS_STATUS_LISTENER;
+ try {
+ if(NotificationService.available() &&
+ Integer.parseInt(Build.VERSION.SDK) >= 5 /* eclaire or higher */) {
+ hookMethod |= HOOK_NOTIFICATIONS;
+ }
+ } catch(NumberFormatException e) {
+ Log.w(TAG, "Could not parse sdk version: " + Build.VERSION.SDK);
+ }
+ }
+ /* If we don't have a way of getting the off<->sleep transitions through
+ * notifications let's just use a timer and simulat the state of the gps
+ * instead.
+ */
+ if((hookMethod & (HOOK_LIBGPS | HOOK_NOTIFICATIONS)) == 0) {
+ hookMethod |= HOOK_TIMER;
+ }
+
+ /* Create the object that keeps track of the physical GPS state. */
+ gpsState = new GpsStateKeeper(hookMethod, sleepTime);
+
+ /* No matter what we are going to register a GpsStatus listener so that we
+ * can get the satellite count. Also if anything goes wrong with the
+ * libgps hook we will revert to using this.
+ */
+ locationManager = (LocationManager)
+ context.getSystemService(Context.LOCATION_SERVICE);
+ gpsListener = new GpsStatus.Listener() {
+ public void onGpsStatusChanged(int event){
+ if(event == GpsStatus.GPS_EVENT_STARTED) {
+ gpsState.updateEvent(GPS_STATUS_SESSION_BEGIN,
+ HOOK_GPS_STATUS_LISTENER);
+ } else if(event == GpsStatus.GPS_EVENT_STOPPED) {
+ gpsState.updateEvent(GPS_STATUS_SESSION_END,
+ HOOK_GPS_STATUS_LISTENER);
+ }
+ synchronized(GPS.this) {
+ lastStatus = locationManager.getGpsStatus(lastStatus);
+ }
+ }
+ };
+ locationManager.addGpsStatusListener(gpsListener);
+
+ /* No matter what we register a notification service listener as well so
+ * that we can get uid information if it's available.
+ */
+ if(hasUidInfo) {
+ notificationReceiver = new NotificationService.DefaultReceiver() {
+ public void noteStartWakelock(int uid, String name, int type) {
+ if(uid == SystemInfo.AID_SYSTEM &&
+ "GpsLocationProvider".equals(name)) {
+ gpsState.updateEvent(GPS_STATUS_ENGINE_ON, HOOK_NOTIFICATIONS);
+ }
+ }
+
+ public void noteStopWakelock(int uid, String name, int type) {
+ if(uid == SystemInfo.AID_SYSTEM &&
+ "GpsLocationProvider".equals(name)) {
+ gpsState.updateEvent(GPS_STATUS_ENGINE_OFF, HOOK_NOTIFICATIONS);
+ }
+ }
+
+ public void noteStartGps(int uid) {
+ updateUidEvent(uid, GPS_STATUS_SESSION_BEGIN, HOOK_NOTIFICATIONS);
+ }
+
+ public void noteStopGps(int uid) {
+ updateUidEvent(uid, GPS_STATUS_SESSION_END, HOOK_NOTIFICATIONS);
+ }
+ };
+ NotificationService.addHook(notificationReceiver);
+ }
+
+ if(gpsStatusFile.exists()) {
+ /* Start a thread to read from the named pipe and feed us status updates.
+ */
+ statusThread = new Thread() {
+ public void run() {
+ try {
+ java.io.FileInputStream fin =
+ new java.io.FileInputStream(gpsStatusFile);
+ for(int event = fin.read(); !interrupted() && event != -1;
+ event = fin.read()) {
+ gpsState.updateEvent(event, HOOK_LIBGPS);
+ }
+ } catch(IOException e) {
+ e.printStackTrace();
+ }
+ if(!interrupted()) {
+ // TODO: Have this instead just switch to use different hooks.
+ Log.w(TAG, "GPS status thread exited. " +
+ "No longer gathering gps data.");
+ }
+ }
+ };
+ statusThread.start();
+ }
+ }
+
+ private void updateUidEvent(int uid, int event, int source) {
+ synchronized(uidStates) {
+ GpsStateKeeper state = uidStates.get(uid);
+ if(state == null) {
+ state = new GpsStateKeeper(HOOK_NOTIFICATIONS | HOOK_TIMER, sleepTime,
+ lastTime);
+ uidStates.put(uid, state);
+ }
+ state.updateEvent(event, source);
+ }
+ }
+
+ @Override
+ protected void onExit() {
+ if(gpsListener != null) {
+ locationManager.removeGpsStatusListener(gpsListener);
+ }
+ if(statusThread != null) {
+ statusThread.interrupt();
+ }
+ if(notificationReceiver != null) {
+ NotificationService.removeHook(notificationReceiver);
+ }
+ super.onExit();
+ }
+
+ @Override
+ public IterationData calculateIteration(long iteration) {
+ IterationData result = IterationData.obtain();
+
+ /* Get the number of satellites that were available in the last update. */
+ int satellites = 0;
+ synchronized(this) {
+ if(lastStatus != null) {
+ for(GpsSatellite satellite : lastStatus.getSatellites()) {
+ satellites++;
+ }
+ }
+ }
+
+ /* Get the power data for the physical gps device. */
+ GpsData power = GpsData.obtain();
+ synchronized(gpsState) {
+ double[] stateTimes = gpsState.getStateTimesLocked();
+ int curState = gpsState.getCurrentStateLocked();
+ power.init(stateTimes, curState == POWER_STATE_ON ? satellites : 0);
+ gpsState.resetTimesLocked();
+ }
+ result.setPowerData(power);
+
+ /* Get the power data for each uid if we have information on it. */
+ if(hasUidInfo) synchronized(uidStates) {
+ lastTime = beginTime + iterationInterval * iteration;
+ for(int i = 0; i < uidStates.size(); i++) {
+ int uid = uidStates.keyAt(i);
+ GpsStateKeeper state = uidStates.valueAt(i);
+
+ double[] stateTimes = state.getStateTimesLocked();
+ int curState = state.getCurrentStateLocked();
+ GpsData uidPower = GpsData.obtain();
+ uidPower.init(stateTimes, curState == POWER_STATE_ON ? satellites : 0);
+ state.resetTimesLocked();
+
+ result.addUidPowerData(uid, uidPower);
+
+ /* Remove state information for uids no longer using the gps. */
+ if(curState == POWER_STATE_OFF) {
+ uidStates.remove(uid);
+ i--;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ @Override
+ public boolean hasUidInformation() {
+ return hasUidInfo;
+ }
+
+ /* This class is used to maintain the actual GPS state in addition to
+ * simulating individual uid states.
+ */
+ private static class GpsStateKeeper {
+ private double[] stateTimes;
+ private long lastTime;
+ private int curState;
+
+ /* The sum of whatever hook sources are valid. See the HOOK_ constants. */
+ private int hookMask;
+ /* The time that the GPS hardware should turn off. This is only used
+ * if HOOK_TIMER is in the hookMask.
+ */
+ private long offTime;
+ /* Gives the time that the GPS stays in the sleep state after the session
+ * has ended in milliseconds.
+ */
+ private long sleepTime;
+
+ public GpsStateKeeper(int hookMask, long sleepTime) {
+ this(hookMask, sleepTime, SystemClock.elapsedRealtime());
+ }
+
+ public GpsStateKeeper(int hookMask, long sleepTime, long lastTime) {
+ this.hookMask = hookMask;
+ this.sleepTime = sleepTime; /* This isn't required if HOOK_TIEMR is not
+ * set. */
+ this.lastTime = lastTime;
+ stateTimes = new double[POWER_STATES];
+ curState = POWER_STATE_OFF;
+ offTime = -1;
+ }
+
+ /* Make sure that you have a lock on this before calling. */
+ public double[] getStateTimesLocked() {
+ updateTimesLocked();
+
+ /* Let's normalize the times so that power measurements are consistent. */
+ double total = 0;
+ for(int i = 0; i < POWER_STATES; i++) {
+ total += stateTimes[i];
+ }
+ if(total == 0) total = 1;
+ for(int i = 0; i < POWER_STATES; i++) {
+ stateTimes[i] /= total;
+ }
+
+ return stateTimes;
+ }
+
+ public void resetTimesLocked() {
+ for(int i = 0; i < POWER_STATES; i++) {
+ stateTimes[i] = 0;
+ }
+ }
+
+ public int getCurrentStateLocked() {
+ return curState;
+ }
+
+ /* Make sure that you have a lock on this before calling. */
+ private void updateTimesLocked() {
+ /* Update the time we were in the previous state. */
+ long curTime = SystemClock.elapsedRealtime();
+
+ /* Check if the GPS has gone to sleep as a result of a timer. */
+ if((hookMask & HOOK_TIMER) != 0 && offTime != -1 &&
+ offTime < curTime) {
+ stateTimes[curState] += (offTime - lastTime) / 1000.0;
+ curState = POWER_STATE_OFF;
+ offTime = -1;
+ }
+
+ /* Update the amount of time that we've been in the current state. */
+ stateTimes[curState] += (curTime - lastTime) / 1000.0;
+ lastTime = curTime;
+ }
+
+ /* When a hook source gets an event it should report it to updateEvent.
+ * The only exception is HOOK_TIMER which is handled within this class
+ * itself.
+ */
+ public void updateEvent(int event, int source) {
+ synchronized(this) {
+ if((hookMask & source) == 0) {
+ /* We are not using this hook source, ignore. */
+ return;
+ }
+
+ updateTimesLocked();
+ int oldState = curState;
+ switch(event) {
+ case GPS_STATUS_SESSION_BEGIN:
+ curState = POWER_STATE_ON;
+ break;
+ case GPS_STATUS_SESSION_END:
+ if(curState == POWER_STATE_ON) {
+ curState = POWER_STATE_SLEEP;
+ }
+ break;
+ case GPS_STATUS_ENGINE_ON:
+ if(curState == POWER_STATE_OFF) {
+ curState = POWER_STATE_SLEEP;
+ }
+ break;
+ case GPS_STATUS_ENGINE_OFF:
+ curState = POWER_STATE_OFF;
+ break;
+ default:
+ Log.w(TAG, "Unknown GPS event captured");
+ }
+ if(curState != oldState) {
+ if(oldState == POWER_STATE_ON && curState == POWER_STATE_SLEEP) {
+ offTime = SystemClock.elapsedRealtime() + sleepTime;
+ } else {
+ /* Any other state transition should reset the off timer. */
+ offTime = -1;
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public String getComponentName() {
+ return "GPS";
+ }
+}
+
diff --git a/src/edu/umich/PowerTutor/components/LCD.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/components/LCD.java
similarity index 96%
rename from src/edu/umich/PowerTutor/components/LCD.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/components/LCD.java
index 869cd59..1587c93 100644
--- a/src/edu/umich/PowerTutor/components/LCD.java
+++ b/PowerTutor2/src/main/java/edu/umich/PowerTutor/components/LCD.java
@@ -1,179 +1,179 @@
-/*
-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 .
-
-Please send inquiries to powertutor@umich.edu
-*/
-
-package edu.umich.PowerTutor.components;
-
-import edu.umich.PowerTutor.PowerNotifications;
-import edu.umich.PowerTutor.service.IterationData;
-import edu.umich.PowerTutor.service.PowerData;
-import edu.umich.PowerTutor.util.ForegroundDetector;
-import edu.umich.PowerTutor.util.NotificationService;
-import edu.umich.PowerTutor.util.Recycler;
-import edu.umich.PowerTutor.util.SystemInfo;
-
-import android.app.ActivityManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.provider.Settings;
-import android.os.Process;
-import android.util.Log;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.util.List;
-
-public class LCD extends PowerComponent {
- public static class LcdData extends PowerData {
- private static Recycler recycler = new Recycler();
-
- public static LcdData obtain() {
- LcdData result = recycler.obtain();
- if(result != null) return result;
- return new LcdData();
- }
-
- @Override
- public void recycle() {
- recycler.recycle(this);
- }
-
- public int brightness;
- public boolean screenOn;
-
- private LcdData() {
- }
-
- public void init(int brightness, boolean screenOn) {
- this.brightness = brightness;
- this.screenOn = screenOn;
- }
-
- public void writeLogDataInfo(OutputStreamWriter out) throws IOException {
- StringBuilder res = new StringBuilder();
- res.append("LCD-brightness ").append(brightness)
- .append("\nLCD-screen-on ").append(screenOn).append("\n");
- out.write(res.toString());
- }
- }
-
- private final String TAG = "LCD";
- private static final String[] BACKLIGHT_BRIGHTNESS_FILES = {
- "/sys/devices/virtual/leds/lcd-backlight/brightness",
- "/sys/devices/platform/trout-backlight.0/leds/lcd-backlight/brightness",
- };
-
- private Context context;
- private ForegroundDetector foregroundDetector;
- private BroadcastReceiver broadcastReceiver;
- private boolean screenOn;
-
- private String brightnessFile;
-
- public LCD(Context context) {
- this.context = context;
- screenOn = true;
-
- if(context == null) {
- return;
- }
-
- foregroundDetector = new ForegroundDetector((ActivityManager)
- context.getSystemService(context.ACTIVITY_SERVICE));
- broadcastReceiver = new BroadcastReceiver() {
- public void onReceive(Context context, Intent intent) {
- synchronized(this) {
- if(intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
- screenOn = false;
- } else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
- screenOn = true;
- }
- }
- };
- };
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
- intentFilter.addAction(Intent.ACTION_SCREEN_ON);
- context.registerReceiver(broadcastReceiver, intentFilter);
-
- for(int i = 0; i < BACKLIGHT_BRIGHTNESS_FILES.length; i++) {
- if(new File(BACKLIGHT_BRIGHTNESS_FILES[i]).exists()) {
- brightnessFile = BACKLIGHT_BRIGHTNESS_FILES[i];
- }
- }
- }
-
- @Override
- protected void onExit() {
- context.unregisterReceiver(broadcastReceiver);
- super.onExit();
- }
-
- @Override
- public IterationData calculateIteration(long iteration) {
- IterationData result = IterationData.obtain();
-
- boolean screen;
- synchronized(this) {
- screen = screenOn;
- }
-
- int brightness;
- if(brightnessFile != null) {
- brightness = (int)SystemInfo.getInstance()
- .readLongFromFile(brightnessFile);
- } else {
- try {
- brightness = Settings.System.getInt(context.getContentResolver(),
- Settings.System.SCREEN_BRIGHTNESS);
- } catch(Settings.SettingNotFoundException ex) {
- Log.w(TAG, "Could not retrieve brightness information");
- return result;
- }
- }
- if(brightness < 0 || 255 < brightness) {
- Log.w(TAG, "Could not retrieve brightness information");
- return result;
- }
-
- LcdData data = LcdData.obtain();
- data.init(brightness, screen);
- result.setPowerData(data);
-
- if(screen) {
- LcdData uidData = LcdData.obtain();
- uidData.init(brightness, screen);
- result.addUidPowerData(foregroundDetector.getForegroundUid(), uidData);
- }
-
- return result;
- }
-
- @Override
- public boolean hasUidInformation() {
- return true;
- }
-
- @Override
- public String getComponentName() {
- return "LCD";
- }
-}
+/*
+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 .
+
+Please send inquiries to powertutor@umich.edu
+*/
+
+package edu.umich.PowerTutor.components;
+
+import edu.umich.PowerTutor.PowerNotifications;
+import edu.umich.PowerTutor.service.IterationData;
+import edu.umich.PowerTutor.service.PowerData;
+import edu.umich.PowerTutor.util.ForegroundDetector;
+import edu.umich.PowerTutor.util.NotificationService;
+import edu.umich.PowerTutor.util.Recycler;
+import edu.umich.PowerTutor.util.SystemInfo;
+
+import android.app.ActivityManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.provider.Settings;
+import android.os.Process;
+import android.util.Log;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.util.List;
+
+public class LCD extends PowerComponent {
+ public static class LcdData extends PowerData {
+ private static Recycler recycler = new Recycler();
+
+ public static LcdData obtain() {
+ LcdData result = recycler.obtain();
+ if(result != null) return result;
+ return new LcdData();
+ }
+
+ @Override
+ public void recycle() {
+ recycler.recycle(this);
+ }
+
+ public int brightness;
+ public boolean screenOn;
+
+ private LcdData() {
+ }
+
+ public void init(int brightness, boolean screenOn) {
+ this.brightness = brightness;
+ this.screenOn = screenOn;
+ }
+
+ public void writeLogDataInfo(OutputStreamWriter out) throws IOException {
+ StringBuilder res = new StringBuilder();
+ res.append("LCD-brightness ").append(brightness)
+ .append("\nLCD-screen-on ").append(screenOn).append("\n");
+ out.write(res.toString());
+ }
+ }
+
+ private final String TAG = "LCD";
+ private static final String[] BACKLIGHT_BRIGHTNESS_FILES = {
+ "/sys/devices/virtual/leds/lcd-backlight/brightness",
+ "/sys/devices/platform/trout-backlight.0/leds/lcd-backlight/brightness",
+ };
+
+ private Context context;
+ private ForegroundDetector foregroundDetector;
+ private BroadcastReceiver broadcastReceiver;
+ private boolean screenOn;
+
+ private String brightnessFile;
+
+ public LCD(Context context) {
+ this.context = context;
+ screenOn = true;
+
+ if(context == null) {
+ return;
+ }
+
+ foregroundDetector = new ForegroundDetector((ActivityManager)
+ context.getSystemService(context.ACTIVITY_SERVICE));
+ broadcastReceiver = new BroadcastReceiver() {
+ public void onReceive(Context context, Intent intent) {
+ synchronized(this) {
+ if(intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
+ screenOn = false;
+ } else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
+ screenOn = true;
+ }
+ }
+ };
+ };
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
+ intentFilter.addAction(Intent.ACTION_SCREEN_ON);
+ context.registerReceiver(broadcastReceiver, intentFilter);
+
+ for(int i = 0; i < BACKLIGHT_BRIGHTNESS_FILES.length; i++) {
+ if(new File(BACKLIGHT_BRIGHTNESS_FILES[i]).exists()) {
+ brightnessFile = BACKLIGHT_BRIGHTNESS_FILES[i];
+ }
+ }
+ }
+
+ @Override
+ protected void onExit() {
+ context.unregisterReceiver(broadcastReceiver);
+ super.onExit();
+ }
+
+ @Override
+ public IterationData calculateIteration(long iteration) {
+ IterationData result = IterationData.obtain();
+
+ boolean screen;
+ synchronized(this) {
+ screen = screenOn;
+ }
+
+ int brightness;
+ if(brightnessFile != null) {
+ brightness = (int)SystemInfo.getInstance()
+ .readLongFromFile(brightnessFile);
+ } else {
+ try {
+ brightness = Settings.System.getInt(context.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS);
+ } catch(Settings.SettingNotFoundException ex) {
+ Log.w(TAG, "Could not retrieve brightness information");
+ return result;
+ }
+ }
+ if(brightness < 0 || 255 < brightness) {
+ Log.w(TAG, "Could not retrieve brightness information");
+ return result;
+ }
+
+ LcdData data = LcdData.obtain();
+ data.init(brightness, screen);
+ result.setPowerData(data);
+
+ if(screen) {
+ LcdData uidData = LcdData.obtain();
+ uidData.init(brightness, screen);
+ result.addUidPowerData(foregroundDetector.getForegroundUid(), uidData);
+ }
+
+ return result;
+ }
+
+ @Override
+ public boolean hasUidInformation() {
+ return true;
+ }
+
+ @Override
+ public String getComponentName() {
+ return "LCD";
+ }
+}
diff --git a/src/edu/umich/PowerTutor/components/OLED.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/components/OLED.java
similarity index 96%
rename from src/edu/umich/PowerTutor/components/OLED.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/components/OLED.java
index 9987709..4be813f 100644
--- a/src/edu/umich/PowerTutor/components/OLED.java
+++ b/PowerTutor2/src/main/java/edu/umich/PowerTutor/components/OLED.java
@@ -1,311 +1,311 @@
-/*
-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 .
-
-Please send inquiries to powertutor@umich.edu
-*/
-
-package edu.umich.PowerTutor.components;
-
-import edu.umich.PowerTutor.PowerNotifications;
-import edu.umich.PowerTutor.phone.PhoneConstants;
-import edu.umich.PowerTutor.service.IterationData;
-import edu.umich.PowerTutor.service.PowerData;
-import edu.umich.PowerTutor.util.NativeLoader;
-import edu.umich.PowerTutor.util.NotificationService;
-import edu.umich.PowerTutor.util.Recycler;
-import edu.umich.PowerTutor.util.SystemInfo;
-import edu.umich.PowerTutor.util.ForegroundDetector;
-
-import android.app.ActivityManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.provider.Settings;
-import android.os.Process;
-import android.util.Log;
-import android.util.DisplayMetrics;
-import android.view.WindowManager;
-
-import java.io.DataOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.RandomAccessFile;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.util.List;
-import java.util.Random;
-
-import java.io.*;
-import java.nio.*;
-import java.nio.channels.*;
-
-public class OLED extends PowerComponent {
- public static class OledData extends PowerData {
- private static Recycler recycler = new Recycler();
-
- public static OledData obtain() {
- OledData result = recycler.obtain();
- if(result != null) return result;
- return new OledData();
- }
-
- @Override
- public void recycle() {
- recycler.recycle(this);
- }
-
- public int brightness;
- public double pixPower;
- public boolean screenOn;
-
- private OledData() {
- }
-
- public void init() {
- this.screenOn = false;
- }
-
- public void init(int brightness, double pixPower) {
- screenOn = true;
- this.brightness = brightness;
- this.pixPower = pixPower;
- }
-
- public void writeLogDataInfo(OutputStreamWriter out) throws IOException {
- out.write("OLED-brightness " + brightness + "\n");
- out.write("OLED-pix-power " + pixPower + "\n");
- out.write("OLED-screen-on " + screenOn + "\n");
- }
- }
-
- private static final String TAG = "OLED";
- private static final String[] BACKLIGHT_BRIGHTNESS_FILES = {
- "/sys/class/leds/lcd-backlight/brightness",
- "/sys/devices/virtual/leds/lcd-backlight/brightness",
- "/sys/devices/platform/trout-backlight.0/leds/lcd-backlight/brightness",
- };
-
- private Context context;
- private ForegroundDetector foregroundDetector;
- private BroadcastReceiver broadcastReceiver;
- private boolean screenOn;
-
- private File frameBufferFile;
-
- private int screenWidth;
- private int screenHeight;
-
- private static final int NUMBER_OF_SAMPLES = 500;
- private int[] samples;
-
- private String brightnessFile;
-
- /* Coefficients pre-computed for pix power calculations.
- */
- private double rcoef;
- private double gcoef;
- private double bcoef;
- private double modul_coef;
-
- public OLED(Context context, PhoneConstants constants) {
- this.context = context;
- screenOn = true;
-
- foregroundDetector = new ForegroundDetector((ActivityManager)
- context.getSystemService(context.ACTIVITY_SERVICE));
- broadcastReceiver = new BroadcastReceiver() {
- public void onReceive(Context context, Intent intent) {
- synchronized(this) {
- if(intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
- screenOn = false;
- } else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
- screenOn = true;
- }
- }
- };
- };
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
- intentFilter.addAction(Intent.ACTION_SCREEN_ON);
- context.registerReceiver(broadcastReceiver, intentFilter);
-
- frameBufferFile = new File("/dev/fb0");
- if(!frameBufferFile.exists()) {
- frameBufferFile = new File("/dev/graphics/fb0");
- }
- if(frameBufferFile.exists()) try {
- /* Check if we already have permission to read the frame buffer. */
- boolean readOk = false;
- try {
- RandomAccessFile fin = new RandomAccessFile(frameBufferFile, "r");
- int b = fin.read();
- fin.close();
- readOk = true;
- } catch(IOException e) {
- }
- /* Don't have permission, try to change permission as root. */
- if(!readOk) {
- java.lang.Process p = Runtime.getRuntime().exec("su");
- DataOutputStream os = new DataOutputStream(p.getOutputStream());
- os.writeBytes("chown " + android.os.Process.myUid() +
- " " + frameBufferFile.getAbsolutePath() + "\n");
- os.writeBytes("chown app_" + (android.os.Process.myUid() -
- SystemInfo.AID_APP) +
- " " + frameBufferFile.getAbsolutePath() + "\n");
- os.writeBytes("chmod 660 " + frameBufferFile.getAbsolutePath() + "\n");
- os.writeBytes("exit\n");
- os.flush();
- p.waitFor();
- if(p.exitValue() != 0) {
- Log.i(TAG, "failed to change permissions on frame buffer");
- }
- }
- } catch (InterruptedException e) {
- Log.i(TAG, "changing permissions on frame buffer interrupted");
- } catch (IOException e) {
- Log.i(TAG, "unexpected exception while changing permission on " +
- "frame buffer");
- e.printStackTrace();
- }
-
- DisplayMetrics metrics = new DisplayMetrics();
- WindowManager windowManager =
- (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
- windowManager.getDefaultDisplay().getMetrics(metrics);
- screenWidth = metrics.widthPixels;
- screenHeight = metrics.heightPixels;
-
- Random r = new Random();
- samples = new int[NUMBER_OF_SAMPLES];
- for(int i = 0; i < NUMBER_OF_SAMPLES; i++) {
- int a = screenWidth * screenHeight * i / NUMBER_OF_SAMPLES;
- int b = screenWidth * screenHeight * (i + 1) / NUMBER_OF_SAMPLES;
- samples[i] = a + r.nextInt(b - a);
- }
-
- double[] channel = constants.oledChannelPower();
- rcoef = channel[0] / 255 / 255;
- gcoef = channel[1] / 255 / 255;
- bcoef = channel[2] / 255 / 255;
- modul_coef = constants.oledModulation() / 255 / 255 / 3 / 3;
-
- for(int i = 0; i < BACKLIGHT_BRIGHTNESS_FILES.length; i++) {
- if(new File(BACKLIGHT_BRIGHTNESS_FILES[i]).exists()) {
- brightnessFile = BACKLIGHT_BRIGHTNESS_FILES[i];
- }
- }
- }
-
- @Override
- protected void onExit() {
- context.unregisterReceiver(broadcastReceiver);
- super.onExit();
- }
-
- @Override
- public IterationData calculateIteration(long iteration) {
- IterationData result = IterationData.obtain();
-
- boolean screen;
- synchronized(this) {
- screen = screenOn;
- }
-
- int brightness;
- if(brightnessFile != null) {
- brightness = (int)SystemInfo.getInstance()
- .readLongFromFile(brightnessFile);
- } else {
- try {
- brightness = Settings.System.getInt(context.getContentResolver(),
- Settings.System.SCREEN_BRIGHTNESS);
- } catch(Settings.SettingNotFoundException ex) {
- Log.w(TAG, "Could not retrieve brightness information");
- return result;
- }
- }
- if(brightness < 0 || 255 < brightness) {
- Log.w(TAG, "Could not retrieve brightness information");
- return result;
- }
-
- double pixPower = 0;
- if(screen && frameBufferFile.exists()) {
- if(NativeLoader.jniLoaded()) {
- pixPower = getScreenPixPower(rcoef, gcoef, bcoef, modul_coef);
- } else try {
- RandomAccessFile fin = new RandomAccessFile(frameBufferFile, "r");
-
- for(int x : samples) {
- fin.seek(x * 4);
- int px = fin.readInt();
- int b = px >> 8 & 0xFF;
- int g = px >> 16 & 0xFF;
- int r = px >> 24 & 0xFF;
-
- /* Calculate the power usage of this one pixel if it were at full
- * brightness. Linearly scale by brightness to get true power
- * consumption. To calculate whole screen compute average of sampled
- * region and multiply by number of pixels.
- */
- int modul_val = r + g + b;
- pixPower += rcoef * (r * r) + gcoef * (g * g) + bcoef * (b * b) -
- modul_coef * (modul_val * modul_val);
- }
- fin.close();
- } catch(FileNotFoundException e) {
- pixPower = -1;
- } catch(IOException e) {
- pixPower = -1;
- e.printStackTrace();
- }
- if(pixPower >= 0) {
- pixPower *= 1.0 * screenWidth * screenHeight / NUMBER_OF_SAMPLES;
- }
- }
-
- OledData data = OledData.obtain();
- if(!screen) {
- data.init();
- } else {
- data.init(brightness, pixPower);
- }
- result.setPowerData(data);
-
- if(screen) {
- OledData uidData = OledData.obtain();
- uidData.init(brightness, pixPower);
- result.addUidPowerData(foregroundDetector.getForegroundUid(), uidData);
- }
-
- return result;
- }
-
- @Override
- public boolean hasUidInformation() {
- return true;
- }
-
- @Override
- public String getComponentName() {
- return "OLED";
- }
-
- public static native double getScreenPixPower(double rcoef, double gcoef,
- double bcoef, double modul_coef);
-}
+/*
+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 .
+
+Please send inquiries to powertutor@umich.edu
+*/
+
+package edu.umich.PowerTutor.components;
+
+import edu.umich.PowerTutor.PowerNotifications;
+import edu.umich.PowerTutor.phone.PhoneConstants;
+import edu.umich.PowerTutor.service.IterationData;
+import edu.umich.PowerTutor.service.PowerData;
+import edu.umich.PowerTutor.util.NativeLoader;
+import edu.umich.PowerTutor.util.NotificationService;
+import edu.umich.PowerTutor.util.Recycler;
+import edu.umich.PowerTutor.util.SystemInfo;
+import edu.umich.PowerTutor.util.ForegroundDetector;
+
+import android.app.ActivityManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.provider.Settings;
+import android.os.Process;
+import android.util.Log;
+import android.util.DisplayMetrics;
+import android.view.WindowManager;
+
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.RandomAccessFile;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.util.List;
+import java.util.Random;
+
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.*;
+
+public class OLED extends PowerComponent {
+ public static class OledData extends PowerData {
+ private static Recycler recycler = new Recycler();
+
+ public static OledData obtain() {
+ OledData result = recycler.obtain();
+ if(result != null) return result;
+ return new OledData();
+ }
+
+ @Override
+ public void recycle() {
+ recycler.recycle(this);
+ }
+
+ public int brightness;
+ public double pixPower;
+ public boolean screenOn;
+
+ private OledData() {
+ }
+
+ public void init() {
+ this.screenOn = false;
+ }
+
+ public void init(int brightness, double pixPower) {
+ screenOn = true;
+ this.brightness = brightness;
+ this.pixPower = pixPower;
+ }
+
+ public void writeLogDataInfo(OutputStreamWriter out) throws IOException {
+ out.write("OLED-brightness " + brightness + "\n");
+ out.write("OLED-pix-power " + pixPower + "\n");
+ out.write("OLED-screen-on " + screenOn + "\n");
+ }
+ }
+
+ private static final String TAG = "OLED";
+ private static final String[] BACKLIGHT_BRIGHTNESS_FILES = {
+ "/sys/class/leds/lcd-backlight/brightness",
+ "/sys/devices/virtual/leds/lcd-backlight/brightness",
+ "/sys/devices/platform/trout-backlight.0/leds/lcd-backlight/brightness",
+ };
+
+ private Context context;
+ private ForegroundDetector foregroundDetector;
+ private BroadcastReceiver broadcastReceiver;
+ private boolean screenOn;
+
+ private File frameBufferFile;
+
+ private int screenWidth;
+ private int screenHeight;
+
+ private static final int NUMBER_OF_SAMPLES = 500;
+ private int[] samples;
+
+ private String brightnessFile;
+
+ /* Coefficients pre-computed for pix power calculations.
+ */
+ private double rcoef;
+ private double gcoef;
+ private double bcoef;
+ private double modul_coef;
+
+ public OLED(Context context, PhoneConstants constants) {
+ this.context = context;
+ screenOn = true;
+
+ foregroundDetector = new ForegroundDetector((ActivityManager)
+ context.getSystemService(context.ACTIVITY_SERVICE));
+ broadcastReceiver = new BroadcastReceiver() {
+ public void onReceive(Context context, Intent intent) {
+ synchronized(this) {
+ if(intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
+ screenOn = false;
+ } else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
+ screenOn = true;
+ }
+ }
+ };
+ };
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
+ intentFilter.addAction(Intent.ACTION_SCREEN_ON);
+ context.registerReceiver(broadcastReceiver, intentFilter);
+
+ frameBufferFile = new File("/dev/fb0");
+ if(!frameBufferFile.exists()) {
+ frameBufferFile = new File("/dev/graphics/fb0");
+ }
+ if(frameBufferFile.exists()) try {
+ /* Check if we already have permission to read the frame buffer. */
+ boolean readOk = false;
+ try {
+ RandomAccessFile fin = new RandomAccessFile(frameBufferFile, "r");
+ int b = fin.read();
+ fin.close();
+ readOk = true;
+ } catch(IOException e) {
+ }
+ /* Don't have permission, try to change permission as root. */
+ if(!readOk) {
+ java.lang.Process p = Runtime.getRuntime().exec("su");
+ DataOutputStream os = new DataOutputStream(p.getOutputStream());
+ os.writeBytes("chown " + android.os.Process.myUid() +
+ " " + frameBufferFile.getAbsolutePath() + "\n");
+ os.writeBytes("chown app_" + (android.os.Process.myUid() -
+ SystemInfo.AID_APP) +
+ " " + frameBufferFile.getAbsolutePath() + "\n");
+ os.writeBytes("chmod 660 " + frameBufferFile.getAbsolutePath() + "\n");
+ os.writeBytes("exit\n");
+ os.flush();
+ p.waitFor();
+ if(p.exitValue() != 0) {
+ Log.i(TAG, "failed to change permissions on frame buffer");
+ }
+ }
+ } catch (InterruptedException e) {
+ Log.i(TAG, "changing permissions on frame buffer interrupted");
+ } catch (IOException e) {
+ Log.i(TAG, "unexpected exception while changing permission on " +
+ "frame buffer");
+ e.printStackTrace();
+ }
+
+ DisplayMetrics metrics = new DisplayMetrics();
+ WindowManager windowManager =
+ (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
+ windowManager.getDefaultDisplay().getMetrics(metrics);
+ screenWidth = metrics.widthPixels;
+ screenHeight = metrics.heightPixels;
+
+ Random r = new Random();
+ samples = new int[NUMBER_OF_SAMPLES];
+ for(int i = 0; i < NUMBER_OF_SAMPLES; i++) {
+ int a = screenWidth * screenHeight * i / NUMBER_OF_SAMPLES;
+ int b = screenWidth * screenHeight * (i + 1) / NUMBER_OF_SAMPLES;
+ samples[i] = a + r.nextInt(b - a);
+ }
+
+ double[] channel = constants.oledChannelPower();
+ rcoef = channel[0] / 255 / 255;
+ gcoef = channel[1] / 255 / 255;
+ bcoef = channel[2] / 255 / 255;
+ modul_coef = constants.oledModulation() / 255 / 255 / 3 / 3;
+
+ for(int i = 0; i < BACKLIGHT_BRIGHTNESS_FILES.length; i++) {
+ if(new File(BACKLIGHT_BRIGHTNESS_FILES[i]).exists()) {
+ brightnessFile = BACKLIGHT_BRIGHTNESS_FILES[i];
+ }
+ }
+ }
+
+ @Override
+ protected void onExit() {
+ context.unregisterReceiver(broadcastReceiver);
+ super.onExit();
+ }
+
+ @Override
+ public IterationData calculateIteration(long iteration) {
+ IterationData result = IterationData.obtain();
+
+ boolean screen;
+ synchronized(this) {
+ screen = screenOn;
+ }
+
+ int brightness;
+ if(brightnessFile != null) {
+ brightness = (int)SystemInfo.getInstance()
+ .readLongFromFile(brightnessFile);
+ } else {
+ try {
+ brightness = Settings.System.getInt(context.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS);
+ } catch(Settings.SettingNotFoundException ex) {
+ Log.w(TAG, "Could not retrieve brightness information");
+ return result;
+ }
+ }
+ if(brightness < 0 || 255 < brightness) {
+ Log.w(TAG, "Could not retrieve brightness information");
+ return result;
+ }
+
+ double pixPower = 0;
+ if(screen && frameBufferFile.exists()) {
+ if(NativeLoader.jniLoaded()) {
+ pixPower = getScreenPixPower(rcoef, gcoef, bcoef, modul_coef);
+ } else try {
+ RandomAccessFile fin = new RandomAccessFile(frameBufferFile, "r");
+
+ for(int x : samples) {
+ fin.seek(x * 4);
+ int px = fin.readInt();
+ int b = px >> 8 & 0xFF;
+ int g = px >> 16 & 0xFF;
+ int r = px >> 24 & 0xFF;
+
+ /* Calculate the power usage of this one pixel if it were at full
+ * brightness. Linearly scale by brightness to get true power
+ * consumption. To calculate whole screen compute average of sampled
+ * region and multiply by number of pixels.
+ */
+ int modul_val = r + g + b;
+ pixPower += rcoef * (r * r) + gcoef * (g * g) + bcoef * (b * b) -
+ modul_coef * (modul_val * modul_val);
+ }
+ fin.close();
+ } catch(FileNotFoundException e) {
+ pixPower = -1;
+ } catch(IOException e) {
+ pixPower = -1;
+ e.printStackTrace();
+ }
+ if(pixPower >= 0) {
+ pixPower *= 1.0 * screenWidth * screenHeight / NUMBER_OF_SAMPLES;
+ }
+ }
+
+ OledData data = OledData.obtain();
+ if(!screen) {
+ data.init();
+ } else {
+ data.init(brightness, pixPower);
+ }
+ result.setPowerData(data);
+
+ if(screen) {
+ OledData uidData = OledData.obtain();
+ uidData.init(brightness, pixPower);
+ result.addUidPowerData(foregroundDetector.getForegroundUid(), uidData);
+ }
+
+ return result;
+ }
+
+ @Override
+ public boolean hasUidInformation() {
+ return true;
+ }
+
+ @Override
+ public String getComponentName() {
+ return "OLED";
+ }
+
+ public static native double getScreenPixPower(double rcoef, double gcoef,
+ double bcoef, double modul_coef);
+}
diff --git a/src/edu/umich/PowerTutor/components/PowerComponent.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/components/PowerComponent.java
similarity index 100%
rename from src/edu/umich/PowerTutor/components/PowerComponent.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/components/PowerComponent.java
diff --git a/src/edu/umich/PowerTutor/components/Sensors.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/components/Sensors.java
similarity index 96%
rename from src/edu/umich/PowerTutor/components/Sensors.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/components/Sensors.java
index e0917fb..f80d62e 100644
--- a/src/edu/umich/PowerTutor/components/Sensors.java
+++ b/PowerTutor2/src/main/java/edu/umich/PowerTutor/components/Sensors.java
@@ -1,217 +1,217 @@
-/*
-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 .
-
-Please send inquiries to powertutor@umich.edu
-*/
-
-package edu.umich.PowerTutor.components;
-
-import edu.umich.PowerTutor.PowerNotifications;
-import edu.umich.PowerTutor.service.IterationData;
-import edu.umich.PowerTutor.service.PowerData;
-import edu.umich.PowerTutor.util.NotificationService;
-import edu.umich.PowerTutor.util.Recycler;
-import edu.umich.PowerTutor.util.SystemInfo;
-
-import android.content.Context;
-import android.hardware.SensorManager;
-import android.os.SystemClock;
-import android.util.Log;
-import android.util.SparseArray;
-
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.util.Set;
-import java.util.TreeSet;
-import java.util.Map;
-import java.util.TreeMap;
-
-public class Sensors extends PowerComponent {
- private final String TAG = "Sensors";
- public static final int MAX_SENSORS = 10;
-
- public static class SensorData extends PowerData {
- private static Recycler recycler = new Recycler();
-
- public static SensorData obtain() {
- SensorData result = recycler.obtain();
- if(result != null) return result;
- return new SensorData();
- }
-
- @Override
- public void recycle() {
- recycler.recycle(this);
- }
-
- public double[] onTime;
-
- private SensorData() {
- onTime = new double[MAX_SENSORS];
- }
-
- public void writeLogDataInfo(OutputStreamWriter out) throws IOException {
- StringBuilder res = new StringBuilder();
- for(int i = 0; i < MAX_SENSORS; i++) {
- if(onTime[i] > 1e-7) {
- res.append("Sensors-time ").append(i).append(" ")
- .append(onTime[i]).append("\n");
- }
- }
- out.write(res.toString());
- }
- }
-
- private Context context;
- private SensorManager sensorManager;
- private PowerNotifications sensorHook;
-
- private SensorStateKeeper sensorState;
- private SparseArray uidStates;
-
- public Sensors(Context context) {
- this.context = context;
- sensorState = new SensorStateKeeper();
- uidStates = new SparseArray();
-
- if(!NotificationService.available()) {
- Log.w(TAG, "Sensor component created although no notification service " +
- "available to receive sensor usage information");
- return;
- }
- sensorManager = (SensorManager)context.getSystemService(
- Context.SENSOR_SERVICE);
- sensorHook = new NotificationService.DefaultReceiver() {
- public void noteStartSensor(int uid, int sensor) {
- if(sensor < 0 || MAX_SENSORS <= sensor) {
- Log.w(TAG, "Received sensor outside of accepted range");
- return;
- }
- synchronized(sensorState) {
- sensorState.startSensor(sensor);
- SensorStateKeeper uidState = uidStates.get(uid);
- if(uidState == null) {
- uidState = new SensorStateKeeper();
- uidStates.put(uid, uidState);
- }
- uidState.startSensor(sensor);
- }
- }
-
- public void noteStopSensor(int uid, int sensor) {
- if(sensor < 0 || MAX_SENSORS <= sensor) {
- Log.w(TAG, "Received sensor outside of accepted range");
- return;
- }
- synchronized(sensorState) {
- sensorState.stopSensor(sensor);
- SensorStateKeeper uidState = uidStates.get(uid);
- if(uidState == null) {
- uidState = new SensorStateKeeper();
- uidStates.put(uid, uidState);
- }
- uidState.stopSensor(sensor);
- }
- }
- };
- NotificationService.addHook(sensorHook);
- }
-
- @Override
- protected void onExit() {
- super.onExit();
- NotificationService.removeHook(sensorHook);
- }
-
- @Override
- public IterationData calculateIteration(long iteration) {
- IterationData result = IterationData.obtain();
- synchronized(sensorState) {
- SensorData globalData = SensorData.obtain();
- sensorState.setupSensorTimes(globalData.onTime, iterationInterval);
- result.setPowerData(globalData);
-
- for(int i = 0; i < uidStates.size(); i++) {
- int uid = uidStates.keyAt(i);
- SensorStateKeeper uidState = uidStates.valueAt(i);
- SensorData uidData = SensorData.obtain();
- uidState.setupSensorTimes(uidData.onTime, iterationInterval);
- result.addUidPowerData(uid, uidData);
-
- if(uidState.sensorsOn() == 0) {
- uidStates.remove(uid);
- i--;
- }
- }
- }
- return result;
- }
-
- private static class SensorStateKeeper {
- private int[] nesting;
- private long[] times;
- private long lastTime;
- private int count;
-
- public SensorStateKeeper() {
- nesting = new int[MAX_SENSORS];
- times = new long[MAX_SENSORS];
- lastTime = SystemClock.elapsedRealtime();
- }
-
- public void startSensor(int sensor) {
- if(nesting[sensor]++ == 0) {
- times[sensor] -= SystemClock.elapsedRealtime() - lastTime;
- count++;
- }
- }
-
- public void stopSensor(int sensor) {
- if(nesting[sensor] == 0) {
- return;
- } else if(--nesting[sensor] == 0) {
- times[sensor] += SystemClock.elapsedRealtime() - lastTime;
- count--;
- }
- }
-
- public int sensorsOn() {
- return count;
- }
-
- public void setupSensorTimes(double[] sensorTimes, long iterationInterval) {
- long now = SystemClock.elapsedRealtime();
- long div = now - lastTime;
- if(div <= 0) div = 1;
- for(int i = 0; i < MAX_SENSORS; i++) {
- sensorTimes[i] = 1.0 * (times[i] +
- (nesting[i] > 0 ? now - lastTime : 0)) / div;
- times[i] = 0;
- }
- lastTime = now;
- }
- }
-
- @Override
- public boolean hasUidInformation() {
- return true;
- }
-
- @Override
- public String getComponentName() {
- return "Sensors";
- }
-}
+/*
+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 .
+
+Please send inquiries to powertutor@umich.edu
+*/
+
+package edu.umich.PowerTutor.components;
+
+import edu.umich.PowerTutor.PowerNotifications;
+import edu.umich.PowerTutor.service.IterationData;
+import edu.umich.PowerTutor.service.PowerData;
+import edu.umich.PowerTutor.util.NotificationService;
+import edu.umich.PowerTutor.util.Recycler;
+import edu.umich.PowerTutor.util.SystemInfo;
+
+import android.content.Context;
+import android.hardware.SensorManager;
+import android.os.SystemClock;
+import android.util.Log;
+import android.util.SparseArray;
+
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.Map;
+import java.util.TreeMap;
+
+public class Sensors extends PowerComponent {
+ private final String TAG = "Sensors";
+ public static final int MAX_SENSORS = 10;
+
+ public static class SensorData extends PowerData {
+ private static Recycler recycler = new Recycler();
+
+ public static SensorData obtain() {
+ SensorData result = recycler.obtain();
+ if(result != null) return result;
+ return new SensorData();
+ }
+
+ @Override
+ public void recycle() {
+ recycler.recycle(this);
+ }
+
+ public double[] onTime;
+
+ private SensorData() {
+ onTime = new double[MAX_SENSORS];
+ }
+
+ public void writeLogDataInfo(OutputStreamWriter out) throws IOException {
+ StringBuilder res = new StringBuilder();
+ for(int i = 0; i < MAX_SENSORS; i++) {
+ if(onTime[i] > 1e-7) {
+ res.append("Sensors-time ").append(i).append(" ")
+ .append(onTime[i]).append("\n");
+ }
+ }
+ out.write(res.toString());
+ }
+ }
+
+ private Context context;
+ private SensorManager sensorManager;
+ private PowerNotifications sensorHook;
+
+ private SensorStateKeeper sensorState;
+ private SparseArray uidStates;
+
+ public Sensors(Context context) {
+ this.context = context;
+ sensorState = new SensorStateKeeper();
+ uidStates = new SparseArray();
+
+ if(!NotificationService.available()) {
+ Log.w(TAG, "Sensor component created although no notification service " +
+ "available to receive sensor usage information");
+ return;
+ }
+ sensorManager = (SensorManager)context.getSystemService(
+ Context.SENSOR_SERVICE);
+ sensorHook = new NotificationService.DefaultReceiver() {
+ public void noteStartSensor(int uid, int sensor) {
+ if(sensor < 0 || MAX_SENSORS <= sensor) {
+ Log.w(TAG, "Received sensor outside of accepted range");
+ return;
+ }
+ synchronized(sensorState) {
+ sensorState.startSensor(sensor);
+ SensorStateKeeper uidState = uidStates.get(uid);
+ if(uidState == null) {
+ uidState = new SensorStateKeeper();
+ uidStates.put(uid, uidState);
+ }
+ uidState.startSensor(sensor);
+ }
+ }
+
+ public void noteStopSensor(int uid, int sensor) {
+ if(sensor < 0 || MAX_SENSORS <= sensor) {
+ Log.w(TAG, "Received sensor outside of accepted range");
+ return;
+ }
+ synchronized(sensorState) {
+ sensorState.stopSensor(sensor);
+ SensorStateKeeper uidState = uidStates.get(uid);
+ if(uidState == null) {
+ uidState = new SensorStateKeeper();
+ uidStates.put(uid, uidState);
+ }
+ uidState.stopSensor(sensor);
+ }
+ }
+ };
+ NotificationService.addHook(sensorHook);
+ }
+
+ @Override
+ protected void onExit() {
+ super.onExit();
+ NotificationService.removeHook(sensorHook);
+ }
+
+ @Override
+ public IterationData calculateIteration(long iteration) {
+ IterationData result = IterationData.obtain();
+ synchronized(sensorState) {
+ SensorData globalData = SensorData.obtain();
+ sensorState.setupSensorTimes(globalData.onTime, iterationInterval);
+ result.setPowerData(globalData);
+
+ for(int i = 0; i < uidStates.size(); i++) {
+ int uid = uidStates.keyAt(i);
+ SensorStateKeeper uidState = uidStates.valueAt(i);
+ SensorData uidData = SensorData.obtain();
+ uidState.setupSensorTimes(uidData.onTime, iterationInterval);
+ result.addUidPowerData(uid, uidData);
+
+ if(uidState.sensorsOn() == 0) {
+ uidStates.remove(uid);
+ i--;
+ }
+ }
+ }
+ return result;
+ }
+
+ private static class SensorStateKeeper {
+ private int[] nesting;
+ private long[] times;
+ private long lastTime;
+ private int count;
+
+ public SensorStateKeeper() {
+ nesting = new int[MAX_SENSORS];
+ times = new long[MAX_SENSORS];
+ lastTime = SystemClock.elapsedRealtime();
+ }
+
+ public void startSensor(int sensor) {
+ if(nesting[sensor]++ == 0) {
+ times[sensor] -= SystemClock.elapsedRealtime() - lastTime;
+ count++;
+ }
+ }
+
+ public void stopSensor(int sensor) {
+ if(nesting[sensor] == 0) {
+ return;
+ } else if(--nesting[sensor] == 0) {
+ times[sensor] += SystemClock.elapsedRealtime() - lastTime;
+ count--;
+ }
+ }
+
+ public int sensorsOn() {
+ return count;
+ }
+
+ public void setupSensorTimes(double[] sensorTimes, long iterationInterval) {
+ long now = SystemClock.elapsedRealtime();
+ long div = now - lastTime;
+ if(div <= 0) div = 1;
+ for(int i = 0; i < MAX_SENSORS; i++) {
+ sensorTimes[i] = 1.0 * (times[i] +
+ (nesting[i] > 0 ? now - lastTime : 0)) / div;
+ times[i] = 0;
+ }
+ lastTime = now;
+ }
+ }
+
+ @Override
+ public boolean hasUidInformation() {
+ return true;
+ }
+
+ @Override
+ public String getComponentName() {
+ return "Sensors";
+ }
+}
diff --git a/src/edu/umich/PowerTutor/components/Threeg.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/components/Threeg.java
similarity index 97%
rename from src/edu/umich/PowerTutor/components/Threeg.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/components/Threeg.java
index 88d8736..607d461 100644
--- a/src/edu/umich/PowerTutor/components/Threeg.java
+++ b/PowerTutor2/src/main/java/edu/umich/PowerTutor/components/Threeg.java
@@ -1,398 +1,398 @@
-/*
-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 .
-
-Please send inquiries to powertutor@umich.edu
-*/
-
-package edu.umich.PowerTutor.components;
-
-import edu.umich.PowerTutor.phone.PhoneConstants;
-import edu.umich.PowerTutor.service.IterationData;
-import edu.umich.PowerTutor.service.PowerData;
-import edu.umich.PowerTutor.service.PowerEstimator;
-import edu.umich.PowerTutor.util.Recycler;
-import edu.umich.PowerTutor.util.SystemInfo;
-
-import android.content.Context;
-import android.os.SystemClock;
-import android.util.Log;
-import android.util.SparseArray;
-import android.telephony.TelephonyManager;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.io.RandomAccessFile;
-
-public class Threeg extends PowerComponent {
- public static class ThreegData extends PowerData {
- private static Recycler recycler = new Recycler();
-
- public static ThreegData obtain() {
- ThreegData result = recycler.obtain();
- if(result != null) return result;
- return new ThreegData();
- }
-
- @Override
- public void recycle() {
- recycler.recycle(this);
- }
-
- public boolean threegOn;
- public long packets;
- public long uplinkBytes;
- public long downlinkBytes;
- public int powerState;
- public String oper;
-
- private ThreegData() {
- }
-
- public void init() {
- threegOn = false;
- }
-
- public void init(long packets, long uplinkBytes, long downlinkBytes,
- int powerState, String oper) {
- threegOn = true;
- this.packets = packets;
- this.uplinkBytes = uplinkBytes;
- this.downlinkBytes = downlinkBytes;
- this.powerState = powerState;
- this.oper = oper;
- }
-
- public void writeLogDataInfo(OutputStreamWriter out) throws IOException {
- StringBuilder res = new StringBuilder();
- res.append("3G-on ").append(threegOn).append("\n");
- if(threegOn) {
- res.append("3G-uplinkBytes ").append(uplinkBytes)
- .append("\n3G-downlinkBytes ").append(downlinkBytes)
- .append("\n3G-packets ").append(packets)
- .append("\n3G-state ").append(Threeg.POWER_STATE_NAMES[powerState])
- .append("\n3G-oper ").append(oper)
- .append("\n");
- }
- out.write(res.toString());
- }
- }
-
- public static final int POWER_STATE_IDLE = 0;
- public static final int POWER_STATE_FACH = 1;
- public static final int POWER_STATE_DCH = 2;
- public static final String[] POWER_STATE_NAMES = {"IDLE", "FACH", "DCH"};
-
- private static final String TAG = "Threeg";
-
- private PhoneConstants phoneConstants;
- private TelephonyManager telephonyManager;
- private SystemInfo sysInfo;
-
- private String oper;
- private int dchFachDelay;
- private int fachIdleDelay;
- private int uplinkQueueSize;
- private int downlinkQueueSize;
-
- private int[] lastUids;
- private ThreegStateKeeper threegState;
- private SparseArray uidStates;
-
- private String transPacketsFile;
- private String readPacketsFile;
- private String readBytesFile;
- private String transBytesFile;
- private File uidStatsFolder;
-
- public Threeg(Context context, PhoneConstants phoneConstants) {
- this.phoneConstants = phoneConstants;
- telephonyManager = (TelephonyManager)context.getSystemService(
- Context.TELEPHONY_SERVICE);
-
- String interfaceName = phoneConstants.threegInterface();
- threegState = new ThreegStateKeeper();
- uidStates = new SparseArray();
- transPacketsFile = "/sys/devices/virtual/net/" +
- interfaceName + "/statistics/tx_packets";
- readPacketsFile = "/sys/devices/virtual/net/" +
- interfaceName + "/statistics/rx_packets";
- readBytesFile = "/sys/devices/virtual/net/" +
- interfaceName + "/statistics/rx_bytes";
- transBytesFile = "/sys/devices/virtual/net/" +
- interfaceName + "/statistics/tx_bytes";
- uidStatsFolder = new File("/proc/uid_stat");
- sysInfo = SystemInfo.getInstance();
- }
-
- @Override
- public IterationData calculateIteration(long iteration) {
- IterationData result = IterationData.obtain();
-
- int netType = telephonyManager.getNetworkType();
-
- if((netType != TelephonyManager.NETWORK_TYPE_UMTS &&
- netType != 8/* TelephonyManager.NETWORK_TYPE_HSDPA */)) {
- // TODO: Actually get models for the different network types.
- netType = TelephonyManager.NETWORK_TYPE_UMTS;
- }
-
- if(telephonyManager.getDataState() != TelephonyManager.DATA_CONNECTED ||
- (netType != TelephonyManager.NETWORK_TYPE_UMTS &&
- netType != 8/* TelephonyManager.NETWORK_TYPE_HSDPA */)) {
- /* We need to allow the real iterface state keeper to reset it's state
- * so that the next update it knows it's coming back from an off state.
- * We also need to clear all the uid information.
- */
- oper = null;
- threegState.interfaceOff();
- uidStates.clear();
-
- ThreegData data = ThreegData.obtain();
- data.init();
- result.setPowerData(data);
- return result;
- }
-
- if(oper == null) {
- oper = telephonyManager.getNetworkOperatorName();
- dchFachDelay = phoneConstants.threegDchFachDelay(oper);
- fachIdleDelay = phoneConstants.threegFachIdleDelay(oper);
- uplinkQueueSize = phoneConstants.threegUplinkQueue(oper);
- downlinkQueueSize = phoneConstants.threegDownlinkQueue(oper);
- }
-
- long transmitPackets = readLongFromFile(transPacketsFile);
- long receivePackets = readLongFromFile(readPacketsFile);
- long transmitBytes = readLongFromFile(transBytesFile);
- long receiveBytes = readLongFromFile(readBytesFile);
- if(transmitBytes == -1 || receiveBytes == -1) {
- /* Couldn't read interface data files. */
- Log.w(TAG, "Failed to read packet and byte counts from wifi interface");
- return result;
- }
-
- if(threegState.isInitialized()) {
- threegState.updateState(transmitPackets, receivePackets,
- transmitBytes, receiveBytes,
- dchFachDelay, fachIdleDelay,
- uplinkQueueSize, downlinkQueueSize);
- ThreegData data = ThreegData.obtain();
- data.init(threegState.getPackets(), threegState.getUplinkBytes(),
- threegState.getDownlinkBytes(), threegState.getPowerState(),
- oper);
- result.setPowerData(data);
- } else {
- threegState.updateState(transmitPackets, receivePackets,
- transmitBytes, receiveBytes,
- dchFachDelay, fachIdleDelay,
- uplinkQueueSize, downlinkQueueSize);
- }
-
- lastUids = sysInfo.getUids(lastUids);
- if(lastUids != null) for(int uid : lastUids) {
- if(uid == -1) {
- continue;
- }
- try {
- ThreegStateKeeper uidState = uidStates.get(uid);
- if(uidState == null) {
- uidState = new ThreegStateKeeper();
- uidStates.put(uid, uidState);
- }
-
- if(!uidState.isStale()) {
- /* We use a huerstic here so that we don't poll for uids that haven't
- * had much activity recently.
- */
- continue;
- }
-
- /* These read operations are the expensive part of polling. */
- receiveBytes = readLongFromFile("/proc/uid_stat/" + uid + "/tcp_rcv");
- transmitBytes = readLongFromFile("/proc/uid_stat/" + uid + "/tcp_snd");
-
- if(receiveBytes == -1 || transmitBytes == -1) {
- Log.w(TAG, "Failed to read uid read/write byte counts");
- } else if(uidState.isInitialized()) {
- uidState.updateState(-1, -1, transmitBytes, receiveBytes,
- dchFachDelay, fachIdleDelay,
- uplinkQueueSize, downlinkQueueSize);
-
- if(uidState.getUplinkBytes() + uidState.getDownlinkBytes() != 0 ||
- uidState.getPowerState() != POWER_STATE_IDLE) {
- ThreegData uidData = ThreegData.obtain();
- uidData.init(uidState.getPackets(),
- uidState.getUplinkBytes(), uidState.getDownlinkBytes(),
- uidState.getPowerState(), oper);
- result.addUidPowerData(uid, uidData);
- }
- } else {
- uidState.updateState(-1, -1, transmitBytes, receiveBytes,
- dchFachDelay, fachIdleDelay,
- uplinkQueueSize, downlinkQueueSize);
- }
- } catch(NumberFormatException e) {
- Log.w(TAG, "Non-uid files in /proc/uid_stat");
- }
- }
-
- return result;
- }
-
- private static class ThreegStateKeeper {
- private long lastTransmitPackets;
- private long lastReceivePackets;
- private long lastTransmitBytes;
- private long lastReceiveBytes;
- private long lastTime;
-
- private long deltaPackets;
- private long deltaUplinkBytes;
- private long deltaDownlinkBytes;
-
- private int powerState;
- private int stateTime;
-
- private long inactiveTime;
-
- public ThreegStateKeeper() {
- lastTransmitBytes = lastReceiveBytes = lastTime = -1;
- deltaUplinkBytes = deltaDownlinkBytes = -1;
- powerState = POWER_STATE_IDLE;
- stateTime = 0;
- inactiveTime = 0;
- }
-
- public void interfaceOff() {
- lastTime = SystemClock.elapsedRealtime();
- powerState = POWER_STATE_IDLE;
- }
-
- public boolean isInitialized() {
- return lastTime != -1;
- }
-
- public void updateState(long transmitPackets, long receivePackets,
- long transmitBytes, long receiveBytes,
- int dchFachDelay, int fachIdleDelay,
- int uplinkQueueSize, int downlinkQueueSize) {
- long curTime = SystemClock.elapsedRealtime();
- if(lastTime != -1 && curTime > lastTime) {
- double deltaTime = curTime - lastTime;
- deltaPackets = transmitPackets + receivePackets -
- lastTransmitPackets - lastReceivePackets;
- deltaUplinkBytes = transmitBytes - lastTransmitBytes;
- deltaDownlinkBytes = receiveBytes - lastReceiveBytes;
- boolean inactive = deltaUplinkBytes == 0 && deltaDownlinkBytes == 0;
- inactiveTime = inactive ? inactiveTime + curTime - lastTime : 0;
-
- // TODO: make this always work.
- int timeMult = 1;
- if(1000 % PowerEstimator.ITERATION_INTERVAL != 0) {
- Log.w(TAG,
- "Cannot handle iteration intervals that are a factor of 1 second");
- } else {
- timeMult = 1000 / PowerEstimator.ITERATION_INTERVAL;
- }
-
- switch(powerState) {
- case POWER_STATE_IDLE:
- if(!inactive) {
- powerState = POWER_STATE_FACH;
- }
- break;
- case POWER_STATE_FACH:
- if(inactive) {
- stateTime++;
- if(stateTime >= fachIdleDelay * timeMult) {
- stateTime = 0;
- powerState = POWER_STATE_IDLE;
- }
- } else {
- stateTime = 0;
- if(deltaUplinkBytes > 0 ||
- deltaDownlinkBytes > 0) {
- powerState = POWER_STATE_DCH;
- }
- }
- break;
- default: // case POWER_STATE_DCH:
- if(inactive) {
- stateTime++;
- if(stateTime >= dchFachDelay * timeMult) {
- stateTime = 0;
- powerState = POWER_STATE_FACH;
- }
- } else {
- stateTime = 0;
- }
- }
- }
- lastTime = curTime;
- lastTransmitPackets = transmitPackets;
- lastReceivePackets = receivePackets;
- lastTransmitBytes = transmitBytes;
- lastReceiveBytes = receiveBytes;
- }
-
- public int getPowerState() {
- return powerState;
- }
-
- public long getPackets() {
- return deltaPackets;
- }
-
- public long getUplinkBytes() {
- return deltaUplinkBytes;
- }
-
- public long getDownlinkBytes() {
- return deltaDownlinkBytes;
- }
-
- /* The idea here is that we don't want to have to read uid information
- * every single iteration for each uid as it just takes too long. So here
- * we are designing a hueristic that helps us avoid polling for too many
- * uids.
- */
- public boolean isStale() {
- if(powerState != POWER_STATE_IDLE) return true;
- long curTime = SystemClock.elapsedRealtime();
- return curTime - lastTime > (long)Math.min(10000, inactiveTime);
- }
- }
-
- private final static byte[] buf = new byte[16];
-
- private long readLongFromFile(String filePath) {
- return sysInfo.readLongFromFile(filePath);
- }
-
- @Override
- public boolean hasUidInformation() {
- return uidStatsFolder.exists();
- }
-
- @Override
- public String getComponentName() {
- return "3G";
- }
-}
+/*
+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 .
+
+Please send inquiries to powertutor@umich.edu
+*/
+
+package edu.umich.PowerTutor.components;
+
+import edu.umich.PowerTutor.phone.PhoneConstants;
+import edu.umich.PowerTutor.service.IterationData;
+import edu.umich.PowerTutor.service.PowerData;
+import edu.umich.PowerTutor.service.PowerEstimator;
+import edu.umich.PowerTutor.util.Recycler;
+import edu.umich.PowerTutor.util.SystemInfo;
+
+import android.content.Context;
+import android.os.SystemClock;
+import android.util.Log;
+import android.util.SparseArray;
+import android.telephony.TelephonyManager;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.RandomAccessFile;
+
+public class Threeg extends PowerComponent {
+ public static class ThreegData extends PowerData {
+ private static Recycler recycler = new Recycler();
+
+ public static ThreegData obtain() {
+ ThreegData result = recycler.obtain();
+ if(result != null) return result;
+ return new ThreegData();
+ }
+
+ @Override
+ public void recycle() {
+ recycler.recycle(this);
+ }
+
+ public boolean threegOn;
+ public long packets;
+ public long uplinkBytes;
+ public long downlinkBytes;
+ public int powerState;
+ public String oper;
+
+ private ThreegData() {
+ }
+
+ public void init() {
+ threegOn = false;
+ }
+
+ public void init(long packets, long uplinkBytes, long downlinkBytes,
+ int powerState, String oper) {
+ threegOn = true;
+ this.packets = packets;
+ this.uplinkBytes = uplinkBytes;
+ this.downlinkBytes = downlinkBytes;
+ this.powerState = powerState;
+ this.oper = oper;
+ }
+
+ public void writeLogDataInfo(OutputStreamWriter out) throws IOException {
+ StringBuilder res = new StringBuilder();
+ res.append("3G-on ").append(threegOn).append("\n");
+ if(threegOn) {
+ res.append("3G-uplinkBytes ").append(uplinkBytes)
+ .append("\n3G-downlinkBytes ").append(downlinkBytes)
+ .append("\n3G-packets ").append(packets)
+ .append("\n3G-state ").append(Threeg.POWER_STATE_NAMES[powerState])
+ .append("\n3G-oper ").append(oper)
+ .append("\n");
+ }
+ out.write(res.toString());
+ }
+ }
+
+ public static final int POWER_STATE_IDLE = 0;
+ public static final int POWER_STATE_FACH = 1;
+ public static final int POWER_STATE_DCH = 2;
+ public static final String[] POWER_STATE_NAMES = {"IDLE", "FACH", "DCH"};
+
+ private static final String TAG = "Threeg";
+
+ private PhoneConstants phoneConstants;
+ private TelephonyManager telephonyManager;
+ private SystemInfo sysInfo;
+
+ private String oper;
+ private int dchFachDelay;
+ private int fachIdleDelay;
+ private int uplinkQueueSize;
+ private int downlinkQueueSize;
+
+ private int[] lastUids;
+ private ThreegStateKeeper threegState;
+ private SparseArray uidStates;
+
+ private String transPacketsFile;
+ private String readPacketsFile;
+ private String readBytesFile;
+ private String transBytesFile;
+ private File uidStatsFolder;
+
+ public Threeg(Context context, PhoneConstants phoneConstants) {
+ this.phoneConstants = phoneConstants;
+ telephonyManager = (TelephonyManager)context.getSystemService(
+ Context.TELEPHONY_SERVICE);
+
+ String interfaceName = phoneConstants.threegInterface();
+ threegState = new ThreegStateKeeper();
+ uidStates = new SparseArray();
+ transPacketsFile = "/sys/devices/virtual/net/" +
+ interfaceName + "/statistics/tx_packets";
+ readPacketsFile = "/sys/devices/virtual/net/" +
+ interfaceName + "/statistics/rx_packets";
+ readBytesFile = "/sys/devices/virtual/net/" +
+ interfaceName + "/statistics/rx_bytes";
+ transBytesFile = "/sys/devices/virtual/net/" +
+ interfaceName + "/statistics/tx_bytes";
+ uidStatsFolder = new File("/proc/uid_stat");
+ sysInfo = SystemInfo.getInstance();
+ }
+
+ @Override
+ public IterationData calculateIteration(long iteration) {
+ IterationData result = IterationData.obtain();
+
+ int netType = telephonyManager.getNetworkType();
+
+ if((netType != TelephonyManager.NETWORK_TYPE_UMTS &&
+ netType != 8/* TelephonyManager.NETWORK_TYPE_HSDPA */)) {
+ // TODO: Actually get models for the different network types.
+ netType = TelephonyManager.NETWORK_TYPE_UMTS;
+ }
+
+ if(telephonyManager.getDataState() != TelephonyManager.DATA_CONNECTED ||
+ (netType != TelephonyManager.NETWORK_TYPE_UMTS &&
+ netType != 8/* TelephonyManager.NETWORK_TYPE_HSDPA */)) {
+ /* We need to allow the real iterface state keeper to reset it's state
+ * so that the next update it knows it's coming back from an off state.
+ * We also need to clear all the uid information.
+ */
+ oper = null;
+ threegState.interfaceOff();
+ uidStates.clear();
+
+ ThreegData data = ThreegData.obtain();
+ data.init();
+ result.setPowerData(data);
+ return result;
+ }
+
+ if(oper == null) {
+ oper = telephonyManager.getNetworkOperatorName();
+ dchFachDelay = phoneConstants.threegDchFachDelay(oper);
+ fachIdleDelay = phoneConstants.threegFachIdleDelay(oper);
+ uplinkQueueSize = phoneConstants.threegUplinkQueue(oper);
+ downlinkQueueSize = phoneConstants.threegDownlinkQueue(oper);
+ }
+
+ long transmitPackets = readLongFromFile(transPacketsFile);
+ long receivePackets = readLongFromFile(readPacketsFile);
+ long transmitBytes = readLongFromFile(transBytesFile);
+ long receiveBytes = readLongFromFile(readBytesFile);
+ if(transmitBytes == -1 || receiveBytes == -1) {
+ /* Couldn't read interface data files. */
+ Log.w(TAG, "Failed to read packet and byte counts from wifi interface");
+ return result;
+ }
+
+ if(threegState.isInitialized()) {
+ threegState.updateState(transmitPackets, receivePackets,
+ transmitBytes, receiveBytes,
+ dchFachDelay, fachIdleDelay,
+ uplinkQueueSize, downlinkQueueSize);
+ ThreegData data = ThreegData.obtain();
+ data.init(threegState.getPackets(), threegState.getUplinkBytes(),
+ threegState.getDownlinkBytes(), threegState.getPowerState(),
+ oper);
+ result.setPowerData(data);
+ } else {
+ threegState.updateState(transmitPackets, receivePackets,
+ transmitBytes, receiveBytes,
+ dchFachDelay, fachIdleDelay,
+ uplinkQueueSize, downlinkQueueSize);
+ }
+
+ lastUids = sysInfo.getUids(lastUids);
+ if(lastUids != null) for(int uid : lastUids) {
+ if(uid == -1) {
+ continue;
+ }
+ try {
+ ThreegStateKeeper uidState = uidStates.get(uid);
+ if(uidState == null) {
+ uidState = new ThreegStateKeeper();
+ uidStates.put(uid, uidState);
+ }
+
+ if(!uidState.isStale()) {
+ /* We use a huerstic here so that we don't poll for uids that haven't
+ * had much activity recently.
+ */
+ continue;
+ }
+
+ /* These read operations are the expensive part of polling. */
+ receiveBytes = readLongFromFile("/proc/uid_stat/" + uid + "/tcp_rcv");
+ transmitBytes = readLongFromFile("/proc/uid_stat/" + uid + "/tcp_snd");
+
+ if(receiveBytes == -1 || transmitBytes == -1) {
+ Log.w(TAG, "Failed to read uid read/write byte counts");
+ } else if(uidState.isInitialized()) {
+ uidState.updateState(-1, -1, transmitBytes, receiveBytes,
+ dchFachDelay, fachIdleDelay,
+ uplinkQueueSize, downlinkQueueSize);
+
+ if(uidState.getUplinkBytes() + uidState.getDownlinkBytes() != 0 ||
+ uidState.getPowerState() != POWER_STATE_IDLE) {
+ ThreegData uidData = ThreegData.obtain();
+ uidData.init(uidState.getPackets(),
+ uidState.getUplinkBytes(), uidState.getDownlinkBytes(),
+ uidState.getPowerState(), oper);
+ result.addUidPowerData(uid, uidData);
+ }
+ } else {
+ uidState.updateState(-1, -1, transmitBytes, receiveBytes,
+ dchFachDelay, fachIdleDelay,
+ uplinkQueueSize, downlinkQueueSize);
+ }
+ } catch(NumberFormatException e) {
+ Log.w(TAG, "Non-uid files in /proc/uid_stat");
+ }
+ }
+
+ return result;
+ }
+
+ private static class ThreegStateKeeper {
+ private long lastTransmitPackets;
+ private long lastReceivePackets;
+ private long lastTransmitBytes;
+ private long lastReceiveBytes;
+ private long lastTime;
+
+ private long deltaPackets;
+ private long deltaUplinkBytes;
+ private long deltaDownlinkBytes;
+
+ private int powerState;
+ private int stateTime;
+
+ private long inactiveTime;
+
+ public ThreegStateKeeper() {
+ lastTransmitBytes = lastReceiveBytes = lastTime = -1;
+ deltaUplinkBytes = deltaDownlinkBytes = -1;
+ powerState = POWER_STATE_IDLE;
+ stateTime = 0;
+ inactiveTime = 0;
+ }
+
+ public void interfaceOff() {
+ lastTime = SystemClock.elapsedRealtime();
+ powerState = POWER_STATE_IDLE;
+ }
+
+ public boolean isInitialized() {
+ return lastTime != -1;
+ }
+
+ public void updateState(long transmitPackets, long receivePackets,
+ long transmitBytes, long receiveBytes,
+ int dchFachDelay, int fachIdleDelay,
+ int uplinkQueueSize, int downlinkQueueSize) {
+ long curTime = SystemClock.elapsedRealtime();
+ if(lastTime != -1 && curTime > lastTime) {
+ double deltaTime = curTime - lastTime;
+ deltaPackets = transmitPackets + receivePackets -
+ lastTransmitPackets - lastReceivePackets;
+ deltaUplinkBytes = transmitBytes - lastTransmitBytes;
+ deltaDownlinkBytes = receiveBytes - lastReceiveBytes;
+ boolean inactive = deltaUplinkBytes == 0 && deltaDownlinkBytes == 0;
+ inactiveTime = inactive ? inactiveTime + curTime - lastTime : 0;
+
+ // TODO: make this always work.
+ int timeMult = 1;
+ if(1000 % PowerEstimator.ITERATION_INTERVAL != 0) {
+ Log.w(TAG,
+ "Cannot handle iteration intervals that are a factor of 1 second");
+ } else {
+ timeMult = 1000 / PowerEstimator.ITERATION_INTERVAL;
+ }
+
+ switch(powerState) {
+ case POWER_STATE_IDLE:
+ if(!inactive) {
+ powerState = POWER_STATE_FACH;
+ }
+ break;
+ case POWER_STATE_FACH:
+ if(inactive) {
+ stateTime++;
+ if(stateTime >= fachIdleDelay * timeMult) {
+ stateTime = 0;
+ powerState = POWER_STATE_IDLE;
+ }
+ } else {
+ stateTime = 0;
+ if(deltaUplinkBytes > 0 ||
+ deltaDownlinkBytes > 0) {
+ powerState = POWER_STATE_DCH;
+ }
+ }
+ break;
+ default: // case POWER_STATE_DCH:
+ if(inactive) {
+ stateTime++;
+ if(stateTime >= dchFachDelay * timeMult) {
+ stateTime = 0;
+ powerState = POWER_STATE_FACH;
+ }
+ } else {
+ stateTime = 0;
+ }
+ }
+ }
+ lastTime = curTime;
+ lastTransmitPackets = transmitPackets;
+ lastReceivePackets = receivePackets;
+ lastTransmitBytes = transmitBytes;
+ lastReceiveBytes = receiveBytes;
+ }
+
+ public int getPowerState() {
+ return powerState;
+ }
+
+ public long getPackets() {
+ return deltaPackets;
+ }
+
+ public long getUplinkBytes() {
+ return deltaUplinkBytes;
+ }
+
+ public long getDownlinkBytes() {
+ return deltaDownlinkBytes;
+ }
+
+ /* The idea here is that we don't want to have to read uid information
+ * every single iteration for each uid as it just takes too long. So here
+ * we are designing a hueristic that helps us avoid polling for too many
+ * uids.
+ */
+ public boolean isStale() {
+ if(powerState != POWER_STATE_IDLE) return true;
+ long curTime = SystemClock.elapsedRealtime();
+ return curTime - lastTime > (long)Math.min(10000, inactiveTime);
+ }
+ }
+
+ private final static byte[] buf = new byte[16];
+
+ private long readLongFromFile(String filePath) {
+ return sysInfo.readLongFromFile(filePath);
+ }
+
+ @Override
+ public boolean hasUidInformation() {
+ return uidStatsFolder.exists();
+ }
+
+ @Override
+ public String getComponentName() {
+ return "3G";
+ }
+}
diff --git a/src/edu/umich/PowerTutor/components/Wifi.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/components/Wifi.java
similarity index 97%
rename from src/edu/umich/PowerTutor/components/Wifi.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/components/Wifi.java
index 0e10292..524a50a 100644
--- a/src/edu/umich/PowerTutor/components/Wifi.java
+++ b/PowerTutor2/src/main/java/edu/umich/PowerTutor/components/Wifi.java
@@ -1,418 +1,418 @@
-/*
-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 .
-
-Please send inquiries to powertutor@umich.edu
-*/
-
-package edu.umich.PowerTutor.components;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-
-import android.content.Context;
-import android.net.wifi.WifiManager;
-import android.os.SystemClock;
-import android.util.Log;
-import android.util.SparseArray;
-import edu.umich.PowerTutor.phone.PhoneConstants;
-import edu.umich.PowerTutor.service.IterationData;
-import edu.umich.PowerTutor.service.PowerData;
-import edu.umich.PowerTutor.util.Recycler;
-import edu.umich.PowerTutor.util.SystemInfo;
-
-public class Wifi extends PowerComponent {
- public static class WifiData extends PowerData {
- private static Recycler recycler = new Recycler();
-
- public static WifiData obtain() {
- WifiData result = recycler.obtain();
- if(result != null) return result;
- return new WifiData();
- }
-
- @Override
- public void recycle() {
- recycler.recycle(this);
- }
-
- public boolean wifiOn;
- public double packets;
- public long uplinkBytes;
- public long downlinkBytes;
- public double uplinkRate;
- public double linkSpeed;
- public int powerState;
-
- private WifiData() {
- }
-
- public void init(double packets, long uplinkBytes, long downlinkBytes,
- double uplinkRate, double linkSpeed, int powerState) {
- wifiOn = true;
- this.packets = packets;
- this.uplinkBytes = uplinkBytes;
- this.downlinkBytes = downlinkBytes;
- this.uplinkRate = uplinkRate;
- this.linkSpeed = linkSpeed;
- this.powerState = powerState;
- }
-
- public void init() {
- wifiOn = false;
- }
-
- public void writeLogDataInfo(OutputStreamWriter out) throws IOException {
- StringBuilder res = new StringBuilder();
- res.append("Wifi-on ").append(wifiOn).append("\n");
- if(wifiOn) {
- res.append("Wifi-packets ").append((long)Math.round(packets))
- .append("\nWifi-uplinkBytes ").append(uplinkBytes)
- .append("\nWifi-downlinkBytes ").append(downlinkBytes)
- .append("\nWifi-uplink ").append((long)Math.round(uplinkRate))
- .append("\nWifi-speed ").append((long)Math.round(linkSpeed))
- .append("\nWifi-state ").append(Wifi.POWER_STATE_NAMES[powerState])
- .append("\n");
- }
- out.write(res.toString());
- }
- }
-
- public static final int POWER_STATE_LOW = 0;
- public static final int POWER_STATE_HIGH = 1;
- public static final String[] POWER_STATE_NAMES = {"LOW", "HIGH"};
-
- private static final String TAG = "Wifi";
-
- private PhoneConstants phoneConstants;
- private WifiManager wifiManager;
- private SystemInfo sysInfo;
-
- private long lastLinkSpeed;
- private int[] lastUids;
- private WifiStateKeeper wifiState;
- private SparseArray uidStates;
-
- private String transPacketsFile;
- private String readPacketsFile;
- private String transBytesFile;
- private String readBytesFile;
- private File uidStatsFolder;
-
- public Wifi(Context context, PhoneConstants phoneConstants) {
- this.phoneConstants = phoneConstants;
- wifiManager = (WifiManager)context.getSystemService(Context.WIFI_SERVICE);
- sysInfo = SystemInfo.getInstance();
-
- /* Try to grab the interface name. If we can't find it will take a wild
- * stab in the dark.
- */
- String interfaceName = SystemInfo.getInstance().getProperty("wifi.interface");
- if(interfaceName == null) interfaceName = "eth0";
-
- lastLinkSpeed = -1;
- wifiState = new WifiStateKeeper(phoneConstants.wifiHighLowTransition(),
- phoneConstants.wifiLowHighTransition());
- uidStates = new SparseArray();
- transPacketsFile = "/sys/devices/virtual/net/" +
- interfaceName + "/statistics/tx_packets";
- readPacketsFile = "/sys/devices/virtual/net/" +
- interfaceName + "/statistics/rx_packets";
- transBytesFile = "/sys/devices/virtual/net/" +
- interfaceName + "/statistics/tx_bytes";
- readBytesFile = "/sys/devices/virtual/net/" +
- interfaceName + "/statistics/rx_bytes";
- uidStatsFolder = new File("/proc/uid_stat");
- }
-
- @Override
- public IterationData calculateIteration(long iteration) {
- IterationData result = IterationData.obtain();
-
- int wifiStateFlag = wifiManager.getWifiState();
- if(wifiStateFlag != WifiManager.WIFI_STATE_ENABLED &&
- wifiStateFlag != WifiManager.WIFI_STATE_DISABLING) {
- /* We need to allow the real iterface state keeper to reset it's state
- * so that the next update it knows it's coming back from an off state.
- * We also need to clear all the uid information.
- */
- wifiState.interfaceOff();
- uidStates.clear();
- lastLinkSpeed = -1;
-
- WifiData data = WifiData.obtain();
- data.init();
- result.setPowerData(data);
- return result;
- }
-
- long transmitPackets = sysInfo.readLongFromFile(transPacketsFile);
- long receivePackets = sysInfo.readLongFromFile(readPacketsFile);
- long transmitBytes = sysInfo.readLongFromFile(transBytesFile);
- long receiveBytes = sysInfo.readLongFromFile(readBytesFile);
- if(transmitPackets == -1 || receivePackets == -1 ||
- transmitBytes == -1 || receiveBytes == -1) {
- /* Couldn't read interface data files. */
- Log.w(TAG, "Failed to read packet and byte counts from wifi interface");
- return result;
- }
-
- /* Update the link speed every 15 seconds as pulling the WifiInfo structure
- * from WifiManager is a little bit expensive. This isn't really something
- * that is likely to change very frequently anyway.
- */
- if(iteration % 15 == 0 || lastLinkSpeed == -1) {
- lastLinkSpeed = wifiManager.getConnectionInfo().getLinkSpeed();
- }
- double linkSpeed = lastLinkSpeed;
-
- if(wifiState.isInitialized()) {
- wifiState.updateState(transmitPackets, receivePackets,
- transmitBytes, receiveBytes);
- WifiData data = WifiData.obtain();
- data.init(wifiState.getPackets(), wifiState.getUplinkBytes(),
- wifiState.getDownlinkBytes(), wifiState.getUplinkRate(),
- linkSpeed, wifiState.getPowerState());
- result.setPowerData(data);
- } else {
- wifiState.updateState(transmitPackets, receivePackets,
- transmitBytes, receiveBytes);
- }
-
- lastUids = sysInfo.getUids(lastUids);
- if(lastUids != null) for(int uid : lastUids) {
- if(uid == -1) {
- continue;
- }
- try {
- WifiStateKeeper uidState = uidStates.get(uid);
- if(uidState == null) {
- uidState = new WifiStateKeeper(phoneConstants.wifiHighLowTransition(),
- phoneConstants.wifiLowHighTransition());
- uidStates.put(uid, uidState);
- }
-
- if(!uidState.isStale()) {
- /* We use a huerstic here so that we don't poll for uids that haven't
- * had much activity recently.
- */
- continue;
- }
-
- /* These read operations are the expensive part of polling. */
- receiveBytes = sysInfo.readLongFromFile(
- "/proc/uid_stat/" + uid + "/tcp_rcv");
- transmitBytes = sysInfo.readLongFromFile(
- "/proc/uid_stat/" + uid + "/tcp_snd");
-
- if(receiveBytes == -1 || transmitBytes == -1) {
- Log.w(TAG, "Failed to read uid read/write byte counts");
- } else if(uidState.isInitialized()) {
- /* We only have information about bytes received but what we really
- * want is the number of packets received so we just have to
- * estimate it.
- */
- long deltaTransmitBytes = transmitBytes - uidState.getTransmitBytes();
- long deltaReceiveBytes = receiveBytes - uidState.getReceiveBytes();
- long estimatedTransmitPackets = (long)Math.round(deltaTransmitBytes /
- wifiState.getAverageTransmitPacketSize());
- long estimatedReceivePackets = (long)Math.round(deltaReceiveBytes /
- wifiState.getAverageReceivePacketSize());
- if(deltaTransmitBytes > 0 && estimatedTransmitPackets == 0) {
- estimatedTransmitPackets = 1;
- }
- if(deltaReceiveBytes > 0 && estimatedReceivePackets == 0) {
- estimatedReceivePackets = 1;
- }
-
- boolean active = transmitBytes != uidState.getTransmitBytes() ||
- receiveBytes != uidState.getReceiveBytes();
- uidState.updateState(
- uidState.getTransmitPackets() + estimatedTransmitPackets,
- uidState.getReceivePackets() + estimatedReceivePackets,
- transmitBytes, receiveBytes);
-
- if(active) {
- WifiData uidData = WifiData.obtain();
- uidData.init(uidState.getPackets(), uidState.getUplinkBytes(),
- uidState.getDownlinkBytes(), uidState.getUplinkRate(),
- linkSpeed, uidState.getPowerState());
- result.addUidPowerData(uid, uidData);
- }
- } else {
- uidState.updateState(0, 0, transmitBytes, receiveBytes);
- }
- } catch(NumberFormatException e) {
- Log.w(TAG, "Non-uid files in /proc/uid_stat");
- }
- }
-
- return result;
- }
-
- private static class WifiStateKeeper {
- private long lastTransmitPackets;
- private long lastReceivePackets;
- private long lastTransmitBytes;
- private long lastReceiveBytes;
- private long lastTime;
-
- private int powerState;
- private double lastPackets;
- private double lastUplinkRate;
- private double lastAverageTransmitPacketSize;
- private double lastAverageReceivePacketSize;
-
- private long deltaUplinkBytes;
- private long deltaDownlinkBytes;
-
- private double highLowTransition;
- private double lowHighTransition;
-
- private long inactiveTime;
-
- public WifiStateKeeper(double highLowTransition, double lowHighTransition) {
- this.highLowTransition = highLowTransition;
- this.lowHighTransition = lowHighTransition;
- lastTransmitPackets = lastReceivePackets = lastTransmitBytes =
- lastTime = -1;
- powerState = POWER_STATE_LOW;
- lastPackets = lastUplinkRate = 0;
- lastAverageTransmitPacketSize = 1000;
- lastAverageReceivePacketSize = 1000;
- inactiveTime = 0;
- }
-
- public void interfaceOff() {
- lastTime = SystemClock.elapsedRealtime();
- powerState = POWER_STATE_LOW;
- }
-
- public boolean isInitialized() {
- return lastTime != -1;
- }
-
- public void updateState(long transmitPackets, long receivePackets,
- long transmitBytes, long receiveBytes) {
- long curTime = SystemClock.elapsedRealtime();
- if(lastTime != -1 && curTime > lastTime) {
- double deltaTime = curTime - lastTime;
- lastUplinkRate = (transmitBytes - lastTransmitBytes) / 1024.0 *
- 7.8125 / deltaTime;
- lastPackets = receivePackets + transmitPackets -
- lastReceivePackets - lastTransmitPackets;
- deltaUplinkBytes = transmitBytes - lastTransmitBytes;
- deltaDownlinkBytes = receiveBytes - lastReceiveBytes;
- if(transmitPackets != lastTransmitPackets) {
- lastAverageTransmitPacketSize = 0.9 * lastAverageTransmitPacketSize +
- 0.1 * (transmitBytes - lastTransmitBytes) /
- (transmitPackets - lastTransmitPackets);
- }
- if(receivePackets != lastReceivePackets) {
- lastAverageReceivePacketSize = 0.9 * lastAverageReceivePacketSize +
- 0.1 * (receiveBytes - lastReceiveBytes) /
- (receivePackets - lastReceivePackets);
- }
-
- if(receiveBytes != lastReceiveBytes ||
- transmitBytes != lastTransmitBytes) {
- inactiveTime = 0;
- } else {
- inactiveTime += curTime - lastTime;
- }
-
- if(lastPackets < highLowTransition) {
- powerState = POWER_STATE_LOW;
- } else if(lastPackets > lowHighTransition) {
- powerState = POWER_STATE_HIGH;
- }
- }
- lastTime = curTime;
- lastTransmitPackets = transmitPackets;
- lastReceivePackets = receivePackets;
- lastTransmitBytes = transmitBytes;
- lastReceiveBytes = receiveBytes;
- }
-
- public int getPowerState() {
- return powerState;
- }
-
- public double getPackets() {
- return lastPackets;
- }
-
- public long getUplinkBytes() {
- return deltaUplinkBytes;
- }
-
- public long getDownlinkBytes() {
- return deltaDownlinkBytes;
- }
-
- public double getUplinkRate() {
- return lastUplinkRate;
- }
-
- public double getAverageTransmitPacketSize() {
- return lastAverageTransmitPacketSize;
- }
-
- public double getAverageReceivePacketSize() {
- return lastAverageReceivePacketSize;
- }
-
- public long getTransmitPackets() {
- return lastTransmitPackets;
- }
-
- public long getReceivePackets() {
- return lastReceivePackets;
- }
-
- public long getTransmitBytes() {
- return lastTransmitBytes;
- }
-
- public long getReceiveBytes() {
- return lastReceiveBytes;
- }
-
- /* The idea here is that we don't want to have to read uid information
- * every single iteration for each uid as it just takes too long. So here
- * we are designing a hueristic that helps us avoid polling for too many
- * uids.
- */
- public boolean isStale() {
- long curTime = SystemClock.elapsedRealtime();
- return curTime - lastTime > (long)Math.min(10000, inactiveTime);
- }
- }
-
- private long readLongFromFile(String filePath) {
- return sysInfo.readLongFromFile(filePath);
- }
-
- @Override
- public boolean hasUidInformation() {
- return uidStatsFolder.exists();
- }
-
- @Override
- public String getComponentName() {
- return "Wifi";
- }
-}
+/*
+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 .
+
+Please send inquiries to powertutor@umich.edu
+*/
+
+package edu.umich.PowerTutor.components;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+
+import android.content.Context;
+import android.net.wifi.WifiManager;
+import android.os.SystemClock;
+import android.util.Log;
+import android.util.SparseArray;
+import edu.umich.PowerTutor.phone.PhoneConstants;
+import edu.umich.PowerTutor.service.IterationData;
+import edu.umich.PowerTutor.service.PowerData;
+import edu.umich.PowerTutor.util.Recycler;
+import edu.umich.PowerTutor.util.SystemInfo;
+
+public class Wifi extends PowerComponent {
+ public static class WifiData extends PowerData {
+ private static Recycler recycler = new Recycler();
+
+ public static WifiData obtain() {
+ WifiData result = recycler.obtain();
+ if(result != null) return result;
+ return new WifiData();
+ }
+
+ @Override
+ public void recycle() {
+ recycler.recycle(this);
+ }
+
+ public boolean wifiOn;
+ public double packets;
+ public long uplinkBytes;
+ public long downlinkBytes;
+ public double uplinkRate;
+ public double linkSpeed;
+ public int powerState;
+
+ private WifiData() {
+ }
+
+ public void init(double packets, long uplinkBytes, long downlinkBytes,
+ double uplinkRate, double linkSpeed, int powerState) {
+ wifiOn = true;
+ this.packets = packets;
+ this.uplinkBytes = uplinkBytes;
+ this.downlinkBytes = downlinkBytes;
+ this.uplinkRate = uplinkRate;
+ this.linkSpeed = linkSpeed;
+ this.powerState = powerState;
+ }
+
+ public void init() {
+ wifiOn = false;
+ }
+
+ public void writeLogDataInfo(OutputStreamWriter out) throws IOException {
+ StringBuilder res = new StringBuilder();
+ res.append("Wifi-on ").append(wifiOn).append("\n");
+ if(wifiOn) {
+ res.append("Wifi-packets ").append((long)Math.round(packets))
+ .append("\nWifi-uplinkBytes ").append(uplinkBytes)
+ .append("\nWifi-downlinkBytes ").append(downlinkBytes)
+ .append("\nWifi-uplink ").append((long)Math.round(uplinkRate))
+ .append("\nWifi-speed ").append((long)Math.round(linkSpeed))
+ .append("\nWifi-state ").append(Wifi.POWER_STATE_NAMES[powerState])
+ .append("\n");
+ }
+ out.write(res.toString());
+ }
+ }
+
+ public static final int POWER_STATE_LOW = 0;
+ public static final int POWER_STATE_HIGH = 1;
+ public static final String[] POWER_STATE_NAMES = {"LOW", "HIGH"};
+
+ private static final String TAG = "Wifi";
+
+ private PhoneConstants phoneConstants;
+ private WifiManager wifiManager;
+ private SystemInfo sysInfo;
+
+ private long lastLinkSpeed;
+ private int[] lastUids;
+ private WifiStateKeeper wifiState;
+ private SparseArray uidStates;
+
+ private String transPacketsFile;
+ private String readPacketsFile;
+ private String transBytesFile;
+ private String readBytesFile;
+ private File uidStatsFolder;
+
+ public Wifi(Context context, PhoneConstants phoneConstants) {
+ this.phoneConstants = phoneConstants;
+ wifiManager = (WifiManager)context.getSystemService(Context.WIFI_SERVICE);
+ sysInfo = SystemInfo.getInstance();
+
+ /* Try to grab the interface name. If we can't find it will take a wild
+ * stab in the dark.
+ */
+ String interfaceName = SystemInfo.getInstance().getProperty("wifi.interface");
+ if(interfaceName == null) interfaceName = "eth0";
+
+ lastLinkSpeed = -1;
+ wifiState = new WifiStateKeeper(phoneConstants.wifiHighLowTransition(),
+ phoneConstants.wifiLowHighTransition());
+ uidStates = new SparseArray();
+ transPacketsFile = "/sys/devices/virtual/net/" +
+ interfaceName + "/statistics/tx_packets";
+ readPacketsFile = "/sys/devices/virtual/net/" +
+ interfaceName + "/statistics/rx_packets";
+ transBytesFile = "/sys/devices/virtual/net/" +
+ interfaceName + "/statistics/tx_bytes";
+ readBytesFile = "/sys/devices/virtual/net/" +
+ interfaceName + "/statistics/rx_bytes";
+ uidStatsFolder = new File("/proc/uid_stat");
+ }
+
+ @Override
+ public IterationData calculateIteration(long iteration) {
+ IterationData result = IterationData.obtain();
+
+ int wifiStateFlag = wifiManager.getWifiState();
+ if(wifiStateFlag != WifiManager.WIFI_STATE_ENABLED &&
+ wifiStateFlag != WifiManager.WIFI_STATE_DISABLING) {
+ /* We need to allow the real iterface state keeper to reset it's state
+ * so that the next update it knows it's coming back from an off state.
+ * We also need to clear all the uid information.
+ */
+ wifiState.interfaceOff();
+ uidStates.clear();
+ lastLinkSpeed = -1;
+
+ WifiData data = WifiData.obtain();
+ data.init();
+ result.setPowerData(data);
+ return result;
+ }
+
+ long transmitPackets = sysInfo.readLongFromFile(transPacketsFile);
+ long receivePackets = sysInfo.readLongFromFile(readPacketsFile);
+ long transmitBytes = sysInfo.readLongFromFile(transBytesFile);
+ long receiveBytes = sysInfo.readLongFromFile(readBytesFile);
+ if(transmitPackets == -1 || receivePackets == -1 ||
+ transmitBytes == -1 || receiveBytes == -1) {
+ /* Couldn't read interface data files. */
+ Log.w(TAG, "Failed to read packet and byte counts from wifi interface");
+ return result;
+ }
+
+ /* Update the link speed every 15 seconds as pulling the WifiInfo structure
+ * from WifiManager is a little bit expensive. This isn't really something
+ * that is likely to change very frequently anyway.
+ */
+ if(iteration % 15 == 0 || lastLinkSpeed == -1) {
+ lastLinkSpeed = wifiManager.getConnectionInfo().getLinkSpeed();
+ }
+ double linkSpeed = lastLinkSpeed;
+
+ if(wifiState.isInitialized()) {
+ wifiState.updateState(transmitPackets, receivePackets,
+ transmitBytes, receiveBytes);
+ WifiData data = WifiData.obtain();
+ data.init(wifiState.getPackets(), wifiState.getUplinkBytes(),
+ wifiState.getDownlinkBytes(), wifiState.getUplinkRate(),
+ linkSpeed, wifiState.getPowerState());
+ result.setPowerData(data);
+ } else {
+ wifiState.updateState(transmitPackets, receivePackets,
+ transmitBytes, receiveBytes);
+ }
+
+ lastUids = sysInfo.getUids(lastUids);
+ if(lastUids != null) for(int uid : lastUids) {
+ if(uid == -1) {
+ continue;
+ }
+ try {
+ WifiStateKeeper uidState = uidStates.get(uid);
+ if(uidState == null) {
+ uidState = new WifiStateKeeper(phoneConstants.wifiHighLowTransition(),
+ phoneConstants.wifiLowHighTransition());
+ uidStates.put(uid, uidState);
+ }
+
+ if(!uidState.isStale()) {
+ /* We use a huerstic here so that we don't poll for uids that haven't
+ * had much activity recently.
+ */
+ continue;
+ }
+
+ /* These read operations are the expensive part of polling. */
+ receiveBytes = sysInfo.readLongFromFile(
+ "/proc/uid_stat/" + uid + "/tcp_rcv");
+ transmitBytes = sysInfo.readLongFromFile(
+ "/proc/uid_stat/" + uid + "/tcp_snd");
+
+ if(receiveBytes == -1 || transmitBytes == -1) {
+ Log.w(TAG, "Failed to read uid read/write byte counts");
+ } else if(uidState.isInitialized()) {
+ /* We only have information about bytes received but what we really
+ * want is the number of packets received so we just have to
+ * estimate it.
+ */
+ long deltaTransmitBytes = transmitBytes - uidState.getTransmitBytes();
+ long deltaReceiveBytes = receiveBytes - uidState.getReceiveBytes();
+ long estimatedTransmitPackets = (long)Math.round(deltaTransmitBytes /
+ wifiState.getAverageTransmitPacketSize());
+ long estimatedReceivePackets = (long)Math.round(deltaReceiveBytes /
+ wifiState.getAverageReceivePacketSize());
+ if(deltaTransmitBytes > 0 && estimatedTransmitPackets == 0) {
+ estimatedTransmitPackets = 1;
+ }
+ if(deltaReceiveBytes > 0 && estimatedReceivePackets == 0) {
+ estimatedReceivePackets = 1;
+ }
+
+ boolean active = transmitBytes != uidState.getTransmitBytes() ||
+ receiveBytes != uidState.getReceiveBytes();
+ uidState.updateState(
+ uidState.getTransmitPackets() + estimatedTransmitPackets,
+ uidState.getReceivePackets() + estimatedReceivePackets,
+ transmitBytes, receiveBytes);
+
+ if(active) {
+ WifiData uidData = WifiData.obtain();
+ uidData.init(uidState.getPackets(), uidState.getUplinkBytes(),
+ uidState.getDownlinkBytes(), uidState.getUplinkRate(),
+ linkSpeed, uidState.getPowerState());
+ result.addUidPowerData(uid, uidData);
+ }
+ } else {
+ uidState.updateState(0, 0, transmitBytes, receiveBytes);
+ }
+ } catch(NumberFormatException e) {
+ Log.w(TAG, "Non-uid files in /proc/uid_stat");
+ }
+ }
+
+ return result;
+ }
+
+ private static class WifiStateKeeper {
+ private long lastTransmitPackets;
+ private long lastReceivePackets;
+ private long lastTransmitBytes;
+ private long lastReceiveBytes;
+ private long lastTime;
+
+ private int powerState;
+ private double lastPackets;
+ private double lastUplinkRate;
+ private double lastAverageTransmitPacketSize;
+ private double lastAverageReceivePacketSize;
+
+ private long deltaUplinkBytes;
+ private long deltaDownlinkBytes;
+
+ private double highLowTransition;
+ private double lowHighTransition;
+
+ private long inactiveTime;
+
+ public WifiStateKeeper(double highLowTransition, double lowHighTransition) {
+ this.highLowTransition = highLowTransition;
+ this.lowHighTransition = lowHighTransition;
+ lastTransmitPackets = lastReceivePackets = lastTransmitBytes =
+ lastTime = -1;
+ powerState = POWER_STATE_LOW;
+ lastPackets = lastUplinkRate = 0;
+ lastAverageTransmitPacketSize = 1000;
+ lastAverageReceivePacketSize = 1000;
+ inactiveTime = 0;
+ }
+
+ public void interfaceOff() {
+ lastTime = SystemClock.elapsedRealtime();
+ powerState = POWER_STATE_LOW;
+ }
+
+ public boolean isInitialized() {
+ return lastTime != -1;
+ }
+
+ public void updateState(long transmitPackets, long receivePackets,
+ long transmitBytes, long receiveBytes) {
+ long curTime = SystemClock.elapsedRealtime();
+ if(lastTime != -1 && curTime > lastTime) {
+ double deltaTime = curTime - lastTime;
+ lastUplinkRate = (transmitBytes - lastTransmitBytes) / 1024.0 *
+ 7.8125 / deltaTime;
+ lastPackets = receivePackets + transmitPackets -
+ lastReceivePackets - lastTransmitPackets;
+ deltaUplinkBytes = transmitBytes - lastTransmitBytes;
+ deltaDownlinkBytes = receiveBytes - lastReceiveBytes;
+ if(transmitPackets != lastTransmitPackets) {
+ lastAverageTransmitPacketSize = 0.9 * lastAverageTransmitPacketSize +
+ 0.1 * (transmitBytes - lastTransmitBytes) /
+ (transmitPackets - lastTransmitPackets);
+ }
+ if(receivePackets != lastReceivePackets) {
+ lastAverageReceivePacketSize = 0.9 * lastAverageReceivePacketSize +
+ 0.1 * (receiveBytes - lastReceiveBytes) /
+ (receivePackets - lastReceivePackets);
+ }
+
+ if(receiveBytes != lastReceiveBytes ||
+ transmitBytes != lastTransmitBytes) {
+ inactiveTime = 0;
+ } else {
+ inactiveTime += curTime - lastTime;
+ }
+
+ if(lastPackets < highLowTransition) {
+ powerState = POWER_STATE_LOW;
+ } else if(lastPackets > lowHighTransition) {
+ powerState = POWER_STATE_HIGH;
+ }
+ }
+ lastTime = curTime;
+ lastTransmitPackets = transmitPackets;
+ lastReceivePackets = receivePackets;
+ lastTransmitBytes = transmitBytes;
+ lastReceiveBytes = receiveBytes;
+ }
+
+ public int getPowerState() {
+ return powerState;
+ }
+
+ public double getPackets() {
+ return lastPackets;
+ }
+
+ public long getUplinkBytes() {
+ return deltaUplinkBytes;
+ }
+
+ public long getDownlinkBytes() {
+ return deltaDownlinkBytes;
+ }
+
+ public double getUplinkRate() {
+ return lastUplinkRate;
+ }
+
+ public double getAverageTransmitPacketSize() {
+ return lastAverageTransmitPacketSize;
+ }
+
+ public double getAverageReceivePacketSize() {
+ return lastAverageReceivePacketSize;
+ }
+
+ public long getTransmitPackets() {
+ return lastTransmitPackets;
+ }
+
+ public long getReceivePackets() {
+ return lastReceivePackets;
+ }
+
+ public long getTransmitBytes() {
+ return lastTransmitBytes;
+ }
+
+ public long getReceiveBytes() {
+ return lastReceiveBytes;
+ }
+
+ /* The idea here is that we don't want to have to read uid information
+ * every single iteration for each uid as it just takes too long. So here
+ * we are designing a hueristic that helps us avoid polling for too many
+ * uids.
+ */
+ public boolean isStale() {
+ long curTime = SystemClock.elapsedRealtime();
+ return curTime - lastTime > (long)Math.min(10000, inactiveTime);
+ }
+ }
+
+ private long readLongFromFile(String filePath) {
+ return sysInfo.readLongFromFile(filePath);
+ }
+
+ @Override
+ public boolean hasUidInformation() {
+ return uidStatsFolder.exists();
+ }
+
+ @Override
+ public String getComponentName() {
+ return "Wifi";
+ }
+}
diff --git a/src/edu/umich/PowerTutor/phone/DreamConstants.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/phone/DreamConstants.java
similarity index 100%
rename from src/edu/umich/PowerTutor/phone/DreamConstants.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/phone/DreamConstants.java
diff --git a/src/edu/umich/PowerTutor/phone/DreamPowerCalculator.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/phone/DreamPowerCalculator.java
similarity index 100%
rename from src/edu/umich/PowerTutor/phone/DreamPowerCalculator.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/phone/DreamPowerCalculator.java
diff --git a/src/edu/umich/PowerTutor/phone/PassionConstants.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/phone/PassionConstants.java
similarity index 100%
rename from src/edu/umich/PowerTutor/phone/PassionConstants.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/phone/PassionConstants.java
diff --git a/src/edu/umich/PowerTutor/phone/PassionPowerCalculator.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/phone/PassionPowerCalculator.java
similarity index 100%
rename from src/edu/umich/PowerTutor/phone/PassionPowerCalculator.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/phone/PassionPowerCalculator.java
diff --git a/src/edu/umich/PowerTutor/phone/PhoneConstants.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/phone/PhoneConstants.java
similarity index 100%
rename from src/edu/umich/PowerTutor/phone/PhoneConstants.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/phone/PhoneConstants.java
diff --git a/src/edu/umich/PowerTutor/phone/PhonePowerCalculator.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/phone/PhonePowerCalculator.java
similarity index 100%
rename from src/edu/umich/PowerTutor/phone/PhonePowerCalculator.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/phone/PhonePowerCalculator.java
diff --git a/src/edu/umich/PowerTutor/phone/PhoneSelector.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/phone/PhoneSelector.java
similarity index 100%
rename from src/edu/umich/PowerTutor/phone/PhoneSelector.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/phone/PhoneSelector.java
diff --git a/src/edu/umich/PowerTutor/phone/PowerFunction.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/phone/PowerFunction.java
similarity index 100%
rename from src/edu/umich/PowerTutor/phone/PowerFunction.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/phone/PowerFunction.java
diff --git a/src/edu/umich/PowerTutor/phone/SapphireConstants.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/phone/SapphireConstants.java
similarity index 100%
rename from src/edu/umich/PowerTutor/phone/SapphireConstants.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/phone/SapphireConstants.java
diff --git a/src/edu/umich/PowerTutor/phone/SapphirePowerCalculator.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/phone/SapphirePowerCalculator.java
similarity index 100%
rename from src/edu/umich/PowerTutor/phone/SapphirePowerCalculator.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/phone/SapphirePowerCalculator.java
diff --git a/src/edu/umich/PowerTutor/service/IterationData.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/service/IterationData.java
similarity index 100%
rename from src/edu/umich/PowerTutor/service/IterationData.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/service/IterationData.java
diff --git a/src/edu/umich/PowerTutor/service/LogUploader.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/service/LogUploader.java
similarity index 96%
rename from src/edu/umich/PowerTutor/service/LogUploader.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/service/LogUploader.java
index b07f542..cbc4e97 100644
--- a/src/edu/umich/PowerTutor/service/LogUploader.java
+++ b/PowerTutor2/src/main/java/edu/umich/PowerTutor/service/LogUploader.java
@@ -1,68 +1,68 @@
-/*
-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 .
-
-Please send inquiries to powertutor@umich.edu
-*/
-
-package edu.umich.PowerTutor.service;
-
-import android.content.Context;
-
-/* This class is responsible for implementing all policy decisions on when to
- * send log information back to an external server. Upon a successful call to
- * shouldUpload() PowerEstimator will then call upload(file). After the call to
- * upload the file with the passed path may be overwritten.
- *
- * This is a stub implementation not supporting log uploading.
- */
-public class LogUploader {
- public LogUploader(Context context) {
- }
-
- /* Returns true if this module supports uploading logs. */
- public static boolean uploadSupported() {
- return false;
- }
-
- /* Returns true if the log should be uploaded now. This may depend on log
- * file size, network conditions, etc. */
- // TODO: This should probably give the file name of the log
- public boolean shouldUpload() {
- return false;
- }
-
- /* Called when the device is plugged in or unplugged. The intended use of
- * this is to improve upload policy decisions. */
- public void plug(boolean plugged) {
- }
-
- /* Initiate the upload of the file with the passed location. */
- public void upload(String origFile) {
- }
-
- /* Returns true if a file is currently being uploaded. */
- public boolean isUploading() {
- return false;
- }
-
- /* Interrupt any threads doing upload work. */
- public void interrupt() {
- }
-
- /* Join any threads that may be performing log upload work. */
- public void join() throws InterruptedException {
- }
-}
+/*
+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 .
+
+Please send inquiries to powertutor@umich.edu
+*/
+
+package edu.umich.PowerTutor.service;
+
+import android.content.Context;
+
+/* This class is responsible for implementing all policy decisions on when to
+ * send log information back to an external server. Upon a successful call to
+ * shouldUpload() PowerEstimator will then call upload(file). After the call to
+ * upload the file with the passed path may be overwritten.
+ *
+ * This is a stub implementation not supporting log uploading.
+ */
+public class LogUploader {
+ public LogUploader(Context context) {
+ }
+
+ /* Returns true if this module supports uploading logs. */
+ public static boolean uploadSupported() {
+ return false;
+ }
+
+ /* Returns true if the log should be uploaded now. This may depend on log
+ * file size, network conditions, etc. */
+ // TODO: This should probably give the file name of the log
+ public boolean shouldUpload() {
+ return false;
+ }
+
+ /* Called when the device is plugged in or unplugged. The intended use of
+ * this is to improve upload policy decisions. */
+ public void plug(boolean plugged) {
+ }
+
+ /* Initiate the upload of the file with the passed location. */
+ public void upload(String origFile) {
+ }
+
+ /* Returns true if a file is currently being uploaded. */
+ public boolean isUploading() {
+ return false;
+ }
+
+ /* Interrupt any threads doing upload work. */
+ public void interrupt() {
+ }
+
+ /* Join any threads that may be performing log upload work. */
+ public void join() throws InterruptedException {
+ }
+}
diff --git a/src/edu/umich/PowerTutor/service/PowerData.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/service/PowerData.java
similarity index 96%
rename from src/edu/umich/PowerTutor/service/PowerData.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/service/PowerData.java
index 2033518..f415661 100644
--- a/src/edu/umich/PowerTutor/service/PowerData.java
+++ b/PowerTutor2/src/main/java/edu/umich/PowerTutor/service/PowerData.java
@@ -1,48 +1,48 @@
-/*
-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 .
-
-Please send inquiries to powertutor@umich.edu
-*/
-
-package edu.umich.PowerTutor.service;
-
-import java.io.OutputStreamWriter;
-import java.io.IOException;
-import java.util.Vector;
-
-public abstract class PowerData {
- private int cachedPower;
-
- public PowerData() {
- }
-
- public void setCachedPower(int power) {
- cachedPower = power;
- }
-
- public int getCachedPower() {
- return cachedPower;
- }
-
- /* To be called when the PowerData object is no longer in use so that it can
- * be used again in the next iteration if it chooses to be.
- */
- public void recycle() {}
-
- /* Simply writes out log information to the passed stream. */
- public abstract void writeLogDataInfo(OutputStreamWriter out)
- throws IOException;
-}
+/*
+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 .
+
+Please send inquiries to powertutor@umich.edu
+*/
+
+package edu.umich.PowerTutor.service;
+
+import java.io.OutputStreamWriter;
+import java.io.IOException;
+import java.util.Vector;
+
+public abstract class PowerData {
+ private int cachedPower;
+
+ public PowerData() {
+ }
+
+ public void setCachedPower(int power) {
+ cachedPower = power;
+ }
+
+ public int getCachedPower() {
+ return cachedPower;
+ }
+
+ /* To be called when the PowerData object is no longer in use so that it can
+ * be used again in the next iteration if it chooses to be.
+ */
+ public void recycle() {}
+
+ /* Simply writes out log information to the passed stream. */
+ public abstract void writeLogDataInfo(OutputStreamWriter out)
+ throws IOException;
+}
diff --git a/src/edu/umich/PowerTutor/service/PowerEstimator.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/service/PowerEstimator.java
similarity index 97%
rename from src/edu/umich/PowerTutor/service/PowerEstimator.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/service/PowerEstimator.java
index ed0325a..60b752c 100644
--- a/src/edu/umich/PowerTutor/service/PowerEstimator.java
+++ b/PowerTutor2/src/main/java/edu/umich/PowerTutor/service/PowerEstimator.java
@@ -1,586 +1,586 @@
-/*
-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 .
-
-Please send inquiries to powertutor@umich.edu
-*/
-
-package edu.umich.PowerTutor.service;
-
-import edu.umich.PowerTutor.components.PowerComponent;
-import edu.umich.PowerTutor.components.OLED;
-import edu.umich.PowerTutor.phone.PhoneSelector;
-import edu.umich.PowerTutor.phone.PhoneConstants;
-import edu.umich.PowerTutor.phone.PowerFunction;
-import edu.umich.PowerTutor.util.BatteryStats;
-import edu.umich.PowerTutor.util.Counter;
-import edu.umich.PowerTutor.util.HistoryBuffer;
-import edu.umich.PowerTutor.util.NotificationService;
-import edu.umich.PowerTutor.util.SystemInfo;
-import edu.umich.PowerTutor.widget.PowerWidget;
-
-import android.content.SharedPreferences;
-import android.content.pm.PackageManager;
-import android.os.SystemClock;
-import android.preference.PreferenceManager;
-import android.provider.Settings;
-import android.util.Log;
-import android.util.SparseArray;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.text.DecimalFormat;
-import java.util.Calendar;
-import java.util.GregorianCalendar;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Vector;
-import java.util.zip.Deflater;
-import java.util.zip.DeflaterOutputStream;
-
-/** This class is responsible for starting the individual power component
- * loggers (CPU, GPS, etc...) and collecting the information they generate.
- * This information is used both to write a log file that will be send back
- * to spidermoneky (or looked at by the user) and to implement the
- * ICounterService IPC interface.
- */
-public class PowerEstimator implements Runnable {
- private static final String TAG = "PowerEstimator";
-
- /* A dictionary used to assist in compression of the log files. Strings that
- * appear more frequently should be put towards the end of the dictionary. It
- * is not critical that every string that be written to the log appear here.
- */
- private static final String DEFLATE_DICTIONARY =
- "onoffidleoff-hookringinglowairplane-modebatteryedgeGPRS3Gunknown" +
- "in-serviceemergency-onlyout-of-servicepower-offdisconnectedconnecting" +
- "associateconnectedsuspendedphone-callservicenetworkbegin.0123456789" +
- "GPSAudioWifi3GLCDCPU-power ";
-
- public static final int ALL_COMPONENTS = -1;
- public static final int ITERATION_INTERVAL = 1000; // 1 second
-
- private UMLoggerService context;
- private SharedPreferences prefs;
- private boolean plugged;
-
- private Vector powerComponents;
- private Vector powerFunctions;
- private Vector histories;
- private Map uidAppIds;
-
- // Miscellaneous data.
- private HistoryBuffer oledScoreHistory;
-
- private Object fileWriteLock = new Object();
- private LogUploader logUploader;
- private OutputStreamWriter logStream;
- private DeflaterOutputStream deflateStream;
-
- private Object iterationLock = new Object();
- private long lastWrittenIteration;
-
- public PowerEstimator(UMLoggerService context){
- this.context = context;
- prefs = PreferenceManager.getDefaultSharedPreferences(context);
- powerComponents = new Vector();
- powerFunctions = new Vector();
- uidAppIds = new HashMap();
- PhoneSelector.generateComponents(context, powerComponents, powerFunctions);
-
- histories = new Vector();
- for(int i = 0; i < powerComponents.size(); i++) {
- histories.add(new HistoryBuffer(300));
- }
- oledScoreHistory = new HistoryBuffer(0);
-
- logUploader = new LogUploader(context);
- openLog(true);
- }
-
- private void openLog(boolean init) {
- /* Open up the log file if possible. */
- try {
- String logFilename = context.getFileStreamPath(
- "PowerTrace.log").getAbsolutePath();
- if(init && prefs.getBoolean("sendPermission", true) &&
- new File(logFilename).length() > 0) {
- /* There is data to send. Make sure that gets going in the sending
- * process before we write over any old logs.
- */
- logUploader.upload(logFilename);
- }
- Deflater deflater = new Deflater();
- deflater.setDictionary(DEFLATE_DICTIONARY.getBytes());
- deflateStream = new DeflaterOutputStream(
- new FileOutputStream(logFilename));
- logStream = new OutputStreamWriter(deflateStream);
- } catch(IOException e) {
- logStream = null;
- Log.e(TAG, "Failed to open log file. No log will be kept.");
- }
- }
-
- /** This is the loop that keeps updating the power profile
- */
- public void run() {
- SystemInfo sysInfo = SystemInfo.getInstance();
- PackageManager pm = context.getPackageManager();
- BatteryStats bst = BatteryStats.getInstance();
-
- int components = powerComponents.size();
- long beginTime = SystemClock.elapsedRealtime();
- for(int i = 0; i < components; i++) {
- powerComponents.get(i).init(beginTime, ITERATION_INTERVAL);
- powerComponents.get(i).start();
- }
- IterationData[] dataTemp = new IterationData[components];
-
- PhoneConstants phoneConstants = PhoneSelector.getConstants(context);
- long[] memInfo = new long[4];
-
- int oledId = -1;
- for(int i = 0; i < components; i++) {
- if("OLED".equals(powerComponents.get(i).getComponentName())) {
- oledId = i;
- break;
- }
- }
-
- double lastCurrent = -1;
-
- /* Indefinitely collect data on each of the power components. */
- boolean firstLogIteration = true;
- for(long iter = -1; !Thread.interrupted(); ) {
- long curTime = SystemClock.elapsedRealtime();
- /* Compute the next iteration that we can make the ending of. We wait
- for the end of the iteration so that the components had a chance to
- collect data already.
- */
- iter = (long)Math.max(iter + 1,
- (curTime - beginTime) / ITERATION_INTERVAL);
- /* Sleep until the next iteration completes. */
- try {
- Thread.currentThread().sleep(
- beginTime + (iter + 1) * ITERATION_INTERVAL - curTime);
- } catch(InterruptedException e) {
- break;
- }
-
- int totalPower = 0;
- for(int i = 0; i < components; i++) {
- PowerComponent comp = powerComponents.get(i);
- IterationData data = comp.getData(iter);
- dataTemp[i] = data;
- if(data == null) {
- /* No data present for this timestamp. No power charged.
- */
- continue;
- }
-
- SparseArray uidPower = data.getUidPowerData();
- for(int j = 0; j < uidPower.size(); j++) {
- int uid = uidPower.keyAt(j);
- PowerData powerData = uidPower.valueAt(j);
- int power = (int)powerFunctions.get(i).calculate(powerData);
- powerData.setCachedPower(power);
- histories.get(i).add(uid, iter, power);
- if(uid == SystemInfo.AID_ALL) {
- totalPower += power;
- }
- if(i == oledId) {
- OLED.OledData oledData = (OLED.OledData)powerData;
- if(oledData.pixPower >= 0) {
- oledScoreHistory.add(uid, iter, (int)(1000 * oledData.pixPower));
- }
- }
- }
- }
-
- /* Update the uid set. */
- synchronized(fileWriteLock) { synchronized(uidAppIds) {
- for(int i = 0; i < components; i++) {
- IterationData data = dataTemp[i];
- if(data == null) {
- continue;
- }
- SparseArray uidPower = data.getUidPowerData();
- for(int j = 0; j < uidPower.size(); j++) {
- int uid = uidPower.keyAt(j);
- if(uid < SystemInfo.AID_APP) {
- uidAppIds.put(uid, null);
- } else {
- /* We only want to update app names when logging so the associcate
- * message gets written.
- */
- String appId = uidAppIds.get(uid);
- String newAppId = sysInfo.getAppId(uid, pm);
- if(!firstLogIteration && logStream != null &&
- (appId == null || !appId.equals(newAppId))) {
- try {
- logStream.write("associate " + uid + " " + newAppId + "\n");
- } catch(IOException e) {
- Log.w(TAG, "Failed to write to log file");
- }
- }
- uidAppIds.put(uid, newAppId);
- }
- }
- }
- }}
-
- synchronized(iterationLock) {
- lastWrittenIteration = iter;
- }
-
- /* Update the icon display every 15 iterations. */
- if(iter % 15 == 14) {
- final double POLY_WEIGHT = 0.02;
- int count = 0;
- int[] history = getComponentHistory(5 * 60, -1,
- SystemInfo.AID_ALL, -1);
- double weightedAvgPower = 0;
- for(int i = history.length - 1; i >= 0; i--) {
- if(history[i] != 0) {
- count++;
- weightedAvgPower *= 1.0 - POLY_WEIGHT;
- weightedAvgPower += POLY_WEIGHT * history[i] / 1000.0;
- }
- }
- double avgPower = -1;
- if(count != 0) {
- avgPower = weightedAvgPower /
- (1.0 - Math.pow(1.0 - POLY_WEIGHT, count));
- }
- avgPower *= 1000;
-
- context.updateNotification((int)Math.min(8, 1 +
- 8 * avgPower / phoneConstants.maxPower()),
- avgPower);
- }
-
- /* Update the widget. */
- if(iter % 60 == 0) {
- PowerWidget.updateWidget(context, this);
- }
-
- if(bst.hasCurrent()) {
- double current = bst.getCurrent();
- if(current != lastCurrent) {
- writeToLog("batt_current " + current + "\n");
- lastCurrent = current;
- }
- }
- if(iter % (5*60) == 0) {
- if(bst.hasTemp()) {
- writeToLog("batt_temp " + bst.getTemp() + "\n");
- }
- if(bst.hasCharge()) {
- writeToLog("batt_charge " + bst.getCharge() + "\n");
- }
- }
- if(iter % (30*60) == 0) {
- if(Settings.System.getInt(context.getContentResolver(),
- "screen_brightness_mode", 0) != 0) {
- writeToLog("setting_brightness automatic\n");
- } else {
- int brightness = Settings.System.getInt(
- context.getContentResolver(),
- Settings.System.SCREEN_BRIGHTNESS, -1);
- if(brightness != -1) {
- writeToLog("setting_brightness " + brightness + "\n");
- }
- }
- int timeout = Settings.System.getInt(
- context.getContentResolver(),
- Settings.System.SCREEN_OFF_TIMEOUT, -1);
- if(timeout != -1) {
- writeToLog("setting_screen_timeout " + timeout + "\n");
- }
- String httpProxy = Settings.Secure.getString(
- context.getContentResolver(),
- Settings.Secure.HTTP_PROXY);
- if(httpProxy != null) {
- writeToLog("setting_httpproxy " + httpProxy + "\n");
- }
- }
-
- /* Let's only grab memory information every 10 seconds to try to keep log
- * file size down and the notice_data table size down.
- */
- boolean hasMem = false;
- if(iter % 10 == 0) {
- hasMem = sysInfo.getMemInfo(memInfo);
- }
-
- synchronized(fileWriteLock) {
- if(logStream != null) try {
- if(firstLogIteration) {
- firstLogIteration = false;
- logStream.write("time " + System.currentTimeMillis() + "\n");
- Calendar cal = new GregorianCalendar();
- logStream.write("localtime_offset " +
- (cal.get(Calendar.ZONE_OFFSET) +
- cal.get(Calendar.DST_OFFSET)) + "\n");
- logStream.write("model " + phoneConstants.modelName() + "\n");
- if(NotificationService.available()) {
- logStream.write("notifications-active\n");
- }
- if(bst.hasFullCapacity()) {
- logStream.write("batt_full_capacity " + bst.getFullCapacity()
- + "\n");
- }
- synchronized(uidAppIds) {
- for(int uid : uidAppIds.keySet()) {
- if(uid < SystemInfo.AID_APP) {
- continue;
- }
- logStream.write("associate " + uid + " " + uidAppIds.get(uid)
- + "\n");
- }
- }
- }
- logStream.write("begin " + iter + "\n");
- logStream.write("total-power " + (long)Math.round(totalPower) + '\n');
- if(hasMem) {
- logStream.write("meminfo " + memInfo[0] + " " + memInfo[1] +
- " " + memInfo[2] + " " + memInfo[3] + "\n");
- }
- for(int i = 0; i < components; i++) {
- IterationData data = dataTemp[i];
- if(data != null) {
- String name = powerComponents.get(i).getComponentName();
- SparseArray uidData = data.getUidPowerData();
- for(int j = 0; j < uidData.size(); j++) {
- int uid = uidData.keyAt(j);
- PowerData powerData = uidData.valueAt(j);
- if(uid == SystemInfo.AID_ALL) {
- logStream.write(name + " " + (long)Math.round(
- powerData.getCachedPower()) + "\n");
- powerData.writeLogDataInfo(logStream);
- } else {
- logStream.write(name + "-" + uid + " " + (long)Math.round(
- powerData.getCachedPower()) + "\n");
- }
- }
- data.recycle();
- }
- }
- } catch(IOException e) {
- Log.w(TAG, "Failed to write to log file");
- }
-
- if(iter % 15 == 0 && prefs.getBoolean("sendPermission", true)) {
- /* Allow for LogUploader to decide if the log needs to be uploaded and
- * begin uploading if it decides it's necessary.
- */
- if(logUploader.shouldUpload()) {
- try {
- logStream.close();
- } catch(IOException e) {
- Log.w(TAG, "Failed to flush and close log stream");
- }
- logStream = null;
- logUploader.upload(context.getFileStreamPath(
- "PowerTrace.log").getAbsolutePath());
- openLog(false);
- firstLogIteration = true;
- }
- }
- }
- }
-
- /* Blank the widget's display and turn off power button. */
- PowerWidget.updateWidgetDone(context);
-
- /* Have all of the power component threads exit. */
- logUploader.interrupt();
- for(int i = 0; i < components; i++) {
- powerComponents.get(i).interrupt();
- }
- try {
- logUploader.join();
- } catch(InterruptedException e) {
- }
- for(int i = 0; i < components; i++) {
- try {
- powerComponents.get(i).join();
- } catch(InterruptedException e) {
- }
- }
-
- /* Close the logstream so that everything gets flushed and written to file
- * before we have to quit.
- */
- synchronized(fileWriteLock) {
- if(logStream != null) try {
- logStream.close();
- } catch(IOException e) {
- Log.w(TAG, "Failed to flush log file on exit");
- }
- }
- }
-
- public void plug(boolean plugged) {
- logUploader.plug(plugged);
- }
-
- public void writeToLog(String m) {
- synchronized(fileWriteLock) {
- if(logStream != null) try {
- logStream.write(m);
- } catch(IOException e) {
- Log.w(TAG, "Failed to write message to power log");
- }
- }
- }
-
- public String[] getComponents() {
- int components = powerComponents.size();
- String[] ret = new String[components];
- for(int i = 0; i < components; i++) {
- ret[i] = powerComponents.get(i).getComponentName();
- }
- return ret;
- }
-
- public int[] getComponentsMaxPower() {
- PhoneConstants constants = PhoneSelector.getConstants(context);
- int components = powerComponents.size();
- int[] ret = new int[components];
- for(int i = 0; i < components; i++) {
- ret[i] = (int)constants.getMaxPower(
- powerComponents.get(i).getComponentName());
- }
- return ret;
- }
-
- public int getNoUidMask() {
- int components = powerComponents.size();
- int ret = 0;
- for(int i = 0; i < components; i++) {
- if(!powerComponents.get(i).hasUidInformation()) {
- ret |= 1 << i;
- }
- }
- return ret;
- }
-
- public int[] getComponentHistory(int count, int componentId, int uid,
- long iteration) {
- if(iteration == -1) synchronized(iterationLock) {
- iteration = lastWrittenIteration;
- }
- int components = powerComponents.size();
- if(componentId == ALL_COMPONENTS) {
- int[] result = new int[count];
- for(int i = 0; i < components; i++) {
- int[] comp = histories.get(i).get(uid, iteration, count);
- for(int j = 0; j < count; j++) {
- result[j] += comp[j];
- }
- }
- return result;
- }
- if(componentId < 0 || components <= componentId) return null;
- return histories.get(componentId).get(uid, iteration, count);
- }
-
- public long[] getTotals(int uid, int windowType) {
- int components = powerComponents.size();
- long[] ret = new long[components];
- for(int i = 0; i < components; i++) {
- ret[i] = histories.get(i).getTotal(uid, windowType) *
- ITERATION_INTERVAL / 1000;
- }
- return ret;
- }
-
- public long getRuntime(int uid, int windowType) {
- long runningTime = 0;
- int components = powerComponents.size();
- for(int i = 0; i < components; i++) {
- long entries = histories.get(i).getCount(uid, windowType);
- runningTime = entries > runningTime ? entries : runningTime;
- }
- return runningTime * ITERATION_INTERVAL / 1000;
- }
-
- public long[] getMeans(int uid, int windowType) {
- long[] ret = getTotals(uid, windowType);
- long runningTime = getRuntime(uid, windowType);
- runningTime = runningTime == 0 ? 1 : runningTime;
- for(int i = 0; i < ret.length; i++) {
- ret[i] /= runningTime;
- }
- return ret;
- }
-
- public UidInfo[] getUidInfo(int windowType, int ignoreMask) {
- long iteration;
- synchronized(iterationLock) {
- iteration = lastWrittenIteration;
- }
- int components = powerComponents.size();
- synchronized(uidAppIds) {
- int pos = 0;
- UidInfo[] result = new UidInfo[uidAppIds.size()];
- for(Integer uid : uidAppIds.keySet()) {
- UidInfo info = UidInfo.obtain();
- int currentPower = 0;
- for(int i = 0; i < components; i++) {
- if((ignoreMask & 1 << i) == 0) {
- currentPower += histories.get(i).get(uid, iteration, 1)[0];
- }
- }
- double scale = ITERATION_INTERVAL / 1000.0;
- info.init(uid, currentPower,
- sumArray(getTotals(uid, windowType), ignoreMask) *
- ITERATION_INTERVAL / 1000,
- getRuntime(uid, windowType) * ITERATION_INTERVAL / 1000);
- result[pos++] = info;
- }
- return result;
- }
- }
-
- private long sumArray(long[] A, int ignoreMask) {
- long ret = 0;
- for(int i = 0; i < A.length; i++) {
- if((ignoreMask & 1 << i) == 0) {
- ret += A[i];
- }
- }
- return ret;
- }
-
- public long getUidExtra(String name, int uid) {
- if("OLEDSCORE".equals(name)) {
- long entries = oledScoreHistory.getCount(uid, Counter.WINDOW_TOTAL);
- if(entries <= 0) return -2;
- double result = oledScoreHistory.getTotal(uid, Counter.WINDOW_TOTAL) /
- 1000.0;
- result /= entries;
- PhoneConstants phoneConstants = PhoneSelector.getConstants(context);
- result *= 255 / (phoneConstants.getMaxPower("OLED") -
- phoneConstants.oledBasePower());
- return (long)Math.round(result * 100);
- }
- return -1;
- }
-}
-
+/*
+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 .
+
+Please send inquiries to powertutor@umich.edu
+*/
+
+package edu.umich.PowerTutor.service;
+
+import edu.umich.PowerTutor.components.PowerComponent;
+import edu.umich.PowerTutor.components.OLED;
+import edu.umich.PowerTutor.phone.PhoneSelector;
+import edu.umich.PowerTutor.phone.PhoneConstants;
+import edu.umich.PowerTutor.phone.PowerFunction;
+import edu.umich.PowerTutor.util.BatteryStats;
+import edu.umich.PowerTutor.util.Counter;
+import edu.umich.PowerTutor.util.HistoryBuffer;
+import edu.umich.PowerTutor.util.NotificationService;
+import edu.umich.PowerTutor.util.SystemInfo;
+import edu.umich.PowerTutor.widget.PowerWidget;
+
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.os.SystemClock;
+import android.preference.PreferenceManager;
+import android.provider.Settings;
+import android.util.Log;
+import android.util.SparseArray;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.text.DecimalFormat;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Vector;
+import java.util.zip.Deflater;
+import java.util.zip.DeflaterOutputStream;
+
+/** This class is responsible for starting the individual power component
+ * loggers (CPU, GPS, etc...) and collecting the information they generate.
+ * This information is used both to write a log file that will be send back
+ * to spidermoneky (or looked at by the user) and to implement the
+ * ICounterService IPC interface.
+ */
+public class PowerEstimator implements Runnable {
+ private static final String TAG = "PowerEstimator";
+
+ /* A dictionary used to assist in compression of the log files. Strings that
+ * appear more frequently should be put towards the end of the dictionary. It
+ * is not critical that every string that be written to the log appear here.
+ */
+ private static final String DEFLATE_DICTIONARY =
+ "onoffidleoff-hookringinglowairplane-modebatteryedgeGPRS3Gunknown" +
+ "in-serviceemergency-onlyout-of-servicepower-offdisconnectedconnecting" +
+ "associateconnectedsuspendedphone-callservicenetworkbegin.0123456789" +
+ "GPSAudioWifi3GLCDCPU-power ";
+
+ public static final int ALL_COMPONENTS = -1;
+ public static final int ITERATION_INTERVAL = 1000; // 1 second
+
+ private UMLoggerService context;
+ private SharedPreferences prefs;
+ private boolean plugged;
+
+ private Vector powerComponents;
+ private Vector powerFunctions;
+ private Vector histories;
+ private Map uidAppIds;
+
+ // Miscellaneous data.
+ private HistoryBuffer oledScoreHistory;
+
+ private Object fileWriteLock = new Object();
+ private LogUploader logUploader;
+ private OutputStreamWriter logStream;
+ private DeflaterOutputStream deflateStream;
+
+ private Object iterationLock = new Object();
+ private long lastWrittenIteration;
+
+ public PowerEstimator(UMLoggerService context){
+ this.context = context;
+ prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ powerComponents = new Vector();
+ powerFunctions = new Vector();
+ uidAppIds = new HashMap();
+ PhoneSelector.generateComponents(context, powerComponents, powerFunctions);
+
+ histories = new Vector();
+ for(int i = 0; i < powerComponents.size(); i++) {
+ histories.add(new HistoryBuffer(300));
+ }
+ oledScoreHistory = new HistoryBuffer(0);
+
+ logUploader = new LogUploader(context);
+ openLog(true);
+ }
+
+ private void openLog(boolean init) {
+ /* Open up the log file if possible. */
+ try {
+ String logFilename = context.getFileStreamPath(
+ "PowerTrace.log").getAbsolutePath();
+ if(init && prefs.getBoolean("sendPermission", true) &&
+ new File(logFilename).length() > 0) {
+ /* There is data to send. Make sure that gets going in the sending
+ * process before we write over any old logs.
+ */
+ logUploader.upload(logFilename);
+ }
+ Deflater deflater = new Deflater();
+ deflater.setDictionary(DEFLATE_DICTIONARY.getBytes());
+ deflateStream = new DeflaterOutputStream(
+ new FileOutputStream(logFilename));
+ logStream = new OutputStreamWriter(deflateStream);
+ } catch(IOException e) {
+ logStream = null;
+ Log.e(TAG, "Failed to open log file. No log will be kept.");
+ }
+ }
+
+ /** This is the loop that keeps updating the power profile
+ */
+ public void run() {
+ SystemInfo sysInfo = SystemInfo.getInstance();
+ PackageManager pm = context.getPackageManager();
+ BatteryStats bst = BatteryStats.getInstance();
+
+ int components = powerComponents.size();
+ long beginTime = SystemClock.elapsedRealtime();
+ for(int i = 0; i < components; i++) {
+ powerComponents.get(i).init(beginTime, ITERATION_INTERVAL);
+ powerComponents.get(i).start();
+ }
+ IterationData[] dataTemp = new IterationData[components];
+
+ PhoneConstants phoneConstants = PhoneSelector.getConstants(context);
+ long[] memInfo = new long[4];
+
+ int oledId = -1;
+ for(int i = 0; i < components; i++) {
+ if("OLED".equals(powerComponents.get(i).getComponentName())) {
+ oledId = i;
+ break;
+ }
+ }
+
+ double lastCurrent = -1;
+
+ /* Indefinitely collect data on each of the power components. */
+ boolean firstLogIteration = true;
+ for(long iter = -1; !Thread.interrupted(); ) {
+ long curTime = SystemClock.elapsedRealtime();
+ /* Compute the next iteration that we can make the ending of. We wait
+ for the end of the iteration so that the components had a chance to
+ collect data already.
+ */
+ iter = (long)Math.max(iter + 1,
+ (curTime - beginTime) / ITERATION_INTERVAL);
+ /* Sleep until the next iteration completes. */
+ try {
+ Thread.currentThread().sleep(
+ beginTime + (iter + 1) * ITERATION_INTERVAL - curTime);
+ } catch(InterruptedException e) {
+ break;
+ }
+
+ int totalPower = 0;
+ for(int i = 0; i < components; i++) {
+ PowerComponent comp = powerComponents.get(i);
+ IterationData data = comp.getData(iter);
+ dataTemp[i] = data;
+ if(data == null) {
+ /* No data present for this timestamp. No power charged.
+ */
+ continue;
+ }
+
+ SparseArray uidPower = data.getUidPowerData();
+ for(int j = 0; j < uidPower.size(); j++) {
+ int uid = uidPower.keyAt(j);
+ PowerData powerData = uidPower.valueAt(j);
+ int power = (int)powerFunctions.get(i).calculate(powerData);
+ powerData.setCachedPower(power);
+ histories.get(i).add(uid, iter, power);
+ if(uid == SystemInfo.AID_ALL) {
+ totalPower += power;
+ }
+ if(i == oledId) {
+ OLED.OledData oledData = (OLED.OledData)powerData;
+ if(oledData.pixPower >= 0) {
+ oledScoreHistory.add(uid, iter, (int)(1000 * oledData.pixPower));
+ }
+ }
+ }
+ }
+
+ /* Update the uid set. */
+ synchronized(fileWriteLock) { synchronized(uidAppIds) {
+ for(int i = 0; i < components; i++) {
+ IterationData data = dataTemp[i];
+ if(data == null) {
+ continue;
+ }
+ SparseArray uidPower = data.getUidPowerData();
+ for(int j = 0; j < uidPower.size(); j++) {
+ int uid = uidPower.keyAt(j);
+ if(uid < SystemInfo.AID_APP) {
+ uidAppIds.put(uid, null);
+ } else {
+ /* We only want to update app names when logging so the associcate
+ * message gets written.
+ */
+ String appId = uidAppIds.get(uid);
+ String newAppId = sysInfo.getAppId(uid, pm);
+ if(!firstLogIteration && logStream != null &&
+ (appId == null || !appId.equals(newAppId))) {
+ try {
+ logStream.write("associate " + uid + " " + newAppId + "\n");
+ } catch(IOException e) {
+ Log.w(TAG, "Failed to write to log file");
+ }
+ }
+ uidAppIds.put(uid, newAppId);
+ }
+ }
+ }
+ }}
+
+ synchronized(iterationLock) {
+ lastWrittenIteration = iter;
+ }
+
+ /* Update the icon display every 15 iterations. */
+ if(iter % 15 == 14) {
+ final double POLY_WEIGHT = 0.02;
+ int count = 0;
+ int[] history = getComponentHistory(5 * 60, -1,
+ SystemInfo.AID_ALL, -1);
+ double weightedAvgPower = 0;
+ for(int i = history.length - 1; i >= 0; i--) {
+ if(history[i] != 0) {
+ count++;
+ weightedAvgPower *= 1.0 - POLY_WEIGHT;
+ weightedAvgPower += POLY_WEIGHT * history[i] / 1000.0;
+ }
+ }
+ double avgPower = -1;
+ if(count != 0) {
+ avgPower = weightedAvgPower /
+ (1.0 - Math.pow(1.0 - POLY_WEIGHT, count));
+ }
+ avgPower *= 1000;
+
+ context.updateNotification((int)Math.min(8, 1 +
+ 8 * avgPower / phoneConstants.maxPower()),
+ avgPower);
+ }
+
+ /* Update the widget. */
+ if(iter % 60 == 0) {
+ PowerWidget.updateWidget(context, this);
+ }
+
+ if(bst.hasCurrent()) {
+ double current = bst.getCurrent();
+ if(current != lastCurrent) {
+ writeToLog("batt_current " + current + "\n");
+ lastCurrent = current;
+ }
+ }
+ if(iter % (5*60) == 0) {
+ if(bst.hasTemp()) {
+ writeToLog("batt_temp " + bst.getTemp() + "\n");
+ }
+ if(bst.hasCharge()) {
+ writeToLog("batt_charge " + bst.getCharge() + "\n");
+ }
+ }
+ if(iter % (30*60) == 0) {
+ if(Settings.System.getInt(context.getContentResolver(),
+ "screen_brightness_mode", 0) != 0) {
+ writeToLog("setting_brightness automatic\n");
+ } else {
+ int brightness = Settings.System.getInt(
+ context.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS, -1);
+ if(brightness != -1) {
+ writeToLog("setting_brightness " + brightness + "\n");
+ }
+ }
+ int timeout = Settings.System.getInt(
+ context.getContentResolver(),
+ Settings.System.SCREEN_OFF_TIMEOUT, -1);
+ if(timeout != -1) {
+ writeToLog("setting_screen_timeout " + timeout + "\n");
+ }
+ String httpProxy = Settings.Secure.getString(
+ context.getContentResolver(),
+ Settings.Secure.HTTP_PROXY);
+ if(httpProxy != null) {
+ writeToLog("setting_httpproxy " + httpProxy + "\n");
+ }
+ }
+
+ /* Let's only grab memory information every 10 seconds to try to keep log
+ * file size down and the notice_data table size down.
+ */
+ boolean hasMem = false;
+ if(iter % 10 == 0) {
+ hasMem = sysInfo.getMemInfo(memInfo);
+ }
+
+ synchronized(fileWriteLock) {
+ if(logStream != null) try {
+ if(firstLogIteration) {
+ firstLogIteration = false;
+ logStream.write("time " + System.currentTimeMillis() + "\n");
+ Calendar cal = new GregorianCalendar();
+ logStream.write("localtime_offset " +
+ (cal.get(Calendar.ZONE_OFFSET) +
+ cal.get(Calendar.DST_OFFSET)) + "\n");
+ logStream.write("model " + phoneConstants.modelName() + "\n");
+ if(NotificationService.available()) {
+ logStream.write("notifications-active\n");
+ }
+ if(bst.hasFullCapacity()) {
+ logStream.write("batt_full_capacity " + bst.getFullCapacity()
+ + "\n");
+ }
+ synchronized(uidAppIds) {
+ for(int uid : uidAppIds.keySet()) {
+ if(uid < SystemInfo.AID_APP) {
+ continue;
+ }
+ logStream.write("associate " + uid + " " + uidAppIds.get(uid)
+ + "\n");
+ }
+ }
+ }
+ logStream.write("begin " + iter + "\n");
+ logStream.write("total-power " + (long)Math.round(totalPower) + '\n');
+ if(hasMem) {
+ logStream.write("meminfo " + memInfo[0] + " " + memInfo[1] +
+ " " + memInfo[2] + " " + memInfo[3] + "\n");
+ }
+ for(int i = 0; i < components; i++) {
+ IterationData data = dataTemp[i];
+ if(data != null) {
+ String name = powerComponents.get(i).getComponentName();
+ SparseArray uidData = data.getUidPowerData();
+ for(int j = 0; j < uidData.size(); j++) {
+ int uid = uidData.keyAt(j);
+ PowerData powerData = uidData.valueAt(j);
+ if(uid == SystemInfo.AID_ALL) {
+ logStream.write(name + " " + (long)Math.round(
+ powerData.getCachedPower()) + "\n");
+ powerData.writeLogDataInfo(logStream);
+ } else {
+ logStream.write(name + "-" + uid + " " + (long)Math.round(
+ powerData.getCachedPower()) + "\n");
+ }
+ }
+ data.recycle();
+ }
+ }
+ } catch(IOException e) {
+ Log.w(TAG, "Failed to write to log file");
+ }
+
+ if(iter % 15 == 0 && prefs.getBoolean("sendPermission", true)) {
+ /* Allow for LogUploader to decide if the log needs to be uploaded and
+ * begin uploading if it decides it's necessary.
+ */
+ if(logUploader.shouldUpload()) {
+ try {
+ logStream.close();
+ } catch(IOException e) {
+ Log.w(TAG, "Failed to flush and close log stream");
+ }
+ logStream = null;
+ logUploader.upload(context.getFileStreamPath(
+ "PowerTrace.log").getAbsolutePath());
+ openLog(false);
+ firstLogIteration = true;
+ }
+ }
+ }
+ }
+
+ /* Blank the widget's display and turn off power button. */
+ PowerWidget.updateWidgetDone(context);
+
+ /* Have all of the power component threads exit. */
+ logUploader.interrupt();
+ for(int i = 0; i < components; i++) {
+ powerComponents.get(i).interrupt();
+ }
+ try {
+ logUploader.join();
+ } catch(InterruptedException e) {
+ }
+ for(int i = 0; i < components; i++) {
+ try {
+ powerComponents.get(i).join();
+ } catch(InterruptedException e) {
+ }
+ }
+
+ /* Close the logstream so that everything gets flushed and written to file
+ * before we have to quit.
+ */
+ synchronized(fileWriteLock) {
+ if(logStream != null) try {
+ logStream.close();
+ } catch(IOException e) {
+ Log.w(TAG, "Failed to flush log file on exit");
+ }
+ }
+ }
+
+ public void plug(boolean plugged) {
+ logUploader.plug(plugged);
+ }
+
+ public void writeToLog(String m) {
+ synchronized(fileWriteLock) {
+ if(logStream != null) try {
+ logStream.write(m);
+ } catch(IOException e) {
+ Log.w(TAG, "Failed to write message to power log");
+ }
+ }
+ }
+
+ public String[] getComponents() {
+ int components = powerComponents.size();
+ String[] ret = new String[components];
+ for(int i = 0; i < components; i++) {
+ ret[i] = powerComponents.get(i).getComponentName();
+ }
+ return ret;
+ }
+
+ public int[] getComponentsMaxPower() {
+ PhoneConstants constants = PhoneSelector.getConstants(context);
+ int components = powerComponents.size();
+ int[] ret = new int[components];
+ for(int i = 0; i < components; i++) {
+ ret[i] = (int)constants.getMaxPower(
+ powerComponents.get(i).getComponentName());
+ }
+ return ret;
+ }
+
+ public int getNoUidMask() {
+ int components = powerComponents.size();
+ int ret = 0;
+ for(int i = 0; i < components; i++) {
+ if(!powerComponents.get(i).hasUidInformation()) {
+ ret |= 1 << i;
+ }
+ }
+ return ret;
+ }
+
+ public int[] getComponentHistory(int count, int componentId, int uid,
+ long iteration) {
+ if(iteration == -1) synchronized(iterationLock) {
+ iteration = lastWrittenIteration;
+ }
+ int components = powerComponents.size();
+ if(componentId == ALL_COMPONENTS) {
+ int[] result = new int[count];
+ for(int i = 0; i < components; i++) {
+ int[] comp = histories.get(i).get(uid, iteration, count);
+ for(int j = 0; j < count; j++) {
+ result[j] += comp[j];
+ }
+ }
+ return result;
+ }
+ if(componentId < 0 || components <= componentId) return null;
+ return histories.get(componentId).get(uid, iteration, count);
+ }
+
+ public long[] getTotals(int uid, int windowType) {
+ int components = powerComponents.size();
+ long[] ret = new long[components];
+ for(int i = 0; i < components; i++) {
+ ret[i] = histories.get(i).getTotal(uid, windowType) *
+ ITERATION_INTERVAL / 1000;
+ }
+ return ret;
+ }
+
+ public long getRuntime(int uid, int windowType) {
+ long runningTime = 0;
+ int components = powerComponents.size();
+ for(int i = 0; i < components; i++) {
+ long entries = histories.get(i).getCount(uid, windowType);
+ runningTime = entries > runningTime ? entries : runningTime;
+ }
+ return runningTime * ITERATION_INTERVAL / 1000;
+ }
+
+ public long[] getMeans(int uid, int windowType) {
+ long[] ret = getTotals(uid, windowType);
+ long runningTime = getRuntime(uid, windowType);
+ runningTime = runningTime == 0 ? 1 : runningTime;
+ for(int i = 0; i < ret.length; i++) {
+ ret[i] /= runningTime;
+ }
+ return ret;
+ }
+
+ public UidInfo[] getUidInfo(int windowType, int ignoreMask) {
+ long iteration;
+ synchronized(iterationLock) {
+ iteration = lastWrittenIteration;
+ }
+ int components = powerComponents.size();
+ synchronized(uidAppIds) {
+ int pos = 0;
+ UidInfo[] result = new UidInfo[uidAppIds.size()];
+ for(Integer uid : uidAppIds.keySet()) {
+ UidInfo info = UidInfo.obtain();
+ int currentPower = 0;
+ for(int i = 0; i < components; i++) {
+ if((ignoreMask & 1 << i) == 0) {
+ currentPower += histories.get(i).get(uid, iteration, 1)[0];
+ }
+ }
+ double scale = ITERATION_INTERVAL / 1000.0;
+ info.init(uid, currentPower,
+ sumArray(getTotals(uid, windowType), ignoreMask) *
+ ITERATION_INTERVAL / 1000,
+ getRuntime(uid, windowType) * ITERATION_INTERVAL / 1000);
+ result[pos++] = info;
+ }
+ return result;
+ }
+ }
+
+ private long sumArray(long[] A, int ignoreMask) {
+ long ret = 0;
+ for(int i = 0; i < A.length; i++) {
+ if((ignoreMask & 1 << i) == 0) {
+ ret += A[i];
+ }
+ }
+ return ret;
+ }
+
+ public long getUidExtra(String name, int uid) {
+ if("OLEDSCORE".equals(name)) {
+ long entries = oledScoreHistory.getCount(uid, Counter.WINDOW_TOTAL);
+ if(entries <= 0) return -2;
+ double result = oledScoreHistory.getTotal(uid, Counter.WINDOW_TOTAL) /
+ 1000.0;
+ result /= entries;
+ PhoneConstants phoneConstants = PhoneSelector.getConstants(context);
+ result *= 255 / (phoneConstants.getMaxPower("OLED") -
+ phoneConstants.oledBasePower());
+ return (long)Math.round(result * 100);
+ }
+ return -1;
+ }
+}
+
diff --git a/src/edu/umich/PowerTutor/service/UMLoggerService.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/service/UMLoggerService.java
similarity index 88%
rename from src/edu/umich/PowerTutor/service/UMLoggerService.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/service/UMLoggerService.java
index 0c144df..1827d83 100644
--- a/src/edu/umich/PowerTutor/service/UMLoggerService.java
+++ b/PowerTutor2/src/main/java/edu/umich/PowerTutor/service/UMLoggerService.java
@@ -1,394 +1,427 @@
-/*
-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 .
-
-Please send inquiries to powertutor@umich.edu
-*/
-
-package edu.umich.PowerTutor.service;
-
-import edu.umich.PowerTutor.R;
-import edu.umich.PowerTutor.ui.PowerTabs;
-import edu.umich.PowerTutor.ui.UMLogger;
-import edu.umich.PowerTutor.util.BatteryStats;
-import edu.umich.PowerTutor.util.SystemInfo;
-
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.app.Service;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.BatteryManager;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.telephony.PhoneStateListener;
-import android.telephony.ServiceState;
-import android.telephony.TelephonyManager;
-import android.util.Log;
-
-import java.io.ByteArrayOutputStream;
-import java.io.ObjectOutputStream;
-import java.io.IOException;
-
-import java.lang.reflect.Method;
-import java.lang.reflect.InvocationTargetException;
-
-public class UMLoggerService extends Service{
- private static final String TAG = "UMLoggerService";
-
- private static final int NOTIFICATION_ID = 1;
- private static final int NOTIFICATION_ID_LETTER = 2;
-
- private Thread estimatorThread;
- private PowerEstimator powerEstimator;
-
- private Notification notification;
-
- private NotificationManager notificationManager;
- private TelephonyManager phoneManager;
-
- @Override
- public IBinder onBind(Intent intent) {
- return binder;
- }
-
- @Override
- public void onCreate() {
- powerEstimator = new PowerEstimator(this);
-
- /* Register to receive phone state messages. */
- phoneManager = (TelephonyManager)this.getSystemService(TELEPHONY_SERVICE);
- phoneManager.listen(phoneListener, PhoneStateListener.LISTEN_CALL_STATE |
- PhoneStateListener.LISTEN_DATA_CONNECTION_STATE |
- PhoneStateListener.LISTEN_SERVICE_STATE |
- PhoneStateListener.LISTEN_SIGNAL_STRENGTH);
-
- /* Register to receive airplane mode and battery low messages. */
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
- filter.addAction(Intent.ACTION_BATTERY_LOW);
- filter.addAction(Intent.ACTION_BATTERY_CHANGED);
- filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
- registerReceiver(broadcastIntentReceiver, filter);
-
- notificationManager = (NotificationManager)getSystemService(
- NOTIFICATION_SERVICE);
- }
-
- @Override
- public void onStart(Intent intent, int startId) {
- super.onStart(intent, startId);
-//android.os.Debug.startMethodTracing("pt.trace");
-
- if(intent.getBooleanExtra("stop", false)) {
- stopSelf();
- return;
- } else if(estimatorThread != null) {
- return;
- }
- showNotification();
- estimatorThread = new Thread(powerEstimator);
- estimatorThread.start();
- }
-
- @Override
- public void onDestroy() {
-//android.os.Debug.stopMethodTracing();
- if(estimatorThread != null) {
- estimatorThread.interrupt();
- while(estimatorThread.isAlive()) {
- try {
- estimatorThread.join();
- } catch(InterruptedException e) {
- }
- }
- }
- unregisterReceiver(broadcastIntentReceiver);
-
- /* See comments in showNotification() for why we are using reflection here.
- */
- boolean foregroundSet = false;
- try {
- Method stopForeground = getClass().getMethod("stopForeground",
- boolean.class);
- stopForeground.invoke(this, true);
- foregroundSet = true;
- } catch (InvocationTargetException e) {
- } catch (IllegalAccessException e) {
- } catch(NoSuchMethodException e) {
- }
- if(!foregroundSet) {
- setForeground(false);
- notificationManager.cancel(NOTIFICATION_ID);
- }
-
- super.onDestroy();
- };
-
- /** This function is to construct the real-time updating notification*/
- public void showNotification(){
- int icon = R.drawable.level;
-
- // icon from resources
- CharSequence tickerText = "PowerTutor"; // ticker-text
- long when = System.currentTimeMillis(); // notification time
- Context context = getApplicationContext(); // application Context
- CharSequence contentTitle = "PowerTutor"; // expanded message title
- CharSequence contentText = ""; // expanded message text
-
- Intent notificationIntent = new Intent(this, UMLogger.class);
- notificationIntent.putExtra("isFromIcon", true);
- PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
- notificationIntent,
- PendingIntent.FLAG_UPDATE_CURRENT);
- /* the next two lines initialize the Notification, using the
- * configurations above.
- */
- notification = new Notification(icon, tickerText, when);
- notification.iconLevel = 2;
- notification.setLatestEventInfo(context, contentTitle,
- contentText, contentIntent);
-
- /* We need to set the service to run in the foreground so that system
- * won't try to destroy the power logging service except in the most
- * critical situations (which should be fairly rare). Due to differences
- * in apis across versions of android we have to use reflection. The newer
- * api simultaneously sets an app to be in the foreground while adding a
- * notification icon so services can't 'hide' in the foreground.
- * In the new api the old call, setForeground, does nothing.
- * See: http://developer.android.com/reference/android/app/Service.html#startForeground%28int,%20android.app.Notification%29
- */
- boolean foregroundSet = false;
- try {
- Method startForeground = getClass().getMethod("startForeground",
- int.class, Notification.class);
- startForeground.invoke(this, NOTIFICATION_ID, notification);
- foregroundSet = true;
- } catch (InvocationTargetException e) {
- } catch (IllegalAccessException e) {
- } catch(NoSuchMethodException e) {
- }
- if(!foregroundSet) {
- setForeground(true);
- notificationManager.notify(NOTIFICATION_ID, notification);
- }
- }
-
- /* This function is to update the notification in real time. This function
- * is apparently fairly expensive cpu wise. Updating once a second caused a
- * 8% cpu utilization penalty.
- */
- public void updateNotification(int level, double totalPower) {
- notification.icon = R.drawable.level;
- notification.iconLevel = level;
-
- // If we know how much charge the battery has left we'll override the
- // normal icon with one that indicates how much time the user can expect
- // left.
- BatteryStats bst = BatteryStats.getInstance();
- if(bst.hasCharge() && bst.hasVoltage()) {
- double charge = bst.getCharge();
- double volt = bst.getVoltage();
- if(charge > 0 && volt > 0) {
- notification.icon = R.drawable.time;
-
- double minutes = charge * volt / (totalPower / 1000) / 60;
- if(minutes < 55) {
- notification.iconLevel = 1 +
- (int)Math.max(0, Math.round(minutes / 10.0) - 1);
- } else {
- notification.iconLevel = (int)Math.min(13,
- 6 + Math.max(0, Math.round(minutes / 60.0) - 1));
- }
- }
- }
-
- CharSequence contentTitle = "PowerTutor";
- CharSequence contentText = "Total Power: " + (int)Math.round(totalPower) +
- " mW";
-
- /* When the user selects the notification the tab view for global power
- * usage will appear.
- */
- Intent notificationIntent = new Intent(this, UMLogger.class);
- notificationIntent.putExtra("isFromIcon", true);
- PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
- notificationIntent, 0);
- notification.setLatestEventInfo(this, contentTitle, contentText,
- contentIntent);
- notificationManager.notify(NOTIFICATION_ID, notification);
- }
-
- private final ICounterService.Stub binder =
- new ICounterService.Stub() {
- public String[] getComponents() {
- return powerEstimator.getComponents();
- }
-
- public int[] getComponentsMaxPower() {
- return powerEstimator.getComponentsMaxPower();
- }
-
- public int getNoUidMask() {
- return powerEstimator.getNoUidMask();
- }
-
- public int[] getComponentHistory(int count, int componentId, int uid) {
- return powerEstimator.getComponentHistory(count, componentId, uid, -1);
- }
-
- public long[] getTotals(int uid, int windowType) {
- return powerEstimator.getTotals(uid, windowType);
- }
-
- public long getRuntime(int uid, int windowType) {
- return powerEstimator.getRuntime(uid, windowType);
- }
-
- public long[] getMeans(int uid, int windowType) {
- return powerEstimator.getMeans(uid, windowType);
- }
-
- public byte[] getUidInfo(int windowType, int ignoreMask) {
- UidInfo[] infos = powerEstimator.getUidInfo(windowType, ignoreMask);
- ByteArrayOutputStream output = new ByteArrayOutputStream();
- try {
- new ObjectOutputStream(output).writeObject(infos);
- } catch(IOException e) {
- return null;
- }
- for(UidInfo info : infos) {
- info.recycle();
- }
- return output.toByteArray();
- }
-
- public long getUidExtra(String name, int uid) {
- return powerEstimator.getUidExtra(name, uid);
- }
- };
-
-
- BroadcastReceiver broadcastIntentReceiver = new BroadcastReceiver() {
- public void onReceive(Context context, Intent intent) {
- if(intent.getAction().equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
- Bundle extra = intent.getExtras();
- try {
- if ((Boolean)extra.get("state")) {
- powerEstimator.writeToLog("airplane-mode on\n");
- } else {
- powerEstimator.writeToLog("airplane-mode off\n");
- }
- } catch(ClassCastException e) {
- // Some people apparently are having this problem. I'm not really
- // sure why this should happen.
- Log.w(TAG, "Couldn't determine airplane mode state");
- }
- } else if(intent.getAction().equals(Intent.ACTION_BATTERY_LOW)) {
- powerEstimator.writeToLog("battery low\n");
- } else if(intent.getAction().equals(Intent.ACTION_BATTERY_CHANGED)) {
- powerEstimator.writeToLog("battery-change " +
- intent.getIntExtra("plugged", -1) + " " +
- intent.getIntExtra("level", -1) + "/" +
- intent.getIntExtra("scale", -1) + " " +
- intent.getIntExtra("voltage", -1) +
- intent.getIntExtra("temperature", -1) + "\n");
- powerEstimator.plug(
- intent.getIntExtra("plugged", -1) != 0);
- } else if(intent.getAction().equals(Intent.ACTION_PACKAGE_REMOVED) ||
- intent.getAction().equals(Intent.ACTION_PACKAGE_REPLACED)) {
- // A package has either been removed or its metadata has changed and we
- // need to clear the cache of metadata for that app.
- SystemInfo.getInstance().voidUidCache(
- intent.getIntExtra(Intent.EXTRA_UID, -1));
- }
- };
- };
-
- PhoneStateListener phoneListener = new PhoneStateListener() {
- public void onServiceStateChanged(ServiceState serviceState) {
- switch(serviceState.getState()) {
- case ServiceState.STATE_EMERGENCY_ONLY:
- powerEstimator.writeToLog("phone-service emergency-only\n");
- break;
- case ServiceState.STATE_IN_SERVICE:
- powerEstimator.writeToLog("phone-service in-service\n");
- switch(phoneManager.getNetworkType()) {
- case(TelephonyManager.NETWORK_TYPE_EDGE):
- powerEstimator.writeToLog("phone-network edge\n");
- break;
- case(TelephonyManager.NETWORK_TYPE_GPRS):
- powerEstimator.writeToLog("phone-network GPRS\n");
- break;
- case 8:
- powerEstimator.writeToLog("phone-network HSDPA\n");
- break;
- case(TelephonyManager.NETWORK_TYPE_UMTS):
- powerEstimator.writeToLog("phone-network UMTS\n");
- break;
- default:
- powerEstimator.writeToLog("phone-network " +
- phoneManager.getNetworkType() + "\n");
- }
- break;
- case ServiceState.STATE_OUT_OF_SERVICE:
- powerEstimator.writeToLog("phone-service out-of-service\n");
- break;
- case ServiceState.STATE_POWER_OFF:
- powerEstimator.writeToLog("phone-service power-off\n");
- break;
- }
- }
-
- public void onCallStateChanged(int state, String incomingNumber) {
- switch(state) {
- case TelephonyManager.CALL_STATE_IDLE:
- powerEstimator.writeToLog("phone-call idle\n");
- break;
- case TelephonyManager.CALL_STATE_OFFHOOK:
- powerEstimator.writeToLog("phone-call off-hook\n");
- break;
- case TelephonyManager.CALL_STATE_RINGING:
- powerEstimator.writeToLog("phone-call ringing\n");
- break;
- }
- }
-
- public void onDataConnectionStateChanged(int state) {
- switch(state) {
- case TelephonyManager.DATA_DISCONNECTED:
- powerEstimator.writeToLog("data disconnected\n");
- break;
- case TelephonyManager.DATA_CONNECTING:
- powerEstimator.writeToLog("data connecting\n");
- break;
- case TelephonyManager.DATA_CONNECTED:
- powerEstimator.writeToLog("data connected\n");
- break;
- case TelephonyManager.DATA_SUSPENDED:
- powerEstimator.writeToLog("data suspended\n");
- break;
- }
- }
-
- public void onSignalStrengthChanged(int asu) {
- powerEstimator.writeToLog("signal " + asu + "\n");
- }
- };
-}
+/*
+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 .
+
+Please send inquiries to powertutor@umich.edu
+*/
+
+package edu.umich.PowerTutor.service;
+
+import edu.umich.PowerTutor.R;
+import edu.umich.PowerTutor.ui.UMLogger;
+import edu.umich.PowerTutor.util.BatteryStats;
+import edu.umich.PowerTutor.util.SystemInfo;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.telephony.PhoneStateListener;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import androidx.core.app.NotificationCompat;
+
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectOutputStream;
+import java.io.IOException;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+
+public class UMLoggerService extends Service{
+ public static final String CHANNEL_ID = "edu.umich.PowerTutor.channel";
+ private static final String TAG = "UMLoggerService";
+
+ private static final int NOTIFICATION_ID = 1;
+ private static final int NOTIFICATION_ID_LETTER = 2;
+
+ private Thread estimatorThread;
+ private PowerEstimator powerEstimator;
+
+ private Notification notification;
+
+ private NotificationManager notificationManager;
+ private TelephonyManager phoneManager;
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return binder;
+ }
+
+ @Override
+ public void onCreate() {
+ powerEstimator = new PowerEstimator(this);
+
+ /* Register to receive phone state messages. */
+ phoneManager = (TelephonyManager)this.getSystemService(TELEPHONY_SERVICE);
+ phoneManager.listen(phoneListener, PhoneStateListener.LISTEN_CALL_STATE |
+ PhoneStateListener.LISTEN_DATA_CONNECTION_STATE |
+ PhoneStateListener.LISTEN_SERVICE_STATE |
+ PhoneStateListener.LISTEN_SIGNAL_STRENGTH);
+
+ /* Register to receive airplane mode and battery low messages. */
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ filter.addAction(Intent.ACTION_BATTERY_LOW);
+ filter.addAction(Intent.ACTION_BATTERY_CHANGED);
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+ registerReceiver(broadcastIntentReceiver, filter);
+
+ notificationManager = (NotificationManager)getSystemService(
+ NOTIFICATION_SERVICE);
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ super.onStartCommand(intent, flags, startId);
+//android.os.Debug.startMethodTracing("pt.trace");
+
+ createNotificationChannel();
+
+ if (intent.getBooleanExtra("stop", false)) {
+ stopSelf();
+ return START_NOT_STICKY;
+ } else if (estimatorThread != null) {
+ return START_NOT_STICKY;
+ }
+ showNotification();
+ estimatorThread = new Thread(powerEstimator);
+ estimatorThread.start();
+
+ return START_NOT_STICKY;
+ }
+
+ private void createNotificationChannel() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ NotificationChannel serviceChannel = new NotificationChannel(
+ CHANNEL_ID,
+ "Foreground Service Channel",
+ NotificationManager.IMPORTANCE_DEFAULT
+ );
+ NotificationManager manager = getSystemService(NotificationManager.class);
+ manager.createNotificationChannel(serviceChannel);
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+//android.os.Debug.stopMethodTracing();
+ if(estimatorThread != null) {
+ estimatorThread.interrupt();
+ while(estimatorThread.isAlive()) {
+ try {
+ estimatorThread.join();
+ } catch(InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ unregisterReceiver(broadcastIntentReceiver);
+
+ /* See comments in showNotification() for why we are using reflection here.
+ */
+ boolean foregroundSet = false;
+ try {
+ Method stopForeground = getClass().getMethod("stopForeground",
+ boolean.class);
+ stopForeground.invoke(this, true);
+ foregroundSet = true;
+ } catch (InvocationTargetException e) {
+ } catch (IllegalAccessException e) {
+ } catch(NoSuchMethodException e) {
+ }
+ if(!foregroundSet) {
+ //setForeground(false); // TODO: Uncommented
+ notificationManager.cancel(NOTIFICATION_ID);
+ }
+
+ super.onDestroy();
+ };
+
+ /** This function is to construct the real-time updating notification*/
+ public void showNotification(){
+ int icon = R.drawable.level;
+
+ // icon from resources
+ CharSequence tickerText = "PowerTutor"; // ticker-text
+ long when = System.currentTimeMillis(); // notification time
+ Context context = getApplicationContext(); // application Context
+ CharSequence contentTitle = "PowerTutor"; // expanded message title
+ CharSequence contentText = ""; // expanded message text
+
+ Intent notificationIntent = new Intent(this, UMLogger.class);
+ notificationIntent.putExtra("isFromIcon", true);
+ PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
+ notificationIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+ /* the next two lines initialize the Notification, using the
+ * configurations above.
+ */
+ Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
+ .setContentTitle(contentTitle)
+ .setContentText(contentText)
+ .setWhen(when)
+ .setSmallIcon(R.drawable.icon)
+ .setContentIntent(contentIntent)
+ .build();
+ notification.tickerText = tickerText;
+ notification.iconLevel = 2;
+
+ //notification = new Notification(icon, tickerText, when);
+ //notification.iconLevel = 2;
+ //notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent);
+
+ /* We need to set the service to run in the foreground so that system
+ * won't try to destroy the power logging service except in the most
+ * critical situations (which should be fairly rare). Due to differences
+ * in apis across versions of android we have to use reflection. The newer
+ * api simultaneously sets an app to be in the foreground while adding a
+ * notification icon so services can't 'hide' in the foreground.
+ * In the new api the old call, setForeground, does nothing.
+ * See: http://developer.android.com/reference/android/app/Service.html#startForeground%28int,%20android.app.Notification%29
+ */
+ boolean foregroundSet = false;
+ try {
+ Method startForeground = getClass().getMethod("startForeground",
+ int.class, Notification.class);
+ startForeground.invoke(this, NOTIFICATION_ID, notification);
+ foregroundSet = true;
+ } catch (InvocationTargetException e) {
+ } catch (IllegalAccessException e) {
+ } catch(NoSuchMethodException e) {
+ }
+ if(!foregroundSet) {
+ //setForeground(true);
+ startForeground(1, notification);
+ notificationManager.notify(NOTIFICATION_ID, notification);
+ }
+ }
+
+ /* This function is to update the notification in real time. This function
+ * is apparently fairly expensive cpu wise. Updating once a second caused a
+ * 8% cpu utilization penalty.
+ */
+ public void updateNotification(int level, double totalPower) {
+ // TODO: Updating notifcation not working right now
+ if (true) return;
+
+ notification.icon = R.drawable.level;
+ notification.iconLevel = level;
+
+ // If we know how much charge the battery has left we'll override the
+ // normal icon with one that indicates how much time the user can expect
+ // left.
+ BatteryStats bst = BatteryStats.getInstance();
+ if(bst.hasCharge() && bst.hasVoltage()) {
+ double charge = bst.getCharge();
+ double volt = bst.getVoltage();
+ if(charge > 0 && volt > 0) {
+ notification.icon = R.drawable.time;
+
+ double minutes = charge * volt / (totalPower / 1000) / 60;
+ if(minutes < 55) {
+ notification.iconLevel = 1 +
+ (int)Math.max(0, Math.round(minutes / 10.0) - 1);
+ } else {
+ notification.iconLevel = (int)Math.min(13,
+ 6 + Math.max(0, Math.round(minutes / 60.0) - 1));
+ }
+ }
+ }
+
+ CharSequence contentTitle = "PowerTutor";
+ CharSequence contentText = "Total Power: " + (int)Math.round(totalPower) +
+ " mW";
+
+ /* When the user selects the notification the tab view for global power
+ * usage will appear.
+ */
+ Intent notificationIntent = new Intent(this, UMLogger.class);
+ notificationIntent.putExtra("isFromIcon", true);
+ PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
+ notificationIntent, 0);
+ // TODO: Uncommented
+ //notification.setLatestEventInfo(this, contentTitle, contentText, contentIntent);
+ notificationManager.notify(NOTIFICATION_ID, notification);
+ }
+
+ private final ICounterService.Stub binder =
+ new ICounterService.Stub() {
+ public String[] getComponents() {
+ return powerEstimator.getComponents();
+ }
+
+ public int[] getComponentsMaxPower() {
+ return powerEstimator.getComponentsMaxPower();
+ }
+
+ public int getNoUidMask() {
+ return powerEstimator.getNoUidMask();
+ }
+
+ public int[] getComponentHistory(int count, int componentId, int uid) {
+ return powerEstimator.getComponentHistory(count, componentId, uid, -1);
+ }
+
+ public long[] getTotals(int uid, int windowType) {
+ return powerEstimator.getTotals(uid, windowType);
+ }
+
+ public long getRuntime(int uid, int windowType) {
+ return powerEstimator.getRuntime(uid, windowType);
+ }
+
+ public long[] getMeans(int uid, int windowType) {
+ return powerEstimator.getMeans(uid, windowType);
+ }
+
+ public byte[] getUidInfo(int windowType, int ignoreMask) {
+ UidInfo[] infos = powerEstimator.getUidInfo(windowType, ignoreMask);
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ try {
+ new ObjectOutputStream(output).writeObject(infos);
+ } catch(IOException e) {
+ return null;
+ }
+ for(UidInfo info : infos) {
+ info.recycle();
+ }
+ return output.toByteArray();
+ }
+
+ public long getUidExtra(String name, int uid) {
+ return powerEstimator.getUidExtra(name, uid);
+ }
+ };
+
+
+ BroadcastReceiver broadcastIntentReceiver = new BroadcastReceiver() {
+ public void onReceive(Context context, Intent intent) {
+ if(intent.getAction().equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
+ Bundle extra = intent.getExtras();
+ try {
+ if ((Boolean)extra.get("state")) {
+ powerEstimator.writeToLog("airplane-mode on\n");
+ } else {
+ powerEstimator.writeToLog("airplane-mode off\n");
+ }
+ } catch(ClassCastException e) {
+ // Some people apparently are having this problem. I'm not really
+ // sure why this should happen.
+ Log.w(TAG, "Couldn't determine airplane mode state");
+ }
+ } else if(intent.getAction().equals(Intent.ACTION_BATTERY_LOW)) {
+ powerEstimator.writeToLog("battery low\n");
+ } else if(intent.getAction().equals(Intent.ACTION_BATTERY_CHANGED)) {
+ powerEstimator.writeToLog("battery-change " +
+ intent.getIntExtra("plugged", -1) + " " +
+ intent.getIntExtra("level", -1) + "/" +
+ intent.getIntExtra("scale", -1) + " " +
+ intent.getIntExtra("voltage", -1) +
+ intent.getIntExtra("temperature", -1) + "\n");
+ powerEstimator.plug(
+ intent.getIntExtra("plugged", -1) != 0);
+ } else if(intent.getAction().equals(Intent.ACTION_PACKAGE_REMOVED) ||
+ intent.getAction().equals(Intent.ACTION_PACKAGE_REPLACED)) {
+ // A package has either been removed or its metadata has changed and we
+ // need to clear the cache of metadata for that app.
+ SystemInfo.getInstance().voidUidCache(
+ intent.getIntExtra(Intent.EXTRA_UID, -1));
+ }
+ };
+ };
+
+ PhoneStateListener phoneListener = new PhoneStateListener() {
+ public void onServiceStateChanged(ServiceState serviceState) {
+ switch(serviceState.getState()) {
+ case ServiceState.STATE_EMERGENCY_ONLY:
+ powerEstimator.writeToLog("phone-service emergency-only\n");
+ break;
+ case ServiceState.STATE_IN_SERVICE:
+ powerEstimator.writeToLog("phone-service in-service\n");
+ switch(phoneManager.getNetworkType()) {
+ case(TelephonyManager.NETWORK_TYPE_EDGE):
+ powerEstimator.writeToLog("phone-network edge\n");
+ break;
+ case(TelephonyManager.NETWORK_TYPE_GPRS):
+ powerEstimator.writeToLog("phone-network GPRS\n");
+ break;
+ case 8:
+ powerEstimator.writeToLog("phone-network HSDPA\n");
+ break;
+ case(TelephonyManager.NETWORK_TYPE_UMTS):
+ powerEstimator.writeToLog("phone-network UMTS\n");
+ break;
+ default:
+ powerEstimator.writeToLog("phone-network " +
+ phoneManager.getNetworkType() + "\n");
+ }
+ break;
+ case ServiceState.STATE_OUT_OF_SERVICE:
+ powerEstimator.writeToLog("phone-service out-of-service\n");
+ break;
+ case ServiceState.STATE_POWER_OFF:
+ powerEstimator.writeToLog("phone-service power-off\n");
+ break;
+ }
+ }
+
+ public void onCallStateChanged(int state, String incomingNumber) {
+ switch(state) {
+ case TelephonyManager.CALL_STATE_IDLE:
+ powerEstimator.writeToLog("phone-call idle\n");
+ break;
+ case TelephonyManager.CALL_STATE_OFFHOOK:
+ powerEstimator.writeToLog("phone-call off-hook\n");
+ break;
+ case TelephonyManager.CALL_STATE_RINGING:
+ powerEstimator.writeToLog("phone-call ringing\n");
+ break;
+ }
+ }
+
+ public void onDataConnectionStateChanged(int state) {
+ switch(state) {
+ case TelephonyManager.DATA_DISCONNECTED:
+ powerEstimator.writeToLog("data disconnected\n");
+ break;
+ case TelephonyManager.DATA_CONNECTING:
+ powerEstimator.writeToLog("data connecting\n");
+ break;
+ case TelephonyManager.DATA_CONNECTED:
+ powerEstimator.writeToLog("data connected\n");
+ break;
+ case TelephonyManager.DATA_SUSPENDED:
+ powerEstimator.writeToLog("data suspended\n");
+ break;
+ }
+ }
+
+ public void onSignalStrengthChanged(int asu) {
+ powerEstimator.writeToLog("signal " + asu + "\n");
+ }
+ };
+}
diff --git a/src/edu/umich/PowerTutor/service/UidInfo.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/service/UidInfo.java
similarity index 100%
rename from src/edu/umich/PowerTutor/service/UidInfo.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/service/UidInfo.java
diff --git a/src/edu/umich/PowerTutor/ui/EditPreferences.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/ui/EditPreferences.java
similarity index 100%
rename from src/edu/umich/PowerTutor/ui/EditPreferences.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/ui/EditPreferences.java
diff --git a/src/edu/umich/PowerTutor/ui/Help.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/ui/Help.java
similarity index 96%
rename from src/edu/umich/PowerTutor/ui/Help.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/ui/Help.java
index 67f29c4..6c0bd59 100644
--- a/src/edu/umich/PowerTutor/ui/Help.java
+++ b/PowerTutor2/src/main/java/edu/umich/PowerTutor/ui/Help.java
@@ -1,49 +1,49 @@
-/*
-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 .
-
-Please send inquiries to powertutor@umich.edu
-*/
-
-package edu.umich.PowerTutor.ui;
-
-import edu.umich.PowerTutor.R;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.view.View;
-import android.widget.TextView;
-
-/**This function implements the UI for help view*/
-public class Help extends Activity {
- private static final String powerTutorUrl = "http://powertutor.org";
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.help);
- TextView s2 = (TextView)this.findViewById(R.id.S2);
-
- s2.setOnClickListener(new TextView.OnClickListener() {
- public void onClick(View v) {
- Intent myIntent = new Intent(Intent.ACTION_VIEW,
- Uri.parse(powerTutorUrl));
- startActivity(myIntent);
- }
- });
- }
-}
+/*
+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 .
+
+Please send inquiries to powertutor@umich.edu
+*/
+
+package edu.umich.PowerTutor.ui;
+
+import edu.umich.PowerTutor.R;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.TextView;
+
+/**This function implements the UI for help view*/
+public class Help extends Activity {
+ private static final String powerTutorUrl = "http://powertutor.org";
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.help);
+ TextView s2 = (TextView)this.findViewById(R.id.S2);
+
+ s2.setOnClickListener(new TextView.OnClickListener() {
+ public void onClick(View v) {
+ Intent myIntent = new Intent(Intent.ACTION_VIEW,
+ Uri.parse(powerTutorUrl));
+ startActivity(myIntent);
+ }
+ });
+ }
+}
diff --git a/src/edu/umich/PowerTutor/ui/MiscView.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/ui/MiscView.java
similarity index 94%
rename from src/edu/umich/PowerTutor/ui/MiscView.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/ui/MiscView.java
index 3b8e484..7541cc3 100644
--- a/src/edu/umich/PowerTutor/ui/MiscView.java
+++ b/PowerTutor2/src/main/java/edu/umich/PowerTutor/ui/MiscView.java
@@ -1,478 +1,468 @@
-/*
-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 .
-
-Please send inquiries to powertutor@umich.edu
-*/
-
-package edu.umich.PowerTutor.ui;
-
-import edu.umich.PowerTutor.R;
-import edu.umich.PowerTutor.phone.PhoneSelector;
-import edu.umich.PowerTutor.service.ICounterService;
-import edu.umich.PowerTutor.service.PowerEstimator;
-import edu.umich.PowerTutor.service.UMLoggerService;
-import edu.umich.PowerTutor.util.Counter;
-import edu.umich.PowerTutor.util.BatteryStats;
-import edu.umich.PowerTutor.util.SystemInfo;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.ListActivity;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.content.SharedPreferences;
-import android.content.pm.PackageManager;
-import android.graphics.Paint;
-import android.os.Bundle;
-import android.os.Environment;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.preference.PreferenceManager;
-import android.util.Log;
-import android.view.Gravity;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.Button;
-import android.widget.LinearLayout;
-import android.widget.ScrollView;
-import android.widget.TextView;
-import android.widget.ListView;
-
-import java.util.ArrayList;
-import java.io.File;
-
-public class MiscView extends Activity {
- private static final String TAG = "MiscView";
-
- private SharedPreferences prefs;
- private int uid;
-
- private Runnable collector;
-
- private Intent serviceIntent;
- private CounterServiceConnection conn;
- private ICounterService counterService;
- private Handler handler;
-
- private BatteryStats batteryStats;
-
- private String[] componentNames;
-
- public void refreshView() {
- final ListView listView = new ListView(this);
-
- ArrayAdapter adapter = new ArrayAdapter(this, 0) {
- public View getView(int position, View convertView, ViewGroup parent) {
- View itemView = getLayoutInflater()
- .inflate(R.layout.misc_item_layout, listView, false);
- TextView title = (TextView)itemView.findViewById(R.id.title);
- TextView summary = (TextView)itemView.findViewById(R.id.summary);
- LinearLayout widgetGroup =
- (LinearLayout)itemView.findViewById(R.id.widget_frame);
- InfoItem item = (InfoItem)getItem(position);
- item.initViews(title, summary, widgetGroup);
- item.setupView();
- return itemView;
- }
- };
-
- final ArrayList allItems = new ArrayList();
- allItems.add(new UidItem());
- allItems.add(new PackageItem());
- allItems.add(new OLEDItem());
- allItems.add(new InstantPowerItem());
- allItems.add(new AveragePowerItem());
- allItems.add(new CurrentItem());
- allItems.add(new ChargeItem());
- allItems.add(new VoltageItem());
- allItems.add(new TempItem());
-
- for(InfoItem inf : allItems) {
- if(inf.available()) {
- adapter.add(inf);
- }
- }
-
- listView.setAdapter(adapter);
- setContentView(listView);
-
- collector = new Runnable() {
- public void run() {
- for(InfoItem inf : allItems) {
- if(inf.available()) {
- inf.setupView();
- }
- }
- if(handler != null) {
- handler.postDelayed(this, 2 * PowerEstimator.ITERATION_INTERVAL);
- }
- }
- };
- if(handler != null) {
- handler.post(collector);
- }
- }
-
- class CounterServiceConnection implements ServiceConnection {
- public void onServiceConnected(ComponentName className,
- IBinder boundService ) {
- counterService = ICounterService.Stub.asInterface((IBinder)boundService);
- try {
- componentNames = counterService.getComponents();
- } catch(RemoteException e) {
- componentNames = new String[0];
- }
- refreshView();
- }
-
- public void onServiceDisconnected(ComponentName className) {
- counterService = null;
- getApplicationContext().unbindService(conn);
- getApplicationContext().bindService(serviceIntent, conn, 0);
- Log.w(TAG, "Unexpectedly lost connection to service");
- }
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- prefs = PreferenceManager.getDefaultSharedPreferences(this);
- uid = getIntent().getIntExtra("uid", SystemInfo.AID_ALL);
- if(savedInstanceState != null) {
- componentNames = savedInstanceState.getStringArray("componentNames");
- }
- batteryStats = BatteryStats.getInstance();
- serviceIntent = new Intent(this, UMLoggerService.class);
- conn = new CounterServiceConnection();
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- handler = new Handler();
- getApplicationContext().bindService(serviceIntent, conn, 0);
- refreshView();
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- getApplicationContext().unbindService(conn);
- if(collector != null) {
- handler.removeCallbacks(collector);
- collector = null;
- handler = null;
- }
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putStringArray("componentNames", componentNames);
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- return true;
- }
-
- @Override
- public boolean onPrepareOptionsMenu(Menu menu) {
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- return false;
- }
-
- @Override
- protected Dialog onCreateDialog(int id) {
- return null;
- }
-
- private abstract class InfoItem {
- protected TextView title;
- protected TextView summary;
- protected TextView txt;
-
- public void initViews(TextView title, TextView summary,
- LinearLayout widgetGroup) {
- this.title = title;
- this.summary = summary;
- txt = new TextView(MiscView.this);
- widgetGroup.addView(txt);
- }
-
- public abstract boolean available();
-
- public abstract void setupView();
- }
-
- private class CurrentItem extends InfoItem {
- public boolean available() {
- return uid == SystemInfo.AID_ALL && batteryStats.hasCurrent();
- }
-
- public void setupView() {
- if(txt == null) return;
- double current = batteryStats.getCurrent();
- if(current <= 0) {
- txt.setText(String.format("%1$.1f mA", -current * 1000));
- } else {
- double cp = batteryStats.getCapacity();
- if(0.01 <= cp && cp <= 0.99 && batteryStats.hasCharge()) {
- long time = (long)(batteryStats.getCharge() / cp * (1.0 - cp) /
- current);
- txt.setText(String.format(
- "%1$.1f mA\n(Charge time %2$d:%3$02d:%4$02d)", current * 1000,
- time / 60 / 60, time / 60 % 60, time % 60));
- }
- }
- txt.setGravity(Gravity.CENTER);
-
- title.setText("Current");
- summary.setText("Battery current sensor reading");
- }
- }
-
- private class ChargeItem extends InfoItem {
- public boolean available() {
- return uid == SystemInfo.AID_ALL && batteryStats.hasCharge();
- }
-
- public void setupView() {
- if(txt == null) return;
- double charge = batteryStats.getCharge() / 60 / 60 * 1e3; //As->mAh
- double perc = batteryStats.getCapacity();
- if(perc < 0) {
- txt.setText(String.format("%1$.1f mAh", charge));
- } else {
- txt.setText(String.format("%1$.1f mAh\n(%2$.0f%%)",
- charge, 100 * perc));
- }
- txt.setGravity(Gravity.CENTER);
-
- title.setText("Charge");
- summary.setText("Battery charge sensor reading");
- }
- }
-
- private class InstantPowerItem extends InfoItem {
- private static final double POLY_WEIGHT = 0.10;
-
- public boolean available() {
- return true;
- }
-
- public void setupView() {
- if(txt == null) return;
- if(counterService != null) try {
- // Compute what we're going to call the temporal power usage.
- int count = 0;
- int[] history = counterService.getComponentHistory(
- 5 * 60, -1, uid);
- double weightedAvgPower = 0;
- for(int i = history.length - 1; i >= 0; i--) {
- if(history[i] != 0) {
- count++;
- weightedAvgPower *= 1.0 - POLY_WEIGHT;
- weightedAvgPower += POLY_WEIGHT * history[i] / 1000.0;
- }
- }
- if(count > 0) {
- double charge = batteryStats.getCharge();
- double volt = batteryStats.getVoltage();
- if(charge > 0 && volt > 0) {
- weightedAvgPower /= 1.0 - Math.pow(1.0 - POLY_WEIGHT, count);
- long time = (long)(charge * volt / weightedAvgPower);
- txt.setText(String.format("%1$.0f mW\n" +
- "time: %2$d:%3$02d:%4$02d", weightedAvgPower * 1000.0,
- time / 60 / 60, time / 60 % 60, time % 60));
- } else {
- txt.setText(String.format("%1$.0f mW", weightedAvgPower * 1000.0));
- }
- } else {
- txt.setText("No data");
- }
- } catch(RemoteException e) {
- txt.setText("Error");
- } else {
- txt.setText("No data");
- }
-
- txt.setGravity(Gravity.CENTER);
- title.setText("Current Power");
- summary.setText("Weighted average of power consumption over the last " +
- "five minutes");
- }
- }
-
- private class AveragePowerItem extends InfoItem {
- public boolean available() {
- return true;
- }
-
- public void setupView() {
- if(txt == null) return;
- if(counterService != null) try {
- // Compute what we're going to call the temporal power usage.
- double power = 0;
- long[] means = counterService.getMeans(uid, Counter.WINDOW_TOTAL);
- if(means != null) for(long p : means) {
- power += p / 1000.0;
- }
-
- if(power > 0) {
- double charge = batteryStats.getCharge();
- double volt = batteryStats.getVoltage();
- if(charge > 0 && volt > 0) {
- long time = (long)(charge * volt / power);
- txt.setText(String.format("%1$.0f mW\n" +
- "time: %2$d:%3$02d:%4$02d", power * 1000.0,
- time / 60 / 60, time / 60 % 60, time % 60));
- } else {
- txt.setText(String.format("%1$.0f mW", power * 1000.0));
- }
- } else {
- txt.setText("No data");
- }
- } catch(RemoteException e) {
- txt.setText("Error");
- } else {
- txt.setText("No data");
- }
-
- txt.setGravity(Gravity.CENTER);
- title.setText("Average Power");
- summary.setText("Average power consumption since profiler started");
- }
- }
-
- private class VoltageItem extends InfoItem {
- public boolean available() {
- return uid == SystemInfo.AID_ALL && batteryStats.hasVoltage();
- }
-
- public void setupView() {
- if(txt == null) return;
- double voltage = batteryStats.getVoltage();
- txt.setText(String.format("%1$.2f V", voltage));
- txt.setGravity(Gravity.CENTER);
-
- title.setText("Voltage");
- summary.setText("Battery voltage sensor reading");
- }
- }
-
- private class TempItem extends InfoItem {
- public boolean available() {
- return uid == SystemInfo.AID_ALL && batteryStats.hasTemp();
- }
-
- public void setupView() {
- if(txt == null) return;
- double celcius = batteryStats.getTemp();
- double farenheit = 32 + celcius * 9.0 / 5.0;
- txt.setText(String.format("%1$.1f \u00b0C\n(%2$.1f \u00b0F)",
- celcius, farenheit));
- txt.setGravity(Gravity.CENTER);
-
- title.setText("Battery Temperature");
- summary.setText("Battery temperature sensor reading");
- }
- }
-
- private class UidItem extends InfoItem {
- public boolean available() {
- return uid != SystemInfo.AID_ALL;
- }
-
- public void setupView() {
- if(txt == null) return;
- txt.setText("" + uid);
- txt.setGravity(Gravity.CENTER);
-
- title.setText("User ID");
- summary.setText("User ID for " + SystemInfo.getInstance().getUidName(uid,
- getApplicationContext().getPackageManager()));
-
- }
- }
-
- private class PackageItem extends InfoItem {
- public boolean available() {
- return uid >= SystemInfo.AID_APP;
- }
-
- public void setupView() {
- if(txt == null) return;
- txt.setText("");
-
- title.setText("Packages");
-
- PackageManager pm = getApplicationContext().getPackageManager();
- String[] packages = pm.getPackagesForUid(uid);
- if(packages != null) {
- StringBuilder buf = new StringBuilder();
- for(String packageName : packages) {
- if(buf.length() != 0) buf.append("\n");
- buf.append(packageName);
- }
- summary.setText(buf.toString());
- } else {
- summary.setText("(None)");
- }
- }
- }
-
- private class OLEDItem extends InfoItem {
- public boolean available() {
- if(uid < SystemInfo.AID_APP) return false;
- return PhoneSelector.hasOled();
- }
-
- public void setupView() {
- if(txt == null) return;
-
- txt.setText("No data");
- if(counterService != null) {
- try {
- long score = counterService.getUidExtra("OLEDSCORE", uid);
- if(score >= 0) {
- txt.setText("" + (100 - score));
- }
- } catch(RemoteException e) {
- Log.w(TAG, "Failed to request oled score information");
- }
- }
-
- title.setText("OLED Score");
- summary.setText("100 is highly efficient\n0 is very inefficient\n" +
- "Independent of brightness");
- }
- }
-}
-
+/*
+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 .
+
+Please send inquiries to powertutor@umich.edu
+*/
+
+package edu.umich.PowerTutor.ui;
+
+import edu.umich.PowerTutor.R;
+import edu.umich.PowerTutor.phone.PhoneSelector;
+import edu.umich.PowerTutor.service.ICounterService;
+import edu.umich.PowerTutor.service.PowerEstimator;
+import edu.umich.PowerTutor.service.UMLoggerService;
+import edu.umich.PowerTutor.util.Counter;
+import edu.umich.PowerTutor.util.BatteryStats;
+import edu.umich.PowerTutor.util.SystemInfo;
+
+import android.app.Activity;
+import android.app.Dialog;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.preference.PreferenceManager;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.ListView;
+
+import java.util.ArrayList;
+
+public class MiscView extends Activity {
+ private static final String TAG = "MiscView";
+
+ private SharedPreferences prefs;
+ private int uid;
+
+ private Runnable collector;
+
+ private Intent serviceIntent;
+ private CounterServiceConnection conn;
+ private ICounterService counterService;
+ private Handler handler;
+
+ private BatteryStats batteryStats;
+
+ private String[] componentNames;
+
+ public void refreshView() {
+ final ListView listView = new ListView(this);
+
+ ArrayAdapter adapter = new ArrayAdapter(this, 0) {
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View itemView = getLayoutInflater()
+ .inflate(R.layout.misc_item_layout, listView, false);
+ TextView title = (TextView)itemView.findViewById(R.id.title);
+ TextView summary = (TextView)itemView.findViewById(R.id.summary);
+ LinearLayout widgetGroup =
+ (LinearLayout)itemView.findViewById(R.id.widget_frame);
+ InfoItem item = (InfoItem)getItem(position);
+ item.initViews(title, summary, widgetGroup);
+ item.setupView();
+ return itemView;
+ }
+ };
+
+ final ArrayList allItems = new ArrayList();
+ allItems.add(new UidItem());
+ allItems.add(new PackageItem());
+ allItems.add(new OLEDItem());
+ allItems.add(new InstantPowerItem());
+ allItems.add(new AveragePowerItem());
+ allItems.add(new CurrentItem());
+ allItems.add(new ChargeItem());
+ allItems.add(new VoltageItem());
+ allItems.add(new TempItem());
+
+ for(InfoItem inf : allItems) {
+ if(inf.available()) {
+ adapter.add(inf);
+ }
+ }
+
+ listView.setAdapter(adapter);
+ setContentView(listView);
+
+ collector = new Runnable() {
+ public void run() {
+ for(InfoItem inf : allItems) {
+ if(inf.available()) {
+ inf.setupView();
+ }
+ }
+ if(handler != null) {
+ handler.postDelayed(this, 2 * PowerEstimator.ITERATION_INTERVAL);
+ }
+ }
+ };
+ if(handler != null) {
+ handler.post(collector);
+ }
+ }
+
+ class CounterServiceConnection implements ServiceConnection {
+ public void onServiceConnected(ComponentName className,
+ IBinder boundService ) {
+ counterService = ICounterService.Stub.asInterface((IBinder)boundService);
+ try {
+ componentNames = counterService.getComponents();
+ } catch(RemoteException e) {
+ componentNames = new String[0];
+ }
+ refreshView();
+ }
+
+ public void onServiceDisconnected(ComponentName className) {
+ counterService = null;
+ getApplicationContext().unbindService(conn);
+ getApplicationContext().bindService(serviceIntent, conn, 0);
+ Log.w(TAG, "Unexpectedly lost connection to service");
+ }
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ prefs = PreferenceManager.getDefaultSharedPreferences(this);
+ uid = getIntent().getIntExtra("uid", SystemInfo.AID_ALL);
+ if(savedInstanceState != null) {
+ componentNames = savedInstanceState.getStringArray("componentNames");
+ }
+ batteryStats = BatteryStats.getInstance();
+ serviceIntent = new Intent(this, UMLoggerService.class);
+ conn = new CounterServiceConnection();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ handler = new Handler();
+ getApplicationContext().bindService(serviceIntent, conn, 0);
+ refreshView();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ getApplicationContext().unbindService(conn);
+ if(collector != null) {
+ handler.removeCallbacks(collector);
+ collector = null;
+ handler = null;
+ }
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putStringArray("componentNames", componentNames);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ return true;
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ return false;
+ }
+
+ @Override
+ protected Dialog onCreateDialog(int id) {
+ return null;
+ }
+
+ private abstract class InfoItem {
+ protected TextView title;
+ protected TextView summary;
+ protected TextView txt;
+
+ public void initViews(TextView title, TextView summary,
+ LinearLayout widgetGroup) {
+ this.title = title;
+ this.summary = summary;
+ txt = new TextView(MiscView.this);
+ widgetGroup.addView(txt);
+ }
+
+ public abstract boolean available();
+
+ public abstract void setupView();
+ }
+
+ private class CurrentItem extends InfoItem {
+ public boolean available() {
+ return uid == SystemInfo.AID_ALL && batteryStats.hasCurrent();
+ }
+
+ public void setupView() {
+ if(txt == null) return;
+ double current = batteryStats.getCurrent();
+ if(current <= 0) {
+ txt.setText(String.format("%1$.1f mA", -current * 1000));
+ } else {
+ double cp = batteryStats.getCapacity();
+ if(0.01 <= cp && cp <= 0.99 && batteryStats.hasCharge()) {
+ long time = (long)(batteryStats.getCharge() / cp * (1.0 - cp) /
+ current);
+ txt.setText(String.format(
+ "%1$.1f mA\n(Charge time %2$d:%3$02d:%4$02d)", current * 1000,
+ time / 60 / 60, time / 60 % 60, time % 60));
+ }
+ }
+ txt.setGravity(Gravity.CENTER);
+
+ title.setText("Current");
+ summary.setText("Battery current sensor reading");
+ }
+ }
+
+ private class ChargeItem extends InfoItem {
+ public boolean available() {
+ return uid == SystemInfo.AID_ALL && batteryStats.hasCharge();
+ }
+
+ public void setupView() {
+ if(txt == null) return;
+ double charge = batteryStats.getCharge() / 60 / 60 * 1e3; //As->mAh
+ double perc = batteryStats.getCapacity();
+ if(perc < 0) {
+ txt.setText(String.format("%1$.1f mAh", charge));
+ } else {
+ txt.setText(String.format("%1$.1f mAh\n(%2$.0f%%)",
+ charge, 100 * perc));
+ }
+ txt.setGravity(Gravity.CENTER);
+
+ title.setText("Charge");
+ summary.setText("Battery charge sensor reading");
+ }
+ }
+
+ private class InstantPowerItem extends InfoItem {
+ private static final double POLY_WEIGHT = 0.10;
+
+ public boolean available() {
+ return true;
+ }
+
+ public void setupView() {
+ if(txt == null) return;
+ if(counterService != null) try {
+ // Compute what we're going to call the temporal power usage.
+ int count = 0;
+ int[] history = counterService.getComponentHistory(
+ 5 * 60, -1, uid);
+ double weightedAvgPower = 0;
+ for(int i = history.length - 1; i >= 0; i--) {
+ if(history[i] != 0) {
+ count++;
+ weightedAvgPower *= 1.0 - POLY_WEIGHT;
+ weightedAvgPower += POLY_WEIGHT * history[i] / 1000.0;
+ }
+ }
+ if(count > 0) {
+ double charge = batteryStats.getCharge();
+ double volt = batteryStats.getVoltage();
+ if(charge > 0 && volt > 0) {
+ weightedAvgPower /= 1.0 - Math.pow(1.0 - POLY_WEIGHT, count);
+ long time = (long)(charge * volt / weightedAvgPower);
+ txt.setText(String.format("%1$.0f mW\n" +
+ "time: %2$d:%3$02d:%4$02d", weightedAvgPower * 1000.0,
+ time / 60 / 60, time / 60 % 60, time % 60));
+ } else {
+ txt.setText(String.format("%1$.0f mW", weightedAvgPower * 1000.0));
+ }
+ } else {
+ txt.setText("No data");
+ }
+ } catch(RemoteException e) {
+ txt.setText("Error");
+ } else {
+ txt.setText("No data");
+ }
+
+ txt.setGravity(Gravity.CENTER);
+ title.setText("Current Power");
+ summary.setText("Weighted average of power consumption over the last " +
+ "five minutes");
+ }
+ }
+
+ private class AveragePowerItem extends InfoItem {
+ public boolean available() {
+ return true;
+ }
+
+ public void setupView() {
+ if(txt == null) return;
+ if(counterService != null) try {
+ // Compute what we're going to call the temporal power usage.
+ double power = 0;
+ long[] means = counterService.getMeans(uid, Counter.WINDOW_TOTAL);
+ if(means != null) for(long p : means) {
+ power += p / 1000.0;
+ }
+
+ if(power > 0) {
+ double charge = batteryStats.getCharge();
+ double volt = batteryStats.getVoltage();
+ if(charge > 0 && volt > 0) {
+ long time = (long)(charge * volt / power);
+ txt.setText(String.format("%1$.0f mW\n" +
+ "time: %2$d:%3$02d:%4$02d", power * 1000.0,
+ time / 60 / 60, time / 60 % 60, time % 60));
+ } else {
+ txt.setText(String.format("%1$.0f mW", power * 1000.0));
+ }
+ } else {
+ txt.setText("No data");
+ }
+ } catch(RemoteException e) {
+ txt.setText("Error");
+ } else {
+ txt.setText("No data");
+ }
+
+ txt.setGravity(Gravity.CENTER);
+ title.setText("Average Power");
+ summary.setText("Average power consumption since profiler started");
+ }
+ }
+
+ private class VoltageItem extends InfoItem {
+ public boolean available() {
+ return uid == SystemInfo.AID_ALL && batteryStats.hasVoltage();
+ }
+
+ public void setupView() {
+ if(txt == null) return;
+ double voltage = batteryStats.getVoltage();
+ txt.setText(String.format("%1$.2f V", voltage));
+ txt.setGravity(Gravity.CENTER);
+
+ title.setText("Voltage");
+ summary.setText("Battery voltage sensor reading");
+ }
+ }
+
+ private class TempItem extends InfoItem {
+ public boolean available() {
+ return uid == SystemInfo.AID_ALL && batteryStats.hasTemp();
+ }
+
+ public void setupView() {
+ if(txt == null) return;
+ double celcius = batteryStats.getTemp();
+ double farenheit = 32 + celcius * 9.0 / 5.0;
+ txt.setText(String.format("%1$.1f \u00b0C\n(%2$.1f \u00b0F)",
+ celcius, farenheit));
+ txt.setGravity(Gravity.CENTER);
+
+ title.setText("Battery Temperature");
+ summary.setText("Battery temperature sensor reading");
+ }
+ }
+
+ private class UidItem extends InfoItem {
+ public boolean available() {
+ return uid != SystemInfo.AID_ALL;
+ }
+
+ public void setupView() {
+ if(txt == null) return;
+ txt.setText("" + uid);
+ txt.setGravity(Gravity.CENTER);
+
+ title.setText("User ID");
+ summary.setText("User ID for " + SystemInfo.getInstance().getUidName(uid,
+ getApplicationContext().getPackageManager()));
+
+ }
+ }
+
+ private class PackageItem extends InfoItem {
+ public boolean available() {
+ return uid >= SystemInfo.AID_APP;
+ }
+
+ public void setupView() {
+ if(txt == null) return;
+ txt.setText("");
+
+ title.setText("Packages");
+
+ PackageManager pm = getApplicationContext().getPackageManager();
+ String[] packages = pm.getPackagesForUid(uid);
+ if(packages != null) {
+ StringBuilder buf = new StringBuilder();
+ for(String packageName : packages) {
+ if(buf.length() != 0) buf.append("\n");
+ buf.append(packageName);
+ }
+ summary.setText(buf.toString());
+ } else {
+ summary.setText("(None)");
+ }
+ }
+ }
+
+ private class OLEDItem extends InfoItem {
+ public boolean available() {
+ if(uid < SystemInfo.AID_APP) return false;
+ return PhoneSelector.hasOled();
+ }
+
+ public void setupView() {
+ if(txt == null) return;
+
+ txt.setText("No data");
+ if(counterService != null) {
+ try {
+ long score = counterService.getUidExtra("OLEDSCORE", uid);
+ if(score >= 0) {
+ txt.setText("" + (100 - score));
+ }
+ } catch(RemoteException e) {
+ Log.w(TAG, "Failed to request oled score information");
+ }
+ }
+
+ title.setText("OLED Score");
+ summary.setText("100 is highly efficient\n0 is very inefficient\n" +
+ "Independent of brightness");
+ }
+ }
+}
+
diff --git a/src/edu/umich/PowerTutor/ui/PowerPie.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/ui/PowerPie.java
similarity index 96%
rename from src/edu/umich/PowerTutor/ui/PowerPie.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/ui/PowerPie.java
index 9600059..986954e 100644
--- a/src/edu/umich/PowerTutor/ui/PowerPie.java
+++ b/PowerTutor2/src/main/java/edu/umich/PowerTutor/ui/PowerPie.java
@@ -1,295 +1,296 @@
-/*
-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 .
-
-Please send inquiries to powertutor@umich.edu
-*/
-
-package edu.umich.PowerTutor.ui;
-
-import org.achartengine.GraphicalView;
-import org.achartengine.chart.PieChart;
-import org.achartengine.model.CategorySeries;
-import org.achartengine.renderer.DefaultRenderer;
-import org.achartengine.renderer.SimpleSeriesRenderer;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.content.ComponentName;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.content.SharedPreferences;
-import android.graphics.Color;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.preference.PreferenceManager;
-import android.util.Log;
-import android.view.Gravity;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-import edu.umich.PowerTutor.service.ICounterService;
-import edu.umich.PowerTutor.service.PowerEstimator;
-import edu.umich.PowerTutor.service.UMLoggerService;
-import edu.umich.PowerTutor.util.Counter;
-import edu.umich.PowerTutor.util.SystemInfo;
-
-public class PowerPie extends Activity {
- private static final String TAG = "PowerPie";
-
- private SharedPreferences prefs;
- private int uid;
-
- private String[] componentNames;
- private int noUidMask;
-
- private Runnable collector;
-
- private Intent serviceIntent;
- private CounterServiceConnection conn;
- private ICounterService counterService;
- private Handler handler;
-
- private TextView displayText;
-
- public static final int[] COLORS = new int[] {
- Color.BLUE, Color.GREEN, Color.MAGENTA, Color.YELLOW,
- Color.RED, Color.LTGRAY, Color.DKGRAY, Color.CYAN
- };
-
- public void refreshView() {
- if(counterService == null) {
- TextView loadingText = new TextView(this);
- loadingText.setText("Waiting for profiler service...");
- loadingText.setGravity(Gravity.CENTER);
- setContentView(loadingText);
- return;
- }
-
- if(uid == SystemInfo.AID_ALL) {
- /* If we are reporting global power usage then just set noUidMask to 0 so
- * that all components get displayed.
- */
- noUidMask = 0;
- }
-
- displayText = new TextView(this);
- displayText.setGravity(Gravity.CENTER);
- updateDisplayText();
-
- final CategorySeries series = new CategorySeries("");
- final DefaultRenderer renderer = new DefaultRenderer();
- renderer.setLabelsTextSize(15);
- renderer.setLegendTextSize(15);
- renderer.setMargins(new int[] { 5, 50, 5, 50 });
-
- PieChart pieChart = new PieChart(series, renderer);
- final GraphicalView chartView = new GraphicalView(this, pieChart);
-
- /* The collector is responsible for periodically updating the screen with
- * new energy usage information for the current uid.
- */
- collector = new Runnable() {
- public void run() {
- try {
- long[] totals = counterService.getTotals(uid,
- prefs.getInt("pieWindowType", 0));
- long sumTotal = 0;
- for(int i = 0; i < totals.length; i++) {
- totals[i] = totals[i] * PowerEstimator.ITERATION_INTERVAL / 1000;
- sumTotal += totals[i];
- }
- int index = 0;
- if(sumTotal < 1e-7) {
- series.set(0, "No data", 1);
- } else for(int i = 0; i < totals.length; i++) {
- if((noUidMask & 1 << i) != 0) {
- continue;
- }
- String prefix;
- double val = totals[i];
- if(val > 1e12) {
- prefix = "G";
- val /= 1e12;
- } else if(val > 1e9) {
- prefix = "M";
- val /= 1e9;
- } else if(val > 1e6) {
- prefix = "k";
- val /= 1e6;
- } else if(val > 1e3) {
- prefix = "";
- val /= 1e3;
- } else {
- prefix = "m";
- }
-
- String label = String.format("%1$s %2$.1f %3$sJ",
- componentNames[i], val, prefix);
- if(series.getItemCount() == index) {
- SimpleSeriesRenderer r = new SimpleSeriesRenderer();
- r.setColor(COLORS[i]);
- renderer.addSeriesRenderer(r);
-
- series.add(label, totals[i]);
- } else {
- series.set(index, label, totals[i]);
- }
- index++;
- }
- chartView.invalidate();
- } catch(RemoteException e) {
- Log.w(TAG, "Failed to contact power tutor profiling service");
- }
- if(handler != null) {
- handler.postDelayed(this, 2 * PowerEstimator.ITERATION_INTERVAL);
- }
- }
- };
- if(handler != null) {
- handler.post(collector);
- }
-
- LinearLayout layout = new LinearLayout(this);
- layout.setOrientation(LinearLayout.VERTICAL);
- layout.addView(displayText);
- layout.addView(chartView);
- setContentView(layout);
- }
-
- public void updateDisplayText() {
- displayText.setText("Displaying energy usage over " +
- Counter.WINDOW_DESCS[prefs.getInt("pieWindowType", 0)] + " for " +
- (uid == SystemInfo.AID_ALL ? " the entire phone." :
- SystemInfo.getInstance().getUidName(uid, getPackageManager()) + "."));
- }
-
- class CounterServiceConnection implements ServiceConnection {
- public void onServiceConnected(ComponentName className,
- IBinder boundService ) {
- counterService = ICounterService.Stub.asInterface((IBinder)boundService);
- try {
- componentNames = counterService.getComponents();
- noUidMask = counterService.getNoUidMask();
- refreshView();
- } catch(RemoteException e) {
- counterService = null;
- }
- }
-
- public void onServiceDisconnected(ComponentName className) {
- counterService = null;
- getApplicationContext().unbindService(conn);
- getApplicationContext().bindService(serviceIntent, conn, 0);
- Log.w(TAG, "Unexpectedly lost connection to service");
- }
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- prefs = PreferenceManager.getDefaultSharedPreferences(this);
- uid = getIntent().getIntExtra("uid", SystemInfo.AID_ALL);
-
- if(savedInstanceState != null) {
- componentNames = savedInstanceState.getStringArray("componentNames");
- noUidMask = savedInstanceState.getInt("noUidMask");
- }
-
- serviceIntent = new Intent(this, UMLoggerService.class);
- conn = new CounterServiceConnection();
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- handler = new Handler();
- getApplicationContext().bindService(serviceIntent, conn, 0);
-
- refreshView();
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- getApplicationContext().unbindService(conn);
- if(collector != null) {
- handler.removeCallbacks(collector);
- handler = null;
- }
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putStringArray("componentNames", componentNames);
- outState.putInt("noUidMask", noUidMask);
- }
-
- private static final int MENU_WINDOW = 0;
- private static final int DIALOG_WINDOW = 0;
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- menu.add(0, MENU_WINDOW, 0, "Time Span");
- return true;
- }
-
- @Override
- public boolean onPrepareOptionsMenu(Menu menu) {
- /* We need to make sure that the user can't cause any of the dialogs to be
- * created before we have contacted the Power Tutor service to get the
- * component names and such.
- */
- for(int i = 0; i < menu.size(); i++) {
- menu.getItem(i).setEnabled(counterService != null);
- }
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch(item.getItemId()) {
- case MENU_WINDOW:
- showDialog(DIALOG_WINDOW);
- return true;
- }
- return false;
- }
-
- @Override
- protected Dialog onCreateDialog(int id) {
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- switch(id) {
- case DIALOG_WINDOW:
- builder.setTitle("Select window type");
- builder.setItems(Counter.WINDOW_NAMES,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int item) {
- prefs.edit().putInt("pieWindowType", item).commit();
- updateDisplayText();
- }
- });
- return builder.create();
- }
- return null;
- }
-}
-
+/*
+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 .
+
+Please send inquiries to powertutor@umich.edu
+*/
+
+package edu.umich.PowerTutor.ui;
+
+import org.achartengine.GraphicalView;
+import org.achartengine.chart.PieChart;
+import org.achartengine.model.CategorySeries;
+import org.achartengine.renderer.DefaultRenderer;
+import org.achartengine.renderer.SimpleSeriesRenderer;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.ComponentName;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.SharedPreferences;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.preference.PreferenceManager;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import edu.umich.PowerTutor.service.ICounterService;
+import edu.umich.PowerTutor.service.PowerEstimator;
+import edu.umich.PowerTutor.service.UMLoggerService;
+import edu.umich.PowerTutor.util.Counter;
+import edu.umich.PowerTutor.util.SystemInfo;
+
+public class PowerPie extends Activity {
+ private static final String TAG = "PowerPie";
+
+ private SharedPreferences prefs;
+ private int uid;
+
+ private String[] componentNames;
+ private int noUidMask;
+
+ private Runnable collector;
+
+ private Intent serviceIntent;
+ private CounterServiceConnection conn;
+ private ICounterService counterService;
+ private Handler handler;
+
+ private TextView displayText;
+
+ public static final int[] COLORS = new int[] {
+ Color.BLUE, Color.GREEN, Color.MAGENTA, Color.YELLOW,
+ Color.RED, Color.LTGRAY, Color.DKGRAY, Color.CYAN
+ };
+
+ public void refreshView() {
+ if(counterService == null) {
+ TextView loadingText = new TextView(this);
+ loadingText.setText("Waiting for profiler service...");
+ loadingText.setGravity(Gravity.CENTER);
+ setContentView(loadingText);
+ return;
+ }
+
+ if(uid == SystemInfo.AID_ALL) {
+ /* If we are reporting global power usage then just set noUidMask to 0 so
+ * that all components get displayed.
+ */
+ noUidMask = 0;
+ }
+
+ displayText = new TextView(this);
+ displayText.setGravity(Gravity.CENTER);
+ updateDisplayText();
+
+ final CategorySeries series = new CategorySeries("");
+ final DefaultRenderer renderer = new DefaultRenderer();
+ renderer.setLabelsTextSize(15);
+ renderer.setLegendTextSize(15);
+ renderer.setMargins(new int[] { 5, 50, 5, 50 });
+
+ PieChart pieChart = new PieChart(series, renderer);
+ final GraphicalView chartView = new GraphicalView(this, pieChart);
+
+ /* The collector is responsible for periodically updating the screen with
+ * new energy usage information for the current uid.
+ */
+ collector = new Runnable() {
+ public void run() {
+ try {
+ long[] totals = counterService.getTotals(uid,
+ prefs.getInt("pieWindowType", 0));
+ long sumTotal = 0;
+ for(int i = 0; i < totals.length; i++) {
+ totals[i] = totals[i] * PowerEstimator.ITERATION_INTERVAL / 1000;
+ sumTotal += totals[i];
+ }
+ int index = 0;
+ if(sumTotal < 1e-7) {
+ series.set(0, "No data", 1);
+ } else for(int i = 0; i < totals.length; i++) {
+ if((noUidMask & 1 << i) != 0) {
+ continue;
+ }
+ String prefix;
+ double val = totals[i];
+ if(val > 1e12) {
+ prefix = "G";
+ val /= 1e12;
+ } else if(val > 1e9) {
+ prefix = "M";
+ val /= 1e9;
+ } else if(val > 1e6) {
+ prefix = "k";
+ val /= 1e6;
+ } else if(val > 1e3) {
+ prefix = "";
+ val /= 1e3;
+ } else {
+ prefix = "m";
+ }
+
+ String label = String.format("%1$s %2$.1f %3$sJ",
+ componentNames[i], val, prefix);
+ if(series.getItemCount() == index) {
+ SimpleSeriesRenderer r = new SimpleSeriesRenderer();
+ r.setColor(COLORS[i]);
+ renderer.addSeriesRenderer(r);
+
+ series.add(label, totals[i]);
+ } else {
+ series.set(index, label, totals[i]);
+ }
+ index++;
+ }
+ chartView.invalidate();
+ } catch(RemoteException e) {
+ Log.w(TAG, "Failed to contact power tutor profiling service");
+ }
+ if(handler != null) {
+ handler.postDelayed(this, 2 * PowerEstimator.ITERATION_INTERVAL);
+ }
+ }
+ };
+ if(handler != null) {
+ handler.post(collector);
+ }
+
+ LinearLayout layout = new LinearLayout(this);
+ layout.setOrientation(LinearLayout.VERTICAL);
+ layout.addView(displayText);
+ layout.addView(chartView);
+ setContentView(layout);
+ }
+
+ public void updateDisplayText() {
+ displayText.setText("Displaying energy usage over " +
+ Counter.WINDOW_DESCS[prefs.getInt("pieWindowType", 0)] + " for " +
+ (uid == SystemInfo.AID_ALL ? " the entire phone." :
+ SystemInfo.getInstance().getUidName(uid, getPackageManager()) + "."));
+ }
+
+ class CounterServiceConnection implements ServiceConnection {
+ public void onServiceConnected(ComponentName className,
+ IBinder boundService ) {
+ counterService = ICounterService.Stub.asInterface((IBinder)boundService);
+ try {
+ componentNames = counterService.getComponents();
+ noUidMask = counterService.getNoUidMask();
+ refreshView();
+ } catch(RemoteException e) {
+ counterService = null;
+ }
+ }
+
+ public void onServiceDisconnected(ComponentName className) {
+ counterService = null;
+ getApplicationContext().unbindService(conn);
+ getApplicationContext().bindService(serviceIntent, conn, 0);
+ Log.w(TAG, "Unexpectedly lost connection to service");
+ }
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ prefs = PreferenceManager.getDefaultSharedPreferences(this);
+ uid = getIntent().getIntExtra("uid", SystemInfo.AID_ALL);
+
+ if(savedInstanceState != null) {
+ componentNames = savedInstanceState.getStringArray("componentNames");
+ noUidMask = savedInstanceState.getInt("noUidMask");
+ }
+
+ serviceIntent = new Intent(this, UMLoggerService.class);
+ conn = new CounterServiceConnection();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ handler = new Handler();
+ getApplicationContext().bindService(serviceIntent, conn, 0);
+
+ refreshView();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ getApplicationContext().unbindService(conn);
+ if(collector != null) {
+ handler.removeCallbacks(collector);
+ handler = null;
+ }
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putStringArray("componentNames", componentNames);
+ outState.putInt("noUidMask", noUidMask);
+ }
+
+ private static final int MENU_WINDOW = 0;
+ private static final int DIALOG_WINDOW = 0;
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ menu.add(0, MENU_WINDOW, 0, "Time Span");
+ return true;
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ /* We need to make sure that the user can't cause any of the dialogs to be
+ * created before we have contacted the Power Tutor service to get the
+ * component names and such.
+ */
+ for(int i = 0; i < menu.size(); i++) {
+ menu.getItem(i).setEnabled(counterService != null);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch(item.getItemId()) {
+ case MENU_WINDOW:
+ showDialog(DIALOG_WINDOW);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ protected Dialog onCreateDialog(int id) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ switch(id) {
+ case DIALOG_WINDOW:
+ builder.setTitle("Select window type");
+ builder.setItems(Counter.WINDOW_NAMES,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int item) {
+ prefs.edit().putInt("pieWindowType", item).commit();
+ updateDisplayText();
+ }
+ });
+ return builder.create();
+ }
+ return null;
+ }
+}
+
diff --git a/src/edu/umich/PowerTutor/ui/PowerTabs.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/ui/PowerTabs.java
similarity index 100%
rename from src/edu/umich/PowerTutor/ui/PowerTabs.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/ui/PowerTabs.java
diff --git a/src/edu/umich/PowerTutor/ui/PowerTop.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/ui/PowerTop.java
similarity index 99%
rename from src/edu/umich/PowerTutor/ui/PowerTop.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/ui/PowerTop.java
index 21324f6..7a17dcc 100644
--- a/src/edu/umich/PowerTutor/ui/PowerTop.java
+++ b/PowerTutor2/src/main/java/edu/umich/PowerTutor/ui/PowerTop.java
@@ -19,7 +19,6 @@
package edu.umich.PowerTutor.ui;
-import edu.umich.PowerTutor.R;
import edu.umich.PowerTutor.service.ICounterService;
import edu.umich.PowerTutor.service.PowerEstimator;
import edu.umich.PowerTutor.service.UMLoggerService;
@@ -33,7 +32,6 @@
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.ComponentName;
-import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
@@ -50,7 +48,6 @@
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ScrollView;
diff --git a/src/edu/umich/PowerTutor/ui/PowerViewer.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/ui/PowerViewer.java
similarity index 96%
rename from src/edu/umich/PowerTutor/ui/PowerViewer.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/ui/PowerViewer.java
index d0c0d96..dfec05f 100644
--- a/src/edu/umich/PowerTutor/ui/PowerViewer.java
+++ b/PowerTutor2/src/main/java/edu/umich/PowerTutor/ui/PowerViewer.java
@@ -1,350 +1,351 @@
-/*
-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 .
-
-Please send inquiries to powertutor@umich.edu
-*/
-
-package edu.umich.PowerTutor.ui;
-
-import org.achartengine.GraphicalView;
-import org.achartengine.chart.CubicLineChart;
-import org.achartengine.model.XYMultipleSeriesDataset;
-import org.achartengine.model.XYSeries;
-import org.achartengine.renderer.XYMultipleSeriesRenderer;
-import org.achartengine.renderer.XYSeriesRenderer;
-
-import android.app.Activity;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.content.SharedPreferences;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.preference.PreferenceManager;
-import android.util.Log;
-import android.view.Gravity;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.LinearLayout;
-import android.widget.ScrollView;
-import android.widget.TextView;
-import edu.umich.PowerTutor.service.ICounterService;
-import edu.umich.PowerTutor.service.PowerEstimator;
-import edu.umich.PowerTutor.service.UMLoggerService;
-import edu.umich.PowerTutor.util.SystemInfo;
-
-public class PowerViewer extends Activity {
- private static final String TAG = "PowerViewer";
-
- private SharedPreferences prefs;
- private int uid;
-
- private int components;
- private String[] componentNames;
- private int[] componentsMaxPower;
- private int noUidMask;
- private boolean collecting;
-
- private ValueCollector[] collectors;
-
- private Intent serviceIntent;
- private CounterServiceConnection conn;
- private ICounterService counterService;
-
- private Handler handler;
- private LinearLayout chartLayout;
-
- public void refreshView() {
- if(counterService == null) {
- TextView loadingText = new TextView(this);
- loadingText.setText("Waiting for profiler service...");
- loadingText.setGravity(Gravity.CENTER);
- setContentView(loadingText);
- return;
- }
-
- chartLayout = new LinearLayout(this);
- chartLayout.setOrientation(LinearLayout.VERTICAL);
-
- if(uid == SystemInfo.AID_ALL) {
- /* If we are reporting global power usage then just set noUidMask to 0 so
- * that all components get displayed.
- */
- noUidMask = 0;
- }
- components = 0;
- for(int i = 0; i < componentNames.length; i++) {
- if((noUidMask & 1 << i) == 0) {
- components++;
- }
- }
- boolean showTotal = prefs.getBoolean("showTotalPower", false);
- collectors = new ValueCollector[(showTotal ? 1 : 0) + components];
-
- int pos = 0;
- for(int i = showTotal ? -1 : 0; i < componentNames.length; i++) {
- if(i != -1 && (noUidMask & 1 << i) != 0) {
- continue;
- }
- String name = i == -1 ? "Total" : componentNames[i];
- double mxPower = (i == -1 ? 2100.0 : componentsMaxPower[i]) * 1.05;
-
- XYSeries series = new XYSeries(name);
- XYMultipleSeriesDataset mseries = new XYMultipleSeriesDataset();
- mseries.addSeries(series);
-
- XYMultipleSeriesRenderer renderer = new XYMultipleSeriesRenderer();
- XYSeriesRenderer srenderer = new XYSeriesRenderer();
- renderer.setYAxisMin(0.0);
- renderer.setYAxisMax(mxPower);
- renderer.setYTitle(name + "(mW)");
-
- int clr = PowerPie.COLORS[(PowerPie.COLORS.length + i) %
- PowerPie.COLORS.length];
- srenderer.setColor(clr);
- srenderer.setFillBelowLine(true);
- srenderer.setFillBelowLineColor(((clr >> 1) & 0x7F7F7F) |
- (clr & 0xFF000000));
- renderer.addSeriesRenderer(srenderer);
-
- View chartView = new GraphicalView(this,
- new CubicLineChart(mseries, renderer, 0.5f));
- chartView.setMinimumHeight(100);
- chartLayout.addView(chartView);
-
- collectors[pos] = new ValueCollector(series, renderer, chartView, i);
- if(handler != null) {
- handler.post(collectors[pos]);
- }
- pos++;
- }
-
- /* We're giving 100 pixels per graph of vertical space for the chart view.
- If we don't specify a minimum height the chart view ends up having a
- height of 0 so this is important. */
- chartLayout.setMinimumHeight(100 * components);
-
- ScrollView scrollView = new ScrollView(this) {
- public boolean onInterceptTouchEvent(android.view.MotionEvent ev) {
- return true;
- }
- };
-
- scrollView.addView(chartLayout);
- scrollView.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
- setContentView(scrollView);
- }
-
- private class CounterServiceConnection implements ServiceConnection {
- public void onServiceConnected(ComponentName className,
- IBinder boundService) {
- counterService = ICounterService.Stub.asInterface((IBinder)boundService);
- try {
- componentNames = counterService.getComponents();
- componentsMaxPower = counterService.getComponentsMaxPower();
- noUidMask = counterService.getNoUidMask();
- refreshView();
- } catch(RemoteException e) {
- counterService = null;
- }
- }
-
- public void onServiceDisconnected(ComponentName className) {
- counterService = null;
- getApplicationContext().unbindService(conn);
- getApplicationContext().bindService(serviceIntent, conn, 0);
- Log.w(TAG, "Unexpectedly lost connection to service");
- }
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- prefs = PreferenceManager.getDefaultSharedPreferences(this);
- uid = getIntent().getIntExtra("uid", SystemInfo.AID_ALL);
-
- collecting = true;
- if(savedInstanceState != null) {
- collecting = savedInstanceState.getBoolean("collecting", true);
- componentNames = savedInstanceState.getStringArray("componentNames");
- noUidMask = savedInstanceState.getInt("noUidMask");
- }
-
- serviceIntent = new Intent(this, UMLoggerService.class);
- conn = new CounterServiceConnection();
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- handler = new Handler();
- getApplicationContext().bindService(serviceIntent, conn, 0);
-
- refreshView();
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- getApplicationContext().unbindService(conn);
- if(collectors != null) for(int i = 0; i < components; i++) {
- handler.removeCallbacks(collectors[i]);
- }
- counterService = null;
- handler = null;
- collecting = true;
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putBoolean("collecting", collecting);
- outState.putStringArray("componentNames", componentNames);
- outState.putInt("noUidMask", noUidMask);
- }
-
- /* Let all of the UI graphs lay themselves out again. */
- private void stateChanged() {
- for(int i = 0; i < components; i++) {
- collectors[i].layout();
- }
- }
-
- private static final int MENU_OPTIONS = 0;
- private static final int MENU_TOGGLE_COLLECTING = 1;
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- menu.add(0, MENU_OPTIONS, 0, "Options");
- menu.add(0, MENU_TOGGLE_COLLECTING, 0, "");
- return true;
- }
-
- @Override
- public boolean onPrepareOptionsMenu(Menu menu) {
- menu.findItem(MENU_TOGGLE_COLLECTING).setTitle(
- collecting ? "Pause" : "Resume");
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch(item.getItemId()) {
- case MENU_OPTIONS:
- startActivity(new Intent(this, ViewerPreferences.class));
- return true;
- case MENU_TOGGLE_COLLECTING:
- collecting = !collecting;
- if(handler != null) {
- if(collecting) for(int i = 0; i < components; i++) {
- collectors[i].reset();
- handler.post(collectors[i]);
- } else for(int i = 0; i < components; i++) {
- handler.removeCallbacks(collectors[i]);
- }
- }
- break;
- }
- return false;
- }
-
- public class ValueCollector implements Runnable {
- private XYSeries series;
- private XYMultipleSeriesRenderer renderer;
- private View chartView;
-
- private int componentId;
- private long lastTime;
-
- int[] values;
-
- private boolean readHistory;
-
- public ValueCollector(XYSeries series, XYMultipleSeriesRenderer renderer,
- View chartView, int componentId) {
- this.series = series;
- this.renderer = renderer;
- this.chartView = chartView;
- this.componentId = componentId;
- lastTime = SystemClock.elapsedRealtime();
- layout();
- }
-
- public void layout() {
- int numVals = Integer.parseInt(prefs.getString("viewNumValues_s", "60"));
- values = new int[numVals];
-
- renderer.clearXTextLabels();
- renderer.setXAxisMin(0);
- renderer.setXAxisMax(numVals - 1);
- renderer.addXTextLabel(numVals - 1, "" + numVals);
- renderer.setXLabels(0);
- for(int j = 0; j < 10; j++) {
- renderer.addXTextLabel(numVals * j / 10, "" + (1 + numVals * j / 10));
- }
-
- reset();
- }
-
- /** Restart points collecting from zero. */
- public void reset() {
- series.clear();
- readHistory = true;
- }
-
- public void run() {
- int numVals = Integer.parseInt(prefs.getString("viewNumValues_s", "60"));
- if(counterService != null) try {
- if(readHistory) {
- values = counterService.getComponentHistory(numVals,
- componentId, uid);
- readHistory = false;
- } else {
- for(int i = numVals - 1; i > 0; i--) {
- values[i] = values[i - 1];
- }
- values[0] = counterService.getComponentHistory(1, componentId,
- uid)[0];
- }
- } catch(RemoteException e) {
- Log.w(TAG, "Failed to get data from service");
- for(int i = 0; i < numVals; i++) {
- values[i] = 0;
- }
- }
-
- series.clear();
- for(int i = 0; i < numVals; i++) {
- series.add(i, values[i]);
- }
-
- long curTime = SystemClock.elapsedRealtime();
- long tryTime = lastTime + PowerEstimator.ITERATION_INTERVAL *
- (long)Math.max(1, 1 + (curTime - lastTime) /
- PowerEstimator.ITERATION_INTERVAL);
- if(handler != null) {
- handler.postDelayed(this, tryTime - curTime);
- }
-
- chartView.invalidate();
- }
- };
-}
+/*
+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 .
+
+Please send inquiries to powertutor@umich.edu
+*/
+
+package edu.umich.PowerTutor.ui;
+
+import org.achartengine.GraphicalView;
+import org.achartengine.chart.CubicLineChart;
+import org.achartengine.model.XYMultipleSeriesDataset;
+import org.achartengine.model.XYSeries;
+import org.achartengine.renderer.XYMultipleSeriesRenderer;
+import org.achartengine.renderer.XYSeriesRenderer;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.preference.PreferenceManager;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.ScrollView;
+import android.widget.TextView;
+
+import edu.umich.PowerTutor.service.ICounterService;
+import edu.umich.PowerTutor.service.PowerEstimator;
+import edu.umich.PowerTutor.service.UMLoggerService;
+import edu.umich.PowerTutor.util.SystemInfo;
+
+public class PowerViewer extends Activity {
+ private static final String TAG = "PowerViewer";
+
+ private SharedPreferences prefs;
+ private int uid;
+
+ private int components;
+ private String[] componentNames;
+ private int[] componentsMaxPower;
+ private int noUidMask;
+ private boolean collecting;
+
+ private ValueCollector[] collectors;
+
+ private Intent serviceIntent;
+ private CounterServiceConnection conn;
+ private ICounterService counterService;
+
+ private Handler handler;
+ private LinearLayout chartLayout;
+
+ public void refreshView() {
+ if(counterService == null) {
+ TextView loadingText = new TextView(this);
+ loadingText.setText("Waiting for profiler service...");
+ loadingText.setGravity(Gravity.CENTER);
+ setContentView(loadingText);
+ return;
+ }
+
+ chartLayout = new LinearLayout(this);
+ chartLayout.setOrientation(LinearLayout.VERTICAL);
+
+ if(uid == SystemInfo.AID_ALL) {
+ /* If we are reporting global power usage then just set noUidMask to 0 so
+ * that all components get displayed.
+ */
+ noUidMask = 0;
+ }
+ components = 0;
+ for(int i = 0; i < componentNames.length; i++) {
+ if((noUidMask & 1 << i) == 0) {
+ components++;
+ }
+ }
+ boolean showTotal = prefs.getBoolean("showTotalPower", false);
+ collectors = new ValueCollector[(showTotal ? 1 : 0) + components];
+
+ int pos = 0;
+ for(int i = showTotal ? -1 : 0; i < componentNames.length; i++) {
+ if(i != -1 && (noUidMask & 1 << i) != 0) {
+ continue;
+ }
+ String name = i == -1 ? "Total" : componentNames[i];
+ double mxPower = (i == -1 ? 2100.0 : componentsMaxPower[i]) * 1.05;
+
+ XYSeries series = new XYSeries(name);
+ XYMultipleSeriesDataset mseries = new XYMultipleSeriesDataset();
+ mseries.addSeries(series);
+
+ XYMultipleSeriesRenderer renderer = new XYMultipleSeriesRenderer();
+ XYSeriesRenderer srenderer = new XYSeriesRenderer();
+ renderer.setYAxisMin(0.0);
+ renderer.setYAxisMax(mxPower);
+ renderer.setYTitle(name + "(mW)");
+
+ int clr = PowerPie.COLORS[(PowerPie.COLORS.length + i) %
+ PowerPie.COLORS.length];
+ srenderer.setColor(clr);
+ srenderer.setFillBelowLine(true);
+ srenderer.setFillBelowLineColor(((clr >> 1) & 0x7F7F7F) |
+ (clr & 0xFF000000));
+ renderer.addSeriesRenderer(srenderer);
+
+ View chartView = new GraphicalView(this,
+ new CubicLineChart(mseries, renderer, 0.5f));
+ chartView.setMinimumHeight(100);
+ chartLayout.addView(chartView);
+
+ collectors[pos] = new ValueCollector(series, renderer, chartView, i);
+ if(handler != null) {
+ handler.post(collectors[pos]);
+ }
+ pos++;
+ }
+
+ /* We're giving 100 pixels per graph of vertical space for the chart view.
+ If we don't specify a minimum height the chart view ends up having a
+ height of 0 so this is important. */
+ chartLayout.setMinimumHeight(100 * components);
+
+ ScrollView scrollView = new ScrollView(this) {
+ public boolean onInterceptTouchEvent(android.view.MotionEvent ev) {
+ return true;
+ }
+ };
+
+ scrollView.addView(chartLayout);
+ scrollView.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
+ setContentView(scrollView);
+ }
+
+ private class CounterServiceConnection implements ServiceConnection {
+ public void onServiceConnected(ComponentName className,
+ IBinder boundService) {
+ counterService = ICounterService.Stub.asInterface((IBinder)boundService);
+ try {
+ componentNames = counterService.getComponents();
+ componentsMaxPower = counterService.getComponentsMaxPower();
+ noUidMask = counterService.getNoUidMask();
+ refreshView();
+ } catch(RemoteException e) {
+ counterService = null;
+ }
+ }
+
+ public void onServiceDisconnected(ComponentName className) {
+ counterService = null;
+ getApplicationContext().unbindService(conn);
+ getApplicationContext().bindService(serviceIntent, conn, 0);
+ Log.w(TAG, "Unexpectedly lost connection to service");
+ }
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ prefs = PreferenceManager.getDefaultSharedPreferences(this);
+ uid = getIntent().getIntExtra("uid", SystemInfo.AID_ALL);
+
+ collecting = true;
+ if(savedInstanceState != null) {
+ collecting = savedInstanceState.getBoolean("collecting", true);
+ componentNames = savedInstanceState.getStringArray("componentNames");
+ noUidMask = savedInstanceState.getInt("noUidMask");
+ }
+
+ serviceIntent = new Intent(this, UMLoggerService.class);
+ conn = new CounterServiceConnection();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ handler = new Handler();
+ getApplicationContext().bindService(serviceIntent, conn, 0);
+
+ refreshView();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ getApplicationContext().unbindService(conn);
+ if(collectors != null) for(int i = 0; i < components; i++) {
+ handler.removeCallbacks(collectors[i]);
+ }
+ counterService = null;
+ handler = null;
+ collecting = true;
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean("collecting", collecting);
+ outState.putStringArray("componentNames", componentNames);
+ outState.putInt("noUidMask", noUidMask);
+ }
+
+ /* Let all of the UI graphs lay themselves out again. */
+ private void stateChanged() {
+ for(int i = 0; i < components; i++) {
+ collectors[i].layout();
+ }
+ }
+
+ private static final int MENU_OPTIONS = 0;
+ private static final int MENU_TOGGLE_COLLECTING = 1;
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ menu.add(0, MENU_OPTIONS, 0, "Options");
+ menu.add(0, MENU_TOGGLE_COLLECTING, 0, "");
+ return true;
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ menu.findItem(MENU_TOGGLE_COLLECTING).setTitle(
+ collecting ? "Pause" : "Resume");
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch(item.getItemId()) {
+ case MENU_OPTIONS:
+ startActivity(new Intent(this, ViewerPreferences.class));
+ return true;
+ case MENU_TOGGLE_COLLECTING:
+ collecting = !collecting;
+ if(handler != null) {
+ if(collecting) for(int i = 0; i < components; i++) {
+ collectors[i].reset();
+ handler.post(collectors[i]);
+ } else for(int i = 0; i < components; i++) {
+ handler.removeCallbacks(collectors[i]);
+ }
+ }
+ break;
+ }
+ return false;
+ }
+
+ public class ValueCollector implements Runnable {
+ private XYSeries series;
+ private XYMultipleSeriesRenderer renderer;
+ private View chartView;
+
+ private int componentId;
+ private long lastTime;
+
+ int[] values;
+
+ private boolean readHistory;
+
+ public ValueCollector(XYSeries series, XYMultipleSeriesRenderer renderer,
+ View chartView, int componentId) {
+ this.series = series;
+ this.renderer = renderer;
+ this.chartView = chartView;
+ this.componentId = componentId;
+ lastTime = SystemClock.elapsedRealtime();
+ layout();
+ }
+
+ public void layout() {
+ int numVals = Integer.parseInt(prefs.getString("viewNumValues_s", "60"));
+ values = new int[numVals];
+
+ renderer.clearXTextLabels();
+ renderer.setXAxisMin(0);
+ renderer.setXAxisMax(numVals - 1);
+ renderer.addXTextLabel(numVals - 1, "" + numVals);
+ renderer.setXLabels(0);
+ for(int j = 0; j < 10; j++) {
+ renderer.addXTextLabel(numVals * j / 10, "" + (1 + numVals * j / 10));
+ }
+
+ reset();
+ }
+
+ /** Restart points collecting from zero. */
+ public void reset() {
+ series.clear();
+ readHistory = true;
+ }
+
+ public void run() {
+ int numVals = Integer.parseInt(prefs.getString("viewNumValues_s", "60"));
+ if(counterService != null) try {
+ if(readHistory) {
+ values = counterService.getComponentHistory(numVals,
+ componentId, uid);
+ readHistory = false;
+ } else {
+ for(int i = numVals - 1; i > 0; i--) {
+ values[i] = values[i - 1];
+ }
+ values[0] = counterService.getComponentHistory(1, componentId,
+ uid)[0];
+ }
+ } catch(RemoteException e) {
+ Log.w(TAG, "Failed to get data from service");
+ for(int i = 0; i < numVals; i++) {
+ values[i] = 0;
+ }
+ }
+
+ series.clear();
+ for(int i = 0; i < numVals; i++) {
+ series.add(i, values[i]);
+ }
+
+ long curTime = SystemClock.elapsedRealtime();
+ long tryTime = lastTime + PowerEstimator.ITERATION_INTERVAL *
+ (long)Math.max(1, 1 + (curTime - lastTime) /
+ PowerEstimator.ITERATION_INTERVAL);
+ if(handler != null) {
+ handler.postDelayed(this, tryTime - curTime);
+ }
+
+ chartView.invalidate();
+ }
+ };
+}
diff --git a/src/edu/umich/PowerTutor/ui/StartupReceiver.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/ui/StartupReceiver.java
similarity index 100%
rename from src/edu/umich/PowerTutor/ui/StartupReceiver.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/ui/StartupReceiver.java
diff --git a/src/edu/umich/PowerTutor/ui/UMLogger.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/ui/UMLogger.java
similarity index 95%
rename from src/edu/umich/PowerTutor/ui/UMLogger.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/ui/UMLogger.java
index ebaca34..8f492d9 100644
--- a/src/edu/umich/PowerTutor/ui/UMLogger.java
+++ b/PowerTutor2/src/main/java/edu/umich/PowerTutor/ui/UMLogger.java
@@ -24,32 +24,25 @@
import edu.umich.PowerTutor.service.ICounterService;
import edu.umich.PowerTutor.service.UMLoggerService;
+import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.ComponentName;
-import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
-import android.content.res.Configuration;
+import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.IBinder;
-import android.os.RemoteException;
import android.preference.PreferenceManager;
-import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
-import android.widget.SeekBar;
-import android.widget.SeekBar.OnSeekBarChangeListener;
-import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
@@ -59,11 +52,6 @@
import java.io.BufferedOutputStream;
import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.net.DatagramPacket;
-import java.net.DatagramSocket;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
/** The main view activity for PowerTutor*/
public class UMLogger extends Activity {
@@ -71,6 +59,8 @@ public class UMLogger extends Activity {
public static final String CURRENT_VERSION = "1.2"; // Don't change this...
+ private static final int LOCATION_PERMISSION_REQUEST_CODE = 42;
+
public static final String SERVER_IP = "spidermonkey.eecs.umich.edu";
public static final int SERVER_PORT = 5204;
@@ -118,6 +108,20 @@ public void onCreate(Bundle savedInstanceState) {
appViewerButton.setEnabled(false);
sysViewerButton.setEnabled(false);
}
+
+ requestLocationPermission();
+ }
+
+ private void requestLocationPermission() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ requestPermissions(
+ new String[] {
+ Manifest.permission.ACCESS_COARSE_LOCATION,
+ Manifest.permission.ACCESS_FINE_LOCATION
+ },
+ LOCATION_PERMISSION_REQUEST_CODE
+ );
+ }
}
@Override
@@ -324,7 +328,7 @@ public void onClick(View v) {
private class CounterServiceConnection implements ServiceConnection {
public void onServiceConnected(ComponentName className,
IBinder boundService) {
- counterService = ICounterService.Stub.asInterface((IBinder)boundService);
+ counterService = ICounterService.Stub.asInterface((IBinder) boundService);
serviceStartButton.setText("Stop Profiler");
serviceStartButton.setEnabled(true);
appViewerButton.setEnabled(true);
diff --git a/src/edu/umich/PowerTutor/ui/ViewerPreferences.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/ui/ViewerPreferences.java
similarity index 100%
rename from src/edu/umich/PowerTutor/ui/ViewerPreferences.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/ui/ViewerPreferences.java
diff --git a/src/edu/umich/PowerTutor/util/BatteryStats.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/util/BatteryStats.java
similarity index 100%
rename from src/edu/umich/PowerTutor/util/BatteryStats.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/util/BatteryStats.java
diff --git a/src/edu/umich/PowerTutor/util/Counter.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/util/Counter.java
similarity index 100%
rename from src/edu/umich/PowerTutor/util/Counter.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/util/Counter.java
diff --git a/src/edu/umich/PowerTutor/util/ForegroundDetector.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/util/ForegroundDetector.java
similarity index 100%
rename from src/edu/umich/PowerTutor/util/ForegroundDetector.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/util/ForegroundDetector.java
diff --git a/src/edu/umich/PowerTutor/util/HexEncode.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/util/HexEncode.java
similarity index 100%
rename from src/edu/umich/PowerTutor/util/HexEncode.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/util/HexEncode.java
diff --git a/src/edu/umich/PowerTutor/util/HistoryBuffer.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/util/HistoryBuffer.java
similarity index 96%
rename from src/edu/umich/PowerTutor/util/HistoryBuffer.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/util/HistoryBuffer.java
index 65fe0a1..bd46e0e 100644
--- a/src/edu/umich/PowerTutor/util/HistoryBuffer.java
+++ b/PowerTutor2/src/main/java/edu/umich/PowerTutor/util/HistoryBuffer.java
@@ -1,136 +1,136 @@
-/*
-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 .
-
-Please send inquiries to powertutor@umich.edu
-*/
-
-package edu.umich.PowerTutor.util;
-
-import android.util.SparseArray;
-
-import java.util.LinkedList;
-import java.util.ListIterator;
-
-public class HistoryBuffer {
- private static class UidData {
- public LinkedList queue;
- public Counter sum;
- public Counter count;
-
- public UidData() {
- queue = new LinkedList();
- sum = new Counter();
- count = new Counter();
- }
- }
-
- private static class HistoryDatum {
- public HistoryDatum() {
- }
-
- public void init(long iteration, int power) {
- this.iteration = iteration;
- this.power = power;
- }
-
- public long iteration;
- public int power;
- }
-
- private int maxSize;
- private SparseArray uidData;
-
- public HistoryBuffer(int maxSize) {
- this.maxSize = maxSize;
- uidData = new SparseArray();
- }
-
- /* The iteration should only increase across successive adds. */
- public synchronized void add(int uid, long iteration, int power) {
- UidData data = uidData.get(uid);
- if(data == null) {
- data = new UidData();
- uidData.put(uid, data);
- }
- data.count.add(1);
- if(power == 0) {
- return;
- }
- data.sum.add(power);
- if(maxSize == 0) {
- return;
- }
-
- LinkedList queue = data.queue;
- HistoryDatum datum;
- if(maxSize <= queue.size()) {
- datum = queue.getLast();
- queue.removeLast();
- } else {
- datum = new HistoryDatum();
- }
- datum.init(iteration, power);
- queue.addFirst(datum);
- }
-
- /* Fills in the previous number timestamps starting from a timestamp and
- * working backwards. Any timestamp with no information is just treated
- * as using no power.
- */
- public synchronized int[] get(int uid, long timestamp, int number) {
- int ind = 0;
- if(number < 0) number = 0;
- if(number > maxSize) number = maxSize;
- int[] ret = new int[number];
- UidData data = uidData.get(uid);
- LinkedList queue = data == null ? null : data.queue;
- if(queue == null || queue.isEmpty()) {
- return ret;
- }
- if(timestamp == -1) {
- timestamp = queue.getFirst().iteration;
- }
- for(ListIterator iter = queue.listIterator();
- iter.hasNext(); ) {
- HistoryDatum datum = iter.next();
- while(datum.iteration < timestamp && ind < number) {
- ind++;
- timestamp--;
- }
- if(ind == number) {
- break;
- }
- if(datum.iteration == timestamp) {
- ret[ind++] = datum.power;
- timestamp--;
- } else {
- /* datum happened after requested interval. */
- }
- }
- return ret;
- }
-
- public synchronized long getTotal(int uid, int windowType) {
- UidData data = uidData.get(uid);
- return data == null ? 0 : data.sum.get(windowType);
- }
-
- public synchronized long getCount(int uid, int windowType) {
- UidData data = uidData.get(uid);
- return data == null ? 0 : data.count.get(windowType);
- }
-}
-
+/*
+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 .
+
+Please send inquiries to powertutor@umich.edu
+*/
+
+package edu.umich.PowerTutor.util;
+
+import android.util.SparseArray;
+
+import java.util.LinkedList;
+import java.util.ListIterator;
+
+public class HistoryBuffer {
+ private static class UidData {
+ public LinkedList queue;
+ public Counter sum;
+ public Counter count;
+
+ public UidData() {
+ queue = new LinkedList();
+ sum = new Counter();
+ count = new Counter();
+ }
+ }
+
+ private static class HistoryDatum {
+ public HistoryDatum() {
+ }
+
+ public void init(long iteration, int power) {
+ this.iteration = iteration;
+ this.power = power;
+ }
+
+ public long iteration;
+ public int power;
+ }
+
+ private int maxSize;
+ private SparseArray uidData;
+
+ public HistoryBuffer(int maxSize) {
+ this.maxSize = maxSize;
+ uidData = new SparseArray();
+ }
+
+ /* The iteration should only increase across successive adds. */
+ public synchronized void add(int uid, long iteration, int power) {
+ UidData data = uidData.get(uid);
+ if(data == null) {
+ data = new UidData();
+ uidData.put(uid, data);
+ }
+ data.count.add(1);
+ if(power == 0) {
+ return;
+ }
+ data.sum.add(power);
+ if(maxSize == 0) {
+ return;
+ }
+
+ LinkedList queue = data.queue;
+ HistoryDatum datum;
+ if(maxSize <= queue.size()) {
+ datum = queue.getLast();
+ queue.removeLast();
+ } else {
+ datum = new HistoryDatum();
+ }
+ datum.init(iteration, power);
+ queue.addFirst(datum);
+ }
+
+ /* Fills in the previous number timestamps starting from a timestamp and
+ * working backwards. Any timestamp with no information is just treated
+ * as using no power.
+ */
+ public synchronized int[] get(int uid, long timestamp, int number) {
+ int ind = 0;
+ if(number < 0) number = 0;
+ if(number > maxSize) number = maxSize;
+ int[] ret = new int[number];
+ UidData data = uidData.get(uid);
+ LinkedList queue = data == null ? null : data.queue;
+ if(queue == null || queue.isEmpty()) {
+ return ret;
+ }
+ if(timestamp == -1) {
+ timestamp = queue.getFirst().iteration;
+ }
+ for(ListIterator iter = queue.listIterator();
+ iter.hasNext(); ) {
+ HistoryDatum datum = iter.next();
+ while(datum.iteration < timestamp && ind < number) {
+ ind++;
+ timestamp--;
+ }
+ if(ind == number) {
+ break;
+ }
+ if(datum.iteration == timestamp) {
+ ret[ind++] = datum.power;
+ timestamp--;
+ } else {
+ /* datum happened after requested interval. */
+ }
+ }
+ return ret;
+ }
+
+ public synchronized long getTotal(int uid, int windowType) {
+ UidData data = uidData.get(uid);
+ return data == null ? 0 : data.sum.get(windowType);
+ }
+
+ public synchronized long getCount(int uid, int windowType) {
+ UidData data = uidData.get(uid);
+ return data == null ? 0 : data.count.get(windowType);
+ }
+}
+
diff --git a/src/edu/umich/PowerTutor/util/NativeLoader.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/util/NativeLoader.java
similarity index 100%
rename from src/edu/umich/PowerTutor/util/NativeLoader.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/util/NativeLoader.java
diff --git a/src/edu/umich/PowerTutor/util/NotificationService.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/util/NotificationService.java
similarity index 100%
rename from src/edu/umich/PowerTutor/util/NotificationService.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/util/NotificationService.java
diff --git a/src/edu/umich/PowerTutor/util/Recycler.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/util/Recycler.java
similarity index 100%
rename from src/edu/umich/PowerTutor/util/Recycler.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/util/Recycler.java
diff --git a/src/edu/umich/PowerTutor/util/SystemInfo.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/util/SystemInfo.java
similarity index 100%
rename from src/edu/umich/PowerTutor/util/SystemInfo.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/util/SystemInfo.java
diff --git a/src/edu/umich/PowerTutor/widget/Configure.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/widget/Configure.java
similarity index 100%
rename from src/edu/umich/PowerTutor/widget/Configure.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/widget/Configure.java
diff --git a/src/edu/umich/PowerTutor/widget/DataSource.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/widget/DataSource.java
similarity index 100%
rename from src/edu/umich/PowerTutor/widget/DataSource.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/widget/DataSource.java
diff --git a/src/edu/umich/PowerTutor/widget/DataSourceConfigure.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/widget/DataSourceConfigure.java
similarity index 100%
rename from src/edu/umich/PowerTutor/widget/DataSourceConfigure.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/widget/DataSourceConfigure.java
diff --git a/src/edu/umich/PowerTutor/widget/PowerWidget.java b/PowerTutor2/src/main/java/edu/umich/PowerTutor/widget/PowerWidget.java
similarity index 100%
rename from src/edu/umich/PowerTutor/widget/PowerWidget.java
rename to PowerTutor2/src/main/java/edu/umich/PowerTutor/widget/PowerWidget.java
diff --git a/jni/Android.mk b/PowerTutor2/src/main/jni/Android.mk
similarity index 100%
rename from jni/Android.mk
rename to PowerTutor2/src/main/jni/Android.mk
diff --git a/jni/bindings.cpp b/PowerTutor2/src/main/jni/bindings.cpp
similarity index 100%
rename from jni/bindings.cpp
rename to PowerTutor2/src/main/jni/bindings.cpp
diff --git a/res/drawable/background.png b/PowerTutor2/src/main/res/drawable/background.png
similarity index 100%
rename from res/drawable/background.png
rename to PowerTutor2/src/main/res/drawable/background.png
diff --git a/res/drawable/help.png b/PowerTutor2/src/main/res/drawable/help.png
similarity index 100%
rename from res/drawable/help.png
rename to PowerTutor2/src/main/res/drawable/help.png
diff --git a/res/drawable/icon.png b/PowerTutor2/src/main/res/drawable/icon.png
similarity index 100%
rename from res/drawable/icon.png
rename to PowerTutor2/src/main/res/drawable/icon.png
diff --git a/res/drawable/level.xml b/PowerTutor2/src/main/res/drawable/level.xml
similarity index 98%
rename from res/drawable/level.xml
rename to PowerTutor2/src/main/res/drawable/level.xml
index 7c87f58..9df13b6 100644
--- a/res/drawable/level.xml
+++ b/PowerTutor2/src/main/res/drawable/level.xml
@@ -1,12 +1,12 @@
-
- android:id="@+id/l1"
-
-
-
-
-
-
-
-
-
-
+
+ android:id="@+id/l1"
+
+
+
+
+
+
+
+
+
+
diff --git a/res/drawable/level_1.png b/PowerTutor2/src/main/res/drawable/level_1.png
similarity index 100%
rename from res/drawable/level_1.png
rename to PowerTutor2/src/main/res/drawable/level_1.png
diff --git a/res/drawable/level_2.png b/PowerTutor2/src/main/res/drawable/level_2.png
similarity index 100%
rename from res/drawable/level_2.png
rename to PowerTutor2/src/main/res/drawable/level_2.png
diff --git a/res/drawable/level_3.png b/PowerTutor2/src/main/res/drawable/level_3.png
similarity index 100%
rename from res/drawable/level_3.png
rename to PowerTutor2/src/main/res/drawable/level_3.png
diff --git a/res/drawable/level_4.png b/PowerTutor2/src/main/res/drawable/level_4.png
similarity index 100%
rename from res/drawable/level_4.png
rename to PowerTutor2/src/main/res/drawable/level_4.png
diff --git a/res/drawable/level_5.png b/PowerTutor2/src/main/res/drawable/level_5.png
similarity index 100%
rename from res/drawable/level_5.png
rename to PowerTutor2/src/main/res/drawable/level_5.png
diff --git a/res/drawable/level_6.png b/PowerTutor2/src/main/res/drawable/level_6.png
similarity index 100%
rename from res/drawable/level_6.png
rename to PowerTutor2/src/main/res/drawable/level_6.png
diff --git a/res/drawable/level_7.png b/PowerTutor2/src/main/res/drawable/level_7.png
similarity index 100%
rename from res/drawable/level_7.png
rename to PowerTutor2/src/main/res/drawable/level_7.png
diff --git a/res/drawable/level_8.png b/PowerTutor2/src/main/res/drawable/level_8.png
similarity index 100%
rename from res/drawable/level_8.png
rename to PowerTutor2/src/main/res/drawable/level_8.png
diff --git a/res/drawable/level_9.png b/PowerTutor2/src/main/res/drawable/level_9.png
similarity index 100%
rename from res/drawable/level_9.png
rename to PowerTutor2/src/main/res/drawable/level_9.png
diff --git a/res/drawable/line.png b/PowerTutor2/src/main/res/drawable/line.png
similarity index 100%
rename from res/drawable/line.png
rename to PowerTutor2/src/main/res/drawable/line.png
diff --git a/res/drawable/power_off.png b/PowerTutor2/src/main/res/drawable/power_off.png
similarity index 100%
rename from res/drawable/power_off.png
rename to PowerTutor2/src/main/res/drawable/power_off.png
diff --git a/res/drawable/power_on.png b/PowerTutor2/src/main/res/drawable/power_on.png
similarity index 100%
rename from res/drawable/power_on.png
rename to PowerTutor2/src/main/res/drawable/power_on.png
diff --git a/res/drawable/time.xml b/PowerTutor2/src/main/res/drawable/time.xml
similarity index 98%
rename from res/drawable/time.xml
rename to PowerTutor2/src/main/res/drawable/time.xml
index 896640f..d8a2e5d 100644
--- a/res/drawable/time.xml
+++ b/PowerTutor2/src/main/res/drawable/time.xml
@@ -1,16 +1,16 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/drawable/time_0.png b/PowerTutor2/src/main/res/drawable/time_0.png
similarity index 100%
rename from res/drawable/time_0.png
rename to PowerTutor2/src/main/res/drawable/time_0.png
diff --git a/res/drawable/time_1.png b/PowerTutor2/src/main/res/drawable/time_1.png
similarity index 100%
rename from res/drawable/time_1.png
rename to PowerTutor2/src/main/res/drawable/time_1.png
diff --git a/res/drawable/time_10.png b/PowerTutor2/src/main/res/drawable/time_10.png
similarity index 100%
rename from res/drawable/time_10.png
rename to PowerTutor2/src/main/res/drawable/time_10.png
diff --git a/res/drawable/time_11.png b/PowerTutor2/src/main/res/drawable/time_11.png
similarity index 100%
rename from res/drawable/time_11.png
rename to PowerTutor2/src/main/res/drawable/time_11.png
diff --git a/res/drawable/time_12.png b/PowerTutor2/src/main/res/drawable/time_12.png
similarity index 100%
rename from res/drawable/time_12.png
rename to PowerTutor2/src/main/res/drawable/time_12.png
diff --git a/res/drawable/time_2.png b/PowerTutor2/src/main/res/drawable/time_2.png
similarity index 100%
rename from res/drawable/time_2.png
rename to PowerTutor2/src/main/res/drawable/time_2.png
diff --git a/res/drawable/time_3.png b/PowerTutor2/src/main/res/drawable/time_3.png
similarity index 100%
rename from res/drawable/time_3.png
rename to PowerTutor2/src/main/res/drawable/time_3.png
diff --git a/res/drawable/time_4.png b/PowerTutor2/src/main/res/drawable/time_4.png
similarity index 100%
rename from res/drawable/time_4.png
rename to PowerTutor2/src/main/res/drawable/time_4.png
diff --git a/res/drawable/time_5.png b/PowerTutor2/src/main/res/drawable/time_5.png
similarity index 100%
rename from res/drawable/time_5.png
rename to PowerTutor2/src/main/res/drawable/time_5.png
diff --git a/res/drawable/time_6.png b/PowerTutor2/src/main/res/drawable/time_6.png
similarity index 100%
rename from res/drawable/time_6.png
rename to PowerTutor2/src/main/res/drawable/time_6.png
diff --git a/res/drawable/time_7.png b/PowerTutor2/src/main/res/drawable/time_7.png
similarity index 100%
rename from res/drawable/time_7.png
rename to PowerTutor2/src/main/res/drawable/time_7.png
diff --git a/res/drawable/time_8.png b/PowerTutor2/src/main/res/drawable/time_8.png
similarity index 100%
rename from res/drawable/time_8.png
rename to PowerTutor2/src/main/res/drawable/time_8.png
diff --git a/res/drawable/time_9.png b/PowerTutor2/src/main/res/drawable/time_9.png
similarity index 100%
rename from res/drawable/time_9.png
rename to PowerTutor2/src/main/res/drawable/time_9.png
diff --git a/res/drawable/widget_bg.png b/PowerTutor2/src/main/res/drawable/widget_bg.png
similarity index 100%
rename from res/drawable/widget_bg.png
rename to PowerTutor2/src/main/res/drawable/widget_bg.png
diff --git a/res/layout/help.xml b/PowerTutor2/src/main/res/layout/help.xml
similarity index 97%
rename from res/layout/help.xml
rename to PowerTutor2/src/main/res/layout/help.xml
index 5746d8f..7b1e555 100644
--- a/res/layout/help.xml
+++ b/PowerTutor2/src/main/res/layout/help.xml
@@ -1,68 +1,68 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/main.xml b/PowerTutor2/src/main/res/layout/main.xml
similarity index 96%
rename from res/layout/main.xml
rename to PowerTutor2/src/main/res/layout/main.xml
index 70dceae..6546181 100644
--- a/res/layout/main.xml
+++ b/PowerTutor2/src/main/res/layout/main.xml
@@ -1,78 +1,78 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/misc_item_layout.xml b/PowerTutor2/src/main/res/layout/misc_item_layout.xml
similarity index 100%
rename from res/layout/misc_item_layout.xml
rename to PowerTutor2/src/main/res/layout/misc_item_layout.xml
diff --git a/res/layout/misc_layout.xml b/PowerTutor2/src/main/res/layout/misc_layout.xml
similarity index 100%
rename from res/layout/misc_layout.xml
rename to PowerTutor2/src/main/res/layout/misc_layout.xml
diff --git a/res/layout/power_tabs.xml b/PowerTutor2/src/main/res/layout/power_tabs.xml
similarity index 100%
rename from res/layout/power_tabs.xml
rename to PowerTutor2/src/main/res/layout/power_tabs.xml
diff --git a/res/layout/widget_configure.xml b/PowerTutor2/src/main/res/layout/widget_configure.xml
similarity index 100%
rename from res/layout/widget_configure.xml
rename to PowerTutor2/src/main/res/layout/widget_configure.xml
diff --git a/res/layout/widget_item_layout.xml b/PowerTutor2/src/main/res/layout/widget_item_layout.xml
similarity index 100%
rename from res/layout/widget_item_layout.xml
rename to PowerTutor2/src/main/res/layout/widget_item_layout.xml
diff --git a/res/layout/widget_layout.xml b/PowerTutor2/src/main/res/layout/widget_layout.xml
similarity index 100%
rename from res/layout/widget_layout.xml
rename to PowerTutor2/src/main/res/layout/widget_layout.xml
diff --git a/res/values/arrays.xml b/PowerTutor2/src/main/res/values/arrays.xml
similarity index 100%
rename from res/values/arrays.xml
rename to PowerTutor2/src/main/res/values/arrays.xml
diff --git a/res/values/strings.xml b/PowerTutor2/src/main/res/values/strings.xml
similarity index 100%
rename from res/values/strings.xml
rename to PowerTutor2/src/main/res/values/strings.xml
diff --git a/res/xml/preferences.xml b/PowerTutor2/src/main/res/xml/preferences.xml
similarity index 100%
rename from res/xml/preferences.xml
rename to PowerTutor2/src/main/res/xml/preferences.xml
diff --git a/res/xml/viewer_preferences.xml b/PowerTutor2/src/main/res/xml/viewer_preferences.xml
similarity index 100%
rename from res/xml/viewer_preferences.xml
rename to PowerTutor2/src/main/res/xml/viewer_preferences.xml
diff --git a/res/xml/widget_info.xml b/PowerTutor2/src/main/res/xml/widget_info.xml
similarity index 100%
rename from res/xml/widget_info.xml
rename to PowerTutor2/src/main/res/xml/widget_info.xml
diff --git a/README b/README
deleted file mode 100644
index c9875c4..0000000
--- a/README
+++ /dev/null
@@ -1,11 +0,0 @@
-PowerTutor Project
-
-Model based power mobile power estimator by application
-
-Contact: powertutor@umich.edu
-
-Website: http://powertutor.org
-
-This package includes the PowerTutor source code. The code responsible for
-uploading logs to our remote server has been removed and replaced by a stub that
-does nothing.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..1513469
--- /dev/null
+++ b/README.md
@@ -0,0 +1,15 @@
+PowerTutor Project
+
+Model based power mobile power estimator by application
+
+PowerTutor is an application for Google phones that displays the power consumed by major system components such as CPU, network interface, display, and GPS receiver and different applications. The application allows software developers to see the impact of design changes on power efficiency. Application users can also use it to determine how their actions are impacting battery life. PowerTutor uses a power consumption model built by direct measurements during careful control of device power management states. This model generally provides power consumption estimates within 5% of actual values. A configurable display for power consumption history is provided. It also provides users with a text-file based output containing detailed results. You can use PowerTutor to monitor the power consumption of any application.
+
+PowerTutor's power model was built on HTC G1, HTC G2 and Nexus one. It will run on other versions of the GPhone, but when used with phones other than the above phone models, power consumption estimates will be rough. We plan to provide power models for other phones in the future.
+
+Contact: powertutor@umich.edu
+
+Website: http://ziyang.eecs.umich.edu/projects/powertutor/
+
+This package includes the PowerTutor source code. The code responsible for
+uploading logs to our remote server has been removed and replaced by a stub that
+does nothing.
\ No newline at end of file
diff --git a/android-9.jar b/android-9.jar
deleted file mode 100644
index 79c7ea4..0000000
Binary files a/android-9.jar and /dev/null differ
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..dad9ef0
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,17 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+buildscript {
+ repositories {
+ google()
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:4.0.1'
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ }
+}
diff --git a/debug.keystore b/debug.keystore
deleted file mode 100644
index 74dc10d..0000000
Binary files a/debug.keystore and /dev/null differ
diff --git a/default.properties b/default.properties
deleted file mode 100644
index bc708b0..0000000
--- a/default.properties
+++ /dev/null
@@ -1,11 +0,0 @@
-# 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
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..f6b961f
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..7d12f7d
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Jul 29 12:24:10 CEST 2020
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
diff --git a/gradlew b/gradlew
new file mode 100755
index 0000000..cccdd3d
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..f955316
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/import-summary.txt b/import-summary.txt
new file mode 100644
index 0000000..28225f0
--- /dev/null
+++ b/import-summary.txt
@@ -0,0 +1,43 @@
+ECLIPSE ANDROID PROJECT IMPORT SUMMARY
+======================================
+
+Ignored Files:
+--------------
+The following files were *not* copied into the new Gradle project; you
+should evaluate whether these are still needed in your project and if
+so manually move them:
+
+* LICENCE
+* Makefile
+* README
+* android-9.jar
+* debug.keystore
+* default.properties
+
+Moved Files:
+------------
+Android Gradle projects use a different directory structure than ADT
+Eclipse projects. Here's how the projects were restructured:
+
+* AndroidManifest.xml => PowerTutor2/src/main/AndroidManifest.xml
+* jni/ => PowerTutor2/src/main/jni/
+* libs/achartengine-1.0.0.jar => PowerTutor2/libs/achartengine-1.0.0.jar
+* res/ => PowerTutor2/src/main/res/
+* src/ => PowerTutor2/src/main/java/
+* src/edu/umich/PowerTutor/PowerNotifications.aidl => PowerTutor2/src/main/aidl/edu/umich/PowerTutor/PowerNotifications.aidl
+* src/edu/umich/PowerTutor/service/ICounterService.aidl => PowerTutor2/src/main/aidl/edu/umich/PowerTutor/service/ICounterService.aidl
+
+Next Steps:
+-----------
+You can now build the project. The Gradle project needs network
+connectivity to download dependencies.
+
+Bugs:
+-----
+If for some reason your project does not build, and you determine that
+it is due to a bug or limitation of the Eclipse to Gradle importer,
+please file a bug at http://b.android.com with category
+Component-Tools.
+
+(This import summary is for your information only, and can be deleted
+after import once you are satisfied with the results.)
diff --git a/local.properties b/local.properties
new file mode 100644
index 0000000..5ed4282
--- /dev/null
+++ b/local.properties
@@ -0,0 +1,7 @@
+## This file must *NOT* be checked into Version Control Systems,
+# as it contains information specific to your local configuration.
+#
+# Location of the SDK. This is only used by Gradle.
+#
+#Wed Jul 29 12:20:07 CEST 2020
+sdk.dir=/Users/timo.bahr/Library/Android/sdk
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..e83f5a0
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1 @@
+include ':PowerTutor2'