diff --git a/.classpath b/.classpath
index a4763d1..1a9a798 100644
--- a/.classpath
+++ b/.classpath
@@ -1,8 +1,14 @@
+
+
+
+
+
+
-
-
+
+
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 0000000..2eefb87
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,13 @@
+## The problem
+
+Describe the issue you are experiencing, or features you want.
+
+## Environment
+
+* Emmagee version
+* Device Information: **Brand**, **Model**, **System**.
+
+## Link to Emmagee logs
+
+Create a [GIST](https://gist.github.com) which is a paste of your _full_ Emmagee logs, and link them here.
+Do _NOT_ paste your full Emmagee logs here, as it will make this issue very long and hard to read!
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e0cba59
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,19 @@
+# built application files
+*.apk
+*.ap_
+
+# files for the dex VM
+*.dex
+
+# Java class files
+*.class
+
+# generated files
+bin/
+gen/
+
+# Local configuration file (sdk path, etc)
+local.properties
+
+lint.xml
+.DS_Store
\ No newline at end of file
diff --git a/AUTHORS b/AUTHORS
index 5ffd5f2..a32824e 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1,2 +1,3 @@
* KevinKong
* andrewleo
+* bingoHuang
\ No newline at end of file
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 82d995a..1368e4a 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -1,42 +1,56 @@
+ android:versionCode="34"
+ android:versionName="2.5.1" >
-
-
+
+
+ android:label="@string/app_name"
+ android:theme="@android:style/Theme.NoTitleBar" >
+ android:configChanges="orientation|keyboardHidden"
+ android:launchMode="singleInstance"
+ android:screenOrientation="portrait" >
-
-
+
-
-
-
-
+ android:launchMode="singleInstance"
+ android:screenOrientation="portrait"
+ android:theme="@style/customTheme" />
+
+
+
+
-
-
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index fc91244..2eddd66 100644
--- a/README.md
+++ b/README.md
@@ -1,57 +1,78 @@
-##Emmagee - a practical, handy performance test tool for specified Android App
-
-Emmagee is a practical, handy performance test tool for specified Android App, which can monitor CPU, memory and
-network traffic. Additionally, it also provides several cool features such as customizing interval of collecting data,
-rendering real-time process status in a floating window, and much more.
-
- * Homepage:
- * Wiki:
- * Issues:
- * Tags: Android, Java
-
-## Features
-
-* Process-specific monitoring
-* Real-time data displaying
-* User-defined collecting interval
-
-## Why should I use Emmagee?
-
-Unlike other performance test tools that only do system-level monitoring, Emmagee provides the ability to monitor any single App. Other advantages that
-you should not miss:
-* Open source
-* Easy to use
-* Floating window rendering real-time process status
-* CSV format report that can be converted into any other format you want
-* Fully support Android 2.2 and above
-
-## How to use Emmagee?
-
-1. Start Emmagee App
-2. Configure interval
-3. Select a target process
-4. Click Start button
-
-And Enjoy!
-
-If you want to stop the test, just go back to Emmagee and click Stop button.
-
-## Contributors
-* NetEase, Inc.
-
-## License
-(The Apache License)
-
-Copyright (c) 2012-2013 NetEase, Inc. and other contributors
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
+## Emmagee - a practical, handy performance test tool for specified Android App
+
+Emmagee is a practical, handy performance test tool for specified Android App, which can monitor CPU, memory,
+network traffic, battery current and status([Some devices are not supported](https://github.com/NetEase/Emmagee/wiki/Some-devices-are-not-supported)), new features such as top activity and heap size if rooted([Root Toast may continously show](https://github.com/NetEase/Emmagee/wiki/FAQ)), are also supported in the [latest version](https://github.com/NetEase/Emmagee/releases). Additionally, it also provides several cool features such as customizing interval of collecting data,
+rendering real-time process status in a floating window, and much more.
+
+ * Homepage: https://github.com/NetEase/Emmagee
+ * Wiki: https://github.com/NetEase/Emmagee/wiki
+ * Issues: https://github.com/NetEase/Emmagee/issues
+ * FAQ: https://github.com/NetEase/Emmagee/wiki/FAQ
+ * Tags: Android, Java
+
+
+
+## Why should I use Emmagee?
+
+Unlike most other performance test tools that only do system-level monitoring, Emmagee provides the ability to monitor any single App. Other advantages that
+you should not miss:
+* Open source
+* Easy to use
+* Process-specific monitoring, including CPU, memory, network traffic, battery current, launching time and status
+* Floating window that renders real-time process status
+* CSV format report that can be converted into any other format you want
+* User-defined collecting interval
+* Fully support Android 2.2 and above
+
+## How to use Emmagee?
+
+First of all ,you should have Emmagee.apk,download [here](https://github.com/NetEase/Emmagee/releases) or
+build the apk file youself [here](https://github.com/NetEase/Emmagee/wiki/How-to-build-emmage.apk%3F),then :
+
+1. Start Emmagee App
+2. Configure interval
+3. Select a target process
+4. Click Start button
+
+And Enjoy!
+
+If you want to stop the test, just go back to Emmagee and click Stop button.
+
+## Android 5.0 and above
+
+* `Android 5.0 and above`: getRunningTasks() and getRunningAppProcesses() are deprecated and only return your application process, so it is unable to get TopActivity from Android 5.0.
+* `Android 7.0`: Google has restricted access to /proc, and also can not get pid of target application from TOP command in Android 7.0, I am so sorry to tell that 7.0 can not be supported.
+
+## Coming Soon
+* We want you to decide!
+
+## How to Contribute?
+
+You are welcome to contribute to Emmagee, meanwhile you'd better follow the rules below
+
+* It's *NOT* recommended to submit a pull request directly to Emmagee's `master` branch. `develop` branch is more appropriate
+* Follow common Java coding conventions
+* Put all Java class files under *com.netease* package
+* Add the following [license](#license) in each Java class file
+
+## Contributors
+* NetEase, Inc.
+* [yrom](https://github.com/yrom)
+* [LukeOwncloud](https://github.com/LukeOwncloud)
+
+## License
+(The Apache License)
+
+Copyright (c) 2012-2015 NetEase, Inc. and other contributors
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/VERSION b/VERSION
index 6c6aa7c..6b4950e 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.1.0
\ No newline at end of file
+2.4
diff --git a/apk/Emmagee.apk b/apk/Emmagee.apk
deleted file mode 100644
index 632dfc1..0000000
Binary files a/apk/Emmagee.apk and /dev/null differ
diff --git a/build.xml b/build.xml
deleted file mode 100644
index 620232b..0000000
--- a/build.xml
+++ /dev/null
@@ -1,51 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/changelog.md b/changelog.md
new file mode 100644
index 0000000..a156c91
--- /dev/null
+++ b/changelog.md
@@ -0,0 +1,44 @@
+Emmagee Changelog
+
+###2.0 (2014-9-22)
+New Features:
+* Optimization of UI
+* I18N support(Chinese Simplified and English)
+* Collect usage of each CPU cores
+
+Issues Fixed:
+* #16 'Save' button is covered when soft keyboard pops up
+* #20 Strings hard-coded for language
+* #21 CSV report enhancement
+* #24 Floating window disappears on some specific devices
+* #25 Report unavailable path of CSV when test is over
+* #26 Dulplicated toast when test is over
+
+###1.3.0 (2014-1-6)
+New Features:
+* Integrate monitoring of app starting time
+
+Issues fixed
+* #12 Add "stop test" button on floating window
+* #13 Optimize interface and user experience
+
+###1.2.1 (2013-11-25)
+Issues fixed:
+* #10 The CSV report displays abnormally when free memory is above 1000.
+
+Improvements to :
+* #9 List the application with alphabet order.
+
+###1.2.0 (2013-9-17)
+* Support quad-core CPU
+* Integrate monitoring of battery current and status
+
+###1.1.0 (2013-4-8)
+* Configure specified recipients to receive test reports
+* Automaticly save the result when monitoring app stops
+
+###1.0.0 (2013-1-18)
+* Process-specific monitoring
+* Floating window that renders real-time process status
+* CSV format report that can be converted into any other format you want
+* User-defined collecting interval
diff --git a/emmagee.xml b/emmagee.xml
new file mode 100644
index 0000000..e617f3f
--- /dev/null
+++ b/emmagee.xml
@@ -0,0 +1,83 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/gen/com/netease/qa/emmagee/R.java b/gen/com/netease/qa/emmagee/R.java
deleted file mode 100644
index c006e99..0000000
--- a/gen/com/netease/qa/emmagee/R.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/* AUTO-GENERATED FILE. DO NOT MODIFY.
- *
- * This class was automatically generated by the
- * aapt tool from the resource data it found. It
- * should not be modified by hand.
- */
-
-package com.netease.qa.emmagee;
-
-public final class R {
- public static final class attr {
- }
- public static final class drawable {
- public static final int button_bg=0x7f020000;
- public static final int close=0x7f020001;
- public static final int ic_launcher=0x7f020002;
- public static final int icon=0x7f020003;
- public static final int meminfo=0x7f020004;
- }
- public static final class id {
- public static final int Lin=0x7f050004;
- public static final int b=0x7f050001;
- public static final int cpu1=0x7f050013;
- public static final int cpu2=0x7f050014;
- public static final int cpu_info=0x7f050000;
- public static final int floating=0x7f050011;
- public static final int floating_Window=0x7f050010;
- public static final int image=0x7f05000a;
- public static final int img1=0x7f050002;
- public static final int img2=0x7f050003;
- public static final int memoryinfo=0x7f05000e;
- public static final int memtotal=0x7f050006;
- public static final int memunused=0x7f050005;
- public static final int processList=0x7f05000c;
- public static final int rb=0x7f050009;
- public static final int save=0x7f050012;
- public static final int test=0x7f05000d;
- public static final int text=0x7f05000b;
- public static final int time=0x7f05000f;
- public static final int traffic=0x7f050007;
- public static final int wifi=0x7f050008;
- }
- public static final class layout {
- public static final int cpu=0x7f030000;
- public static final int floating=0x7f030001;
- public static final int list_item=0x7f030002;
- public static final int mainpage=0x7f030003;
- public static final int memory=0x7f030004;
- public static final int settings=0x7f030005;
- public static final int systemstat=0x7f030006;
- }
- public static final class string {
- public static final int app_name=0x7f040001;
- public static final int app_name1=0x7f040002;
- public static final int app_name2=0x7f040003;
- public static final int app_name3=0x7f040004;
- public static final int app_name4=0x7f040005;
- public static final int bg=0x7f040009;
- public static final int closewifi=0x7f040011;
- public static final int collect=0x7f04000c;
- public static final int cpu=0x7f040008;
- public static final int hello=0x7f040000;
- public static final int memoryinfo=0x7f04000b;
- public static final int ok=0x7f04000f;
- public static final int openwifi=0x7f040010;
- public static final int seconds=0x7f04000d;
- public static final int start=0x7f040007;
- public static final int system=0x7f040006;
- public static final int testmemory=0x7f04000a;
- public static final int window=0x7f04000e;
- }
-}
diff --git a/libs/activation.jar b/libs/activation.jar
new file mode 100644
index 0000000..614a03c
Binary files /dev/null and b/libs/activation.jar differ
diff --git a/libs/additionnal.jar b/libs/additionnal.jar
new file mode 100644
index 0000000..aeb9611
Binary files /dev/null and b/libs/additionnal.jar differ
diff --git a/libs/android-support-v4.jar b/libs/android-support-v4.jar
new file mode 100644
index 0000000..428bdbc
Binary files /dev/null and b/libs/android-support-v4.jar differ
diff --git a/libs/commons-io-2.5.jar b/libs/commons-io-2.5.jar
new file mode 100644
index 0000000..1234918
Binary files /dev/null and b/libs/commons-io-2.5.jar differ
diff --git a/libs/mail.jar b/libs/mail.jar
new file mode 100644
index 0000000..2157537
Binary files /dev/null and b/libs/mail.jar differ
diff --git a/project.properties b/project.properties
index f049142..a5578ba 100644
--- a/project.properties
+++ b/project.properties
@@ -8,4 +8,4 @@
# project structure.
# Project target.
-target=android-10
+target=android-19
diff --git a/res/drawable-hdpi/actionbar_bg.9.png b/res/drawable-hdpi/actionbar_bg.9.png
new file mode 100644
index 0000000..ea41b6e
Binary files /dev/null and b/res/drawable-hdpi/actionbar_bg.9.png differ
diff --git a/res/drawable-hdpi/bottom_bg.png b/res/drawable-hdpi/bottom_bg.png
new file mode 100644
index 0000000..fa426fe
Binary files /dev/null and b/res/drawable-hdpi/bottom_bg.png differ
diff --git a/res/drawable-hdpi/btn_back.png b/res/drawable-hdpi/btn_back.png
new file mode 100644
index 0000000..d154f5d
Binary files /dev/null and b/res/drawable-hdpi/btn_back.png differ
diff --git a/res/drawable-hdpi/btn_save.png b/res/drawable-hdpi/btn_save.png
new file mode 100644
index 0000000..43fc20e
Binary files /dev/null and b/res/drawable-hdpi/btn_save.png differ
diff --git a/res/drawable-hdpi/btn_test.png b/res/drawable-hdpi/btn_test.png
new file mode 100644
index 0000000..e497a64
Binary files /dev/null and b/res/drawable-hdpi/btn_test.png differ
diff --git a/res/drawable-hdpi/btn_test_press.png b/res/drawable-hdpi/btn_test_press.png
new file mode 100644
index 0000000..f87e0db
Binary files /dev/null and b/res/drawable-hdpi/btn_test_press.png differ
diff --git a/res/drawable-hdpi/checked_icon.png b/res/drawable-hdpi/checked_icon.png
new file mode 100644
index 0000000..66727de
Binary files /dev/null and b/res/drawable-hdpi/checked_icon.png differ
diff --git a/res/drawable-hdpi/left_arrow.png b/res/drawable-hdpi/left_arrow.png
new file mode 100644
index 0000000..d102ef9
Binary files /dev/null and b/res/drawable-hdpi/left_arrow.png differ
diff --git a/res/drawable-hdpi/refresh.png b/res/drawable-hdpi/refresh.png
new file mode 100644
index 0000000..2eab627
Binary files /dev/null and b/res/drawable-hdpi/refresh.png differ
diff --git a/res/drawable-hdpi/right_arrow.png b/res/drawable-hdpi/right_arrow.png
new file mode 100644
index 0000000..ca9c12d
Binary files /dev/null and b/res/drawable-hdpi/right_arrow.png differ
diff --git a/res/drawable-hdpi/seekbar_thumb_normal.png b/res/drawable-hdpi/seekbar_thumb_normal.png
new file mode 100644
index 0000000..91b9362
Binary files /dev/null and b/res/drawable-hdpi/seekbar_thumb_normal.png differ
diff --git a/res/drawable-hdpi/seekbar_thumb_pressed.png b/res/drawable-hdpi/seekbar_thumb_pressed.png
new file mode 100644
index 0000000..b897d22
Binary files /dev/null and b/res/drawable-hdpi/seekbar_thumb_pressed.png differ
diff --git a/res/drawable-hdpi/settings.png b/res/drawable-hdpi/settings.png
new file mode 100644
index 0000000..68d9f5f
Binary files /dev/null and b/res/drawable-hdpi/settings.png differ
diff --git a/res/drawable-hdpi/settings_pressed.png b/res/drawable-hdpi/settings_pressed.png
new file mode 100644
index 0000000..b8e5bff
Binary files /dev/null and b/res/drawable-hdpi/settings_pressed.png differ
diff --git a/res/drawable-hdpi/unchecked_icon.png b/res/drawable-hdpi/unchecked_icon.png
new file mode 100644
index 0000000..ef99cc5
Binary files /dev/null and b/res/drawable-hdpi/unchecked_icon.png differ
diff --git a/res/drawable/custom_checkbox.xml b/res/drawable/custom_checkbox.xml
new file mode 100644
index 0000000..faaac8f
--- /dev/null
+++ b/res/drawable/custom_checkbox.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/drawable/custom_seekbar.xml b/res/drawable/custom_seekbar.xml
new file mode 100644
index 0000000..00981cc
--- /dev/null
+++ b/res/drawable/custom_seekbar.xml
@@ -0,0 +1,17 @@
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/drawable/float_button.xml b/res/drawable/float_button.xml
new file mode 100644
index 0000000..bf1fc3c
--- /dev/null
+++ b/res/drawable/float_button.xml
@@ -0,0 +1,23 @@
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/drawable/seekbar_thumb.xml b/res/drawable/seekbar_thumb.xml
new file mode 100644
index 0000000..a0028b4
--- /dev/null
+++ b/res/drawable/seekbar_thumb.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/drawable/settings_button.xml b/res/drawable/settings_button.xml
new file mode 100644
index 0000000..872cc86
--- /dev/null
+++ b/res/drawable/settings_button.xml
@@ -0,0 +1,5 @@
+
+
+
+
diff --git a/res/drawable/table_border.xml b/res/drawable/table_border.xml
new file mode 100644
index 0000000..bb001ed
--- /dev/null
+++ b/res/drawable/table_border.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/drawable/test_button.xml b/res/drawable/test_button.xml
new file mode 100644
index 0000000..36582e6
--- /dev/null
+++ b/res/drawable/test_button.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
diff --git a/res/layout/about.xml b/res/layout/about.xml
new file mode 100644
index 0000000..0d6c386
--- /dev/null
+++ b/res/layout/about.xml
@@ -0,0 +1,125 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/cpu.xml b/res/layout/cpu.xml
deleted file mode 100644
index e3f23ec..0000000
--- a/res/layout/cpu.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/res/layout/floating.xml b/res/layout/floating.xml
index f4345ba..67f3277 100644
--- a/res/layout/floating.xml
+++ b/res/layout/floating.xml
@@ -16,12 +16,6 @@
android:layout_marginTop="4dp"
android:src="@drawable/meminfo" />
-
-
-
-
+ android:textColor="#FFFFFF" />
+
+
+
+
+
diff --git a/res/layout/list_item.xml b/res/layout/list_item.xml
index 2c8eccb..a91263b 100644
--- a/res/layout/list_item.xml
+++ b/res/layout/list_item.xml
@@ -13,11 +13,21 @@
android:id="@+id/image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginRight="10dip" />
+ android:adjustViewBounds="true"
+ android:maxHeight="100px"
+ android:maxWidth="100px"
+ android:layout_gravity="center"
+ android:layout_marginRight="10dp"
+ android:layout_marginLeft="10dp" />
+ android:layout_height="wrap_content"
+ android:textSize="18sp"
+ android:textColor="#5B5B5B"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:gravity="center_vertical"/>
\ No newline at end of file
diff --git a/res/layout/mail_settings.xml b/res/layout/mail_settings.xml
new file mode 100644
index 0000000..19bd1fc
--- /dev/null
+++ b/res/layout/mail_settings.xml
@@ -0,0 +1,143 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/mainpage.xml b/res/layout/mainpage.xml
index 10ceb82..d22ac55 100644
--- a/res/layout/mainpage.xml
+++ b/res/layout/mainpage.xml
@@ -2,29 +2,46 @@
+
+
+
+
+
+ android:layout_weight="1"
+ android:cacheColorHint="@color/light_purple"
+ android:listSelector="@color/light_purple" />
+ android:layout_height="@dimen/layout_height_test"
+ android:background="@drawable/bottom_bg" >
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_marginBottom="@dimen/layout_marginBottom_test"
+ android:layout_marginLeft="@dimen/layout_marginLeft_test"
+ android:layout_marginRight="@dimen/layout_marginRight_test"
+ android:layout_marginTop="@dimen/layout_marginTop_test"
+ android:background="@drawable/test_button"
+ android:text="@string/start_test"
+ android:textColor="@color/white"
+ android:textSize="@dimen/text_size_test" />
\ No newline at end of file
diff --git a/res/layout/memory.xml b/res/layout/memory.xml
deleted file mode 100644
index 0b2cd2b..0000000
--- a/res/layout/memory.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/res/layout/navigation_bar.xml b/res/layout/navigation_bar.xml
new file mode 100644
index 0000000..5762d94
--- /dev/null
+++ b/res/layout/navigation_bar.xml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/settings.xml b/res/layout/settings.xml
index ba1c883..85e1eca 100644
--- a/res/layout/settings.xml
+++ b/res/layout/settings.xml
@@ -2,6 +2,7 @@
-
-
-
-
-
-
-
-
-
+ layout="@layout/navigation_bar" />
-
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:orientation="vertical"
+ android:paddingBottom="@dimen/activity_vertical_margin"
+ android:paddingLeft="@dimen/activity_horizontal_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ android:paddingTop="@dimen/activity_vertical_margin" >
-
+ android:layout_marginTop="@dimen/textview_horizontal_margin"
+ android:background="@color/white"
+ android:orientation="vertical" >
-
-
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+ android:layout_marginTop="@dimen/textview_horizontal_margin"
+ android:background="@color/white"
+ android:orientation="vertical" >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/systemstat.xml b/res/layout/systemstat.xml
deleted file mode 100644
index 3da4953..0000000
--- a/res/layout/systemstat.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/res/layout/table_row.xml b/res/layout/table_row.xml
new file mode 100644
index 0000000..b034efd
--- /dev/null
+++ b/res/layout/table_row.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
diff --git a/res/layout/test_list.xml b/res/layout/test_list.xml
new file mode 100644
index 0000000..86937f0
--- /dev/null
+++ b/res/layout/test_list.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/test_list_item.xml b/res/layout/test_list_item.xml
new file mode 100644
index 0000000..c3b4fe3
--- /dev/null
+++ b/res/layout/test_list_item.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/test_report.xml b/res/layout/test_report.xml
new file mode 100644
index 0000000..9c8e166
--- /dev/null
+++ b/res/layout/test_report.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
new file mode 100644
index 0000000..1742cdc
--- /dev/null
+++ b/res/values-zh-rCN/strings.xml
@@ -0,0 +1,96 @@
+
+
+
+ Emmagee
+ 设置
+ 退出
+
+ 开始测试
+ 停止测试
+
+ 采集频率(s)
+ 发件人:
+ 密码:
+ SMTP服务器:
+ 收件人(多人以空格分割):
+ 显示浮窗
+ 保存
+
+ 计算中...
+ 开启WIFI
+ 关闭WIFI
+ 应用/剩余内存:
+ 应用/总体CPU:
+ 启动时间:
+ 总使用率(%)
+ 应用包名
+ 应用名称
+ 应用PID
+ 机器内存大小(MB)
+ 机器CPU型号
+ Android系统版本
+ 手机型号
+ 时间
+ 应用占用内存PSS(MB)
+ 应用占用内存比(%)
+ 机器剩余内存(MB)
+ 应用占用CPU率(%)
+ CPU总使用率(%)
+ 流量(KB)
+ 电量(%)
+ 电流(mA)
+ 温度(C)
+ 电压(V)
+ 帧率
+ 统计出错
+
+ 注释:已知部分不支持的机型可在此查阅:https://github.com/NetEase/Emmagee/wiki/Some-devices-are-not-supported
+ 电流: 小于0是放电大于0是充电
+ 启动时间: 为空是应用已启动或者未搜集到启动时间
+ N/A: 不支持或者数据异常
+
+ 该程序无法启动
+ 请选择需要测试的应用程序
+ 测试结果文件:
+ 保存成功
+ 发件人邮箱:
+ 收件人邮箱:
+ 格式不正确
+ 配置不完整
+ 设置WIFI失败
+ 测试结果报表已发送至邮箱:
+ 测试结果未成功发送至邮箱,结果保存在:
+
+ 确定
+ 取消
+ 确定退出程序?
+ 再点击一次退出应用
+
+ 关于
+ 邮件配置
+ 版本
+ Emmagee是一个简单易上手的Android性能监测工具,
+ 主要用于监控单个App的CPU,内存,流量,电量,电流,帧率以及整体性能状态,
+ 同时支持自定义的监控频率以及性能数据的实时显示.
+ 开发团队:网易(杭州)质量保障部
+ 项目地址:https://github.com/NetEase/Emmagee
+
+ 栈顶Activity名称
+ GBK
+
+ Dalvik heap alloc/size(KB)
+ Native heap alloc/size(KB)
+
+ 统计Heap数据
+ (需要root)
+
+ 无法获取root权限,请确认手机是否已root
+ 应用退出后停止监听
+
+ 列表更新成功
+ 由于Google的限制,Emmagee无法支持7.0及以上版本
+
+ 屏幕保持常亮
+ WakeLock已开启,屏幕会保持常亮
+ 测试报告
+
diff --git a/res/values/colors.xml b/res/values/colors.xml
new file mode 100644
index 0000000..e040738
--- /dev/null
+++ b/res/values/colors.xml
@@ -0,0 +1,16 @@
+
+
+
+ #FFFFFF
+ #000000
+ #EFEFF4
+ #818181
+ #D2D1D5
+ #A6A6A6
+ #0079FF
+ #FF0000
+ #00C378
+ #1D9C00
+ #CECED2
+ #FF4500
+
\ No newline at end of file
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
new file mode 100644
index 0000000..0a3fd03
--- /dev/null
+++ b/res/values/dimens.xml
@@ -0,0 +1,38 @@
+
+
+
+ 16dp
+ 16dp
+ 15dp
+ 30dp
+ 20dp
+ 10dp
+ 10dp
+ 3dp
+ 3dp
+ 16sp
+ 12sp
+ 12sp
+ 20sp
+ 30sp
+
+ 140dp
+ 40dp
+
+ 10dp
+ 10dp
+
+ 1px
+
+ 5px
+
+ 60dp
+
+ 50dp
+ 6dp
+ 2dp
+ 30dp
+ 30dp
+ 16sp
+
+
diff --git a/res/values/strings.xml b/res/values/strings.xml
index b35c390..615e32d 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1,23 +1,94 @@
-
-
-
- Hello World, RunningTaskActivity!
- Emmagee
- Main
- 系统信息
- 测试
- 设置
- 检测系统信息
- 启动程序
- 开始测试
- 开始测试
- 测试内存
- memoryInfo
- 采集频率:
- 秒
- 是否显示浮窗
- 保存
- 开启wifi
- 关闭wifi
-
-
\ No newline at end of file
+
+
+
+ Emmagee
+ Settings
+ Exit
+
+ Start Test
+ Stop Test
+
+ Collecting Interval(s)
+ Sender:
+ Password:
+ SMTP Server:
+ Receivers(separated by space):
+ Show Floating Window
+ Save
+
+ Calculating...
+ WIFI Off
+ WIFI On
+ App/Available Memory:
+ App/System CPU:
+ Start Time:
+ \u0020Total Usage(%)
+ Package Name
+ App Name
+ App PID
+ Device Memory Size(MB)
+ Device CPU Type
+ Android Version
+ Device Brand/Model
+ Timestamp
+ App Used Memory PSS(MB)
+ App Used Memory(%)
+ System Available Memory(MB)
+ App Used CPU(%)
+ Total Used CPU(%)
+ Net Traffic(KB)
+ Battery(%)
+ Current(mA)
+ Temperature(C)
+ Voltage(V)
+ FPS
+ Stat Error
+
+ Note: for unsupported devices refer to: https://github.com/NetEase/Emmagee/wiki/Some-devices-are-not-supported
+ Current: negative for discharging while positive for charging
+ Start time: empty means either the app has been started or failed to fetch start time
+ N/A: not supported or data exception
+
+ The app can not be launched
+ Please select target app
+ Test result file:
+ Saved
+ Sender\'s email:
+ Receiver\'s email:
+ Invalid email format
+ Incomplete email settings
+ Failed to set WIFI
+ Test result has been sent to:
+ Test result failed to be sent to your mailbox, saved at:
+
+ OK
+ Cancel
+ Are you sure to exit?
+ Click again to exit
+
+ About
+ Mail Settings
+ Version
+ Emmagee is a practical, handy performance test tool for specified
+ Android App, which can monitor CPU, memory, network traffic, battery current, fps and status.
+ Additionally, it also provides several cool features such as customizing interval of collecting
+ data, rendering real-time process status in a floating window, and much more.
+ Team:QA, Netease(Hangzhou)
+ Project Site:https://github.com/NetEase/Emmagee
+
+ Top Activity Name
+ UTF-8
+ Dalvik heap alloc/size(KB)
+ Native heap alloc/size(KB)
+
+ Collect Heap
+ (root is necessary)
+ Fail to get root permission, please check if this phone is rooted
+ Stop monitoring when app exited
+
+ Update List Successfully
+ Don\'t support 7.0 and above while Google has restricted access to shell commands and file system
+ WakeLock
+ WakeLock is on,screen will stay awake
+ Test Reports
+
diff --git a/res/values/styles.xml b/res/values/styles.xml
new file mode 100644
index 0000000..35cf030
--- /dev/null
+++ b/res/values/styles.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/src/com/netease/qa/emmagee/activity/AboutActivity.java b/src/com/netease/qa/emmagee/activity/AboutActivity.java
new file mode 100644
index 0000000..552ec8f
--- /dev/null
+++ b/src/com/netease/qa/emmagee/activity/AboutActivity.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2012-2013 NetEase, Inc. and other contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.netease.qa.emmagee.activity;
+
+import com.netease.qa.emmagee.R;
+
+import android.app.Activity;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.Window;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+/**
+ * About Page of Emmagee
+ *
+ * @author andrewleo
+ */
+public class AboutActivity extends Activity {
+
+ private static final String LOG_TAG = "Emmagee-"
+ + AboutActivity.class.getSimpleName();
+
+ private TextView appVersion;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ Log.i(LOG_TAG, "onCreate");
+ super.onCreate(savedInstanceState);
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
+ setContentView(R.layout.about);
+
+ appVersion = (TextView)findViewById(R.id.app_version);
+ appVersion.setText(getVersion());
+
+ TextView title = (TextView)findViewById(R.id.nb_title);
+ title.setText(R.string.about);
+
+ ImageView btnSave = (ImageView) findViewById(R.id.btn_set);
+ btnSave.setVisibility(ImageView.INVISIBLE);
+
+ LinearLayout layGoBack = (LinearLayout) findViewById(R.id.lay_go_back);
+
+ layGoBack.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View arg0) {
+ AboutActivity.this.finish();
+ }
+ });
+ }
+
+ /**
+ * get app version
+ * @return app version
+ */
+ public String getVersion() {
+ try {
+ PackageManager manager = this.getPackageManager();
+ PackageInfo info = manager.getPackageInfo(this.getPackageName(), 0);
+ String version = info.versionName;
+ return version;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return "-";
+ }
+ }
+
+ @Override
+ public void finish() {
+ super.finish();
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ }
+
+}
diff --git a/src/com/netease/qa/emmagee/activity/MailSettingsActivity.java b/src/com/netease/qa/emmagee/activity/MailSettingsActivity.java
new file mode 100644
index 0000000..ddd2a8e
--- /dev/null
+++ b/src/com/netease/qa/emmagee/activity/MailSettingsActivity.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2012-2013 NetEase, Inc. and other contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.netease.qa.emmagee.activity;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.netease.qa.emmagee.R;
+import com.netease.qa.emmagee.utils.EncryptData;
+import com.netease.qa.emmagee.utils.Settings;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.Window;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+
+/**
+ * Mail Setting Page of Emmagee
+ *
+ * @author andrewleo
+ */
+public class MailSettingsActivity extends Activity {
+
+ private static final String LOG_TAG = "Emmagee-" + MailSettingsActivity.class.getSimpleName();
+ private static final String BLANK_STRING = "";
+
+ private EditText edtRecipients;
+ private EditText edtSender;
+ private EditText edtPassword;
+ private EditText edtSmtp;
+ private String sender;
+ private String prePassword, curPassword;
+ private String recipients, smtp;
+ private String[] receivers;
+ private TextView title;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ Log.i(LOG_TAG, "onCreate");
+ super.onCreate(savedInstanceState);
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
+ setContentView(R.layout.mail_settings);
+
+ final EncryptData des = new EncryptData("emmagee");
+
+ edtSender = (EditText) findViewById(R.id.sender);
+ edtPassword = (EditText) findViewById(R.id.password);
+ edtRecipients = (EditText) findViewById(R.id.recipients);
+ edtSmtp = (EditText) findViewById(R.id.smtp);
+ title = (TextView) findViewById(R.id.nb_title);
+ LinearLayout layGoBack = (LinearLayout) findViewById(R.id.lay_go_back);
+ LinearLayout layBtnSet = (LinearLayout) findViewById(R.id.lay_btn_set);
+
+ title.setText(R.string.mail_settings);
+
+ SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
+ sender = preferences.getString(Settings.KEY_SENDER, BLANK_STRING);
+ prePassword = preferences.getString(Settings.KEY_PASSWORD, BLANK_STRING);
+ recipients = preferences.getString(Settings.KEY_RECIPIENTS, BLANK_STRING);
+ smtp = preferences.getString(Settings.KEY_SMTP, BLANK_STRING);
+
+ edtRecipients.setText(recipients);
+ edtSender.setText(sender);
+ edtPassword.setText(prePassword);
+ edtSmtp.setText(smtp);
+
+ layGoBack.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View arg0) {
+ MailSettingsActivity.this.finish();
+ }
+ });
+ layBtnSet.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ sender = edtSender.getText().toString().trim();
+ if (!BLANK_STRING.equals(sender) && !checkMailFormat(sender)) {
+ Toast.makeText(MailSettingsActivity.this, getString(R.string.sender_mail_toast) + getString(R.string.format_incorrect_format),
+ Toast.LENGTH_LONG).show();
+ return;
+ }
+ recipients = edtRecipients.getText().toString().trim();
+ receivers = recipients.split("\\s+");
+ for (int i = 0; i < receivers.length; i++) {
+ if (!BLANK_STRING.equals(receivers[i]) && !checkMailFormat(receivers[i])) {
+ Toast.makeText(MailSettingsActivity.this,
+ getString(R.string.receiver_mail_toast) + "[" + receivers[i] + "]" + getString(R.string.format_incorrect_format),
+ Toast.LENGTH_LONG).show();
+ return;
+ }
+ }
+ curPassword = edtPassword.getText().toString().trim();
+ smtp = edtSmtp.getText().toString().trim();
+ if (checkMailConfig(sender, recipients, smtp, curPassword) == -1) {
+ Toast.makeText(MailSettingsActivity.this, getString(R.string.info_incomplete_toast), Toast.LENGTH_LONG).show();
+ return;
+ }
+ SharedPreferences preferences = Settings.getDefaultSharedPreferences(getApplicationContext());
+ Editor editor = preferences.edit();
+ editor.putString(Settings.KEY_SENDER, sender);
+
+ try {
+ editor.putString(Settings.KEY_PASSWORD, curPassword.equals(prePassword) ? curPassword : des.encrypt(curPassword));
+ } catch (Exception e) {
+ editor.putString(Settings.KEY_PASSWORD, curPassword);
+ }
+ editor.putString(Settings.KEY_RECIPIENTS, recipients);
+ editor.putString(Settings.KEY_SMTP, smtp);
+ editor.commit();
+ Toast.makeText(MailSettingsActivity.this, getString(R.string.save_success_toast), Toast.LENGTH_LONG).show();
+ Intent intent = new Intent();
+ setResult(Activity.RESULT_FIRST_USER, intent);
+ MailSettingsActivity.this.finish();
+ }
+ });
+ }
+
+ @Override
+ public void finish() {
+ super.finish();
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ }
+
+ /**
+ * check if mail configurations are available
+ *
+ * @param sender
+ * @param recipients
+ * @param smtp
+ * @param curPassword
+ * @return true: valid configurations
+ *
+ */
+ private int checkMailConfig(String sender, String recipients, String smtp, String curPassword) {
+ if (!BLANK_STRING.equals(curPassword) && !BLANK_STRING.equals(sender) && !BLANK_STRING.equals(recipients) && !BLANK_STRING.equals(smtp)) {
+ return 1;
+ } else if (BLANK_STRING.equals(curPassword) && BLANK_STRING.equals(sender) && BLANK_STRING.equals(recipients) && BLANK_STRING.equals(smtp)) {
+ return 0;
+ } else
+ return -1;
+ }
+
+ /**
+ * check mail format
+ *
+ * @return true: valid email address
+ */
+ private boolean checkMailFormat(String mail) {
+ String strPattern = "^[a-zA-Z0-9][\\w\\.-]*[a-zA-Z0-9]@[a-zA-Z0-9][\\w\\.-]*" + "[a-zA-Z0-9]\\.[a-zA-Z][a-zA-Z\\.]*[a-zA-Z]$";
+ Pattern p = Pattern.compile(strPattern);
+ Matcher m = p.matcher(mail);
+ return m.matches();
+ }
+}
diff --git a/src/com/netease/qa/emmagee/activity/MainPageActivity.java b/src/com/netease/qa/emmagee/activity/MainPageActivity.java
index 553c9b5..32b465b 100644
--- a/src/com/netease/qa/emmagee/activity/MainPageActivity.java
+++ b/src/com/netease/qa/emmagee/activity/MainPageActivity.java
@@ -16,313 +16,352 @@
*/
package com.netease.qa.emmagee.activity;
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.OutputStreamWriter;
import java.util.List;
+import com.netease.qa.emmagee.R;
+import com.netease.qa.emmagee.service.EmmageeService;
+import com.netease.qa.emmagee.utils.ProcessInfo;
+import com.netease.qa.emmagee.utils.Programe;
+import com.netease.qa.emmagee.utils.Settings;
+import com.netease.qa.emmagee.utils.WakeLockHelper;
+
import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.content.DialogInterface;
+import android.content.BroadcastReceiver;
+import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
-import android.view.Menu;
-import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.Window;
-import android.view.WindowManager;
+import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.ImageView;
+import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.RadioButton;
import android.widget.TextView;
import android.widget.Toast;
-import com.netease.qa.emmagee.service.EmmageeService;
-import com.netease.qa.emmagee.utils.ProcessInfo;
-import com.netease.qa.emmagee.utils.Programe;
-import com.netease.qa.emmagee.R;
-
+/**
+ * Main Page of Emmagee
+ *
+ * @author andrewleo
+ */
public class MainPageActivity extends Activity {
- private final String LOG_TAG = "Emmagee-"
- + MainPageActivity.class.getSimpleName();
+ private static final String LOG_TAG = "Emmagee-" + MainPageActivity.class.getSimpleName();
+
+ private static final int TIMEOUT = 20000;
- private final int TIMEOUT = 20000;
-
- private List processList;
private ProcessInfo processInfo;
- private Intent MonitorService;
+ private Intent monitorService;
private ListView lstViProgramme;
private Button btnTest;
- private boolean isTesting = true;
- private boolean isRadioChecked = false;
private int pid, uid;
- private String processName, packageName, settingTempFile;
+ private boolean isServiceStop = false;
+ private UpdateReceiver receiver;
+
+ private TextView nbTitle;
+ private ImageView ivGoBack;
+ private ImageView ivBtnSet;
+ private LinearLayout layBtnSet;
+ private Long mExitTime = (long) 0;
+ private ListAdapter la;
@Override
public void onCreate(Bundle savedInstanceState) {
Log.i(LOG_TAG, "MainActivity::onCreate");
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
- getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
- WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.mainpage);
- createNewFile();
+
+ initTitleLayout();
+ loadSettings();
processInfo = new ProcessInfo();
- lstViProgramme = (ListView) findViewById(R.id.processList);
- btnTest = (Button) findViewById(R.id.test);
- lstViProgramme.setAdapter(new ListAdapter());
btnTest.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
- MonitorService = new Intent();
- MonitorService.setClass(MainPageActivity.this,
- EmmageeService.class);
- if (isTesting) {
- if (isRadioChecked == true) {
- Intent intent = getPackageManager()
- .getLaunchIntentForPackage(packageName);
- Log.d(LOG_TAG, packageName);
- startActivity(intent);
- waitForAppStart(packageName);
- MonitorService.putExtra("processName", processName);
- MonitorService.putExtra("pid", pid);
- MonitorService.putExtra("uid", uid);
- MonitorService.putExtra("packageName", packageName);
- MonitorService.putExtra("settingTempFile",
- settingTempFile);
- startService(MonitorService);
- btnTest.setText("停止测试");
- isTesting = false;
+ if (Build.VERSION.SDK_INT < 24) {
+ monitorService = new Intent();
+ monitorService.setClass(MainPageActivity.this, EmmageeService.class);
+ if (getString(R.string.start_test).equals(btnTest.getText().toString())) {
+ ListAdapter adapter = (ListAdapter) lstViProgramme.getAdapter();
+ if (adapter.checkedProg != null) {
+ String packageName = adapter.checkedProg.getPackageName();
+ String processName = adapter.checkedProg.getProcessName();
+ Intent intent = getPackageManager().getLaunchIntentForPackage(packageName);
+ String startActivity = "";
+ Log.d(LOG_TAG, packageName);
+ // clear logcat
+ try {
+ Runtime.getRuntime().exec("logcat -c");
+ } catch (IOException e) {
+ Log.d(LOG_TAG, e.getMessage());
+ }
+ try {
+ startActivity = intent.resolveActivity(getPackageManager()).getShortClassName();
+ startActivity(intent);
+ } catch (Exception e) {
+ Toast.makeText(MainPageActivity.this, getString(R.string.can_not_start_app_toast), Toast.LENGTH_LONG).show();
+ return;
+ }
+ waitForAppStart(packageName);
+ monitorService.putExtra("processName", processName);
+ monitorService.putExtra("pid", pid);
+ monitorService.putExtra("uid", uid);
+ monitorService.putExtra("packageName", packageName);
+ monitorService.putExtra("startActivity", startActivity);
+ startService(monitorService);
+ isServiceStop = false;
+ btnTest.setText(getString(R.string.stop_test));
+ } else {
+ Toast.makeText(MainPageActivity.this, getString(R.string.choose_app_toast), Toast.LENGTH_LONG).show();
+ }
} else {
- Toast.makeText(MainPageActivity.this, "请选择需要测试的应用程序",
+ btnTest.setText(getString(R.string.start_test));
+ Toast.makeText(MainPageActivity.this, getString(R.string.test_result_file_toast) + EmmageeService.resultFilePath,
Toast.LENGTH_LONG).show();
+ stopService(monitorService);
}
} else {
- btnTest.setText("开始测试");
- isTesting = true;
- Toast.makeText(MainPageActivity.this,
- "测试结果文件:" + EmmageeService.resultFilePath,
- Toast.LENGTH_LONG).show();
- stopService(MonitorService);
+ Toast.makeText(MainPageActivity.this, getString(R.string.nougat_warning),Toast.LENGTH_LONG).show();
}
}
});
+
+ la = new ListAdapter(processInfo.getAllPackages(getBaseContext()));
+ lstViProgramme.setAdapter(la);
+ lstViProgramme.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView> adapterView, View view, int i, long l) {
+ RadioButton rdBtn = (RadioButton) ((LinearLayout) view).getChildAt(0);
+ rdBtn.setChecked(true);
+ }
+ });
+
+ nbTitle.setText(getString(R.string.app_name));
+ ivGoBack.setImageResource(R.drawable.refresh);
+ ivBtnSet.setImageResource(R.drawable.settings_button);
+ layBtnSet.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ goToSettingsActivity();
+ }
+ });
+
+ ivGoBack.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View arg0) {
+ Toast.makeText(MainPageActivity.this, R.string.update_list, Toast.LENGTH_SHORT).show();
+ la.swapItems(processInfo.getAllPackages(getBaseContext()));
+ }
+ });
+
+ receiver = new UpdateReceiver();
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(EmmageeService.SERVICE_ACTION);
+ registerReceiver(receiver, filter);
+ }
+
+ private void initTitleLayout() {
+ ivGoBack = (ImageView) findViewById(R.id.go_back);
+ nbTitle = (TextView) findViewById(R.id.nb_title);
+ ivBtnSet = (ImageView) findViewById(R.id.btn_set);
+ lstViProgramme = (ListView) findViewById(R.id.processList);
+ btnTest = (Button) findViewById(R.id.test);
+ layBtnSet = (LinearLayout) findViewById(R.id.lay_btn_set);
+ }
+
+ private void loadSettings() {
+ SharedPreferences preferences = Settings.getDefaultSharedPreferences(this);
+ boolean wakeLock = preferences.getBoolean(Settings.KEY_WACK_LOCK, false);
+ if (wakeLock) {
+ Toast.makeText(this, R.string.wake_lock_on_toast, Toast.LENGTH_LONG).show();
+ Settings.getDefaultWakeLock(this).acquireFullWakeLock();
+ }
}
/**
- * create new file to reserve setting data
+ * customized BroadcastReceiver
+ *
+ * @author andrewleo
*/
- private void createNewFile() {
- Log.i(LOG_TAG, "create new file to save setting data");
- settingTempFile = getBaseContext().getFilesDir().getPath()
- + "\\Emmagee_Settings.txt";
- File settingFile = new File(settingTempFile);
- if (!settingFile.exists())
- try {
- settingFile.createNewFile();
- BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
- new FileOutputStream(settingFile)));
- bw.write("5" + "\r\n" + "true");
- bw.close();
- } catch (IOException e) {
- Log.d(LOG_TAG, "create new file exception :" + e.getMessage());
+ public class UpdateReceiver extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ isServiceStop = intent.getExtras().getBoolean("isServiceStop");
+ if (isServiceStop) {
+ btnTest.setText(getString(R.string.start_test));
}
+ }
+ }
+
+ @Override
+ protected void onStart() {
+ Log.d(LOG_TAG, "onStart");
+ super.onStart();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ Log.d(LOG_TAG, "onResume");
+ if (isServiceStop) {
+ btnTest.setText(getString(R.string.start_test));
+ }
}
/**
- * wait for test application started , timeout is 20s
+ * wait for test application started.
*
* @param packageName
- * package name of test application
+ * package name of test application
*/
private void waitForAppStart(String packageName) {
Log.d(LOG_TAG, "wait for app start");
boolean isProcessStarted = false;
long startTime = System.currentTimeMillis();
while (System.currentTimeMillis() < startTime + TIMEOUT) {
- processList = processInfo.getRunningProcess(getBaseContext());
- for (Programe programe : processList) {
- if ((programe.getPackageName() != null)
- && (programe.getPackageName().equals(packageName))) {
- pid = programe.getPid();
- Log.d(LOG_TAG, "pid:" + pid);
- uid = programe.getUid();
- if (pid != 0) {
- isProcessStarted = true;
- break;
- }
- }
+ pid = processInfo.getPidByPackageName(getBaseContext(), packageName);
+ if (pid != 0) {
+ isProcessStarted = true;
+ break;
}
- if (isProcessStarted)
+ if (isProcessStarted) {
break;
+ }
}
}
/**
- * override return key to show a dialog
+ * show a dialog when click return key.
+ *
+ * @return Return true to prevent this event from being propagated further,
+ * or false to indicate that you have not handled this event and it
+ * should continue to be propagated.
*/
public boolean onKeyDown(int keyCode, KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
- showDialog(0);
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ if ((System.currentTimeMillis() - mExitTime) > 2000) {
+ Toast.makeText(this, R.string.quite_alert, Toast.LENGTH_SHORT).show();
+ mExitTime = System.currentTimeMillis();
+ } else {
+ if (monitorService != null) {
+ Log.d(LOG_TAG, "stop service");
+ stopService(monitorService);
+ }
+ System.exit(0);
+ }
+ return true;
}
return super.onKeyDown(keyCode, event);
}
- /**
- * set menu options
- */
- public boolean onCreateOptionsMenu(Menu menu) {
- menu.add(0, Menu.FIRST, 0, "退出").setIcon(
- android.R.drawable.ic_menu_delete);
- menu.add(0, Menu.FIRST, 1, "设置").setIcon(
- android.R.drawable.ic_menu_directions);
- return true;
- }
-
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getOrder()) {
- case 0:
- showDialog(0);
- break;
- case 1:
- Intent intent = new Intent();
- intent.setClass(MainPageActivity.this, SettingsActivity.class);
- intent.putExtra("settingTempFile", settingTempFile);
- startActivityForResult(intent, Activity.RESULT_FIRST_USER);
- break;
- default:
- break;
- }
- return false;
+ private void goToSettingsActivity() {
+ Intent intent = new Intent();
+ intent.setClass(MainPageActivity.this, SettingsActivity.class);
+ startActivityForResult(intent, Activity.RESULT_FIRST_USER);
}
/**
- * create a dialog
- */
- protected Dialog onCreateDialog(int id) {
- switch (id) {
- case 0:
- return new AlertDialog.Builder(this)
- .setTitle("确定退出程序?")
- .setPositiveButton(
- "确定",
- new android.content.DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog,
- int which) {
- if (MonitorService != null) {
- Log.d(LOG_TAG, "stop service");
- stopService(MonitorService);
- }
- Log.d(LOG_TAG, "exit Emmagee");
- EmmageeService.closeOpenedStream();
- finish();
- System.exit(0);
- }
- }).setNegativeButton("取消", null).create();
- default:
- return null;
- }
- }
-
- /**
- * customizing adapter
+ * customizing adapter.
*
+ * @author andrewleo
*/
private class ListAdapter extends BaseAdapter {
- List programe;
- int tempPosition = -1;
+ List programes;
+ Programe checkedProg;
+ int lastCheckedPosition = -1;
- class Viewholder {
- TextView txtAppName;
- ImageView imgViAppIcon;
- RadioButton rdoBtnApp;
- }
-
- public ListAdapter() {
- programe = processInfo.getRunningProcess(getBaseContext());
+ public ListAdapter(List programes) {
+ this.programes = programes;
}
@Override
public int getCount() {
- return programe.size();
+ return programes.size();
}
@Override
public Object getItem(int position) {
- return programe.get(position);
+ return programes.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
+
+ public void swapItems(List programes) {
+ this.programes = programes;
+ notifyDataSetChanged();
+ }
@Override
public View getView(int position, View convertView, ViewGroup parent) {
- Viewholder holder = new Viewholder();
- final int i = position;
- convertView = MainPageActivity.this.getLayoutInflater().inflate(
- R.layout.list_item, null);
- holder.imgViAppIcon = (ImageView) convertView
- .findViewById(R.id.image);
- holder.txtAppName = (TextView) convertView.findViewById(R.id.text);
- holder.rdoBtnApp = (RadioButton) convertView.findViewById(R.id.rb);
- holder.rdoBtnApp.setId(position);
- holder.rdoBtnApp
- .setOnCheckedChangeListener(new OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(CompoundButton buttonView,
- boolean isChecked) {
- if (isChecked) {
- isRadioChecked = true;
- // Radio function
- if (tempPosition != -1) {
- RadioButton tempButton = (RadioButton) findViewById(tempPosition);
- if ((tempButton != null)
- && (tempPosition != i)) {
- tempButton.setChecked(false);
- }
- }
-
- tempPosition = buttonView.getId();
- packageName = programe.get(tempPosition)
- .getPackageName();
- processName = programe.get(tempPosition)
- .getProcessName();
- }
- }
- });
- if (tempPosition == position) {
- if (!holder.rdoBtnApp.isChecked())
- holder.rdoBtnApp.setChecked(true);
+ Programe pr = (Programe) programes.get(position);
+ if (convertView == null)
+ convertView = getLayoutInflater().inflate(R.layout.list_item, parent, false);
+ Viewholder holder = (Viewholder) convertView.getTag();
+ if (holder == null) {
+ holder = new Viewholder();
+ convertView.setTag(holder);
+ holder.imgViAppIcon = (ImageView) convertView.findViewById(R.id.image);
+ holder.txtAppName = (TextView) convertView.findViewById(R.id.text);
+ holder.rdoBtnApp = (RadioButton) convertView.findViewById(R.id.rb);
+ holder.rdoBtnApp.setFocusable(false);
+ holder.rdoBtnApp.setOnCheckedChangeListener(checkedChangeListener);
}
- Programe pr = (Programe) programe.get(position);
holder.imgViAppIcon.setImageDrawable(pr.getIcon());
holder.txtAppName.setText(pr.getProcessName());
+ holder.rdoBtnApp.setId(position);
+ holder.rdoBtnApp.setChecked(checkedProg != null && getItem(position) == checkedProg);
return convertView;
}
+
+ OnCheckedChangeListener checkedChangeListener = new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (isChecked) {
+ final int checkedPosition = buttonView.getId();
+ if (lastCheckedPosition != -1) {
+ RadioButton tempButton = (RadioButton) findViewById(lastCheckedPosition);
+ if ((tempButton != null) && (lastCheckedPosition != checkedPosition)) {
+ tempButton.setChecked(false);
+ }
+ }
+ checkedProg = programes.get(checkedPosition);
+ lastCheckedPosition = checkedPosition;
+ }
+ }
+ };
}
- @Override
- public void finish() {
- super.finish();
+ /**
+ * save status of all installed processes
+ *
+ * @author andrewleo
+ */
+ static class Viewholder {
+ TextView txtAppName;
+ ImageView imgViAppIcon;
+ RadioButton rdoBtnApp;
}
@Override
protected void onDestroy() {
+ unregisterReceiver(receiver);
super.onDestroy();
}
}
diff --git a/src/com/netease/qa/emmagee/activity/SettingsActivity.java b/src/com/netease/qa/emmagee/activity/SettingsActivity.java
index 714adc1..35944cb 100644
--- a/src/com/netease/qa/emmagee/activity/SettingsActivity.java
+++ b/src/com/netease/qa/emmagee/activity/SettingsActivity.java
@@ -16,134 +16,222 @@
*/
package com.netease.qa.emmagee.activity;
-import java.io.BufferedWriter;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.io.RandomAccessFile;
+import java.io.DataOutputStream;
+
+import com.netease.qa.emmagee.R;
+import com.netease.qa.emmagee.utils.Settings;
+import com.netease.qa.emmagee.utils.WakeLockHelper;
import android.app.Activity;
import android.content.Intent;
+import android.content.SharedPreferences;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
-import android.widget.Button;
+import android.view.Window;
import android.widget.CheckBox;
-import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+import android.widget.SeekBar;
+import android.widget.SeekBar.OnSeekBarChangeListener;
+import android.widget.TextView;
import android.widget.Toast;
-import com.netease.qa.emmagee.R;
-
+/**
+ * Setting Page of Emmagee
+ *
+ * @author andrewleo
+ */
public class SettingsActivity extends Activity {
- private final String LOG_TAG = "Emmagee-"
- + SettingsActivity.class.getSimpleName();
-
+ private static final String LOG_TAG = "Emmagee-" + SettingsActivity.class.getSimpleName();
+
private CheckBox chkFloat;
- private EditText edtTime;
- private String time;
- private String settingTempFile;
+ private CheckBox chkRoot;
+ private CheckBox chkAutoStop;
+ private CheckBox chkWakeLock;
+ private TextView tvTime;
+ private LinearLayout about;
+ private LinearLayout mailSettings;
+ private LinearLayout testReport;
+
+ private SharedPreferences preferences;
+ private WakeLockHelper wakeLockHelper;
@Override
public void onCreate(Bundle savedInstanceState) {
Log.i(LOG_TAG, "onCreate");
super.onCreate(savedInstanceState);
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.settings);
- Intent intent = this.getIntent();
- settingTempFile = intent.getStringExtra("settingTempFile");
+ wakeLockHelper = Settings.getDefaultWakeLock(this);
+ // init views
chkFloat = (CheckBox) findViewById(R.id.floating);
- edtTime = (EditText) findViewById(R.id.time);
- Button btnSave = (Button) findViewById(R.id.save);
- boolean floatingTag = true;
+ chkRoot = (CheckBox) findViewById(R.id.is_root);
+ chkAutoStop = (CheckBox) findViewById(R.id.auto_stop);
+ chkWakeLock = (CheckBox) findViewById(R.id.wake_lock);
+ tvTime = (TextView) findViewById(R.id.time);
+ testReport = (LinearLayout) findViewById(R.id.test_report);
+ about = (LinearLayout) findViewById(R.id.about);
+ mailSettings = (LinearLayout) findViewById(R.id.mail_settings);
+ SeekBar timeBar = (SeekBar) findViewById(R.id.timeline);
+ ImageView btnSave = (ImageView) findViewById(R.id.btn_set);
+ RelativeLayout floatingItem = (RelativeLayout) findViewById(R.id.floating_item);
+ RelativeLayout autoStopItem = (RelativeLayout) findViewById(R.id.auto_stop_item);
+ RelativeLayout wakeLockItem = (RelativeLayout) findViewById(R.id.wake_lock_item);
+ LinearLayout layGoBack = (LinearLayout) findViewById(R.id.lay_go_back);
+ LinearLayout layHeapItem = (LinearLayout) findViewById(R.id.heap_item);
- try {
- RandomAccessFile raf = new RandomAccessFile(settingTempFile, "r");
- String f = raf.readLine();
- if (f == null || (f != null && f.equals(""))) {
- time = "5";
- } else
- time = f;
- String tag = raf.readLine();
- if (tag != null && tag.equals("false"))
- floatingTag = false;
- } catch (FileNotFoundException e) {
- Log.e(LOG_TAG,
- "FileNotFoundException: " + e.getMessage());
- e.printStackTrace();
- } catch (IOException e) {
- Log.e(LOG_TAG, "IOException: " + e.getMessage());
- e.printStackTrace();
- }
+ btnSave.setVisibility(ImageView.INVISIBLE);
+
+ preferences = Settings.getDefaultSharedPreferences(getApplicationContext());
+ int interval = preferences.getInt(Settings.KEY_INTERVAL, 5);
+ boolean isfloat = preferences.getBoolean(Settings.KEY_ISFLOAT, true);
+ boolean isRoot = preferences.getBoolean(Settings.KEY_ROOT, false);
+ boolean autoStop = preferences.getBoolean(Settings.KEY_AUTO_STOP, true);
+ boolean wakeLock = preferences.getBoolean(Settings.KEY_WACK_LOCK, false);
+
+ tvTime.setText(String.valueOf(interval));
+ chkFloat.setChecked(isfloat);
+ chkRoot.setChecked(isRoot);
+ chkAutoStop.setChecked(autoStop);
+ chkWakeLock.setChecked(wakeLock);
+
+ // start activity listener
+ layGoBack.setOnClickListener(startActivityListener(MainPageActivity.class));
+ testReport.setOnClickListener(startActivityListener(TestListActivity.class));
+ mailSettings.setOnClickListener(startActivityListener(MailSettingsActivity.class));
+ about.setOnClickListener(startActivityListener(AboutActivity.class));
+
+ timeBar.setProgress(interval);
+ timeBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar arg0, int arg1, boolean arg2) {
+ tvTime.setText(Integer.toString(arg1 + 1));
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar arg0) {
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar arg0) {
+ // when tracking stoped, update preferences
+ int interval = arg0.getProgress() + 1;
+ preferences.edit().putInt(Settings.KEY_INTERVAL, interval).commit();
+ }
+ });
- edtTime.setText(time);
- chkFloat.setChecked(floatingTag);
- // edtTime.setInputType(InputType.TYPE_CLASS_NUMBER);
- btnSave.setOnClickListener(new OnClickListener() {
+ floatingItem.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View arg0) {
+ boolean isChecked = chkFloat.isChecked();
+ chkFloat.setChecked(!isChecked);
+ preferences.edit().putBoolean(Settings.KEY_ISFLOAT, !isChecked).commit();
+ }
+ });
+
+ autoStopItem.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View arg0) {
+ boolean isChecked = chkAutoStop.isChecked();
+ chkAutoStop.setChecked(!isChecked);
+ preferences.edit().putBoolean(Settings.KEY_AUTO_STOP, !isChecked).commit();
+ }
+ });
+
+ wakeLockItem.setOnClickListener(new OnClickListener() {
@Override
- public void onClick(View v) {
- time = edtTime.getText().toString().trim();
- if (!isNumeric(time)) {
- Toast.makeText(SettingsActivity.this, "输入数据无效,请重新输入",
- Toast.LENGTH_LONG).show();
- edtTime.setText("");
- } else if (time.equals("") || Long.parseLong(time) == 0) {
- Toast.makeText(SettingsActivity.this, "输入数据为空,请重新输入",
- Toast.LENGTH_LONG).show();
- edtTime.setText("");
- } else if (Integer.parseInt(time) > 600) {
- Toast.makeText(SettingsActivity.this, "数据超过最大值600,请重新输入",
- Toast.LENGTH_LONG).show();
+ public void onClick(View arg0) {
+ boolean isChecked = chkWakeLock.isChecked();
+ chkWakeLock.setChecked(!isChecked);
+ preferences.edit().putBoolean(Settings.KEY_WACK_LOCK, !isChecked).commit();
+ if (chkWakeLock.isChecked()) {
+ wakeLockHelper.acquireFullWakeLock();
} else {
- try {
- BufferedWriter bw = new BufferedWriter(
- new OutputStreamWriter(new FileOutputStream(
- settingTempFile)));
- time = Integer.toString(Integer.parseInt(time));
- bw.write(time + "\r\n" + chkFloat.isChecked());
- bw.close();
- Toast.makeText(SettingsActivity.this, "保存成功",
- Toast.LENGTH_LONG).show();
- Intent intent = new Intent();
- setResult(Activity.RESULT_FIRST_USER, intent);
- SettingsActivity.this.finish();
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
+ wakeLockHelper.releaseWakeLock();
}
}
});
- }
+
+ // get root permission
+ layHeapItem.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View arg0) {
+ // if root checkbox is checked, change status to
+ // opposite;otherwise, try to upgrade app to root
+ boolean isChecked = chkRoot.isChecked();
+ if (isChecked) {
+ chkRoot.setChecked(!isChecked);
+ preferences.edit().putBoolean(Settings.KEY_ROOT, !isChecked).commit();
+ } else {
+ boolean root = upgradeRootPermission(getPackageCodePath());
+ if (root) {
+ Log.d(LOG_TAG, "root succeed");
+ chkRoot.setChecked(!isChecked);
+ preferences.edit().putBoolean(Settings.KEY_ROOT, !isChecked).commit();
+ } else {
+ // if root failed, tell user to check if phone is rooted
+ Toast.makeText(getBaseContext(), getString(R.string.root_failed_notification), Toast.LENGTH_LONG).show();
+ }
+ }
- @Override
- public void finish() {
- super.finish();
+ }
+ });
}
+ private OnClickListener startActivityListener(final Class> specClass) {
+ return new OnClickListener() {
+ @Override
+ public void onClick(View arg0) {
+ Intent intent = new Intent();
+ intent.setClass(SettingsActivity.this, specClass);
+ startActivityForResult(intent, Activity.RESULT_FIRST_USER);
+ }
+ };
+ }
+
@Override
protected void onDestroy() {
super.onDestroy();
}
/**
- * is input a number
+ * upgrade app to get root permission
*
- * @param inputStr
- * input string
- * @return
+ * @return is root successfully
*/
- private boolean isNumeric(String inputStr) {
- for (int i = inputStr.length(); --i >= 0;) {
- if (!Character.isDigit(inputStr.charAt(i))) {
+ public static boolean upgradeRootPermission(String pkgCodePath) {
+ Process process = null;
+ DataOutputStream os = null;
+ try {
+ String cmd = "chmod 777 " + pkgCodePath;
+ process = Runtime.getRuntime().exec("su"); // 切换到root帐号
+ os = new DataOutputStream(process.getOutputStream());
+ os.writeBytes(cmd + "\n");
+ os.writeBytes("exit\n");
+ os.flush();
+ int existValue = process.waitFor();
+ if (existValue == 0) {
+ return true;
+ } else {
return false;
}
+ } catch (Exception e) {
+ Log.w(LOG_TAG, "upgradeRootPermission exception=" + e.getMessage());
+ return false;
+ } finally {
+ try {
+ if (os != null) {
+ os.close();
+ }
+ process.destroy();
+ } catch (Exception e) {
+ }
}
- return true;
}
-
}
diff --git a/src/com/netease/qa/emmagee/activity/TestListActivity.java b/src/com/netease/qa/emmagee/activity/TestListActivity.java
new file mode 100644
index 0000000..521d026
--- /dev/null
+++ b/src/com/netease/qa/emmagee/activity/TestListActivity.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2012-2013 NetEase, Inc. and other contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.netease.qa.emmagee.activity;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import com.netease.qa.emmagee.R;
+import com.netease.qa.emmagee.utils.Settings;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.TextView;
+
+/**
+ * Test Report
+ *
+ * @author andrewleo
+ */
+public class TestListActivity extends Activity {
+
+ private static final String LOG_TAG = "Emmagee-"
+ + TestListActivity.class.getSimpleName();
+ static final String CSV_PATH_KEY = "csvPath";
+
+ private ListAdapter la;
+ private ListView lstViReport;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ Log.i(LOG_TAG, "onCreate");
+ super.onCreate(savedInstanceState);
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
+ setContentView(R.layout.test_list);
+
+ TextView title = (TextView)findViewById(R.id.nb_title);
+ lstViReport = (ListView)findViewById(R.id.test_list);
+ ImageView btnSave = (ImageView) findViewById(R.id.btn_set);
+
+ btnSave.setVisibility(ImageView.INVISIBLE);
+ title.setText(R.string.test_report);
+
+ LinearLayout layGoBack = (LinearLayout) findViewById(R.id.lay_go_back);
+
+ layGoBack.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View arg0) {
+ TestListActivity.this.finish();
+ }
+ });
+ la = new ListAdapter(listReports());
+ lstViReport.setAdapter(la);
+ lstViReport.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView> adapterView, View view, int i, long l) {
+ Intent intent = new Intent();
+ intent.setClass(TestListActivity.this, TestReportActivity.class);
+ intent.putExtra(CSV_PATH_KEY, la.getCSVPath(i));
+ startActivity(intent);
+ }
+ });
+ }
+
+ /**
+ * customizing adapter.
+ *
+ * @author andrewleo
+ */
+ private class ListAdapter extends BaseAdapter {
+ List reports;
+
+ public ListAdapter(List reports) {
+ this.reports = reports;
+ }
+
+ @Override
+ public int getCount() {
+ return reports.size();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return reports.get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ public String getCSVPath(int position) {
+ return Settings.EMMAGEE_RESULT_DIR + getItem(position) + ".csv";
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ String pr = (String) reports.get(position);
+ if (convertView == null)
+ convertView = getLayoutInflater().inflate(R.layout.test_list_item, parent, false);
+ Viewholder holder = (Viewholder) convertView.getTag();
+ if (holder == null) {
+ holder = new Viewholder();
+ convertView.setTag(holder);
+ holder.name = (TextView) convertView.findViewById(R.id.package_name);
+ }
+ holder.name.setText(pr);
+ return convertView;
+ }
+
+ }
+
+ private static class Viewholder {
+ TextView name;
+ }
+
+ /**
+ * list all test report
+ */
+ private ArrayList listReports() {
+ ArrayList reportList = new ArrayList();
+ File reportDir = new File(Settings.EMMAGEE_RESULT_DIR);
+ if (reportDir.isDirectory()) {
+ File files[] = reportDir.listFiles();
+ Arrays.sort(files, Collections.reverseOrder());
+ for (File file: files) {
+ if (isLegalReport(file)) {
+ String baseName = file.getName().substring(0, file.getName().lastIndexOf("."));
+ reportList.add(baseName);
+ }
+ }
+ }
+ return reportList;
+ }
+
+ private boolean isLegalReport(File file) {
+ return !file.isDirectory() && file.getName().endsWith(".csv");
+ }
+}
diff --git a/src/com/netease/qa/emmagee/activity/TestReportActivity.java b/src/com/netease/qa/emmagee/activity/TestReportActivity.java
new file mode 100644
index 0000000..1c36932
--- /dev/null
+++ b/src/com/netease/qa/emmagee/activity/TestReportActivity.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2012-2013 NetEase, Inc. and other contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.netease.qa.emmagee.activity;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.commons.io.FileUtils;
+
+import com.netease.qa.emmagee.R;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.graphics.Color;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.Window;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TableLayout;
+import android.widget.TableRow;
+import android.widget.TextView;
+
+/**
+ * About Page of Emmagee
+ *
+ * @author andrewleo
+ */
+public class TestReportActivity extends Activity {
+
+ private static final String LOG_TAG = "Emmagee-" + TestReportActivity.class.getSimpleName();
+ private TableLayout tl;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ Log.i(LOG_TAG, "onCreate");
+ super.onCreate(savedInstanceState);
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
+ setContentView(R.layout.test_report);
+
+ TextView title = (TextView) findViewById(R.id.nb_title);
+ ImageView btnSave = (ImageView) findViewById(R.id.btn_set);
+ tl = (TableLayout) findViewById(R.id.table_layout);
+
+ btnSave.setVisibility(ImageView.INVISIBLE);
+ title.setText(R.string.test_report);
+
+ LinearLayout layGoBack = (LinearLayout) findViewById(R.id.lay_go_back);
+
+ layGoBack.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View arg0) {
+ TestReportActivity.this.finish();
+ }
+ });
+
+ Intent intent = getIntent();
+ String csvPath = intent.getStringExtra(TestListActivity.CSV_PATH_KEY);
+
+ try {
+ String content = FileUtils.readFileToString(new File(csvPath), "gbk");
+ String[] lines = content.split("\r\n");
+ int index = 0;
+ for (String line: lines) {
+ addTableRow(line, index);
+ index++;
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ private void addTableRow(String line, int index) {
+ TableRow row = new TableRow(this);
+ TableRow.LayoutParams lp = new TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT);
+ row.setLayoutParams(lp);
+ String[] items = line.split(",");
+ int i = 0;
+ for (String item: items) {
+ TextView tv = new TextView(this);
+ tv.setTextColor(Color.BLACK);
+ tv.setTextSize(18);
+ tv.setText(item);
+ tv.setBackgroundResource(R.drawable.table_border);
+ if (i != 0) {
+ tv.setGravity(Gravity.RIGHT);
+ }
+ row.addView(tv, i);
+ i++;
+ }
+ tl.addView(row, index);
+ }
+
+}
diff --git a/src/com/netease/qa/emmagee/service/EmmageeService.java b/src/com/netease/qa/emmagee/service/EmmageeService.java
index a9144a5..e9f14e2 100644
--- a/src/com/netease/qa/emmagee/service/EmmageeService.java
+++ b/src/com/netease/qa/emmagee/service/EmmageeService.java
@@ -16,26 +16,52 @@
*/
package com.netease.qa.emmagee.service;
+import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
+import java.io.FileReader;
import java.io.IOException;
+import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
-import java.io.RandomAccessFile;
import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
+import java.util.Locale;
+
+import com.netease.qa.emmagee.R;
+import com.netease.qa.emmagee.activity.MainPageActivity;
+import com.netease.qa.emmagee.utils.Constants;
+import com.netease.qa.emmagee.utils.CpuInfo;
+import com.netease.qa.emmagee.utils.CurrentInfo;
+import com.netease.qa.emmagee.utils.EncryptData;
+import com.netease.qa.emmagee.utils.FpsInfo;
+import com.netease.qa.emmagee.utils.MailSender;
+import com.netease.qa.emmagee.utils.MemoryInfo;
+import com.netease.qa.emmagee.utils.MyApplication;
+import com.netease.qa.emmagee.utils.ProcessInfo;
+import com.netease.qa.emmagee.utils.Programe;
+import com.netease.qa.emmagee.utils.Settings;
import android.app.Activity;
+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.content.SharedPreferences;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.net.wifi.WifiManager;
+import android.os.BatteryManager;
+import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
+import android.support.v4.app.NotificationCompat;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
@@ -45,33 +71,32 @@
import android.view.View.OnTouchListener;
import android.view.WindowManager;
import android.widget.Button;
-import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
-import com.netease.qa.emmagee.utils.CpuInfo;
-import com.netease.qa.emmagee.utils.MemoryInfo;
-import com.netease.qa.emmagee.utils.MyApplication;
-import com.netease.qa.emmagee.R;
-
+/**
+ * Service running in background
+ *
+ * @author andrewleo
+ */
public class EmmageeService extends Service {
private final static String LOG_TAG = "Emmagee-"
+ EmmageeService.class.getSimpleName();
+ private static final String BLANK_STRING = "";
+
private WindowManager windowManager = null;
private WindowManager.LayoutParams wmParams = null;
private View viFloatingWindow;
private float mTouchStartX;
private float mTouchStartY;
- private float startX;
- private float startY;
private float x;
private float y;
private TextView txtTotalMem;
private TextView txtUnusedMem;
private TextView txtTraffic;
- private ImageView imgViIcon;
+ private Button btnStop;
private Button btnWifi;
private int delaytime;
private DecimalFormat fomart;
@@ -79,41 +104,114 @@ public class EmmageeService extends Service {
private WifiManager wifiManager;
private Handler handler = new Handler();
private CpuInfo cpuInfo;
- private String time;
private boolean isFloating;
- private String processName, packageName, settingTempFile;
+ private boolean isRoot;
+ private boolean isAutoStop = false;
+ private String processName, packageName, startActivity;
private int pid, uid;
+ private boolean isServiceStop = false;
+ private String sender, password, recipients, smtp;
+ private String[] receivers;
+ private EncryptData des;
+ private ProcessInfo procInfo;
+ private int statusBarHeight;
public static BufferedWriter bw;
public static FileOutputStream out;
public static OutputStreamWriter osw;
public static String resultFilePath;
+ public static boolean isStop = false;
+
+ private String totalBatt;
+ private String temperature;
+ private String voltage;
+ private CurrentInfo currentInfo;
+ private FpsInfo fpsInfo;
+ private BatteryInfoBroadcastReceiver batteryBroadcast = null;
+
+ // get start time
+ private static final int MAX_START_TIME_COUNT = 5;
+ private static final String START_TIME = "#startTime";
+ private int getStartTimeCount = 0;
+ private boolean isGetStartTime = true;
+ private String startTime = "";
+ public static final String SERVICE_ACTION = "com.netease.action.emmageeService";
+ private static final String BATTERY_CHANGED = "android.intent.action.BATTERY_CHANGED";
@Override
public void onCreate() {
- Log.i(LOG_TAG, "onCreate");
+ Log.i(LOG_TAG, "service onCreate");
super.onCreate();
+ isServiceStop = false;
+ isStop = false;
+ fpsInfo = new FpsInfo();
memoryInfo = new MemoryInfo();
+ procInfo = new ProcessInfo();
fomart = new DecimalFormat();
+ fomart.setDecimalFormatSymbols(new DecimalFormatSymbols(Locale.US));
+ fomart.setGroupingUsed(false);
fomart.setMaximumFractionDigits(2);
fomart.setMinimumFractionDigits(0);
+ des = new EncryptData("emmagee");
+ currentInfo = new CurrentInfo();
+ statusBarHeight = getStatusBarHeight();
+ batteryBroadcast = new BatteryInfoBroadcastReceiver();
+ registerReceiver(batteryBroadcast, new IntentFilter(BATTERY_CHANGED));
+ }
+
+ /**
+ * 电池信息监控监听器
+ *
+ * @author andrewleo
+ *
+ */
+ public class BatteryInfoBroadcastReceiver extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+
+ if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
+ int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
+ int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
+ totalBatt = String.valueOf(level * 100 / scale);
+ voltage = String.valueOf(intent.getIntExtra(
+ BatteryManager.EXTRA_VOLTAGE, -1) * 1.0 / 1000);
+ temperature = String.valueOf(intent.getIntExtra(
+ BatteryManager.EXTRA_TEMPERATURE, -1) * 1.0 / 10);
+ }
+
+ }
+
}
@Override
- public void onStart(Intent intent, int startId) {
- Log.i(LOG_TAG, "onStart");
- setForeground(true);
- super.onStart(intent, startId);
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ Log.i(LOG_TAG, "service onStart");
+ PendingIntent contentIntent = PendingIntent.getActivity(
+ getBaseContext(), 0, new Intent(this, MainPageActivity.class),
+ 0);
+ NotificationCompat.Builder builder = new NotificationCompat.Builder(
+ this);
+ builder.setContentIntent(contentIntent).setSmallIcon(R.drawable.icon)
+ .setWhen(System.currentTimeMillis()).setAutoCancel(true)
+ .setContentTitle("Emmagee");
+ startForeground(startId, builder.build());
pid = intent.getExtras().getInt("pid");
- uid = intent.getExtras().getInt("uid");
+ //uid = intent.getExtras().getInt("uid");
processName = intent.getExtras().getString("processName");
packageName = intent.getExtras().getString("packageName");
- settingTempFile = intent.getExtras().getString("settingTempFile");
+ startActivity = intent.getExtras().getString("startActivity");
+ try {
+ PackageManager pm = getPackageManager();
+ ApplicationInfo ainfo = pm.getApplicationInfo(packageName, PackageManager.GET_ACTIVITIES);
+ uid = ainfo.uid;
+ }catch (PackageManager.NameNotFoundException e) {
+ e.printStackTrace();
+ }
cpuInfo = new CpuInfo(getBaseContext(), pid, Integer.toString(uid));
- readSettingInfo(intent);
- delaytime = Integer.parseInt(time) * 1000;
+ readSettingInfo();
if (isFloating) {
viFloatingWindow = LayoutInflater.from(this).inflate(
R.layout.floating, null);
@@ -126,84 +224,124 @@ public void onStart(Intent intent, int startId) {
wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
if (wifiManager.isWifiEnabled()) {
- btnWifi.setText(R.string.closewifi);
+ btnWifi.setText(R.string.close_wifi);
} else {
- btnWifi.setText(R.string.openwifi);
+ btnWifi.setText(R.string.open_wifi);
}
- txtUnusedMem.setText("计算中,请稍后...");
+ txtUnusedMem.setText(getString(R.string.calculating));
txtUnusedMem.setTextColor(android.graphics.Color.RED);
txtTotalMem.setTextColor(android.graphics.Color.RED);
txtTraffic.setTextColor(android.graphics.Color.RED);
- imgViIcon = (ImageView) viFloatingWindow.findViewById(R.id.img2);
- imgViIcon.setVisibility(View.GONE);
+ btnStop = (Button) viFloatingWindow.findViewById(R.id.stop);
+ btnStop.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent();
+ intent.putExtra("isServiceStop", true);
+ intent.setAction(SERVICE_ACTION);
+ sendBroadcast(intent);
+ stopSelf();
+ }
+ });
createFloatingWindow();
}
createResultCsv();
- handler.postDelayed(task, delaytime);
+ handler.postDelayed(task, 1000);
+ return START_NOT_STICKY;
}
/**
- * read configuration file
+ * read configuration file.
*
* @throws IOException
*/
- private void readSettingInfo(Intent intent) {
- try {
- RandomAccessFile raf = new RandomAccessFile(new File(
- settingTempFile), "r");
- time = raf.readLine();
- isFloating = raf.readLine().equals("true") ? true : false;
- } catch (IOException e) {
- time = "5";
- isFloating = true;
- Log.e(LOG_TAG, e.getMessage());
- }
+ private void readSettingInfo() {
+ SharedPreferences preferences = Settings
+ .getDefaultSharedPreferences(getApplicationContext());
+ int interval = preferences.getInt(Settings.KEY_INTERVAL, 5);
+ delaytime = interval * 1000;
+ isFloating = preferences.getBoolean(Settings.KEY_ISFLOAT, true);
+ sender = preferences.getString(Settings.KEY_SENDER, BLANK_STRING);
+ password = preferences.getString(Settings.KEY_PASSWORD, BLANK_STRING);
+ recipients = preferences.getString(Settings.KEY_RECIPIENTS,
+ BLANK_STRING);
+ receivers = recipients.split("\\s+");
+ smtp = preferences.getString(Settings.KEY_SMTP, BLANK_STRING);
+ isRoot = preferences.getBoolean(Settings.KEY_ROOT, false);
+ isAutoStop = preferences.getBoolean(Settings.KEY_AUTO_STOP, true);
}
/**
- * write the test result to csv format report
+ * write the test result to csv format report.
*/
private void createResultCsv() {
Calendar cal = Calendar.getInstance();
SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmss");
+ String heapData = "";
String mDateTime = formatter.format(cal.getTime().getTime());
-
- if (android.os.Environment.getExternalStorageState().equals(
- android.os.Environment.MEDIA_MOUNTED)) {
- resultFilePath = android.os.Environment
- .getExternalStorageDirectory()
- + File.separator
- + "Emmagee_TestResult_" + mDateTime + ".csv";
- } else {
- resultFilePath = getBaseContext().getFilesDir().getPath()
- + File.separator + "Emmagee_TestResult_" + mDateTime
- + ".csv";
- }
+ resultFilePath = Settings.EMMAGEE_RESULT_DIR + mDateTime + "_" + packageName
+ + ".csv";
try {
File resultFile = new File(resultFilePath);
+ resultFile.getParentFile().mkdirs();
resultFile.createNewFile();
out = new FileOutputStream(resultFile);
- osw = new OutputStreamWriter(out, "utf-8");
+ osw = new OutputStreamWriter(out);
bw = new BufferedWriter(osw);
long totalMemorySize = memoryInfo.getTotalMemory();
String totalMemory = fomart.format((double) totalMemorySize / 1024);
- bw.write("指定应用的CPU内存监控情况\r\n" + "应用包名:," + packageName + "\r\n"
- + "应用名称: ," + processName + "\r\n" + "应用PID: ," + pid
- + "\r\n" + "机器内存大小(MB):," + totalMemory + "MB\r\n"
- + "机器CPU型号:," + cpuInfo.getCpuName() + "\r\n"
- + "机器android系统版本:," + memoryInfo.getSDKVersion() + "\r\n"
- + "手机型号:," + memoryInfo.getPhoneType() + "\r\n" + "UID:,"
- + uid + "\r\n");
- bw.write("时间" + "," + "应用占用内存PSS(MB)" + "," + "应用占用内存比(%)" + ","
- + " 机器剩余内存(MB)" + "," + "应用占用CPU率(%)" + "," + "CPU总使用率(%)"
- + "," + "流量(KB):" + "\r\n");
+ String multiCpuTitle = BLANK_STRING;
+ // titles of multiple cpu cores
+ ArrayList cpuList = cpuInfo.getCpuList();
+ for (int i = 0; i < cpuList.size(); i++) {
+ multiCpuTitle += Constants.COMMA + cpuList.get(i)
+ + getString(R.string.total_usage);
+ }
+ bw.write(getString(R.string.process_package) + Constants.COMMA
+ + packageName + Constants.LINE_END
+ + getString(R.string.process_name) + Constants.COMMA
+ + processName + Constants.LINE_END
+ + getString(R.string.process_pid) + Constants.COMMA + pid
+ + Constants.LINE_END + getString(R.string.mem_size)
+ + Constants.COMMA + totalMemory + "MB" + Constants.LINE_END
+ + getString(R.string.cpu_type) + Constants.COMMA
+ + cpuInfo.getCpuName() + Constants.LINE_END
+ + getString(R.string.android_system_version)
+ + Constants.COMMA + memoryInfo.getSDKVersion()
+ + Constants.LINE_END + getString(R.string.mobile_type)
+ + Constants.COMMA + memoryInfo.getPhoneType()
+ + Constants.LINE_END + "UID" + Constants.COMMA + uid
+ + Constants.LINE_END);
+
+ if (isGrantedReadLogsPermission()) {
+ bw.write(START_TIME);
+ }
+ if (isRoot) {
+ heapData = getString(R.string.native_heap) + Constants.COMMA
+ + getString(R.string.dalvik_heap) + Constants.COMMA;
+ }
+ bw.write(getString(R.string.timestamp) + Constants.COMMA
+ + getString(R.string.top_activity) + Constants.COMMA
+ + heapData + getString(R.string.used_mem_PSS)
+ + Constants.COMMA + getString(R.string.used_mem_ratio)
+ + Constants.COMMA + getString(R.string.mobile_free_mem)
+ + Constants.COMMA + getString(R.string.app_used_cpu_ratio)
+ + Constants.COMMA
+ + getString(R.string.total_used_cpu_ratio) + multiCpuTitle
+ + Constants.COMMA + getString(R.string.traffic)
+ + Constants.COMMA + getString(R.string.battery)
+ + Constants.COMMA + getString(R.string.current)
+ + Constants.COMMA + getString(R.string.temperature)
+ + Constants.COMMA + getString(R.string.voltage)
+ + Constants.COMMA + getString(R.string.fps)
+ + Constants.LINE_END);
} catch (IOException e) {
Log.e(LOG_TAG, e.getMessage());
}
}
/**
- * create floating window
+ * create a floating window to show real-time data.
*/
private void createFloatingWindow() {
SharedPreferences shared = getSharedPreferences("float_flag",
@@ -226,26 +364,17 @@ private void createFloatingWindow() {
viFloatingWindow.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
x = event.getRawX();
- y = event.getRawY() - 25;
+ y = event.getRawY() - statusBarHeight;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
- // state = MotionEvent.ACTION_DOWN;
- startX = x;
- startY = y;
mTouchStartX = event.getX();
mTouchStartY = event.getY();
- Log.d("startP", "startX" + mTouchStartX + "====startY"
- + mTouchStartY);
break;
case MotionEvent.ACTION_MOVE:
- // state = MotionEvent.ACTION_MOVE;
updateViewPosition();
break;
-
case MotionEvent.ACTION_UP:
- // state = MotionEvent.ACTION_UP;
updateViewPosition();
- showImg();
mTouchStartX = mTouchStartY = 0;
break;
}
@@ -260,16 +389,17 @@ public void onClick(View v) {
btnWifi = (Button) viFloatingWindow.findViewById(R.id.wifi);
String buttonText = (String) btnWifi.getText();
String wifiText = getResources().getString(
- R.string.openwifi);
+ R.string.open_wifi);
if (buttonText.equals(wifiText)) {
wifiManager.setWifiEnabled(true);
- btnWifi.setText(R.string.closewifi);
+ btnWifi.setText(R.string.close_wifi);
} else {
wifiManager.setWifiEnabled(false);
- btnWifi.setText(R.string.openwifi);
+ btnWifi.setText(R.string.open_wifi);
}
} catch (Exception e) {
- Toast.makeText(viFloatingWindow.getContext(), "操作wifi失败",
+ Toast.makeText(viFloatingWindow.getContext(),
+ getString(R.string.wifi_fail_toast),
Toast.LENGTH_LONG).show();
Log.e(LOG_TAG, e.toString());
}
@@ -277,29 +407,81 @@ public void onClick(View v) {
});
}
+ private Runnable task = new Runnable() {
+
+ public void run() {
+ if (!isServiceStop) {
+ dataRefresh();
+ handler.postDelayed(this, delaytime);
+ if (isFloating && viFloatingWindow != null) {
+ windowManager.updateViewLayout(viFloatingWindow, wmParams);
+ }
+ // get app start time from logcat on every task running
+ getStartTimeFromLogcat();
+ } else {
+ Intent intent = new Intent();
+ intent.putExtra("isServiceStop", true);
+ intent.setAction(SERVICE_ACTION);
+ sendBroadcast(intent);
+ stopSelf();
+ }
+ }
+ };
+
/**
- * show the image
+ * Try to get start time from logcat.
*/
- private void showImg() {
- if (Math.abs(x - startX) < 1.5 && Math.abs(y - startY) < 1.5
- && !imgViIcon.isShown()) {
- imgViIcon.setVisibility(View.VISIBLE);
- } else if (imgViIcon.isShown()) {
- imgViIcon.setVisibility(View.GONE);
+ private void getStartTimeFromLogcat() {
+ if (!isGetStartTime || getStartTimeCount >= MAX_START_TIME_COUNT) {
+ return;
}
- }
+ try {
+ // filter logcat by Tag:ActivityManager and Level:Info
+ String logcatCommand = "logcat -v time -d ActivityManager:I *:S";
+ Process process = Runtime.getRuntime().exec(logcatCommand);
+ BufferedReader bufferedReader = new BufferedReader(
+ new InputStreamReader(process.getInputStream()));
+ StringBuilder strBuilder = new StringBuilder();
+ String line = BLANK_STRING;
- private Runnable task = new Runnable() {
- public void run() {
- dataRefresh();
- handler.postDelayed(this, delaytime);
- if (isFloating)
- windowManager.updateViewLayout(viFloatingWindow, wmParams);
+ while ((line = bufferedReader.readLine()) != null) {
+ strBuilder.append(line);
+ strBuilder.append(Constants.LINE_END);
+ String regex = ".*Displayed.*" + startActivity
+ + ".*\\+(.*)ms.*";
+ if (line.matches(regex)) {
+ Log.w("my logs", line);
+ if (line.contains("total")) {
+ line = line.substring(0, line.indexOf("total"));
+ }
+ startTime = line.substring(line.lastIndexOf("+") + 1,
+ line.lastIndexOf("ms") + 2);
+ Toast.makeText(EmmageeService.this,
+ getString(R.string.start_time) + startTime,
+ Toast.LENGTH_LONG).show();
+ isGetStartTime = false;
+ break;
+ }
+ }
+ getStartTimeCount++;
+ } catch (IOException e) {
+ Log.d(LOG_TAG, e.getMessage());
}
- };
+ }
/**
- * refresh the data showing in floating window
+ * Above JellyBean, we cannot grant READ_LOGS permission...
+ *
+ * @return
+ */
+ private boolean isGrantedReadLogsPermission() {
+ int permissionState = getPackageManager().checkPermission(
+ android.Manifest.permission.READ_LOGS, getPackageName());
+ return permissionState == PackageManager.PERMISSION_GRANTED;
+ }
+
+ /**
+ * refresh the performance data showing in floating window.
*
* @throws FileNotFoundException
*
@@ -310,59 +492,99 @@ private void dataRefresh() {
long freeMemory = memoryInfo.getFreeMemorySize(getBaseContext());
String freeMemoryKb = fomart.format((double) freeMemory / 1024);
String processMemory = fomart.format((double) pidMemory / 1024);
- ArrayList processInfo = cpuInfo.getCpuRatioInfo();
+ String currentBatt = String.valueOf(currentInfo.getCurrentValue());
+ // 异常数据过滤
+ try {
+ if (Math.abs(Double.parseDouble(currentBatt)) >= 500) {
+ currentBatt = Constants.NA;
+ }
+ } catch (Exception e) {
+ currentBatt = Constants.NA;
+ }
+ ArrayList processInfo = cpuInfo.getCpuRatioInfo(totalBatt,
+ currentBatt, temperature, voltage,
+ String.valueOf(fpsInfo.fps()), isRoot);
if (isFloating) {
- String processCpuRatio = "0";
- String totalCpuRatio = "0";
+ String processCpuRatio = "0.00";
+ String totalCpuRatio = "0.00";
String trafficSize = "0";
- int tempTraffic = 0;
+ long tempTraffic = 0L;
double trafficMb = 0;
boolean isMb = false;
if (!processInfo.isEmpty()) {
processCpuRatio = processInfo.get(0);
totalCpuRatio = processInfo.get(1);
trafficSize = processInfo.get(2);
- if (trafficSize != null && !trafficSize.equals("")
- && !trafficSize.equals("-1")) {
- tempTraffic = Integer.parseInt(trafficSize);
+ if (!(BLANK_STRING.equals(trafficSize))
+ && !("-1".equals(trafficSize))) {
+ tempTraffic = Long.parseLong(trafficSize);
if (tempTraffic > 1024) {
isMb = true;
trafficMb = (double) tempTraffic / 1024;
}
}
+ // 如果cpu使用率存在且都不小于0,则输出
+ if (processCpuRatio != null && totalCpuRatio != null) {
+ txtUnusedMem.setText(getString(R.string.process_free_mem)
+ + processMemory + "/" + freeMemoryKb + "MB");
+ txtTotalMem.setText(getString(R.string.process_overall_cpu)
+ + processCpuRatio + "%/" + totalCpuRatio + "%");
+ String batt = getString(R.string.current) + currentBatt;
+ if ("-1".equals(trafficSize)) {
+ txtTraffic.setText(batt + Constants.COMMA
+ + getString(R.string.traffic) + Constants.NA);
+ } else if (isMb)
+ txtTraffic.setText(batt + Constants.COMMA
+ + getString(R.string.traffic)
+ + fomart.format(trafficMb) + "MB");
+ else
+ txtTraffic.setText(batt + Constants.COMMA
+ + getString(R.string.traffic) + trafficSize
+ + "KB");
+ }
+ // 当内存为0切cpu使用率为0时则是被测应用退出
+ if ("0".equals(processMemory)) {
+ if (isAutoStop) {
+ closeOpenedStream();
+ isServiceStop = true;
+ return;
+ } else {
+ Log.i(LOG_TAG, "未设置自动停止测试,继续监听");
+ // 如果设置应用退出后不自动停止,则需要每次监听时重新获取pid
+ Programe programe = procInfo.getProgrameByPackageName(
+ this, packageName);
+ if (programe != null && programe.getPid() > 0) {
+ pid = programe.getPid();
+ uid = programe.getUid();
+ cpuInfo = new CpuInfo(getBaseContext(), pid,
+ Integer.toString(uid));
+ }
+ }
+ }
}
- if (processCpuRatio != null && totalCpuRatio != null) {
- txtUnusedMem.setText("占用内存:" + processMemory + "MB" + ",机器剩余:"
- + freeMemoryKb + "MB");
- txtTotalMem.setText("占用CPU:" + processCpuRatio + "%"
- + ",总体CPU:" + totalCpuRatio + "%");
- if (trafficSize.equals("-1")) {
- txtTraffic.setText("本程序或本设备不支持流量统计");
- } else if (isMb)
- txtTraffic.setText("消耗流量:" + fomart.format(trafficMb)
- + "MB");
- else
- txtTraffic.setText("消耗流量:" + trafficSize + "KB");
- }
+
}
}
/**
- * update the position of floating window
+ * update the position of floating window.
*/
private void updateViewPosition() {
wmParams.x = (int) (x - mTouchStartX);
wmParams.y = (int) (y - mTouchStartY);
- windowManager.updateViewLayout(viFloatingWindow, wmParams);
+ if (viFloatingWindow != null) {
+ windowManager.updateViewLayout(viFloatingWindow, wmParams);
+ }
}
/**
- * close all opened stream
+ * close all opened stream.
*/
- public static void closeOpenedStream() {
+ public void closeOpenedStream() {
try {
- if (bw != null)
+ if (bw != null) {
bw.close();
+ }
if (osw != null)
osw.close();
if (out != null)
@@ -374,16 +596,96 @@ public static void closeOpenedStream() {
@Override
public void onDestroy() {
- Log.i(LOG_TAG, "onDestroy");
- super.onDestroy();
- if (windowManager != null)
+ Log.i(LOG_TAG, "service onDestroy");
+ if (windowManager != null) {
windowManager.removeView(viFloatingWindow);
+ viFloatingWindow = null;
+ }
handler.removeCallbacks(task);
closeOpenedStream();
+ // replace the start time in file
+ if (!BLANK_STRING.equals(startTime)) {
+ replaceFileString(resultFilePath, START_TIME,
+ getString(R.string.start_time) + startTime
+ + Constants.LINE_END);
+ } else {
+ replaceFileString(resultFilePath, START_TIME, BLANK_STRING);
+ }
+ isStop = true;
+ unregisterReceiver(batteryBroadcast);
+ boolean isSendSuccessfully = false;
+ try {
+ isSendSuccessfully = MailSender.sendTextMail(sender,
+ des.decrypt(password), smtp,
+ "Emmagee Performance Test Report", "see attachment",
+ resultFilePath, receivers);
+ } catch (Exception e) {
+ isSendSuccessfully = false;
+ }
+ if (isSendSuccessfully) {
+ Toast.makeText(this,
+ getString(R.string.send_success_toast) + recipients,
+ Toast.LENGTH_LONG).show();
+ } else {
+ Toast.makeText(
+ this,
+ getString(R.string.send_fail_toast)
+ + EmmageeService.resultFilePath, Toast.LENGTH_LONG)
+ .show();
+ }
+ super.onDestroy();
+ stopForeground(true);
+ }
+
+ /**
+ * Replaces all matches for replaceType within this replaceString in file on
+ * the filePath
+ *
+ * @param filePath
+ * @param replaceType
+ * @param replaceString
+ */
+ private void replaceFileString(String filePath, String replaceType,
+ String replaceString) {
+ try {
+ File file = new File(filePath);
+ BufferedReader reader = new BufferedReader(new FileReader(file));
+ String line = BLANK_STRING;
+ String oldtext = BLANK_STRING;
+ while ((line = reader.readLine()) != null) {
+ oldtext += line + Constants.LINE_END;
+ }
+ reader.close();
+ // replace a word in a file
+ String newtext = oldtext.replaceAll(replaceType, replaceString);
+ BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
+ new FileOutputStream(filePath),
+ getString(R.string.csv_encoding)));
+ writer.write(newtext);
+ writer.close();
+ } catch (IOException e) {
+ Log.d(LOG_TAG, e.getMessage());
+ }
+ }
+
+ /**
+ * get height of status bar
+ *
+ * @return height of status bar, if default method does not work, return 25
+ */
+ public int getStatusBarHeight() {
+ // set status bar height to 25
+ int barHeight = 25;
+ int resourceId = getResources().getIdentifier("status_bar_height",
+ "dimen", "android");
+ if (resourceId > 0) {
+ barHeight = getResources().getDimensionPixelSize(resourceId);
+ }
+ return barHeight;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
-}
+}
\ No newline at end of file
diff --git a/src/com/netease/qa/emmagee/utils/Constants.java b/src/com/netease/qa/emmagee/utils/Constants.java
new file mode 100644
index 0000000..cb840c7
--- /dev/null
+++ b/src/com/netease/qa/emmagee/utils/Constants.java
@@ -0,0 +1,16 @@
+package com.netease.qa.emmagee.utils;
+
+/**
+ * Constant Class
+ *
+ * @author andrewleo
+ *
+ */
+public final class Constants {
+
+ public static final String NA = "N/A";
+ public static final String COMMA = ",";
+ public static final String LINE_END = "\r\n";
+ public static final String COLON = ":";
+
+}
diff --git a/src/com/netease/qa/emmagee/utils/CpuInfo.java b/src/com/netease/qa/emmagee/utils/CpuInfo.java
index 467edbb..cd28587 100644
--- a/src/com/netease/qa/emmagee/utils/CpuInfo.java
+++ b/src/com/netease/qa/emmagee/utils/CpuInfo.java
@@ -16,44 +16,61 @@
*/
package com.netease.qa.emmagee.utils;
+import java.io.File;
+import java.io.FileFilter;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
+import java.util.Locale;
+import java.util.regex.Pattern;
+import com.netease.qa.emmagee.R;
import com.netease.qa.emmagee.service.EmmageeService;
import android.content.Context;
+import android.os.Build;
import android.util.Log;
+/**
+ * operate CPU information
+ *
+ * @author andrewleo
+ */
public class CpuInfo {
- private static final String LOG_TAG = "Emmagee-"
- + CpuInfo.class.getSimpleName();
+ private static final String LOG_TAG = "Emmagee-" + CpuInfo.class.getSimpleName();
private Context context;
private long processCpu;
- private long idleCpu;
- private long totalCpu;
+ private ArrayList idleCpu = new ArrayList();
+ private ArrayList totalCpu = new ArrayList();
private boolean isInitialStatics = true;
private SimpleDateFormat formatterFile;
private MemoryInfo mi;
private long totalMemorySize;
- private long initialTraffic;
+ private long preTraffic;
private long lastestTraffic;
private long traffic;
private TrafficInfo trafficInfo;
- private ArrayList CpuUsedRatio;
- private long totalCpu2;
+ private ArrayList cpuUsedRatio = new ArrayList();
+ private ArrayList totalCpu2 = new ArrayList();
private long processCpu2;
- private long idleCpu2;
+ private ArrayList idleCpu2 = new ArrayList();
private String processCpuRatio = "";
- private String totalCpuRatio = "";
+ private ArrayList totalCpuRatio = new ArrayList();
private int pid;
+ private static final String INTEL_CPU_NAME = "model name";
+ private static final String CPU_DIR_PATH = "/sys/devices/system/cpu/";
+ private static final String CPU_X86 = "x86";
+ private static final String CPU_INFO_PATH = "/proc/cpuinfo";
+ private static final String CPU_STAT = "/proc/stat";
+
public CpuInfo(Context context, int pid, String uid) {
this.pid = pid;
this.context = context;
@@ -61,11 +78,11 @@ public CpuInfo(Context context, int pid, String uid) {
formatterFile = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
mi = new MemoryInfo();
totalMemorySize = mi.getTotalMemory();
- CpuUsedRatio = new ArrayList();
+ cpuUsedRatio = new ArrayList();
}
/**
- * read the status of CPU
+ * read the status of CPU.
*
* @throws FileNotFoundException
*/
@@ -74,8 +91,7 @@ public void readCpuStat() {
String cpuStatPath = "/proc/" + processPid + "/stat";
try {
// monitor cpu stat of certain process
- RandomAccessFile processCpuInfo = new RandomAccessFile(cpuStatPath,
- "r");
+ RandomAccessFile processCpuInfo = new RandomAccessFile(cpuStatPath, "r");
String line = "";
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.setLength(0);
@@ -86,21 +102,27 @@ public void readCpuStat() {
processCpu = Long.parseLong(tok[13]) + Long.parseLong(tok[14]);
processCpuInfo.close();
} catch (FileNotFoundException e) {
- Log.e(LOG_TAG, "FileNotFoundException: " + e.getMessage());
- e.printStackTrace();
+ Log.w(LOG_TAG, "FileNotFoundException: " + e.getMessage());
} catch (IOException e) {
e.printStackTrace();
}
+ readTotalCpuStat();
+ }
+ /**
+ * read stat of each CPU cores
+ */
+ private void readTotalCpuStat() {
try {
// monitor total and idle cpu stat of certain process
- RandomAccessFile cpuInfo = new RandomAccessFile("/proc/stat", "r");
- String[] toks = cpuInfo.readLine().split(" ");
- idleCpu = Long.parseLong(toks[5]);
- totalCpu = Long.parseLong(toks[2]) + Long.parseLong(toks[3])
- + Long.parseLong(toks[4]) + Long.parseLong(toks[6])
- + Long.parseLong(toks[5]) + Long.parseLong(toks[7])
- + Long.parseLong(toks[8]);
+ RandomAccessFile cpuInfo = new RandomAccessFile(CPU_STAT, "r");
+ String line = "";
+ while ((null != (line = cpuInfo.readLine())) && line.startsWith("cpu")) {
+ String[] toks = line.split("\\s+");
+ idleCpu.add(Long.parseLong(toks[4]));
+ totalCpu.add(Long.parseLong(toks[1]) + Long.parseLong(toks[2]) + Long.parseLong(toks[3]) + Long.parseLong(toks[4])
+ + Long.parseLong(toks[6]) + Long.parseLong(toks[5]) + Long.parseLong(toks[7]));
+ }
cpuInfo.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
@@ -110,205 +132,212 @@ public void readCpuStat() {
}
/**
- * get CPU name
+ * get CPU name.
*
* @return CPU name
*/
public String getCpuName() {
try {
- RandomAccessFile cpu_stat = new RandomAccessFile("/proc/cpuinfo",
- "r");
- String[] cpu = cpu_stat.readLine().split(":"); // cpu信息的前一段是含有processor字符串,此处替换为不显示
- return cpu[1];
+ RandomAccessFile cpuStat = new RandomAccessFile(CPU_INFO_PATH, "r");
+ // check cpu type
+ String line;
+ while (null != (line = cpuStat.readLine())) {
+ String[] values = line.split(":");
+ if (values[0].contains(INTEL_CPU_NAME) || values[0].contains("Processor")) {
+ cpuStat.close();
+ Log.d(LOG_TAG, "CPU name="+values[1]);
+ return values[1];
+ }
+ }
} catch (IOException e) {
Log.e(LOG_TAG, "IOException: " + e.getMessage());
}
return "";
}
+ /**
+ * display directories naming with "cpu*"
+ *
+ * @author andrewleo
+ */
+ class CpuFilter implements FileFilter {
+ @Override
+ public boolean accept(File pathname) {
+ // Check if filename matchs "cpu[0-9]"
+ if (Pattern.matches("cpu[0-9]", pathname.getName())) {
+ return true;
+ }
+ return false;
+ }
+ }
+
+ /**
+ * get CPU core numbers
+ *
+ * @return cpu core numbers
+ */
+ public int getCpuNum() {
+ try {
+ // Get directory containing CPU info
+ File dir = new File(CPU_DIR_PATH);
+ // Filter to only list the devices we care about
+ File[] files = dir.listFiles(new CpuFilter());
+ return files.length;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return 1;
+ }
+ }
+
+ /**
+ * get CPU core list
+ *
+ * @return cpu core list
+ */
+ public ArrayList getCpuList() {
+ ArrayList cpuList = new ArrayList();
+ try {
+ // Get directory containing CPU info
+ File dir = new File(CPU_DIR_PATH);
+ // Filter to only list the devices we care about
+ File[] files = dir.listFiles(new CpuFilter());
+ for (int i = 0; i < files.length; i++) {
+ cpuList.add(files[i].getName());
+ }
+ return cpuList;
+ } catch (Exception e) {
+ e.printStackTrace();
+ cpuList.add("cpu0");
+ return cpuList;
+ }
+ }
+
/**
* reserve used ratio of process CPU and total CPU, meanwhile collect
- * network traffic
+ * network traffic.
*
* @return network traffic ,used ratio of process CPU and total CPU in
* certain interval
*/
- public ArrayList getCpuRatioInfo() {
+ public ArrayList getCpuRatioInfo(String totalBatt, String currentBatt, String temperature, String voltage, String fps, boolean isRoot) {
+ String heapData = "";
DecimalFormat fomart = new DecimalFormat();
+ fomart.setDecimalFormatSymbols(new DecimalFormatSymbols(Locale.US));
+ fomart.setGroupingUsed(false);
fomart.setMaximumFractionDigits(2);
fomart.setMinimumFractionDigits(2);
+ cpuUsedRatio.clear();
+ idleCpu.clear();
+ totalCpu.clear();
+ totalCpuRatio.clear();
readCpuStat();
- CpuUsedRatio.clear();
try {
+ String mDateTime2;
Calendar cal = Calendar.getInstance();
- String mDateTime2 = formatterFile.format(cal.getTime().getTime()
- + 8 * 60 * 60 * 1000);
-
- if (isInitialStatics == true) {
- initialTraffic = trafficInfo.getTrafficInfo();
+ if ((Build.MODEL.equals("sdk")) || (Build.MODEL.equals("google_sdk"))) {
+ mDateTime2 = formatterFile.format(cal.getTime().getTime() + 8 * 60 * 60 * 1000);
+ totalBatt = Constants.NA;
+ currentBatt = Constants.NA;
+ temperature = Constants.NA;
+ voltage = Constants.NA;
+ } else
+ mDateTime2 = formatterFile.format(cal.getTime().getTime());
+ if (isInitialStatics) {
+ preTraffic = trafficInfo.getTrafficInfo();
isInitialStatics = false;
} else {
lastestTraffic = trafficInfo.getTrafficInfo();
- if (initialTraffic == -1)
+ if (preTraffic == -1)
traffic = -1;
- else
- traffic = (lastestTraffic - initialTraffic + 1023) / 1024;
- processCpuRatio = fomart
- .format(100 * ((double) (processCpu - processCpu2) / (double) (totalCpu - totalCpu2)));
- totalCpuRatio = fomart
- .format(100 * ((double) ((totalCpu - idleCpu) - (totalCpu2 - idleCpu2)) / (double) (totalCpu - totalCpu2)));
+ else {
+ if (lastestTraffic > preTraffic) {
+ traffic += (lastestTraffic - preTraffic + 1023) / 1024;
+ }
+ }
+ preTraffic = lastestTraffic;
+ Log.d(LOG_TAG, "lastestTraffic===" + lastestTraffic);
+ Log.d(LOG_TAG, "preTraffic===" + preTraffic);
+ StringBuffer totalCpuBuffer = new StringBuffer();
+ if (null != totalCpu2 && totalCpu2.size() > 0) {
+ processCpuRatio = fomart.format(100 * ((double) (processCpu - processCpu2) / ((double) (totalCpu.get(0) - totalCpu2.get(0)))));
+ for (int i = 0; i < (totalCpu.size() > totalCpu2.size() ? totalCpu2.size() : totalCpu.size()); i++) {
+ String cpuRatio = "0.00";
+ if (totalCpu.get(i) - totalCpu2.get(i) > 0) {
+ cpuRatio = fomart
+ .format(100 * ((double) ((totalCpu.get(i) - idleCpu.get(i)) - (totalCpu2.get(i) - idleCpu2.get(i))) / (double) (totalCpu
+ .get(i) - totalCpu2.get(i))));
+ }
+ totalCpuRatio.add(cpuRatio);
+ totalCpuBuffer.append(cpuRatio + Constants.COMMA);
+ }
+ } else {
+ processCpuRatio = "0";
+ totalCpuRatio.add("0");
+ totalCpuBuffer.append("0,");
+ totalCpu2 = (ArrayList) totalCpu.clone();
+ processCpu2 = processCpu;
+ idleCpu2 = (ArrayList) idleCpu.clone();
+ }
+ // 多核cpu的值写入csv文件中
+ for (int i = 0; i < getCpuNum() - totalCpuRatio.size() + 1; i++) {
+ totalCpuBuffer.append("0.00,");
+ }
long pidMemory = mi.getPidMemorySize(pid, context);
String pMemory = fomart.format((double) pidMemory / 1024);
long freeMemory = mi.getFreeMemorySize(context);
String fMemory = fomart.format((double) freeMemory / 1024);
- String percent = "统计出错";
+ String percent = context.getString(R.string.stat_error);
if (totalMemorySize != 0) {
- percent = fomart
- .format(((double) pidMemory / (double) totalMemorySize) * 100);
+ percent = fomart.format(((double) pidMemory / (double) totalMemorySize) * 100);
}
- // whether certain device supports traffic statics
- if (traffic == -1) {
- EmmageeService.bw.write(mDateTime2 + "," + pMemory + ","
- + percent + "," + fMemory + "," + processCpuRatio
- + "," + totalCpuRatio + "," + "本程序或本设备不支持流量统计"
- + "\r\n");
- } else {
- EmmageeService.bw.write(mDateTime2 + "," + pMemory + ","
- + percent + "," + fMemory + "," + processCpuRatio
- + "," + totalCpuRatio + "," + traffic + "\r\n");
+ if (isPositive(processCpuRatio) && isPositive(totalCpuRatio.get(0))) {
+ String trafValue;
+ // whether certain device supports traffic statics or not
+ if (traffic == -1) {
+ trafValue = Constants.NA;
+ } else {
+ trafValue = String.valueOf(traffic);
+ }
+ if(isRoot){
+ String[][] heapArray = MemoryInfo.getHeapSize(pid, context);
+ heapData = heapArray[0][1]+"/"+heapArray[0][0]+Constants.COMMA+heapArray[1][1]+"/"+heapArray[1][0]+Constants.COMMA;
+ }
+ EmmageeService.bw.write(mDateTime2 + Constants.COMMA + ProcessInfo.getTopActivity(context) + Constants.COMMA +heapData+ pMemory
+ + Constants.COMMA + percent + Constants.COMMA + fMemory + Constants.COMMA + processCpuRatio + Constants.COMMA
+ + totalCpuBuffer.toString() + trafValue + Constants.COMMA + totalBatt + Constants.COMMA + currentBatt + Constants.COMMA
+ + temperature + Constants.COMMA + voltage + Constants.COMMA + fps + Constants.LINE_END);
+ totalCpu2 = (ArrayList) totalCpu.clone();
+ processCpu2 = processCpu;
+ idleCpu2 = (ArrayList) idleCpu.clone();
+ cpuUsedRatio.add(processCpuRatio);
+ cpuUsedRatio.add(totalCpuRatio.get(0));
+ cpuUsedRatio.add(String.valueOf(traffic));
}
}
- totalCpu2 = totalCpu;
- processCpu2 = processCpu;
- idleCpu2 = idleCpu;
- CpuUsedRatio.add(processCpuRatio);
- CpuUsedRatio.add(totalCpuRatio);
- CpuUsedRatio.add(String.valueOf(traffic));
} catch (IOException e) {
e.printStackTrace();
- // PttService.closeOpenedStream()
}
- return CpuUsedRatio;
+ return cpuUsedRatio;
+ }
+ /**
+ * is text a positive number
+ *
+ * @param text
+ * @return
+ */
+ private boolean isPositive(String text) {
+ Double num;
+ try {
+ num = Double.parseDouble(text);
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ return num >= 0;
}
- // TODO coming soon
- // public String cpuinfo() {
- // String sys_info = "";
- // String s;
- // try {
- // RandomAccessFile reader_stat = new RandomAccessFile("/proc/stat",
- // "r");
- // RandomAccessFile reader_info = new RandomAccessFile(
- // "/proc/cpuinfo", "r");
- // sys_info = reader_info.readLine(); // CPU型号
- // String load_info;
- // String cpu_stat = reader_stat.readLine(); // cpu行信息
- // String cpu0_stat = reader_stat.readLine(); // cpu0
- // String cpu1_stat = reader_stat.readLine(); // cpu1
- //
- // String[] tok = cpu_stat.split(" ");
- // String[] tok1 = cpu0_stat.split(" ");
- // String[] tok2 = cpu1_stat.split(" ");
- //
- // // 判断单核
- // if (tok[2].equals(tok1[1])) {
- // long idle_s1 = Long.parseLong(tok[5]);
- // long cpu_s1 = Long.parseLong(tok[2]) + Long.parseLong(tok[3])
- // + Long.parseLong(tok[4]) + Long.parseLong(tok[6])
- // + Long.parseLong(tok[5]) + Long.parseLong(tok[7])
- // + Long.parseLong(tok[8]);
- //
- // try {
- // Thread.sleep(1000);
- //
- // } catch (Exception e) {
- // }
- //
- // reader_stat.seek(0);
- //
- // load_info = reader_stat.readLine();
- //
- // reader_stat.close();
- //
- // tok = load_info.split(" ");
- // long idle_s2 = Long.parseLong(tok[5]);
- //
- // long cpu_s2 = Long.parseLong(tok[2]) + Long.parseLong(tok[3])
- // + Long.parseLong(tok[4]) + Long.parseLong(tok[6])
- // + Long.parseLong(tok[5]) + Long.parseLong(tok[7])
- // + Long.parseLong(tok[8]);
- //
- // return "CPU使用率为:"
- // + (100 * ((cpu_s2 - idle_s2) - (cpu_s1 - idle_s1)) / (cpu_s2 - cpu_s1))
- // + "%";
- //
- // }
- //
- // // 双核情况
- // else if (tok2[0].equals("cpu1")) {
- // // 双核
- // reader_stat = new RandomAccessFile("/proc/stat", "r");
- // long[] idle_d1 = null;
- // long[] cpu_d1 = null;
- // long[] idle_d2 = null;
- // long[] cpu_d2 = null;
- // idle_d1[0] = Long.parseLong(tok1[4]); // cpu0空闲时间
- // cpu_d1[0] = Long.parseLong(tok1[2]) + Long.parseLong(tok1[3])
- // + Long.parseLong(tok1[4]) + Long.parseLong(tok1[6])
- // + Long.parseLong(tok1[5]) + Long.parseLong(tok1[7])
- // + Long.parseLong(tok1[1]); // cpu0非空闲时间
- // idle_d1[1] = Long.parseLong(tok2[4]);
- // cpu_d1[1] = Long.parseLong(tok2[2]) + Long.parseLong(tok2[3])
- // + Long.parseLong(tok2[4]) + Long.parseLong(tok2[6])
- // + Long.parseLong(tok2[5]) + Long.parseLong(tok2[7])
- // + Long.parseLong(tok2[1]);
- //
- // try {
- // Thread.sleep(1000);
- //
- // } catch (Exception e) {
- // }
- //
- // reader_stat.seek(0);
- //
- // cpu_stat = reader_stat.readLine(); // cpu行信息
- // cpu0_stat = reader_stat.readLine(); // cpu0
- // cpu1_stat = reader_stat.readLine();
- //
- // tok1 = cpu0_stat.split(" ");
- // tok2 = cpu1_stat.split(" ");
- //
- // idle_d2[0] = Long.parseLong(tok1[4]); // cpu0空闲时间
- // cpu_d2[0] = Long.parseLong(tok1[2]) + Long.parseLong(tok1[3])
- // + Long.parseLong(tok1[4]) + Long.parseLong(tok1[6])
- // + Long.parseLong(tok1[5]) + Long.parseLong(tok1[7])
- // + Long.parseLong(tok1[1]); // cpu0非空闲时间
- // idle_d2[1] = Long.parseLong(tok2[4]);
- // cpu_d2[1] = Long.parseLong(tok2[2]) + Long.parseLong(tok2[3])
- // + Long.parseLong(tok2[4]) + Long.parseLong(tok2[6])
- // + Long.parseLong(tok2[5]) + Long.parseLong(tok2[7])
- // + Long.parseLong(tok2[1]);
- //
- // reader_stat.close();
- // return "CPU1使用率为:"
- // + (100 * ((cpu_d2[0] - idle_d2[0]) - (cpu_d1[0] - idle_d1[0])) /
- // (cpu_d2[0] - cpu_d1[0]))
- // + "%"
- // + "\n"
- // + "CPU2使用率为:"
- // + (100 * ((cpu_d2[1] - idle_d2[1]) - (cpu_d1[1] - idle_d1[1])) /
- // (cpu_d2[1] - cpu_d1[1]))
- // + "%";
- // }
- // } catch (IOException ex) {
- // Log.e(LOG_TAG, ex.getMessage());
- //
- // }
- // return "0";
- // }
}
diff --git a/src/com/netease/qa/emmagee/utils/CurrentInfo.java b/src/com/netease/qa/emmagee/utils/CurrentInfo.java
new file mode 100644
index 0000000..c12cc2f
--- /dev/null
+++ b/src/com/netease/qa/emmagee/utils/CurrentInfo.java
@@ -0,0 +1,176 @@
+package com.netease.qa.emmagee.utils;
+
+import java.io.BufferedReader;
+import java.io.DataInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileReader;
+import java.util.Locale;
+
+import android.os.Build;
+import android.util.Log;
+
+/**
+ * Current info
+ *
+ * @author andrewleo
+ *
+ */
+public class CurrentInfo {
+ private static final String LOG_TAG = "Emmagee-CurrentInfo";
+ private static final String BUILD_MODEL = Build.MODEL.toLowerCase(Locale.ENGLISH);
+ private static final String I_MBAT = "I_MBAT: ";
+ private static final String CURRENT_NOW = "/sys/class/power_supply/battery/current_now";
+ private static final String BATT_CURRENT = "/sys/class/power_supply/battery/batt_current";
+ private static final String SMEM_TEXT = "/sys/class/power_supply/battery/smem_text";
+ private static final String BATT_CURRENT_ADC = "/sys/class/power_supply/battery/batt_current_adc";
+ private static final String CURRENT_AVG = "/sys/class/power_supply/battery/current_avg";
+
+ /**
+ * read system file to get current value
+ *
+ * @return current value
+ */
+ public Long getCurrentValue() {
+ File f = null;
+ Log.d(LOG_TAG, BUILD_MODEL);
+ // galaxy s4,oppo find,samgsung note2
+ if (BUILD_MODEL.contains("sgh-i337") || BUILD_MODEL.contains("gt-i9505") || BUILD_MODEL.contains("sch-i545")
+ || BUILD_MODEL.contains("find 5") || BUILD_MODEL.contains("sgh-m919") || BUILD_MODEL.contains("sgh-i537")
+ || BUILD_MODEL.contains("x907") || BUILD_MODEL.contains("gt-n7100")) {
+ f = new File(CURRENT_NOW);
+ if (f.exists()) {
+ return getCurrentValue(f, false);
+ }
+ }
+
+ // samsung galaxy
+ if (BUILD_MODEL.contains("gt-p31") || BUILD_MODEL.contains("gt-p51")) {
+ f = new File(CURRENT_AVG);
+ if (f.exists()) {
+ return getCurrentValue(f, false);
+ }
+ }
+
+ // htc desire hd ,desire z
+ if (BUILD_MODEL.contains("desire hd") || BUILD_MODEL.contains("desire z")) {
+ f = new File(BATT_CURRENT);
+ if (f.exists())
+ return getCurrentValue(f, false);
+ }
+
+ // htc sensation z710e
+ f = new File(BATT_CURRENT);
+ if (f.exists())
+ return getCurrentValue(f, false);
+
+ // htc one V
+ f = new File(SMEM_TEXT);
+ if (f.exists())
+ return getSMemValue();
+
+ // nexus one,meizu
+ f = new File(CURRENT_NOW);
+ if (f.exists())
+ return getCurrentValue(f, true);
+
+ // meizu pro 5
+ f = new File("/sys/class/power_supply/bq2753x-0/current_now");
+ if (f.exists())
+ return getCurrentValue(f, true);
+
+ // galaxy note, galaxy s2
+ f = new File(BATT_CURRENT_ADC);
+ if (f.exists())
+ return getCurrentValue(f, false);
+
+ // acer V360
+ f = new File("/sys/class/power_supply/battery/BatteryAverageCurrent");
+ if (f.exists())
+ return getCurrentValue(f, false);
+
+ // moto milestone,moto mb526
+ f = new File("/sys/devices/platform/cpcap_battery/power_supply/usb/current_now");
+ if (f.exists())
+ return getCurrentValue(f, false);
+
+ return null;
+ }
+
+ /**
+ * get current value from smem_text
+ *
+ * @return current value
+ */
+ public Long getSMemValue() {
+ boolean success = false;
+ String text = null;
+ Long value = null;
+ try {
+ FileReader fr = new FileReader(SMEM_TEXT);
+ BufferedReader br = new BufferedReader(fr);
+ String line = br.readLine();
+ while (line != null) {
+ if (line.contains(I_MBAT)) {
+ text = line.substring(line.indexOf(I_MBAT) + 8);
+ success = true;
+ break;
+ }
+ line = br.readLine();
+ }
+ fr.close();
+ br.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ if (success) {
+ try {
+ value = Long.parseLong(text);
+ } catch (NumberFormatException nfe) {
+ nfe.printStackTrace();
+ value = null;
+ }
+ }
+ return value;
+ }
+
+ /**
+ * read system file to get current value
+ *
+ * @param file
+ * @param convertToMillis
+ * @return current value
+ */
+ public Long getCurrentValue(File file, boolean convertToMillis) {
+ Log.d(LOG_TAG, "*** getCurrentValue ***");
+ Log.d(LOG_TAG, "*** " + convertToMillis + " ***");
+ String line = null;
+ Long value = null;
+ FileInputStream fs = null;
+ DataInputStream ds = null;
+ try {
+ fs = new FileInputStream(file);
+ ds = new DataInputStream(fs);
+ line = ds.readLine();
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ fs.close();
+ ds.close();
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+ if (line != null) {
+ try {
+ value = Long.parseLong(line);
+ } catch (NumberFormatException nfe) {
+ value = null;
+ }
+ if (convertToMillis)
+ value = value / 1000;
+ }
+ return value;
+ }
+}
diff --git a/src/com/netease/qa/emmagee/utils/CustomizedAuthenticator.java b/src/com/netease/qa/emmagee/utils/CustomizedAuthenticator.java
new file mode 100644
index 0000000..e2be858
--- /dev/null
+++ b/src/com/netease/qa/emmagee/utils/CustomizedAuthenticator.java
@@ -0,0 +1,26 @@
+package com.netease.qa.emmagee.utils;
+
+import javax.mail.Authenticator;
+import javax.mail.PasswordAuthentication;
+
+/**
+ * Customized Authenticator
+ *
+ * @author andrewleo
+ */
+public class CustomizedAuthenticator extends Authenticator {
+ String userName = null;
+ String password = null;
+
+ public CustomizedAuthenticator() {
+ }
+
+ public CustomizedAuthenticator(String username, String password) {
+ this.userName = username;
+ this.password = password;
+ }
+
+ protected PasswordAuthentication getPasswordAuthentication() {
+ return new PasswordAuthentication(userName, password);
+ }
+}
diff --git a/src/com/netease/qa/emmagee/utils/EncryptData.java b/src/com/netease/qa/emmagee/utils/EncryptData.java
new file mode 100644
index 0000000..c595d18
--- /dev/null
+++ b/src/com/netease/qa/emmagee/utils/EncryptData.java
@@ -0,0 +1,179 @@
+package com.netease.qa.emmagee.utils;
+
+import java.security.Key;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+
+/**
+ * 提供加密算法,可以对输入的字符串进行加密、解密操作
+ *
+ * @author andrewleo
+ */
+public class EncryptData {
+ private static String strDefaultKey = "emmagee";
+
+ private Cipher encryptCipher = null;
+
+ private Cipher decryptCipher = null;
+
+ /**
+ * 将byte数组转换为表示16进制值的字符串, 如:byte[]{8,18}转换为:0813, 和public static byte[]
+ * hexStr2ByteArr(String strIn) 互为可逆的转换过程
+ *
+ * @param arrB
+ * 需要转换的byte数组
+ * @return 转换后的字符串
+ * @throws Exception
+ * 本方法不处理任何异常,所有异常全部抛出
+ */
+ public static String byteArr2HexStr(byte[] arrB) throws Exception {
+ int iLen = arrB.length;
+ // 每个byte用两个字符才能表示,所以字符串的长度是数组长度的两倍
+ StringBuffer sb = new StringBuffer(iLen * 2);
+ for (int i = 0; i < iLen; i++) {
+ int intTmp = arrB[i];
+ // 把负数转换为正数
+ while (intTmp < 0) {
+ intTmp = intTmp + 256;
+ }
+ // 小于0F的数需要在前面补0
+ if (intTmp < 16) {
+ sb.append("0");
+ }
+ sb.append(Integer.toString(intTmp, 16));
+ }
+ return sb.toString();
+ }
+
+ /**
+ * 将表示16进制值的字符串转换为byte数组, 和public static String byteArr2HexStr(byte[] arrB)
+ * 互为可逆的转换过程
+ *
+ * @param strIn
+ * 需要转换的字符串
+ * @return 转换后的byte数组
+ * @throws Exception
+ * 本方法不处理任何异常,所有异常全部抛出
+ * @author
+ */
+ public static byte[] hexStr2ByteArr(String strIn) throws Exception {
+ byte[] arrB = strIn.getBytes();
+ int iLen = arrB.length;
+
+ // 两个字符表示一个字节,所以字节数组长度是字符串长度除以2
+ byte[] arrOut = new byte[iLen / 2];
+ for (int i = 0; i < iLen; i = i + 2) {
+ String strTmp = new String(arrB, i, 2);
+ arrOut[i / 2] = (byte) Integer.parseInt(strTmp, 16);
+ }
+ return arrOut;
+ }
+
+ /**
+ * 默认构造方法,使用默认密钥
+ *
+ * @throws Exception
+ */
+ public EncryptData() throws Exception {
+ this(strDefaultKey);
+ }
+
+ /**
+ * 指定密钥构造方法
+ *
+ * @param strKey
+ * 指定的密钥
+ * @throws Exception
+ */
+ public EncryptData(String strKey) {
+ try {
+ Key key = getKey(strKey.getBytes());
+
+ encryptCipher = Cipher.getInstance("DES");
+ encryptCipher.init(Cipher.ENCRYPT_MODE, key);
+
+ decryptCipher = Cipher.getInstance("DES");
+ decryptCipher.init(Cipher.DECRYPT_MODE, key);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * 加密字节数组
+ *
+ * @param arrB
+ * 需加密的字节数组
+ * @return 加密后的字节数组
+ * @throws BadPaddingException
+ * @throws IllegalBlockSizeException
+ * @throws Exception
+ */
+ public byte[] encrypt(byte[] arrB) throws Exception {
+ return encryptCipher.doFinal(arrB);
+ }
+
+ /**
+ * 加密字符串
+ *
+ * @param strIn
+ * 需加密的字符串
+ * @return 加密后的字符串
+ * @throws Exception
+ */
+ public String encrypt(String strIn) throws Exception {
+ if (strIn == null) {
+ strIn = "";
+ }
+ return byteArr2HexStr(encrypt(strIn.getBytes("utf-8")));
+ }
+
+ /**
+ * 解密字节数组
+ *
+ * @param arrB
+ * 需解密的字节数组
+ * @return 解密后的字节数组
+ * @throws Exception
+ */
+ public byte[] decrypt(byte[] arrB) throws Exception {
+ return decryptCipher.doFinal(arrB);
+ }
+
+ /**
+ * 解密字符串
+ *
+ * @param strIn
+ * 需解密的字符串
+ * @return 解密后的字符串
+ * @throws Exception
+ */
+ public String decrypt(String strIn) throws Exception {
+ return new String(decrypt(hexStr2ByteArr(strIn)));
+ }
+
+ /**
+ * 从指定字符串生成密钥,密钥所需的字节数组长度为8位 不足8位时后面补0,超出8位只取前8位
+ *
+ * @param arrBTmp
+ * 构成该字符串的字节数组
+ * @return 生成的密钥
+ * @throws java.lang.Exception
+ */
+ private Key getKey(byte[] arrBTmp) throws Exception {
+ // 创建一个空的8位字节数组(默认值为0)
+ byte[] arrB = new byte[8];
+
+ // 将原始字节数组转换为8位
+ for (int i = 0; i < arrBTmp.length && i < arrB.length; i++) {
+ arrB[i] = arrBTmp[i];
+ }
+
+ // 生成密钥
+ Key key = new javax.crypto.spec.SecretKeySpec(arrB, "DES");
+
+ return key;
+ }
+}
diff --git a/src/com/netease/qa/emmagee/utils/FpsInfo.java b/src/com/netease/qa/emmagee/utils/FpsInfo.java
new file mode 100644
index 0000000..dc0068d
--- /dev/null
+++ b/src/com/netease/qa/emmagee/utils/FpsInfo.java
@@ -0,0 +1,68 @@
+package com.netease.qa.emmagee.utils;
+
+import java.io.BufferedReader;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+public class FpsInfo {
+
+ private static Process process;
+ private static BufferedReader ir;
+ private static DataOutputStream os = null;
+ private static long startTime = 0L;
+ private static int lastFrameNum = 0;
+ private static boolean ok = true;
+
+ /**
+ * get frame per second
+ *
+ * @return frame per second
+ */
+ public static float fps() {
+ if (ok) {
+ long nowTime = System.nanoTime();
+ float f = (float) (nowTime - startTime) / 1000000.0F;
+ startTime = nowTime;
+ int nowFrameNum = getFrameNum();
+ final float fps = Math.round((nowFrameNum - lastFrameNum) * 1000
+ / f);
+ lastFrameNum = nowFrameNum;
+ return fps;
+ } else {
+ return -1;
+ }
+
+ }
+
+ /**
+ * get frame value
+ *
+ * @return frame value
+ */
+ public static final int getFrameNum() {
+ try {
+ if (process == null) {
+ process = Runtime.getRuntime().exec("su");
+ os = new DataOutputStream(process.getOutputStream());
+ ir = new BufferedReader(new InputStreamReader(
+ process.getInputStream()));
+ }
+ os.writeBytes("service call SurfaceFlinger 1013" + "\n");
+ os.flush();
+ String str1 = ir.readLine();
+ if (str1 != null) {
+ int start = str1.indexOf("(");
+ int end = str1.indexOf(" ");
+ if ((start != -1) & (end > start)) {
+ String str2 = str1.substring(start + 1, end);
+ return Integer.parseInt((String) str2, 16);
+ }
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ ok = false;
+ return -1;
+ }
+}
diff --git a/src/com/netease/qa/emmagee/utils/MailSender.java b/src/com/netease/qa/emmagee/utils/MailSender.java
new file mode 100644
index 0000000..04504db
--- /dev/null
+++ b/src/com/netease/qa/emmagee/utils/MailSender.java
@@ -0,0 +1,109 @@
+package com.netease.qa.emmagee.utils;
+
+import java.io.File;
+import java.util.Date;
+import java.util.Properties;
+
+import javax.activation.CommandMap;
+import javax.activation.DataHandler;
+import javax.activation.DataSource;
+import javax.activation.FileDataSource;
+import javax.activation.MailcapCommandMap;
+import javax.mail.Address;
+import javax.mail.BodyPart;
+import javax.mail.Message;
+import javax.mail.MessagingException;
+import javax.mail.Multipart;
+import javax.mail.Session;
+import javax.mail.Transport;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeBodyPart;
+import javax.mail.internet.MimeMessage;
+import javax.mail.internet.MimeMultipart;
+
+/**
+ * 发送邮件给多个接收者、抄送邮件
+ *
+ * @author andrewleo
+ */
+public class MailSender {
+
+ private static final String LOG_TAG = MailSender.class.getSimpleName();
+ private static final int PORT = 25;
+
+ /**
+ * 以文本格式发送邮件
+ *
+ * 待发送的邮件的信息
+ */
+ public static boolean sendTextMail(String sender, String encryptPassword, String smtp, String subject, String content, String file,
+ String[] maillists) {
+ if (maillists == null || maillists.length == 0 || ("".equals(maillists[0].trim()))) {
+ return false;
+ } else {
+ // Get system properties
+ Properties props = new Properties();
+
+ // Setup mail server
+ props.put("mail.smtp.host", smtp);
+ props.put("mail.smtp.port", PORT);
+ // Get session
+ props.put("mail.smtp.auth", "true"); // 如果需要密码验证,把这里的false改成true
+
+ // 判断是否需要身份认证
+ CustomizedAuthenticator authenticator = null;
+ if (true) {
+ // 如果需要身份认证,则创建一个密码验证器
+ authenticator = new CustomizedAuthenticator(sender, encryptPassword);
+ }
+ // 根据邮件会话属性和密码验证器构造一个发送邮件的session
+ Session sendMailSession = Session.getInstance(props, authenticator);
+ try {
+ // 根据session创建一个邮件消息
+ Message mailMessage = new MimeMessage(sendMailSession);
+ // 创建邮件发送者地址
+ Address from = new InternetAddress(sender);
+ // 设置邮件消息的发送者
+ mailMessage.setFrom(from);
+ // 创建邮件的接收者地址,并设置到邮件消息中
+ for (int i = 0; i < maillists.length; i++) {
+ // Message.RecipientType.TO属性表示接收者的类型为TO
+ mailMessage.addRecipient(Message.RecipientType.TO, new InternetAddress(maillists[i]));
+ }
+
+ // 设置邮件消息的主题
+ mailMessage.setSubject(subject);
+ // 设置邮件消息发送的时间
+ mailMessage.setSentDate(new Date());
+
+ Multipart multipart = new MimeMultipart();
+ BodyPart bodyPart = new MimeBodyPart();
+ bodyPart.setText(content);
+ multipart.addBodyPart(bodyPart);
+
+ File attach = new File(file);
+ if (attach.exists()) {
+ MimeBodyPart attachPart = new MimeBodyPart();
+ DataSource source = new FileDataSource(attach);
+ attachPart.setDataHandler(new DataHandler(source));
+ attachPart.setFileName(attach.getName());
+
+ multipart.addBodyPart(attachPart);
+ }
+ mailMessage.setContent(multipart);
+ MailcapCommandMap mc = (MailcapCommandMap) CommandMap.getDefaultCommandMap();
+ mc.addMailcap("text/html;; x-java-content-handler=com.sun.mail.handlers.text_html");
+ mc.addMailcap("text/xml;; x-java-content-handler=com.sun.mail.handlers.text_xml");
+ mc.addMailcap("text/plain;; x-java-content-handler=com.sun.mail.handlers.text_plain");
+ mc.addMailcap("multipart/*;; x-java-content-handler=com.sun.mail.handlers.multipart_mixed");
+ mc.addMailcap("message/rfc822;; x-java-content-handler=com.sun.mail.handlers.message_rfc822");
+ CommandMap.setDefaultCommandMap(mc);
+ Transport.send(mailMessage);
+ return true;
+ } catch (MessagingException ex) {
+ ex.printStackTrace();
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/com/netease/qa/emmagee/utils/MemoryInfo.java b/src/com/netease/qa/emmagee/utils/MemoryInfo.java
index 74802fe..84be2a4 100644
--- a/src/com/netease/qa/emmagee/utils/MemoryInfo.java
+++ b/src/com/netease/qa/emmagee/utils/MemoryInfo.java
@@ -17,21 +17,29 @@
package com.netease.qa.emmagee.utils;
import java.io.BufferedReader;
+import java.io.DataOutputStream;
import java.io.FileReader;
import java.io.IOException;
+import java.io.InputStreamReader;
import android.app.ActivityManager;
import android.content.Context;
import android.os.Debug;
import android.util.Log;
+/**
+ * operate memory information
+ *
+ * @author andrewleo
+ */
public class MemoryInfo {
- private static final String LOG_TAG = "Emmagee-"
- + MemoryInfo.class.getSimpleName();
+ private static final String LOG_TAG = "Emmagee-" + MemoryInfo.class.getSimpleName();
+
+ private static Process process;
/**
- * read the total memory of certain device
+ * get total memory of certain device.
*
* @return total memory of device
*/
@@ -49,6 +57,7 @@ public long getTotalMemory() {
memTotal = total[1].trim();
}
}
+ localBufferedReader.close();
String[] memKb = memTotal.split(" ");
memTotal = memKb[0].trim();
Log.d(LOG_TAG, "memTotal: " + memTotal);
@@ -60,22 +69,21 @@ public long getTotalMemory() {
}
/**
- * get free memory
+ * get free memory.
*
* @return free memory of device
*
*/
public long getFreeMemorySize(Context context) {
ActivityManager.MemoryInfo outInfo = new ActivityManager.MemoryInfo();
- ActivityManager am = (ActivityManager) context
- .getSystemService(Context.ACTIVITY_SERVICE);
+ ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
am.getMemoryInfo(outInfo);
long avaliMem = outInfo.availMem;
return avaliMem / 1024;
}
/**
- * get the memory of process with certain pid
+ * get the memory of process with certain pid.
*
* @param pid
* pid of process
@@ -84,21 +92,16 @@ public long getFreeMemorySize(Context context) {
* @return memory usage of certain process
*/
public int getPidMemorySize(int pid, Context context) {
- ActivityManager am = (ActivityManager) context
- .getSystemService(Context.ACTIVITY_SERVICE);
+ ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
int[] myMempid = new int[] { pid };
Debug.MemoryInfo[] memoryInfo = am.getProcessMemoryInfo(myMempid);
memoryInfo[0].getTotalSharedDirty();
-
- // int memSize = memoryInfo[0].dalvikPrivateDirty;
- // TODO PSS
int memSize = memoryInfo[0].getTotalPss();
- // int memSize = memoryInfo[0].getTotalPrivateDirty();
return memSize;
}
/**
- * get the sdk version of phone
+ * get the sdk version of phone.
*
* @return sdk version
*/
@@ -107,11 +110,87 @@ public String getSDKVersion() {
}
/**
- * get phone type
+ * get phone type.
*
* @return phone type
*/
public String getPhoneType() {
return android.os.Build.MODEL;
}
+
+ /**
+ * get app heap size, it is more importance than total memory
+ *
+ * @return heap size
+ */
+ public static String[][] getHeapSize(int pid, Context context) {
+ String[][] heapData = parseMeminfo(pid);
+ return heapData;
+ }
+
+ /**
+ * dumpsys meminfo, and parse the result to get native and heap data
+ *
+ * @param pid
+ * process id
+ * @return native and heap data
+ */
+ public static String[][] parseMeminfo(int pid) {
+
+ boolean infoStart = false;
+ // [][],00:native heap size,01:native heap alloc;10: dalvik heap
+ // size,11: dalvik heap alloc
+ String[][] heapData = new String[2][2];
+
+ try {
+ Runtime runtime = Runtime.getRuntime();
+ process = runtime.exec("su");
+ DataOutputStream os = new DataOutputStream(process.getOutputStream());
+ os.writeBytes("dumpsys meminfo " + pid + "\n");
+ os.writeBytes("exit\n");
+ os.flush();
+
+ BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
+ String line = "";
+
+ while ((line = bufferedReader.readLine()) != null) {
+ line = line.trim();
+ if (line.contains("Permission Denial")) {
+ break;
+ } else {
+ // 当读取到MEMINFO in pid 这一行时,下一行就是需要获取的数据
+ if (line.contains("MEMINFO in pid")) {
+ infoStart = true;
+ } else if (infoStart) {
+ String[] lineItems = line.split("\\s+");
+ int length = lineItems.length;
+ if (line.startsWith("size")) {
+ heapData[0][0] = lineItems[1];
+ heapData[1][0] = lineItems[2];
+ } else if (line.startsWith("allocated")) {
+ heapData[0][1] = lineItems[1];
+ heapData[1][1] = lineItems[2];
+ break;
+ } else if (line.startsWith("Native")) {
+ Log.d(LOG_TAG, "Native");
+ Log.d(LOG_TAG, "lineItems[4]=" + lineItems[4]);
+ Log.d(LOG_TAG, "lineItems[5]=" + lineItems[5]);
+ heapData[0][0] = lineItems[length-3];
+ heapData[0][1] = lineItems[length-2];
+ } else if (line.startsWith("Dalvik")) {
+ Log.d(LOG_TAG, "Dalvik");
+ Log.d(LOG_TAG, "lineItems[4]=" + lineItems[4]);
+ Log.d(LOG_TAG, "lineItems[5]=" + lineItems[5]);
+ heapData[1][0] = lineItems[length-3];
+ heapData[1][1] = lineItems[length-2];
+ break;
+ }
+ }
+ }
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return heapData;
+ }
}
diff --git a/src/com/netease/qa/emmagee/utils/MyApplication.java b/src/com/netease/qa/emmagee/utils/MyApplication.java
index 1e2f694..9d86f15 100644
--- a/src/com/netease/qa/emmagee/utils/MyApplication.java
+++ b/src/com/netease/qa/emmagee/utils/MyApplication.java
@@ -16,14 +16,36 @@
*/
package com.netease.qa.emmagee.utils;
+import java.io.File;
+
import android.app.Application;
import android.view.WindowManager;
+/**
+ * my application class
+ *
+ * @author andrewleo
+ */
public class MyApplication extends Application {
-
+
private WindowManager.LayoutParams wmParams = new WindowManager.LayoutParams();
public WindowManager.LayoutParams getMywmParams() {
return wmParams;
}
+
+ @Override
+ public void onCreate() {
+ initAppConfig();
+ super.onCreate();
+ }
+
+ private void initAppConfig() {
+ // create directory of emmagee
+ File dir = new File(Settings.EMMAGEE_RESULT_DIR);
+ if (!dir.exists()) {
+ dir.mkdirs();
+ }
+ }
+
}
diff --git a/src/com/netease/qa/emmagee/utils/ProcessInfo.java b/src/com/netease/qa/emmagee/utils/ProcessInfo.java
index 9eb934e..d6d9e23 100644
--- a/src/com/netease/qa/emmagee/utils/ProcessInfo.java
+++ b/src/com/netease/qa/emmagee/utils/ProcessInfo.java
@@ -16,28 +16,38 @@
*/
package com.netease.qa.emmagee.utils;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
-import com.netease.qa.emmagee.activity.MainPageActivity;
-
import android.app.ActivityManager;
import android.app.ActivityManager.RunningAppProcessInfo;
+import android.app.ActivityManager.RunningTaskInfo;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.os.Build;
import android.util.Log;
+/**
+ * get information of processes
+ *
+ * @author andrewleo
+ */
public class ProcessInfo {
- private final String LOG_TAG = "Emmagee-"
+ private static final String LOG_TAG = "Emmagee-"
+ ProcessInfo.class.getSimpleName();
- private final String PACKAGE_NAME = "com.netease.qa.emmagee";
+ private static final String PACKAGE_NAME = "com.netease.qa.emmagee";
+ private static final int ANDROID_M = 22;
/**
* get information of all running processes,including package name ,process
- * name ,icon ,pid and uid
+ * name ,icon ,pid and uid.
*
* @param context
* context of activity
@@ -45,16 +55,13 @@ public class ProcessInfo {
*/
public List getRunningProcess(Context context) {
Log.i(LOG_TAG, "get running processes");
+ List progressList = new ArrayList();
+ PackageManager pm = context.getPackageManager();
ActivityManager am = (ActivityManager) context
.getSystemService(Context.ACTIVITY_SERVICE);
List run = am.getRunningAppProcesses();
- PackageManager pm = context.getPackageManager();
- List progressList = new ArrayList();
- boolean launchTag;
-
for (ApplicationInfo appinfo : getPackagesInfo(context)) {
- launchTag = false;
Programe programe = new Programe();
if (((appinfo.flags & ApplicationInfo.FLAG_SYSTEM) > 0)
|| ((appinfo.processName != null) && (appinfo.processName
@@ -65,7 +72,6 @@ public List getRunningProcess(Context context) {
if ((runningProcess.processName != null)
&& runningProcess.processName
.equals(appinfo.processName)) {
- launchTag = true;
programe.setPid(runningProcess.pid);
programe.setUid(runningProcess.uid);
break;
@@ -73,16 +79,86 @@ public List getRunningProcess(Context context) {
}
programe.setPackageName(appinfo.processName);
programe.setProcessName(appinfo.loadLabel(pm).toString());
- if (launchTag == true) {
- programe.setIcon(appinfo.loadIcon(pm));
+ programe.setIcon(appinfo.loadIcon(pm));
+ progressList.add(programe);
+ }
+ Collections.sort(progressList);
+ return progressList;
+ }
+
+ /**
+ * get pid by package name
+ *
+ * @param context
+ * context of activity
+ * @return pid
+ */
+ public int getPidByPackageName(Context context, String packageName) {
+ Log.i(LOG_TAG, "start getLaunchedPid");
+ ActivityManager am = (ActivityManager) context
+ .getSystemService(Context.ACTIVITY_SERVICE);
+ // Note: getRunningAppProcesses return itself in API 22
+ if (Build.VERSION.SDK_INT < ANDROID_M) {
+ List run = am.getRunningAppProcesses();
+ for (RunningAppProcessInfo runningProcess : run) {
+ if ((runningProcess.processName != null)
+ && runningProcess.processName.equals(packageName)) {
+ return runningProcess.pid;
+ }
+ }
+ } else {
+ Log.i(LOG_TAG, "use top command to get pid");
+ try {
+ Process p = Runtime.getRuntime().exec("top -m 100 -n 1");
+ BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(
+ p.getInputStream()));
+ String line = "";
+ while ((line = bufferedReader.readLine()) != null) {
+ if (line.contains(packageName)) {
+ line = line.trim();
+ String[] splitLine = line.split("\\s+");
+ if (packageName.equals(splitLine[splitLine.length - 1])) {
+ return Integer.parseInt(splitLine[0]);
+ }
+ }
+ }
+ } catch (IOException e1) {
+ e1.printStackTrace();
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * get information of all installed packages
+ *
+ * @param context
+ * context of activity
+ * @return all installed packages
+ */
+ public List getAllPackages(Context context) {
+ Log.i(LOG_TAG, "getAllPackages");
+ List progressList = new ArrayList();
+ PackageManager pm = context.getPackageManager();
+
+ for (ApplicationInfo appinfo : getPackagesInfo(context)) {
+ Programe programe = new Programe();
+ if (((appinfo.flags & ApplicationInfo.FLAG_SYSTEM) > 0)
+ || ((appinfo.processName != null) && (appinfo.processName
+ .equals(PACKAGE_NAME)))) {
+ continue;
}
+ programe.setPackageName(appinfo.processName);
+ programe.setProcessName(appinfo.loadLabel(pm).toString());
+ programe.setIcon(appinfo.loadIcon(pm));
progressList.add(programe);
}
+ Collections.sort(progressList);
return progressList;
}
/**
- * get information of all applications
+ * get information of all applications.
*
* @param context
* context of activity
@@ -94,4 +170,53 @@ private List getPackagesInfo(Context context) {
.getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES);
return appList;
}
+
+ /**
+ * get pid by package name
+ *
+ * @param context
+ * context of activity
+ * @param packageName
+ * package name of monitoring app
+ * @return pid
+ */
+ public Programe getProgrameByPackageName(Context context, String packageName) {
+ if (Build.VERSION.SDK_INT < ANDROID_M) {
+ List processList = getRunningProcess(context);
+ for (Programe programe : processList) {
+ if ((programe.getPackageName() != null)
+ && (programe.getPackageName().equals(packageName))) {
+ return programe;
+ }
+ }
+ } else {
+ Programe programe = new Programe();
+ int pid = getPidByPackageName(context, packageName);
+ programe.setPid(pid);
+ programe.setUid(0);
+ return programe;
+ }
+ return null;
+ }
+
+ /**
+ * get top activity name
+ *
+ * @param context
+ * context of activity
+ * @return top activity name
+ */
+ public static String getTopActivity(Context context) {
+ ActivityManager manager = (ActivityManager) context
+ .getSystemService(Context.ACTIVITY_SERVICE);
+ // Note: getRunningTasks is deprecated in API 21(Official)
+ if (Build.VERSION.SDK_INT >= 21) {
+ return Constants.NA;
+ }
+ List runningTaskInfos = manager.getRunningTasks(1);
+ if (runningTaskInfos != null)
+ return (runningTaskInfos.get(0).topActivity).toString();
+ else
+ return null;
+ }
}
diff --git a/src/com/netease/qa/emmagee/utils/Programe.java b/src/com/netease/qa/emmagee/utils/Programe.java
index 37b014b..1860118 100644
--- a/src/com/netease/qa/emmagee/utils/Programe.java
+++ b/src/com/netease/qa/emmagee/utils/Programe.java
@@ -18,7 +18,13 @@
import android.graphics.drawable.Drawable;
-public class Programe {
+/**
+ * details of installed processes ,including
+ * icon,packagename,pid,uid,processname
+ *
+ * @author andrewleo
+ */
+public class Programe implements Comparable {
private Drawable icon;
private String processName;
private String packageName;
@@ -65,4 +71,9 @@ public int getPid() {
public void setPid(int pid) {
this.pid = pid;
}
+
+ @Override
+ public int compareTo(Programe arg0) {
+ return (this.getProcessName().compareTo(arg0.getProcessName()));
+ }
}
diff --git a/src/com/netease/qa/emmagee/utils/Settings.java b/src/com/netease/qa/emmagee/utils/Settings.java
new file mode 100644
index 0000000..cf1bef4
--- /dev/null
+++ b/src/com/netease/qa/emmagee/utils/Settings.java
@@ -0,0 +1,38 @@
+package com.netease.qa.emmagee.utils;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.preference.PreferenceManager;
+
+/**
+ * Parameters in Setting Activity
+ *
+ * @author yrom
+ *
+ */
+public final class Settings {
+
+ public static final String KEY_SENDER = "sender";
+ public static final String KEY_PASSWORD = "password";
+ public static final String KEY_RECIPIENTS = "recipients";
+ public static final String KEY_SMTP = "smtp";
+ public static final String KEY_ISFLOAT = "isfloat";
+ public static final String KEY_INTERVAL = "interval";
+ public static final String KEY_ROOT = "root";
+ public static final String KEY_AUTO_STOP = "autoStop";
+ public static final String KEY_WACK_LOCK = "wakeLock";
+ public static final String EMMAGEE_RESULT_DIR = "/sdcard/Emmagee/";
+ private static WakeLockHelper wakeLockHelper;
+
+ public static SharedPreferences getDefaultSharedPreferences(Context context) {
+ return PreferenceManager.getDefaultSharedPreferences(context);
+ }
+
+ public static WakeLockHelper getDefaultWakeLock(Context context) {
+ if (wakeLockHelper == null) {
+ wakeLockHelper = new WakeLockHelper(context);
+ }
+ return wakeLockHelper;
+ }
+
+}
diff --git a/src/com/netease/qa/emmagee/utils/TrafficInfo.java b/src/com/netease/qa/emmagee/utils/TrafficInfo.java
index 906b6a2..c0f4b00 100644
--- a/src/com/netease/qa/emmagee/utils/TrafficInfo.java
+++ b/src/com/netease/qa/emmagee/utils/TrafficInfo.java
@@ -16,52 +16,82 @@
*/
package com.netease.qa.emmagee.utils;
-import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
+import android.net.TrafficStats;
import android.util.Log;
+/**
+ * information of network traffic
+ *
+ * @author andrewleo
+ */
public class TrafficInfo {
- private static final String LOG_TAG = "Emmagee-"
- + TrafficInfo.class.getSimpleName();
-
+ private static final String LOG_TAG = "Emmagee-" + TrafficInfo.class.getSimpleName();
+ private static final int UNSUPPORTED = -1;
+
private String uid;
-
- public TrafficInfo(String uid){
+
+ public TrafficInfo(String uid) {
this.uid = uid;
}
/**
- * get total network traffic, which is the sum of upload and download traffic
+ * get total network traffic, which is the sum of upload and download
+ * traffic.
*
* @return total traffic include received and send traffic
*/
public long getTrafficInfo() {
- Log.i(LOG_TAG,"get traffic information");
+ Log.i(LOG_TAG, "get traffic information");
+ Log.d(LOG_TAG, "uid = " + uid);
+ long traffic = trafficFromApi();
+ return traffic <= 0 ? trafficFromFiles() : traffic;
+ }
+
+ /**
+ * Use TrafficStats getUidRxBytes and getUidTxBytes to get network
+ * traffic,these API return both tcp and udp usage
+ *
+ * @return
+ */
+ private long trafficFromApi() {
+ long rcvTraffic = UNSUPPORTED, sndTraffic = UNSUPPORTED;
+ rcvTraffic = TrafficStats.getUidRxBytes(Integer.parseInt(uid));
+ sndTraffic = TrafficStats.getUidTxBytes(Integer.parseInt(uid));
+ return rcvTraffic + sndTraffic < 0 ? UNSUPPORTED : rcvTraffic + sndTraffic;
+ }
+
+ /**
+ * read files in uid_stat to get traffic info
+ *
+ * @return
+ */
+ private long trafficFromFiles() {
+ RandomAccessFile rafRcv = null, rafSnd = null;
+ long rcvTraffic = UNSUPPORTED, sndTraffic = UNSUPPORTED;
String rcvPath = "/proc/uid_stat/" + uid + "/tcp_rcv";
String sndPath = "/proc/uid_stat/" + uid + "/tcp_snd";
- long rcvTraffic = -1;
- long sndTraffic = -1;
try {
- RandomAccessFile rafRcv = new RandomAccessFile(rcvPath, "r");
- RandomAccessFile rafSnd = new RandomAccessFile(sndPath, "r");
+ rafRcv = new RandomAccessFile(rcvPath, "r");
+ rafSnd = new RandomAccessFile(sndPath, "r");
rcvTraffic = Long.parseLong(rafRcv.readLine());
sndTraffic = Long.parseLong(rafSnd.readLine());
- } catch (FileNotFoundException e) {
- rcvTraffic = -1;
- sndTraffic = -1;
- } catch (NumberFormatException e) {
- Log.e(LOG_TAG, "NumberFormatException: " + e.getMessage());
- e.printStackTrace();
- } catch (IOException e) {
- Log.e(LOG_TAG, "IOException: " + e.getMessage());
- e.printStackTrace();
+ Log.d(LOG_TAG, String.format("rcvTraffic, sndTraffic = %s, %s", rcvTraffic, sndTraffic));
+ } catch (Exception e) {
+ }
+ finally {
+ try {
+ if (rafRcv != null) {
+ rafRcv.close();
+ }
+ if (rafSnd != null)
+ rafSnd.close();
+ } catch (IOException e) {}
}
- if (rcvTraffic == -1 || sndTraffic == -1) {
- return -1;
- } else
- return (rcvTraffic + sndTraffic);
+ return rcvTraffic + sndTraffic < 0 ? UNSUPPORTED : rcvTraffic + sndTraffic;
}
+
}
diff --git a/src/com/netease/qa/emmagee/utils/WakeLockHelper.java b/src/com/netease/qa/emmagee/utils/WakeLockHelper.java
new file mode 100644
index 0000000..8faacad
--- /dev/null
+++ b/src/com/netease/qa/emmagee/utils/WakeLockHelper.java
@@ -0,0 +1,29 @@
+package com.netease.qa.emmagee.utils;
+
+import android.content.Context;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+
+public class WakeLockHelper {
+ private WakeLock wakeLock = null;
+ private Context context;
+
+ public WakeLockHelper(Context context) {
+ this.context = context;
+ }
+
+ public void acquireFullWakeLock() {
+ if (null == wakeLock) {
+ PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+ wakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, "WakeLock");
+ }
+ wakeLock.acquire();
+ }
+
+ public void releaseWakeLock() {
+ if (null != wakeLock) {
+ wakeLock.release();
+ wakeLock = null;
+ }
+ }
+}