├── .classpath
├── .github
└── ISSUE_TEMPLATE.md
├── .gitignore
├── .project
├── .settings
└── org.eclipse.core.resources.prefs
├── AUTHORS
├── AndroidManifest.xml
├── LICENSE
├── README.md
├── VERSION
├── changelog.md
├── emmagee.xml
├── libs
├── activation.jar
├── additionnal.jar
├── android-support-v4.jar
├── commons-io-2.5.jar
└── mail.jar
├── project.properties
├── res
├── drawable-hdpi
│ ├── actionbar_bg.9.png
│ ├── bottom_bg.png
│ ├── btn_back.png
│ ├── btn_save.png
│ ├── btn_test.png
│ ├── btn_test_press.png
│ ├── button_bg.png
│ ├── checked_icon.png
│ ├── close.png
│ ├── ic_launcher.png
│ ├── icon.png
│ ├── left_arrow.png
│ ├── meminfo.png
│ ├── refresh.png
│ ├── right_arrow.png
│ ├── seekbar_thumb_normal.png
│ ├── seekbar_thumb_pressed.png
│ ├── settings.png
│ ├── settings_pressed.png
│ └── unchecked_icon.png
├── drawable-ldpi
│ ├── ic_launcher.png
│ ├── icon.png
│ └── meminfo.png
├── drawable-mdpi
│ ├── button_bg.png
│ ├── ic_launcher.png
│ ├── icon.png
│ └── meminfo.png
├── drawable
│ ├── custom_checkbox.xml
│ ├── custom_seekbar.xml
│ ├── float_button.xml
│ ├── seekbar_thumb.xml
│ ├── settings_button.xml
│ ├── table_border.xml
│ └── test_button.xml
├── layout
│ ├── about.xml
│ ├── floating.xml
│ ├── list_item.xml
│ ├── mail_settings.xml
│ ├── mainpage.xml
│ ├── navigation_bar.xml
│ ├── settings.xml
│ ├── table_row.xml
│ ├── test_list.xml
│ ├── test_list_item.xml
│ └── test_report.xml
├── values-zh-rCN
│ └── strings.xml
└── values
│ ├── colors.xml
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
└── src
└── com
└── netease
└── qa
└── emmagee
├── activity
├── AboutActivity.java
├── MailSettingsActivity.java
├── MainPageActivity.java
├── SettingsActivity.java
├── TestListActivity.java
└── TestReportActivity.java
├── service
└── EmmageeService.java
└── utils
├── Constants.java
├── CpuInfo.java
├── CurrentInfo.java
├── CustomizedAuthenticator.java
├── EncryptData.java
├── FpsInfo.java
├── MailSender.java
├── MemoryInfo.java
├── MyApplication.java
├── ProcessInfo.java
├── Programe.java
├── Settings.java
├── TrafficInfo.java
└── WakeLockHelper.java
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## The problem
2 |
3 | Describe the issue you are experiencing, or features you want.
4 |
5 | ## Environment
6 |
7 | * Emmagee version
8 | * Device Information: **Brand**, **Model**, **System**.
9 |
10 | ## Link to Emmagee logs
11 |
12 | Create a [GIST](https://gist.github.com) which is a paste of your _full_ Emmagee logs, and link them here.
13 | Do _NOT_ paste your full Emmagee logs here, as it will make this issue very long and hard to read!
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # built application files
2 | *.apk
3 | *.ap_
4 |
5 | # files for the dex VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # generated files
12 | bin/
13 | gen/
14 |
15 | # Local configuration file (sdk path, etc)
16 | local.properties
17 |
18 | lint.xml
19 | .DS_Store
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | Emmagee
4 |
5 |
6 |
7 |
8 |
9 | com.android.ide.eclipse.adt.ResourceManagerBuilder
10 |
11 |
12 |
13 |
14 | com.android.ide.eclipse.adt.PreCompilerBuilder
15 |
16 |
17 |
18 |
19 | org.eclipse.jdt.core.javabuilder
20 |
21 |
22 |
23 |
24 | com.android.ide.eclipse.adt.ApkBuilder
25 |
26 |
27 |
28 |
29 |
30 | com.android.ide.eclipse.adt.AndroidNature
31 | org.eclipse.jdt.core.javanature
32 |
33 |
34 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.core.resources.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | encoding/=UTF-8
3 |
--------------------------------------------------------------------------------
/AUTHORS:
--------------------------------------------------------------------------------
1 | * KevinKong
2 | * andrewleo
3 | * bingoHuang
4 |
--------------------------------------------------------------------------------
/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
14 |
19 |
20 |
21 |
22 |
23 |
24 |
29 |
32 |
35 |
38 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2012-2013 NetEase, Inc. and other contributors
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 | You may obtain a copy of the License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Emmagee - a practical, handy performance test tool for specified Android App
2 |
3 | Emmagee is a practical, handy performance test tool for specified Android App, which can monitor CPU, memory,
4 | 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,
5 | rendering real-time process status in a floating window, and much more.
6 |
7 | * Homepage: https://github.com/NetEase/Emmagee
8 | * Wiki: https://github.com/NetEase/Emmagee/wiki
9 | * Issues: https://github.com/NetEase/Emmagee/issues
10 | * FAQ: https://github.com/NetEase/Emmagee/wiki/FAQ
11 | * Tags: Android, Java
12 |
13 |
14 |
15 | ## Why should I use Emmagee?
16 |
17 | Unlike most other performance test tools that only do system-level monitoring, Emmagee provides the ability to monitor any single App. Other advantages that
18 | you should not miss:
19 | * Open source
20 | * Easy to use
21 | * Process-specific monitoring, including CPU, memory, network traffic, battery current, launching time and status
22 | * Floating window that renders real-time process status
23 | * CSV format report that can be converted into any other format you want
24 | * User-defined collecting interval
25 | * Fully support Android 2.2 and above
26 |
27 | ## How to use Emmagee?
28 |
29 | First of all ,you should have Emmagee.apk,download [here](https://github.com/NetEase/Emmagee/releases) or
30 | build the apk file youself [here](https://github.com/NetEase/Emmagee/wiki/How-to-build-emmage.apk%3F),then :
31 |
32 | 1. Start Emmagee App
33 | 2. Configure interval
34 | 3. Select a target process
35 | 4. Click Start button
36 |
37 | And Enjoy!
38 |
39 | If you want to stop the test, just go back to Emmagee and click Stop button.
40 |
41 | ## Android 5.0 and above
42 |
43 | * `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.
44 | * `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.
45 |
46 | ## Coming Soon
47 | * We want you to decide!
48 |
49 | ## How to Contribute?
50 |
51 | You are welcome to contribute to Emmagee, meanwhile you'd better follow the rules below
52 |
53 | * It's *NOT* recommended to submit a pull request directly to Emmagee's `master` branch. `develop` branch is more appropriate
54 | * Follow common Java coding conventions
55 | * Put all Java class files under *com.netease* package
56 | * Add the following [license](#license) in each Java class file
57 |
58 | ## Contributors
59 | * NetEase, Inc.
60 | * [yrom](https://github.com/yrom)
61 | * [LukeOwncloud](https://github.com/LukeOwncloud)
62 |
63 | ## License
64 | (The Apache License)
65 |
66 | Copyright (c) 2012-2015 NetEase, Inc. and other contributors
67 |
68 | Licensed under the Apache License, Version 2.0 (the "License");
69 | you may not use this file except in compliance with the License.
70 | You may obtain a copy of the License at
71 |
72 | http://www.apache.org/licenses/LICENSE-2.0
73 |
74 | Unless required by applicable law or agreed to in writing, software
75 | distributed under the License is distributed on an "AS IS" BASIS,
76 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
77 | See the License for the specific language governing permissions and
78 | limitations under the License.
79 |
--------------------------------------------------------------------------------
/VERSION:
--------------------------------------------------------------------------------
1 | 2.4
2 |
--------------------------------------------------------------------------------
/changelog.md:
--------------------------------------------------------------------------------
1 | Emmagee Changelog
2 |
3 | ###2.0 (2014-9-22)
4 | New Features:
5 | * Optimization of UI
6 | * I18N support(Chinese Simplified and English)
7 | * Collect usage of each CPU cores
8 |
9 | Issues Fixed:
10 | * #16 'Save' button is covered when soft keyboard pops up
11 | * #20 Strings hard-coded for language
12 | * #21 CSV report enhancement
13 | * #24 Floating window disappears on some specific devices
14 | * #25 Report unavailable path of CSV when test is over
15 | * #26 Dulplicated toast when test is over
16 |
17 | ###1.3.0 (2014-1-6)
18 | New Features:
19 | * Integrate monitoring of app starting time
20 |
21 | Issues fixed
22 | * #12 Add "stop test" button on floating window
23 | * #13 Optimize interface and user experience
24 |
25 | ###1.2.1 (2013-11-25)
26 | Issues fixed:
27 | * #10 The CSV report displays abnormally when free memory is above 1000.
28 |
29 | Improvements to :
30 | * #9 List the application with alphabet order.
31 |
32 | ###1.2.0 (2013-9-17)
33 | * Support quad-core CPU
34 | * Integrate monitoring of battery current and status
35 |
36 | ###1.1.0 (2013-4-8)
37 | * Configure specified recipients to receive test reports
38 | * Automaticly save the result when monitoring app stops
39 |
40 | ###1.0.0 (2013-1-18)
41 | * Process-specific monitoring
42 | * Floating window that renders real-time process status
43 | * CSV format report that can be converted into any other format you want
44 | * User-defined collecting interval
45 |
--------------------------------------------------------------------------------
/emmagee.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
21 |
22 |
25 |
26 |
27 |
28 |
29 |
30 |
34 |
35 |
38 |
39 |
40 |
41 |
42 |
43 |
46 |
47 |
50 |
51 |
52 |
53 |
54 |
55 |
59 |
60 |
63 |
64 |
65 |
66 |
67 |
68 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/libs/activation.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEase/Emmagee/6a382dffe74b5be6d2de78cb0c640cc67e9ce650/libs/activation.jar
--------------------------------------------------------------------------------
/libs/additionnal.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEase/Emmagee/6a382dffe74b5be6d2de78cb0c640cc67e9ce650/libs/additionnal.jar
--------------------------------------------------------------------------------
/libs/android-support-v4.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEase/Emmagee/6a382dffe74b5be6d2de78cb0c640cc67e9ce650/libs/android-support-v4.jar
--------------------------------------------------------------------------------
/libs/commons-io-2.5.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEase/Emmagee/6a382dffe74b5be6d2de78cb0c640cc67e9ce650/libs/commons-io-2.5.jar
--------------------------------------------------------------------------------
/libs/mail.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEase/Emmagee/6a382dffe74b5be6d2de78cb0c640cc67e9ce650/libs/mail.jar
--------------------------------------------------------------------------------
/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system use,
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 |
10 | # Project target.
11 | target=android-19
12 |
--------------------------------------------------------------------------------
/res/drawable-hdpi/actionbar_bg.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEase/Emmagee/6a382dffe74b5be6d2de78cb0c640cc67e9ce650/res/drawable-hdpi/actionbar_bg.9.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/bottom_bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEase/Emmagee/6a382dffe74b5be6d2de78cb0c640cc67e9ce650/res/drawable-hdpi/bottom_bg.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/btn_back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEase/Emmagee/6a382dffe74b5be6d2de78cb0c640cc67e9ce650/res/drawable-hdpi/btn_back.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/btn_save.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEase/Emmagee/6a382dffe74b5be6d2de78cb0c640cc67e9ce650/res/drawable-hdpi/btn_save.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/btn_test.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEase/Emmagee/6a382dffe74b5be6d2de78cb0c640cc67e9ce650/res/drawable-hdpi/btn_test.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/btn_test_press.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEase/Emmagee/6a382dffe74b5be6d2de78cb0c640cc67e9ce650/res/drawable-hdpi/btn_test_press.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/button_bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEase/Emmagee/6a382dffe74b5be6d2de78cb0c640cc67e9ce650/res/drawable-hdpi/button_bg.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/checked_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEase/Emmagee/6a382dffe74b5be6d2de78cb0c640cc67e9ce650/res/drawable-hdpi/checked_icon.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEase/Emmagee/6a382dffe74b5be6d2de78cb0c640cc67e9ce650/res/drawable-hdpi/close.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEase/Emmagee/6a382dffe74b5be6d2de78cb0c640cc67e9ce650/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEase/Emmagee/6a382dffe74b5be6d2de78cb0c640cc67e9ce650/res/drawable-hdpi/icon.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/left_arrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEase/Emmagee/6a382dffe74b5be6d2de78cb0c640cc67e9ce650/res/drawable-hdpi/left_arrow.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/meminfo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEase/Emmagee/6a382dffe74b5be6d2de78cb0c640cc67e9ce650/res/drawable-hdpi/meminfo.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/refresh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEase/Emmagee/6a382dffe74b5be6d2de78cb0c640cc67e9ce650/res/drawable-hdpi/refresh.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/right_arrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEase/Emmagee/6a382dffe74b5be6d2de78cb0c640cc67e9ce650/res/drawable-hdpi/right_arrow.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/seekbar_thumb_normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEase/Emmagee/6a382dffe74b5be6d2de78cb0c640cc67e9ce650/res/drawable-hdpi/seekbar_thumb_normal.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/seekbar_thumb_pressed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEase/Emmagee/6a382dffe74b5be6d2de78cb0c640cc67e9ce650/res/drawable-hdpi/seekbar_thumb_pressed.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEase/Emmagee/6a382dffe74b5be6d2de78cb0c640cc67e9ce650/res/drawable-hdpi/settings.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/settings_pressed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEase/Emmagee/6a382dffe74b5be6d2de78cb0c640cc67e9ce650/res/drawable-hdpi/settings_pressed.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/unchecked_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEase/Emmagee/6a382dffe74b5be6d2de78cb0c640cc67e9ce650/res/drawable-hdpi/unchecked_icon.png
--------------------------------------------------------------------------------
/res/drawable-ldpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEase/Emmagee/6a382dffe74b5be6d2de78cb0c640cc67e9ce650/res/drawable-ldpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-ldpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEase/Emmagee/6a382dffe74b5be6d2de78cb0c640cc67e9ce650/res/drawable-ldpi/icon.png
--------------------------------------------------------------------------------
/res/drawable-ldpi/meminfo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEase/Emmagee/6a382dffe74b5be6d2de78cb0c640cc67e9ce650/res/drawable-ldpi/meminfo.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/button_bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEase/Emmagee/6a382dffe74b5be6d2de78cb0c640cc67e9ce650/res/drawable-mdpi/button_bg.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEase/Emmagee/6a382dffe74b5be6d2de78cb0c640cc67e9ce650/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEase/Emmagee/6a382dffe74b5be6d2de78cb0c640cc67e9ce650/res/drawable-mdpi/icon.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/meminfo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEase/Emmagee/6a382dffe74b5be6d2de78cb0c640cc67e9ce650/res/drawable-mdpi/meminfo.png
--------------------------------------------------------------------------------
/res/drawable/custom_checkbox.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/res/drawable/custom_seekbar.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
5 |
6 |
7 |
8 |
9 | -
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/res/drawable/float_button.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | -
6 |
7 |
8 |
9 |
10 |
11 |
12 | -
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/res/drawable/seekbar_thumb.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/res/drawable/settings_button.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/res/drawable/table_border.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
8 |
9 |
14 |
15 |
--------------------------------------------------------------------------------
/res/drawable/test_button.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/res/layout/about.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
17 |
18 |
19 |
29 |
30 |
36 |
37 |
42 |
43 |
47 |
48 |
56 |
57 |
58 |
64 |
65 |
70 |
71 |
76 |
77 |
78 |
79 |
84 |
85 |
91 |
92 |
98 |
99 |
107 |
108 |
114 |
115 |
121 |
122 |
123 |
124 |
125 |
--------------------------------------------------------------------------------
/res/layout/floating.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
18 |
19 |
26 |
27 |
34 |
35 |
43 |
44 |
52 |
53 |
58 |
59 |
67 |
75 |
76 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/res/layout/list_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
22 |
23 |
32 |
33 |
--------------------------------------------------------------------------------
/res/layout/mail_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
17 |
18 |
19 |
29 |
30 |
37 |
38 |
43 |
44 |
50 |
51 |
58 |
59 |
60 |
64 |
65 |
70 |
71 |
77 |
78 |
86 |
87 |
88 |
92 |
93 |
98 |
99 |
105 |
106 |
113 |
114 |
115 |
119 |
120 |
125 |
126 |
132 |
133 |
139 |
140 |
141 |
142 |
143 |
--------------------------------------------------------------------------------
/res/layout/mainpage.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
18 |
19 |
20 |
27 |
28 |
32 |
33 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/res/layout/navigation_bar.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
16 |
17 |
22 |
23 |
24 |
35 |
36 |
42 |
43 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/res/layout/settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
17 |
18 |
19 |
29 |
30 |
36 |
37 |
43 |
44 |
50 |
51 |
55 |
56 |
63 |
64 |
65 |
66 |
67 |
81 |
82 |
86 |
87 |
92 |
93 |
101 |
102 |
113 |
114 |
115 |
122 |
123 |
129 |
130 |
141 |
142 |
143 |
150 |
151 |
157 |
158 |
169 |
170 |
171 |
176 |
177 |
184 |
185 |
191 |
192 |
196 |
197 |
209 |
210 |
211 |
212 |
213 |
219 |
225 |
226 |
232 |
233 |
237 |
238 |
243 |
244 |
245 |
246 |
250 |
251 |
257 |
258 |
264 |
265 |
269 |
270 |
275 |
276 |
277 |
278 |
282 |
283 |
289 |
290 |
297 |
298 |
302 |
303 |
308 |
309 |
310 |
311 |
312 |
313 |
--------------------------------------------------------------------------------
/res/layout/table_row.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
9 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/res/layout/test_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
18 |
19 |
20 |
27 |
28 |
--------------------------------------------------------------------------------
/res/layout/test_list_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
18 |
19 |
--------------------------------------------------------------------------------
/res/layout/test_report.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
17 |
18 |
19 |
24 |
25 |
28 |
29 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/res/values-zh-rCN/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Emmagee
5 | 设置
6 | 退出
7 |
8 | 开始测试
9 | 停止测试
10 |
11 | 采集频率(s)
12 | 发件人:
13 | 密码:
14 | SMTP服务器:
15 | 收件人(多人以空格分割):
16 | 显示浮窗
17 | 保存
18 |
19 | 计算中...
20 | 开启WIFI
21 | 关闭WIFI
22 | 应用/剩余内存:
23 | 应用/总体CPU:
24 | 启动时间:
25 | 总使用率(%)
26 | 应用包名
27 | 应用名称
28 | 应用PID
29 | 机器内存大小(MB)
30 | 机器CPU型号
31 | Android系统版本
32 | 手机型号
33 | 时间
34 | 应用占用内存PSS(MB)
35 | 应用占用内存比(%)
36 | 机器剩余内存(MB)
37 | 应用占用CPU率(%)
38 | CPU总使用率(%)
39 | 流量(KB)
40 | 电量(%)
41 | 电流(mA)
42 | 温度(C)
43 | 电压(V)
44 | 帧率
45 | 统计出错
46 |
47 | 注释:已知部分不支持的机型可在此查阅:https://github.com/NetEase/Emmagee/wiki/Some-devices-are-not-supported
48 | 电流: 小于0是放电大于0是充电
49 | 启动时间: 为空是应用已启动或者未搜集到启动时间
50 | N/A: 不支持或者数据异常
51 |
52 | 该程序无法启动
53 | 请选择需要测试的应用程序
54 | 测试结果文件:
55 | 保存成功
56 | 发件人邮箱:
57 | 收件人邮箱:
58 | 格式不正确
59 | 配置不完整
60 | 设置WIFI失败
61 | 测试结果报表已发送至邮箱:
62 | 测试结果未成功发送至邮箱,结果保存在:
63 |
64 | 确定
65 | 取消
66 | 确定退出程序?
67 | 再点击一次退出应用
68 |
69 | 关于
70 | 邮件配置
71 | 版本
72 | Emmagee是一个简单易上手的Android性能监测工具,
73 | 主要用于监控单个App的CPU,内存,流量,电量,电流,帧率以及整体性能状态,
74 | 同时支持自定义的监控频率以及性能数据的实时显示.
75 | 开发团队:网易(杭州)质量保障部
76 | 项目地址:https://github.com/NetEase/Emmagee
77 |
78 | 栈顶Activity名称
79 | GBK
80 |
81 | Dalvik heap alloc/size(KB)
82 | Native heap alloc/size(KB)
83 |
84 | 统计Heap数据
85 | (需要root)
86 |
87 | 无法获取root权限,请确认手机是否已root
88 | 应用退出后停止监听
89 |
90 | 列表更新成功
91 | 由于Google的限制,Emmagee无法支持7.0及以上版本
92 |
93 | 屏幕保持常亮
94 | WakeLock已开启,屏幕会保持常亮
95 | 测试报告
96 |
97 |
--------------------------------------------------------------------------------
/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | #FFFFFF
5 | #000000
6 | #EFEFF4
7 | #818181
8 | #D2D1D5
9 | #A6A6A6
10 | #0079FF
11 | #FF0000
12 | #00C378
13 | #1D9C00
14 | #CECED2
15 | #FF4500
16 |
--------------------------------------------------------------------------------
/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 16dp
5 | 16dp
6 | 15dp
7 | 30dp
8 | 20dp
9 | 10dp
10 | 10dp
11 | 3dp
12 | 3dp
13 | 16sp
14 | 12sp
15 | 12sp
16 | 20sp
17 | 30sp
18 |
19 | 140dp
20 | 40dp
21 |
22 | 10dp
23 | 10dp
24 |
25 | 1px
26 |
27 | 5px
28 |
29 | 60dp
30 |
31 | 50dp
32 | 6dp
33 | 2dp
34 | 30dp
35 | 30dp
36 | 16sp
37 |
38 |
39 |
--------------------------------------------------------------------------------
/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Emmagee
5 | Settings
6 | Exit
7 |
8 | Start Test
9 | Stop Test
10 |
11 | Collecting Interval(s)
12 | Sender:
13 | Password:
14 | SMTP Server:
15 | Receivers(separated by space):
16 | Show Floating Window
17 | Save
18 |
19 | Calculating...
20 | WIFI Off
21 | WIFI On
22 | App/Available Memory:
23 | App/System CPU:
24 | Start Time:
25 | \u0020Total Usage(%)
26 | Package Name
27 | App Name
28 | App PID
29 | Device Memory Size(MB)
30 | Device CPU Type
31 | Android Version
32 | Device Brand/Model
33 | Timestamp
34 | App Used Memory PSS(MB)
35 | App Used Memory(%)
36 | System Available Memory(MB)
37 | App Used CPU(%)
38 | Total Used CPU(%)
39 | Net Traffic(KB)
40 | Battery(%)
41 | Current(mA)
42 | Temperature(C)
43 | Voltage(V)
44 | FPS
45 | Stat Error
46 |
47 | Note: for unsupported devices refer to: https://github.com/NetEase/Emmagee/wiki/Some-devices-are-not-supported
48 | Current: negative for discharging while positive for charging
49 | Start time: empty means either the app has been started or failed to fetch start time
50 | N/A: not supported or data exception
51 |
52 | The app can not be launched
53 | Please select target app
54 | Test result file:
55 | Saved
56 | Sender\'s email:
57 | Receiver\'s email:
58 | Invalid email format
59 | Incomplete email settings
60 | Failed to set WIFI
61 | Test result has been sent to:
62 | Test result failed to be sent to your mailbox, saved at:
63 |
64 | OK
65 | Cancel
66 | Are you sure to exit?
67 | Click again to exit
68 |
69 | About
70 | Mail Settings
71 | Version
72 | Emmagee is a practical, handy performance test tool for specified
73 | Android App, which can monitor CPU, memory, network traffic, battery current, fps and status.
74 | Additionally, it also provides several cool features such as customizing interval of collecting
75 | data, rendering real-time process status in a floating window, and much more.
76 | Team:QA, Netease(Hangzhou)
77 | Project Site:https://github.com/NetEase/Emmagee
78 |
79 | Top Activity Name
80 | UTF-8
81 | Dalvik heap alloc/size(KB)
82 | Native heap alloc/size(KB)
83 |
84 | Collect Heap
85 | (root is necessary)
86 | Fail to get root permission, please check if this phone is rooted
87 | Stop monitoring when app exited
88 |
89 | Update List Successfully
90 | Don\'t support 7.0 and above while Google has restricted access to shell commands and file system
91 | WakeLock
92 | WakeLock is on,screen will stay awake
93 | Test Reports
94 |
95 |
--------------------------------------------------------------------------------
/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
--------------------------------------------------------------------------------
/src/com/netease/qa/emmagee/activity/AboutActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012-2013 NetEase, Inc. and other contributors
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 | package com.netease.qa.emmagee.activity;
18 |
19 | import com.netease.qa.emmagee.R;
20 |
21 | import android.app.Activity;
22 | import android.content.pm.PackageInfo;
23 | import android.content.pm.PackageManager;
24 | import android.os.Bundle;
25 | import android.util.Log;
26 | import android.view.View;
27 | import android.view.View.OnClickListener;
28 | import android.view.Window;
29 | import android.widget.ImageView;
30 | import android.widget.LinearLayout;
31 | import android.widget.TextView;
32 |
33 | /**
34 | * About Page of Emmagee
35 | *
36 | * @author andrewleo
37 | */
38 | public class AboutActivity extends Activity {
39 |
40 | private static final String LOG_TAG = "Emmagee-"
41 | + AboutActivity.class.getSimpleName();
42 |
43 | private TextView appVersion;
44 |
45 | @Override
46 | public void onCreate(Bundle savedInstanceState) {
47 | Log.i(LOG_TAG, "onCreate");
48 | super.onCreate(savedInstanceState);
49 | requestWindowFeature(Window.FEATURE_NO_TITLE);
50 | setContentView(R.layout.about);
51 |
52 | appVersion = (TextView)findViewById(R.id.app_version);
53 | appVersion.setText(getVersion());
54 |
55 | TextView title = (TextView)findViewById(R.id.nb_title);
56 | title.setText(R.string.about);
57 |
58 | ImageView btnSave = (ImageView) findViewById(R.id.btn_set);
59 | btnSave.setVisibility(ImageView.INVISIBLE);
60 |
61 | LinearLayout layGoBack = (LinearLayout) findViewById(R.id.lay_go_back);
62 |
63 | layGoBack.setOnClickListener(new OnClickListener() {
64 | @Override
65 | public void onClick(View arg0) {
66 | AboutActivity.this.finish();
67 | }
68 | });
69 | }
70 |
71 | /**
72 | * get app version
73 | * @return app version
74 | */
75 | public String getVersion() {
76 | try {
77 | PackageManager manager = this.getPackageManager();
78 | PackageInfo info = manager.getPackageInfo(this.getPackageName(), 0);
79 | String version = info.versionName;
80 | return version;
81 | } catch (Exception e) {
82 | e.printStackTrace();
83 | return "-";
84 | }
85 | }
86 |
87 | @Override
88 | public void finish() {
89 | super.finish();
90 | }
91 |
92 | @Override
93 | protected void onDestroy() {
94 | super.onDestroy();
95 | }
96 |
97 | }
98 |
--------------------------------------------------------------------------------
/src/com/netease/qa/emmagee/activity/MailSettingsActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012-2013 NetEase, Inc. and other contributors
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 | package com.netease.qa.emmagee.activity;
18 |
19 | import java.util.regex.Matcher;
20 | import java.util.regex.Pattern;
21 |
22 | import com.netease.qa.emmagee.R;
23 | import com.netease.qa.emmagee.utils.EncryptData;
24 | import com.netease.qa.emmagee.utils.Settings;
25 |
26 | import android.app.Activity;
27 | import android.content.Intent;
28 | import android.content.SharedPreferences;
29 | import android.content.SharedPreferences.Editor;
30 | import android.os.Bundle;
31 | import android.preference.PreferenceManager;
32 | import android.util.Log;
33 | import android.view.View;
34 | import android.view.View.OnClickListener;
35 | import android.view.Window;
36 | import android.widget.EditText;
37 | import android.widget.LinearLayout;
38 | import android.widget.TextView;
39 | import android.widget.Toast;
40 |
41 | /**
42 | * Mail Setting Page of Emmagee
43 | *
44 | * @author andrewleo
45 | */
46 | public class MailSettingsActivity extends Activity {
47 |
48 | private static final String LOG_TAG = "Emmagee-" + MailSettingsActivity.class.getSimpleName();
49 | private static final String BLANK_STRING = "";
50 |
51 | private EditText edtRecipients;
52 | private EditText edtSender;
53 | private EditText edtPassword;
54 | private EditText edtSmtp;
55 | private String sender;
56 | private String prePassword, curPassword;
57 | private String recipients, smtp;
58 | private String[] receivers;
59 | private TextView title;
60 |
61 | @Override
62 | public void onCreate(Bundle savedInstanceState) {
63 | Log.i(LOG_TAG, "onCreate");
64 | super.onCreate(savedInstanceState);
65 | requestWindowFeature(Window.FEATURE_NO_TITLE);
66 | setContentView(R.layout.mail_settings);
67 |
68 | final EncryptData des = new EncryptData("emmagee");
69 |
70 | edtSender = (EditText) findViewById(R.id.sender);
71 | edtPassword = (EditText) findViewById(R.id.password);
72 | edtRecipients = (EditText) findViewById(R.id.recipients);
73 | edtSmtp = (EditText) findViewById(R.id.smtp);
74 | title = (TextView) findViewById(R.id.nb_title);
75 | LinearLayout layGoBack = (LinearLayout) findViewById(R.id.lay_go_back);
76 | LinearLayout layBtnSet = (LinearLayout) findViewById(R.id.lay_btn_set);
77 |
78 | title.setText(R.string.mail_settings);
79 |
80 | SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
81 | sender = preferences.getString(Settings.KEY_SENDER, BLANK_STRING);
82 | prePassword = preferences.getString(Settings.KEY_PASSWORD, BLANK_STRING);
83 | recipients = preferences.getString(Settings.KEY_RECIPIENTS, BLANK_STRING);
84 | smtp = preferences.getString(Settings.KEY_SMTP, BLANK_STRING);
85 |
86 | edtRecipients.setText(recipients);
87 | edtSender.setText(sender);
88 | edtPassword.setText(prePassword);
89 | edtSmtp.setText(smtp);
90 |
91 | layGoBack.setOnClickListener(new OnClickListener() {
92 | @Override
93 | public void onClick(View arg0) {
94 | MailSettingsActivity.this.finish();
95 | }
96 | });
97 | layBtnSet.setOnClickListener(new OnClickListener() {
98 | @Override
99 | public void onClick(View v) {
100 | sender = edtSender.getText().toString().trim();
101 | if (!BLANK_STRING.equals(sender) && !checkMailFormat(sender)) {
102 | Toast.makeText(MailSettingsActivity.this, getString(R.string.sender_mail_toast) + getString(R.string.format_incorrect_format),
103 | Toast.LENGTH_LONG).show();
104 | return;
105 | }
106 | recipients = edtRecipients.getText().toString().trim();
107 | receivers = recipients.split("\\s+");
108 | for (int i = 0; i < receivers.length; i++) {
109 | if (!BLANK_STRING.equals(receivers[i]) && !checkMailFormat(receivers[i])) {
110 | Toast.makeText(MailSettingsActivity.this,
111 | getString(R.string.receiver_mail_toast) + "[" + receivers[i] + "]" + getString(R.string.format_incorrect_format),
112 | Toast.LENGTH_LONG).show();
113 | return;
114 | }
115 | }
116 | curPassword = edtPassword.getText().toString().trim();
117 | smtp = edtSmtp.getText().toString().trim();
118 | if (checkMailConfig(sender, recipients, smtp, curPassword) == -1) {
119 | Toast.makeText(MailSettingsActivity.this, getString(R.string.info_incomplete_toast), Toast.LENGTH_LONG).show();
120 | return;
121 | }
122 | SharedPreferences preferences = Settings.getDefaultSharedPreferences(getApplicationContext());
123 | Editor editor = preferences.edit();
124 | editor.putString(Settings.KEY_SENDER, sender);
125 |
126 | try {
127 | editor.putString(Settings.KEY_PASSWORD, curPassword.equals(prePassword) ? curPassword : des.encrypt(curPassword));
128 | } catch (Exception e) {
129 | editor.putString(Settings.KEY_PASSWORD, curPassword);
130 | }
131 | editor.putString(Settings.KEY_RECIPIENTS, recipients);
132 | editor.putString(Settings.KEY_SMTP, smtp);
133 | editor.commit();
134 | Toast.makeText(MailSettingsActivity.this, getString(R.string.save_success_toast), Toast.LENGTH_LONG).show();
135 | Intent intent = new Intent();
136 | setResult(Activity.RESULT_FIRST_USER, intent);
137 | MailSettingsActivity.this.finish();
138 | }
139 | });
140 | }
141 |
142 | @Override
143 | public void finish() {
144 | super.finish();
145 | }
146 |
147 | @Override
148 | protected void onDestroy() {
149 | super.onDestroy();
150 | }
151 |
152 | /**
153 | * check if mail configurations are available
154 | *
155 | * @param sender
156 | * @param recipients
157 | * @param smtp
158 | * @param curPassword
159 | * @return true: valid configurations
160 | *
161 | */
162 | private int checkMailConfig(String sender, String recipients, String smtp, String curPassword) {
163 | if (!BLANK_STRING.equals(curPassword) && !BLANK_STRING.equals(sender) && !BLANK_STRING.equals(recipients) && !BLANK_STRING.equals(smtp)) {
164 | return 1;
165 | } else if (BLANK_STRING.equals(curPassword) && BLANK_STRING.equals(sender) && BLANK_STRING.equals(recipients) && BLANK_STRING.equals(smtp)) {
166 | return 0;
167 | } else
168 | return -1;
169 | }
170 |
171 | /**
172 | * check mail format
173 | *
174 | * @return true: valid email address
175 | */
176 | private boolean checkMailFormat(String mail) {
177 | 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]$";
178 | Pattern p = Pattern.compile(strPattern);
179 | Matcher m = p.matcher(mail);
180 | return m.matches();
181 | }
182 | }
183 |
--------------------------------------------------------------------------------
/src/com/netease/qa/emmagee/activity/MainPageActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012-2013 NetEase, Inc. and other contributors
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 | package com.netease.qa.emmagee.activity;
18 |
19 | import java.io.IOException;
20 | import java.util.List;
21 |
22 | import com.netease.qa.emmagee.R;
23 | import com.netease.qa.emmagee.service.EmmageeService;
24 | import com.netease.qa.emmagee.utils.ProcessInfo;
25 | import com.netease.qa.emmagee.utils.Programe;
26 | import com.netease.qa.emmagee.utils.Settings;
27 | import com.netease.qa.emmagee.utils.WakeLockHelper;
28 |
29 | import android.app.Activity;
30 | import android.content.BroadcastReceiver;
31 | import android.content.Context;
32 | import android.content.Intent;
33 | import android.content.IntentFilter;
34 | import android.content.SharedPreferences;
35 | import android.os.Build;
36 | import android.os.Bundle;
37 | import android.util.Log;
38 | import android.view.KeyEvent;
39 | import android.view.View;
40 | import android.view.View.OnClickListener;
41 | import android.view.ViewGroup;
42 | import android.view.Window;
43 | import android.widget.AdapterView;
44 | import android.widget.BaseAdapter;
45 | import android.widget.Button;
46 | import android.widget.CompoundButton;
47 | import android.widget.CompoundButton.OnCheckedChangeListener;
48 | import android.widget.ImageView;
49 | import android.widget.LinearLayout;
50 | import android.widget.ListView;
51 | import android.widget.RadioButton;
52 | import android.widget.TextView;
53 | import android.widget.Toast;
54 |
55 | /**
56 | * Main Page of Emmagee
57 | *
58 | * @author andrewleo
59 | */
60 | public class MainPageActivity extends Activity {
61 |
62 | private static final String LOG_TAG = "Emmagee-" + MainPageActivity.class.getSimpleName();
63 |
64 | private static final int TIMEOUT = 20000;
65 |
66 | private ProcessInfo processInfo;
67 | private Intent monitorService;
68 | private ListView lstViProgramme;
69 | private Button btnTest;
70 | private int pid, uid;
71 | private boolean isServiceStop = false;
72 | private UpdateReceiver receiver;
73 |
74 | private TextView nbTitle;
75 | private ImageView ivGoBack;
76 | private ImageView ivBtnSet;
77 | private LinearLayout layBtnSet;
78 | private Long mExitTime = (long) 0;
79 | private ListAdapter la;
80 |
81 | @Override
82 | public void onCreate(Bundle savedInstanceState) {
83 | Log.i(LOG_TAG, "MainActivity::onCreate");
84 | super.onCreate(savedInstanceState);
85 | requestWindowFeature(Window.FEATURE_NO_TITLE);
86 | setContentView(R.layout.mainpage);
87 |
88 | initTitleLayout();
89 | loadSettings();
90 | processInfo = new ProcessInfo();
91 | btnTest.setOnClickListener(new OnClickListener() {
92 | @Override
93 | public void onClick(View v) {
94 | if (Build.VERSION.SDK_INT < 24) {
95 | monitorService = new Intent();
96 | monitorService.setClass(MainPageActivity.this, EmmageeService.class);
97 | if (getString(R.string.start_test).equals(btnTest.getText().toString())) {
98 | ListAdapter adapter = (ListAdapter) lstViProgramme.getAdapter();
99 | if (adapter.checkedProg != null) {
100 | String packageName = adapter.checkedProg.getPackageName();
101 | String processName = adapter.checkedProg.getProcessName();
102 | Intent intent = getPackageManager().getLaunchIntentForPackage(packageName);
103 | String startActivity = "";
104 | Log.d(LOG_TAG, packageName);
105 | // clear logcat
106 | try {
107 | Runtime.getRuntime().exec("logcat -c");
108 | } catch (IOException e) {
109 | Log.d(LOG_TAG, e.getMessage());
110 | }
111 | try {
112 | startActivity = intent.resolveActivity(getPackageManager()).getShortClassName();
113 | startActivity(intent);
114 | } catch (Exception e) {
115 | Toast.makeText(MainPageActivity.this, getString(R.string.can_not_start_app_toast), Toast.LENGTH_LONG).show();
116 | return;
117 | }
118 | waitForAppStart(packageName);
119 | monitorService.putExtra("processName", processName);
120 | monitorService.putExtra("pid", pid);
121 | monitorService.putExtra("uid", uid);
122 | monitorService.putExtra("packageName", packageName);
123 | monitorService.putExtra("startActivity", startActivity);
124 | startService(monitorService);
125 | isServiceStop = false;
126 | btnTest.setText(getString(R.string.stop_test));
127 | } else {
128 | Toast.makeText(MainPageActivity.this, getString(R.string.choose_app_toast), Toast.LENGTH_LONG).show();
129 | }
130 | } else {
131 | btnTest.setText(getString(R.string.start_test));
132 | Toast.makeText(MainPageActivity.this, getString(R.string.test_result_file_toast) + EmmageeService.resultFilePath,
133 | Toast.LENGTH_LONG).show();
134 | stopService(monitorService);
135 | }
136 | } else {
137 | Toast.makeText(MainPageActivity.this, getString(R.string.nougat_warning),Toast.LENGTH_LONG).show();
138 | }
139 | }
140 | });
141 |
142 | la = new ListAdapter(processInfo.getAllPackages(getBaseContext()));
143 | lstViProgramme.setAdapter(la);
144 | lstViProgramme.setOnItemClickListener(new AdapterView.OnItemClickListener() {
145 | @Override
146 | public void onItemClick(AdapterView> adapterView, View view, int i, long l) {
147 | RadioButton rdBtn = (RadioButton) ((LinearLayout) view).getChildAt(0);
148 | rdBtn.setChecked(true);
149 | }
150 | });
151 |
152 | nbTitle.setText(getString(R.string.app_name));
153 | ivGoBack.setImageResource(R.drawable.refresh);
154 | ivBtnSet.setImageResource(R.drawable.settings_button);
155 | layBtnSet.setOnClickListener(new OnClickListener() {
156 | @Override
157 | public void onClick(View view) {
158 | goToSettingsActivity();
159 | }
160 | });
161 |
162 | ivGoBack.setOnClickListener(new OnClickListener() {
163 | @Override
164 | public void onClick(View arg0) {
165 | Toast.makeText(MainPageActivity.this, R.string.update_list, Toast.LENGTH_SHORT).show();
166 | la.swapItems(processInfo.getAllPackages(getBaseContext()));
167 | }
168 | });
169 |
170 | receiver = new UpdateReceiver();
171 | IntentFilter filter = new IntentFilter();
172 | filter.addAction(EmmageeService.SERVICE_ACTION);
173 | registerReceiver(receiver, filter);
174 | }
175 |
176 | private void initTitleLayout() {
177 | ivGoBack = (ImageView) findViewById(R.id.go_back);
178 | nbTitle = (TextView) findViewById(R.id.nb_title);
179 | ivBtnSet = (ImageView) findViewById(R.id.btn_set);
180 | lstViProgramme = (ListView) findViewById(R.id.processList);
181 | btnTest = (Button) findViewById(R.id.test);
182 | layBtnSet = (LinearLayout) findViewById(R.id.lay_btn_set);
183 | }
184 |
185 | private void loadSettings() {
186 | SharedPreferences preferences = Settings.getDefaultSharedPreferences(this);
187 | boolean wakeLock = preferences.getBoolean(Settings.KEY_WACK_LOCK, false);
188 | if (wakeLock) {
189 | Toast.makeText(this, R.string.wake_lock_on_toast, Toast.LENGTH_LONG).show();
190 | Settings.getDefaultWakeLock(this).acquireFullWakeLock();
191 | }
192 | }
193 |
194 | /**
195 | * customized BroadcastReceiver
196 | *
197 | * @author andrewleo
198 | */
199 | public class UpdateReceiver extends BroadcastReceiver {
200 |
201 | @Override
202 | public void onReceive(Context context, Intent intent) {
203 | isServiceStop = intent.getExtras().getBoolean("isServiceStop");
204 | if (isServiceStop) {
205 | btnTest.setText(getString(R.string.start_test));
206 | }
207 | }
208 | }
209 |
210 | @Override
211 | protected void onStart() {
212 | Log.d(LOG_TAG, "onStart");
213 | super.onStart();
214 | }
215 |
216 | @Override
217 | public void onResume() {
218 | super.onResume();
219 | Log.d(LOG_TAG, "onResume");
220 | if (isServiceStop) {
221 | btnTest.setText(getString(R.string.start_test));
222 | }
223 | }
224 |
225 | /**
226 | * wait for test application started.
227 | *
228 | * @param packageName
229 | * package name of test application
230 | */
231 | private void waitForAppStart(String packageName) {
232 | Log.d(LOG_TAG, "wait for app start");
233 | boolean isProcessStarted = false;
234 | long startTime = System.currentTimeMillis();
235 | while (System.currentTimeMillis() < startTime + TIMEOUT) {
236 | pid = processInfo.getPidByPackageName(getBaseContext(), packageName);
237 | if (pid != 0) {
238 | isProcessStarted = true;
239 | break;
240 | }
241 | if (isProcessStarted) {
242 | break;
243 | }
244 | }
245 | }
246 |
247 | /**
248 | * show a dialog when click return key.
249 | *
250 | * @return Return true to prevent this event from being propagated further,
251 | * or false to indicate that you have not handled this event and it
252 | * should continue to be propagated.
253 | */
254 | public boolean onKeyDown(int keyCode, KeyEvent event) {
255 | if (keyCode == KeyEvent.KEYCODE_BACK) {
256 | if ((System.currentTimeMillis() - mExitTime) > 2000) {
257 | Toast.makeText(this, R.string.quite_alert, Toast.LENGTH_SHORT).show();
258 | mExitTime = System.currentTimeMillis();
259 | } else {
260 | if (monitorService != null) {
261 | Log.d(LOG_TAG, "stop service");
262 | stopService(monitorService);
263 | }
264 | System.exit(0);
265 | }
266 | return true;
267 | }
268 | return super.onKeyDown(keyCode, event);
269 | }
270 |
271 | private void goToSettingsActivity() {
272 | Intent intent = new Intent();
273 | intent.setClass(MainPageActivity.this, SettingsActivity.class);
274 | startActivityForResult(intent, Activity.RESULT_FIRST_USER);
275 | }
276 |
277 | /**
278 | * customizing adapter.
279 | *
280 | * @author andrewleo
281 | */
282 | private class ListAdapter extends BaseAdapter {
283 | List programes;
284 | Programe checkedProg;
285 | int lastCheckedPosition = -1;
286 |
287 | public ListAdapter(List programes) {
288 | this.programes = programes;
289 | }
290 |
291 | @Override
292 | public int getCount() {
293 | return programes.size();
294 | }
295 |
296 | @Override
297 | public Object getItem(int position) {
298 | return programes.get(position);
299 | }
300 |
301 | @Override
302 | public long getItemId(int position) {
303 | return position;
304 | }
305 |
306 | public void swapItems(List programes) {
307 | this.programes = programes;
308 | notifyDataSetChanged();
309 | }
310 |
311 | @Override
312 | public View getView(int position, View convertView, ViewGroup parent) {
313 | Programe pr = (Programe) programes.get(position);
314 | if (convertView == null)
315 | convertView = getLayoutInflater().inflate(R.layout.list_item, parent, false);
316 | Viewholder holder = (Viewholder) convertView.getTag();
317 | if (holder == null) {
318 | holder = new Viewholder();
319 | convertView.setTag(holder);
320 | holder.imgViAppIcon = (ImageView) convertView.findViewById(R.id.image);
321 | holder.txtAppName = (TextView) convertView.findViewById(R.id.text);
322 | holder.rdoBtnApp = (RadioButton) convertView.findViewById(R.id.rb);
323 | holder.rdoBtnApp.setFocusable(false);
324 | holder.rdoBtnApp.setOnCheckedChangeListener(checkedChangeListener);
325 | }
326 | holder.imgViAppIcon.setImageDrawable(pr.getIcon());
327 | holder.txtAppName.setText(pr.getProcessName());
328 | holder.rdoBtnApp.setId(position);
329 | holder.rdoBtnApp.setChecked(checkedProg != null && getItem(position) == checkedProg);
330 | return convertView;
331 | }
332 |
333 | OnCheckedChangeListener checkedChangeListener = new OnCheckedChangeListener() {
334 | @Override
335 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
336 | if (isChecked) {
337 | final int checkedPosition = buttonView.getId();
338 | if (lastCheckedPosition != -1) {
339 | RadioButton tempButton = (RadioButton) findViewById(lastCheckedPosition);
340 | if ((tempButton != null) && (lastCheckedPosition != checkedPosition)) {
341 | tempButton.setChecked(false);
342 | }
343 | }
344 | checkedProg = programes.get(checkedPosition);
345 | lastCheckedPosition = checkedPosition;
346 | }
347 | }
348 | };
349 | }
350 |
351 | /**
352 | * save status of all installed processes
353 | *
354 | * @author andrewleo
355 | */
356 | static class Viewholder {
357 | TextView txtAppName;
358 | ImageView imgViAppIcon;
359 | RadioButton rdoBtnApp;
360 | }
361 |
362 | @Override
363 | protected void onDestroy() {
364 | unregisterReceiver(receiver);
365 | super.onDestroy();
366 | }
367 | }
368 |
--------------------------------------------------------------------------------
/src/com/netease/qa/emmagee/activity/SettingsActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012-2013 NetEase, Inc. and other contributors
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 | package com.netease.qa.emmagee.activity;
18 |
19 | import java.io.DataOutputStream;
20 |
21 | import com.netease.qa.emmagee.R;
22 | import com.netease.qa.emmagee.utils.Settings;
23 | import com.netease.qa.emmagee.utils.WakeLockHelper;
24 |
25 | import android.app.Activity;
26 | import android.content.Intent;
27 | import android.content.SharedPreferences;
28 | import android.os.Bundle;
29 | import android.util.Log;
30 | import android.view.View;
31 | import android.view.View.OnClickListener;
32 | import android.view.Window;
33 | import android.widget.CheckBox;
34 | import android.widget.ImageView;
35 | import android.widget.LinearLayout;
36 | import android.widget.RelativeLayout;
37 | import android.widget.SeekBar;
38 | import android.widget.SeekBar.OnSeekBarChangeListener;
39 | import android.widget.TextView;
40 | import android.widget.Toast;
41 |
42 | /**
43 | * Setting Page of Emmagee
44 | *
45 | * @author andrewleo
46 | */
47 | public class SettingsActivity extends Activity {
48 |
49 | private static final String LOG_TAG = "Emmagee-" + SettingsActivity.class.getSimpleName();
50 |
51 | private CheckBox chkFloat;
52 | private CheckBox chkRoot;
53 | private CheckBox chkAutoStop;
54 | private CheckBox chkWakeLock;
55 | private TextView tvTime;
56 | private LinearLayout about;
57 | private LinearLayout mailSettings;
58 | private LinearLayout testReport;
59 |
60 | private SharedPreferences preferences;
61 | private WakeLockHelper wakeLockHelper;
62 |
63 | @Override
64 | public void onCreate(Bundle savedInstanceState) {
65 | Log.i(LOG_TAG, "onCreate");
66 | super.onCreate(savedInstanceState);
67 | requestWindowFeature(Window.FEATURE_NO_TITLE);
68 | setContentView(R.layout.settings);
69 |
70 | wakeLockHelper = Settings.getDefaultWakeLock(this);
71 |
72 | // init views
73 | chkFloat = (CheckBox) findViewById(R.id.floating);
74 | chkRoot = (CheckBox) findViewById(R.id.is_root);
75 | chkAutoStop = (CheckBox) findViewById(R.id.auto_stop);
76 | chkWakeLock = (CheckBox) findViewById(R.id.wake_lock);
77 | tvTime = (TextView) findViewById(R.id.time);
78 | testReport = (LinearLayout) findViewById(R.id.test_report);
79 | about = (LinearLayout) findViewById(R.id.about);
80 | mailSettings = (LinearLayout) findViewById(R.id.mail_settings);
81 | SeekBar timeBar = (SeekBar) findViewById(R.id.timeline);
82 | ImageView btnSave = (ImageView) findViewById(R.id.btn_set);
83 | RelativeLayout floatingItem = (RelativeLayout) findViewById(R.id.floating_item);
84 | RelativeLayout autoStopItem = (RelativeLayout) findViewById(R.id.auto_stop_item);
85 | RelativeLayout wakeLockItem = (RelativeLayout) findViewById(R.id.wake_lock_item);
86 | LinearLayout layGoBack = (LinearLayout) findViewById(R.id.lay_go_back);
87 | LinearLayout layHeapItem = (LinearLayout) findViewById(R.id.heap_item);
88 |
89 | btnSave.setVisibility(ImageView.INVISIBLE);
90 |
91 | preferences = Settings.getDefaultSharedPreferences(getApplicationContext());
92 | int interval = preferences.getInt(Settings.KEY_INTERVAL, 5);
93 | boolean isfloat = preferences.getBoolean(Settings.KEY_ISFLOAT, true);
94 | boolean isRoot = preferences.getBoolean(Settings.KEY_ROOT, false);
95 | boolean autoStop = preferences.getBoolean(Settings.KEY_AUTO_STOP, true);
96 | boolean wakeLock = preferences.getBoolean(Settings.KEY_WACK_LOCK, false);
97 |
98 | tvTime.setText(String.valueOf(interval));
99 | chkFloat.setChecked(isfloat);
100 | chkRoot.setChecked(isRoot);
101 | chkAutoStop.setChecked(autoStop);
102 | chkWakeLock.setChecked(wakeLock);
103 |
104 | // start activity listener
105 | layGoBack.setOnClickListener(startActivityListener(MainPageActivity.class));
106 | testReport.setOnClickListener(startActivityListener(TestListActivity.class));
107 | mailSettings.setOnClickListener(startActivityListener(MailSettingsActivity.class));
108 | about.setOnClickListener(startActivityListener(AboutActivity.class));
109 |
110 | timeBar.setProgress(interval);
111 | timeBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
112 | @Override
113 | public void onProgressChanged(SeekBar arg0, int arg1, boolean arg2) {
114 | tvTime.setText(Integer.toString(arg1 + 1));
115 | }
116 |
117 | @Override
118 | public void onStartTrackingTouch(SeekBar arg0) {
119 | }
120 |
121 | @Override
122 | public void onStopTrackingTouch(SeekBar arg0) {
123 | // when tracking stoped, update preferences
124 | int interval = arg0.getProgress() + 1;
125 | preferences.edit().putInt(Settings.KEY_INTERVAL, interval).commit();
126 | }
127 | });
128 |
129 | floatingItem.setOnClickListener(new OnClickListener() {
130 | @Override
131 | public void onClick(View arg0) {
132 | boolean isChecked = chkFloat.isChecked();
133 | chkFloat.setChecked(!isChecked);
134 | preferences.edit().putBoolean(Settings.KEY_ISFLOAT, !isChecked).commit();
135 | }
136 | });
137 |
138 | autoStopItem.setOnClickListener(new OnClickListener() {
139 | @Override
140 | public void onClick(View arg0) {
141 | boolean isChecked = chkAutoStop.isChecked();
142 | chkAutoStop.setChecked(!isChecked);
143 | preferences.edit().putBoolean(Settings.KEY_AUTO_STOP, !isChecked).commit();
144 | }
145 | });
146 |
147 | wakeLockItem.setOnClickListener(new OnClickListener() {
148 | @Override
149 | public void onClick(View arg0) {
150 | boolean isChecked = chkWakeLock.isChecked();
151 | chkWakeLock.setChecked(!isChecked);
152 | preferences.edit().putBoolean(Settings.KEY_WACK_LOCK, !isChecked).commit();
153 | if (chkWakeLock.isChecked()) {
154 | wakeLockHelper.acquireFullWakeLock();
155 | } else {
156 | wakeLockHelper.releaseWakeLock();
157 | }
158 | }
159 | });
160 |
161 | // get root permission
162 | layHeapItem.setOnClickListener(new OnClickListener() {
163 | @Override
164 | public void onClick(View arg0) {
165 | // if root checkbox is checked, change status to
166 | // opposite;otherwise, try to upgrade app to root
167 | boolean isChecked = chkRoot.isChecked();
168 | if (isChecked) {
169 | chkRoot.setChecked(!isChecked);
170 | preferences.edit().putBoolean(Settings.KEY_ROOT, !isChecked).commit();
171 | } else {
172 | boolean root = upgradeRootPermission(getPackageCodePath());
173 | if (root) {
174 | Log.d(LOG_TAG, "root succeed");
175 | chkRoot.setChecked(!isChecked);
176 | preferences.edit().putBoolean(Settings.KEY_ROOT, !isChecked).commit();
177 | } else {
178 | // if root failed, tell user to check if phone is rooted
179 | Toast.makeText(getBaseContext(), getString(R.string.root_failed_notification), Toast.LENGTH_LONG).show();
180 | }
181 | }
182 |
183 | }
184 | });
185 | }
186 |
187 | private OnClickListener startActivityListener(final Class> specClass) {
188 | return new OnClickListener() {
189 | @Override
190 | public void onClick(View arg0) {
191 | Intent intent = new Intent();
192 | intent.setClass(SettingsActivity.this, specClass);
193 | startActivityForResult(intent, Activity.RESULT_FIRST_USER);
194 | }
195 | };
196 | }
197 |
198 | @Override
199 | protected void onDestroy() {
200 | super.onDestroy();
201 | }
202 |
203 | /**
204 | * upgrade app to get root permission
205 | *
206 | * @return is root successfully
207 | */
208 | public static boolean upgradeRootPermission(String pkgCodePath) {
209 | Process process = null;
210 | DataOutputStream os = null;
211 | try {
212 | String cmd = "chmod 777 " + pkgCodePath;
213 | process = Runtime.getRuntime().exec("su"); // 切换到root帐号
214 | os = new DataOutputStream(process.getOutputStream());
215 | os.writeBytes(cmd + "\n");
216 | os.writeBytes("exit\n");
217 | os.flush();
218 | int existValue = process.waitFor();
219 | if (existValue == 0) {
220 | return true;
221 | } else {
222 | return false;
223 | }
224 | } catch (Exception e) {
225 | Log.w(LOG_TAG, "upgradeRootPermission exception=" + e.getMessage());
226 | return false;
227 | } finally {
228 | try {
229 | if (os != null) {
230 | os.close();
231 | }
232 | process.destroy();
233 | } catch (Exception e) {
234 | }
235 | }
236 | }
237 | }
238 |
--------------------------------------------------------------------------------
/src/com/netease/qa/emmagee/activity/TestListActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012-2013 NetEase, Inc. and other contributors
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 | package com.netease.qa.emmagee.activity;
18 |
19 | import java.io.File;
20 | import java.util.ArrayList;
21 | import java.util.Arrays;
22 | import java.util.Collections;
23 | import java.util.List;
24 |
25 | import com.netease.qa.emmagee.R;
26 | import com.netease.qa.emmagee.utils.Settings;
27 |
28 | import android.app.Activity;
29 | import android.content.Intent;
30 | import android.os.Bundle;
31 | import android.util.Log;
32 | import android.view.View;
33 | import android.view.View.OnClickListener;
34 | import android.view.ViewGroup;
35 | import android.view.Window;
36 | import android.widget.AdapterView;
37 | import android.widget.BaseAdapter;
38 | import android.widget.ImageView;
39 | import android.widget.LinearLayout;
40 | import android.widget.ListView;
41 | import android.widget.TextView;
42 |
43 | /**
44 | * Test Report
45 | *
46 | * @author andrewleo
47 | */
48 | public class TestListActivity extends Activity {
49 |
50 | private static final String LOG_TAG = "Emmagee-"
51 | + TestListActivity.class.getSimpleName();
52 | static final String CSV_PATH_KEY = "csvPath";
53 |
54 | private ListAdapter la;
55 | private ListView lstViReport;
56 |
57 | @Override
58 | public void onCreate(Bundle savedInstanceState) {
59 | Log.i(LOG_TAG, "onCreate");
60 | super.onCreate(savedInstanceState);
61 | requestWindowFeature(Window.FEATURE_NO_TITLE);
62 | setContentView(R.layout.test_list);
63 |
64 | TextView title = (TextView)findViewById(R.id.nb_title);
65 | lstViReport = (ListView)findViewById(R.id.test_list);
66 | ImageView btnSave = (ImageView) findViewById(R.id.btn_set);
67 |
68 | btnSave.setVisibility(ImageView.INVISIBLE);
69 | title.setText(R.string.test_report);
70 |
71 | LinearLayout layGoBack = (LinearLayout) findViewById(R.id.lay_go_back);
72 |
73 | layGoBack.setOnClickListener(new OnClickListener() {
74 | @Override
75 | public void onClick(View arg0) {
76 | TestListActivity.this.finish();
77 | }
78 | });
79 | la = new ListAdapter(listReports());
80 | lstViReport.setAdapter(la);
81 | lstViReport.setOnItemClickListener(new AdapterView.OnItemClickListener() {
82 | @Override
83 | public void onItemClick(AdapterView> adapterView, View view, int i, long l) {
84 | Intent intent = new Intent();
85 | intent.setClass(TestListActivity.this, TestReportActivity.class);
86 | intent.putExtra(CSV_PATH_KEY, la.getCSVPath(i));
87 | startActivity(intent);
88 | }
89 | });
90 | }
91 |
92 | /**
93 | * customizing adapter.
94 | *
95 | * @author andrewleo
96 | */
97 | private class ListAdapter extends BaseAdapter {
98 | List reports;
99 |
100 | public ListAdapter(List reports) {
101 | this.reports = reports;
102 | }
103 |
104 | @Override
105 | public int getCount() {
106 | return reports.size();
107 | }
108 |
109 | @Override
110 | public Object getItem(int position) {
111 | return reports.get(position);
112 | }
113 |
114 | @Override
115 | public long getItemId(int position) {
116 | return position;
117 | }
118 |
119 | public String getCSVPath(int position) {
120 | return Settings.EMMAGEE_RESULT_DIR + getItem(position) + ".csv";
121 | }
122 |
123 | @Override
124 | public View getView(int position, View convertView, ViewGroup parent) {
125 | String pr = (String) reports.get(position);
126 | if (convertView == null)
127 | convertView = getLayoutInflater().inflate(R.layout.test_list_item, parent, false);
128 | Viewholder holder = (Viewholder) convertView.getTag();
129 | if (holder == null) {
130 | holder = new Viewholder();
131 | convertView.setTag(holder);
132 | holder.name = (TextView) convertView.findViewById(R.id.package_name);
133 | }
134 | holder.name.setText(pr);
135 | return convertView;
136 | }
137 |
138 | }
139 |
140 | private static class Viewholder {
141 | TextView name;
142 | }
143 |
144 | /**
145 | * list all test report
146 | */
147 | private ArrayList listReports() {
148 | ArrayList reportList = new ArrayList();
149 | File reportDir = new File(Settings.EMMAGEE_RESULT_DIR);
150 | if (reportDir.isDirectory()) {
151 | File files[] = reportDir.listFiles();
152 | Arrays.sort(files, Collections.reverseOrder());
153 | for (File file: files) {
154 | if (isLegalReport(file)) {
155 | String baseName = file.getName().substring(0, file.getName().lastIndexOf("."));
156 | reportList.add(baseName);
157 | }
158 | }
159 | }
160 | return reportList;
161 | }
162 |
163 | private boolean isLegalReport(File file) {
164 | return !file.isDirectory() && file.getName().endsWith(".csv");
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/src/com/netease/qa/emmagee/activity/TestReportActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012-2013 NetEase, Inc. and other contributors
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 | package com.netease.qa.emmagee.activity;
18 |
19 | import java.io.File;
20 | import java.io.IOException;
21 |
22 | import org.apache.commons.io.FileUtils;
23 |
24 | import com.netease.qa.emmagee.R;
25 |
26 | import android.app.Activity;
27 | import android.content.Intent;
28 | import android.graphics.Color;
29 | import android.net.Uri;
30 | import android.os.Bundle;
31 | import android.util.Log;
32 | import android.view.Gravity;
33 | import android.view.View;
34 | import android.view.View.OnClickListener;
35 | import android.view.Window;
36 | import android.widget.ImageView;
37 | import android.widget.LinearLayout;
38 | import android.widget.TableLayout;
39 | import android.widget.TableRow;
40 | import android.widget.TextView;
41 |
42 | /**
43 | * About Page of Emmagee
44 | *
45 | * @author andrewleo
46 | */
47 | public class TestReportActivity extends Activity {
48 |
49 | private static final String LOG_TAG = "Emmagee-" + TestReportActivity.class.getSimpleName();
50 | private TableLayout tl;
51 |
52 | @Override
53 | public void onCreate(Bundle savedInstanceState) {
54 | Log.i(LOG_TAG, "onCreate");
55 | super.onCreate(savedInstanceState);
56 | requestWindowFeature(Window.FEATURE_NO_TITLE);
57 | setContentView(R.layout.test_report);
58 |
59 | TextView title = (TextView) findViewById(R.id.nb_title);
60 | ImageView btnSave = (ImageView) findViewById(R.id.btn_set);
61 | tl = (TableLayout) findViewById(R.id.table_layout);
62 |
63 | btnSave.setVisibility(ImageView.INVISIBLE);
64 | title.setText(R.string.test_report);
65 |
66 | LinearLayout layGoBack = (LinearLayout) findViewById(R.id.lay_go_back);
67 |
68 | layGoBack.setOnClickListener(new OnClickListener() {
69 | @Override
70 | public void onClick(View arg0) {
71 | TestReportActivity.this.finish();
72 | }
73 | });
74 |
75 | Intent intent = getIntent();
76 | String csvPath = intent.getStringExtra(TestListActivity.CSV_PATH_KEY);
77 |
78 | try {
79 | String content = FileUtils.readFileToString(new File(csvPath), "gbk");
80 | String[] lines = content.split("\r\n");
81 | int index = 0;
82 | for (String line: lines) {
83 | addTableRow(line, index);
84 | index++;
85 | }
86 | } catch (IOException e) {
87 | e.printStackTrace();
88 | }
89 |
90 | }
91 |
92 | private void addTableRow(String line, int index) {
93 | TableRow row = new TableRow(this);
94 | TableRow.LayoutParams lp = new TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT);
95 | row.setLayoutParams(lp);
96 | String[] items = line.split(",");
97 | int i = 0;
98 | for (String item: items) {
99 | TextView tv = new TextView(this);
100 | tv.setTextColor(Color.BLACK);
101 | tv.setTextSize(18);
102 | tv.setText(item);
103 | tv.setBackgroundResource(R.drawable.table_border);
104 | if (i != 0) {
105 | tv.setGravity(Gravity.RIGHT);
106 | }
107 | row.addView(tv, i);
108 | i++;
109 | }
110 | tl.addView(row, index);
111 | }
112 |
113 | }
114 |
--------------------------------------------------------------------------------
/src/com/netease/qa/emmagee/service/EmmageeService.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012-2013 NetEase, Inc. and other contributors
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 | package com.netease.qa.emmagee.service;
18 |
19 | import java.io.BufferedReader;
20 | import java.io.BufferedWriter;
21 | import java.io.File;
22 | import java.io.FileNotFoundException;
23 | import java.io.FileOutputStream;
24 | import java.io.FileReader;
25 | import java.io.IOException;
26 | import java.io.InputStreamReader;
27 | import java.io.OutputStreamWriter;
28 | import java.text.DecimalFormat;
29 | import java.text.DecimalFormatSymbols;
30 | import java.text.SimpleDateFormat;
31 | import java.util.ArrayList;
32 | import java.util.Calendar;
33 | import java.util.Locale;
34 |
35 | import com.netease.qa.emmagee.R;
36 | import com.netease.qa.emmagee.activity.MainPageActivity;
37 | import com.netease.qa.emmagee.utils.Constants;
38 | import com.netease.qa.emmagee.utils.CpuInfo;
39 | import com.netease.qa.emmagee.utils.CurrentInfo;
40 | import com.netease.qa.emmagee.utils.EncryptData;
41 | import com.netease.qa.emmagee.utils.FpsInfo;
42 | import com.netease.qa.emmagee.utils.MailSender;
43 | import com.netease.qa.emmagee.utils.MemoryInfo;
44 | import com.netease.qa.emmagee.utils.MyApplication;
45 | import com.netease.qa.emmagee.utils.ProcessInfo;
46 | import com.netease.qa.emmagee.utils.Programe;
47 | import com.netease.qa.emmagee.utils.Settings;
48 |
49 | import android.app.Activity;
50 | import android.app.PendingIntent;
51 | import android.app.Service;
52 | import android.content.BroadcastReceiver;
53 | import android.content.Context;
54 | import android.content.Intent;
55 | import android.content.IntentFilter;
56 | import android.content.SharedPreferences;
57 | import android.content.pm.ApplicationInfo;
58 | import android.content.pm.PackageManager;
59 | import android.net.wifi.WifiManager;
60 | import android.os.BatteryManager;
61 | import android.os.Build;
62 | import android.os.Handler;
63 | import android.os.IBinder;
64 | import android.support.v4.app.NotificationCompat;
65 | import android.util.Log;
66 | import android.view.Gravity;
67 | import android.view.LayoutInflater;
68 | import android.view.MotionEvent;
69 | import android.view.View;
70 | import android.view.View.OnClickListener;
71 | import android.view.View.OnTouchListener;
72 | import android.view.WindowManager;
73 | import android.widget.Button;
74 | import android.widget.TextView;
75 | import android.widget.Toast;
76 |
77 | /**
78 | * Service running in background
79 | *
80 | * @author andrewleo
81 | */
82 | public class EmmageeService extends Service {
83 |
84 | private final static String LOG_TAG = "Emmagee-"
85 | + EmmageeService.class.getSimpleName();
86 |
87 | private static final String BLANK_STRING = "";
88 |
89 | private WindowManager windowManager = null;
90 | private WindowManager.LayoutParams wmParams = null;
91 | private View viFloatingWindow;
92 | private float mTouchStartX;
93 | private float mTouchStartY;
94 | private float x;
95 | private float y;
96 | private TextView txtTotalMem;
97 | private TextView txtUnusedMem;
98 | private TextView txtTraffic;
99 | private Button btnStop;
100 | private Button btnWifi;
101 | private int delaytime;
102 | private DecimalFormat fomart;
103 | private MemoryInfo memoryInfo;
104 | private WifiManager wifiManager;
105 | private Handler handler = new Handler();
106 | private CpuInfo cpuInfo;
107 | private boolean isFloating;
108 | private boolean isRoot;
109 | private boolean isAutoStop = false;
110 | private String processName, packageName, startActivity;
111 | private int pid, uid;
112 | private boolean isServiceStop = false;
113 | private String sender, password, recipients, smtp;
114 | private String[] receivers;
115 | private EncryptData des;
116 | private ProcessInfo procInfo;
117 | private int statusBarHeight;
118 |
119 | public static BufferedWriter bw;
120 | public static FileOutputStream out;
121 | public static OutputStreamWriter osw;
122 | public static String resultFilePath;
123 | public static boolean isStop = false;
124 |
125 | private String totalBatt;
126 | private String temperature;
127 | private String voltage;
128 | private CurrentInfo currentInfo;
129 | private FpsInfo fpsInfo;
130 | private BatteryInfoBroadcastReceiver batteryBroadcast = null;
131 |
132 | // get start time
133 | private static final int MAX_START_TIME_COUNT = 5;
134 | private static final String START_TIME = "#startTime";
135 | private int getStartTimeCount = 0;
136 | private boolean isGetStartTime = true;
137 | private String startTime = "";
138 | public static final String SERVICE_ACTION = "com.netease.action.emmageeService";
139 | private static final String BATTERY_CHANGED = "android.intent.action.BATTERY_CHANGED";
140 |
141 | @Override
142 | public void onCreate() {
143 | Log.i(LOG_TAG, "service onCreate");
144 | super.onCreate();
145 | isServiceStop = false;
146 | isStop = false;
147 | fpsInfo = new FpsInfo();
148 | memoryInfo = new MemoryInfo();
149 | procInfo = new ProcessInfo();
150 | fomart = new DecimalFormat();
151 | fomart.setDecimalFormatSymbols(new DecimalFormatSymbols(Locale.US));
152 | fomart.setGroupingUsed(false);
153 | fomart.setMaximumFractionDigits(2);
154 | fomart.setMinimumFractionDigits(0);
155 | des = new EncryptData("emmagee");
156 | currentInfo = new CurrentInfo();
157 | statusBarHeight = getStatusBarHeight();
158 | batteryBroadcast = new BatteryInfoBroadcastReceiver();
159 | registerReceiver(batteryBroadcast, new IntentFilter(BATTERY_CHANGED));
160 | }
161 |
162 | /**
163 | * 电池信息监控监听器
164 | *
165 | * @author andrewleo
166 | *
167 | */
168 | public class BatteryInfoBroadcastReceiver extends BroadcastReceiver {
169 |
170 | @Override
171 | public void onReceive(Context context, Intent intent) {
172 |
173 | if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
174 | int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
175 | int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
176 | totalBatt = String.valueOf(level * 100 / scale);
177 | voltage = String.valueOf(intent.getIntExtra(
178 | BatteryManager.EXTRA_VOLTAGE, -1) * 1.0 / 1000);
179 | temperature = String.valueOf(intent.getIntExtra(
180 | BatteryManager.EXTRA_TEMPERATURE, -1) * 1.0 / 10);
181 | }
182 |
183 | }
184 |
185 | }
186 |
187 | @Override
188 | public int onStartCommand(Intent intent, int flags, int startId) {
189 | Log.i(LOG_TAG, "service onStart");
190 | PendingIntent contentIntent = PendingIntent.getActivity(
191 | getBaseContext(), 0, new Intent(this, MainPageActivity.class),
192 | 0);
193 | NotificationCompat.Builder builder = new NotificationCompat.Builder(
194 | this);
195 | builder.setContentIntent(contentIntent).setSmallIcon(R.drawable.icon)
196 | .setWhen(System.currentTimeMillis()).setAutoCancel(true)
197 | .setContentTitle("Emmagee");
198 | startForeground(startId, builder.build());
199 |
200 | pid = intent.getExtras().getInt("pid");
201 | //uid = intent.getExtras().getInt("uid");
202 | processName = intent.getExtras().getString("processName");
203 | packageName = intent.getExtras().getString("packageName");
204 | startActivity = intent.getExtras().getString("startActivity");
205 |
206 | try {
207 | PackageManager pm = getPackageManager();
208 | ApplicationInfo ainfo = pm.getApplicationInfo(packageName, PackageManager.GET_ACTIVITIES);
209 | uid = ainfo.uid;
210 | }catch (PackageManager.NameNotFoundException e) {
211 | e.printStackTrace();
212 | }
213 | cpuInfo = new CpuInfo(getBaseContext(), pid, Integer.toString(uid));
214 | readSettingInfo();
215 | if (isFloating) {
216 | viFloatingWindow = LayoutInflater.from(this).inflate(
217 | R.layout.floating, null);
218 | txtUnusedMem = (TextView) viFloatingWindow
219 | .findViewById(R.id.memunused);
220 | txtTotalMem = (TextView) viFloatingWindow
221 | .findViewById(R.id.memtotal);
222 | txtTraffic = (TextView) viFloatingWindow.findViewById(R.id.traffic);
223 | btnWifi = (Button) viFloatingWindow.findViewById(R.id.wifi);
224 |
225 | wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
226 | if (wifiManager.isWifiEnabled()) {
227 | btnWifi.setText(R.string.close_wifi);
228 | } else {
229 | btnWifi.setText(R.string.open_wifi);
230 | }
231 | txtUnusedMem.setText(getString(R.string.calculating));
232 | txtUnusedMem.setTextColor(android.graphics.Color.RED);
233 | txtTotalMem.setTextColor(android.graphics.Color.RED);
234 | txtTraffic.setTextColor(android.graphics.Color.RED);
235 | btnStop = (Button) viFloatingWindow.findViewById(R.id.stop);
236 | btnStop.setOnClickListener(new OnClickListener() {
237 | @Override
238 | public void onClick(View v) {
239 | Intent intent = new Intent();
240 | intent.putExtra("isServiceStop", true);
241 | intent.setAction(SERVICE_ACTION);
242 | sendBroadcast(intent);
243 | stopSelf();
244 | }
245 | });
246 | createFloatingWindow();
247 | }
248 | createResultCsv();
249 | handler.postDelayed(task, 1000);
250 | return START_NOT_STICKY;
251 | }
252 |
253 | /**
254 | * read configuration file.
255 | *
256 | * @throws IOException
257 | */
258 | private void readSettingInfo() {
259 | SharedPreferences preferences = Settings
260 | .getDefaultSharedPreferences(getApplicationContext());
261 | int interval = preferences.getInt(Settings.KEY_INTERVAL, 5);
262 | delaytime = interval * 1000;
263 | isFloating = preferences.getBoolean(Settings.KEY_ISFLOAT, true);
264 | sender = preferences.getString(Settings.KEY_SENDER, BLANK_STRING);
265 | password = preferences.getString(Settings.KEY_PASSWORD, BLANK_STRING);
266 | recipients = preferences.getString(Settings.KEY_RECIPIENTS,
267 | BLANK_STRING);
268 | receivers = recipients.split("\\s+");
269 | smtp = preferences.getString(Settings.KEY_SMTP, BLANK_STRING);
270 | isRoot = preferences.getBoolean(Settings.KEY_ROOT, false);
271 | isAutoStop = preferences.getBoolean(Settings.KEY_AUTO_STOP, true);
272 | }
273 |
274 | /**
275 | * write the test result to csv format report.
276 | */
277 | private void createResultCsv() {
278 | Calendar cal = Calendar.getInstance();
279 | SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmss");
280 | String heapData = "";
281 | String mDateTime = formatter.format(cal.getTime().getTime());
282 | resultFilePath = Settings.EMMAGEE_RESULT_DIR + mDateTime + "_" + packageName
283 | + ".csv";
284 | try {
285 | File resultFile = new File(resultFilePath);
286 | resultFile.getParentFile().mkdirs();
287 | resultFile.createNewFile();
288 | out = new FileOutputStream(resultFile);
289 | osw = new OutputStreamWriter(out);
290 | bw = new BufferedWriter(osw);
291 | long totalMemorySize = memoryInfo.getTotalMemory();
292 | String totalMemory = fomart.format((double) totalMemorySize / 1024);
293 | String multiCpuTitle = BLANK_STRING;
294 | // titles of multiple cpu cores
295 | ArrayList cpuList = cpuInfo.getCpuList();
296 | for (int i = 0; i < cpuList.size(); i++) {
297 | multiCpuTitle += Constants.COMMA + cpuList.get(i)
298 | + getString(R.string.total_usage);
299 | }
300 | bw.write(getString(R.string.process_package) + Constants.COMMA
301 | + packageName + Constants.LINE_END
302 | + getString(R.string.process_name) + Constants.COMMA
303 | + processName + Constants.LINE_END
304 | + getString(R.string.process_pid) + Constants.COMMA + pid
305 | + Constants.LINE_END + getString(R.string.mem_size)
306 | + Constants.COMMA + totalMemory + "MB" + Constants.LINE_END
307 | + getString(R.string.cpu_type) + Constants.COMMA
308 | + cpuInfo.getCpuName() + Constants.LINE_END
309 | + getString(R.string.android_system_version)
310 | + Constants.COMMA + memoryInfo.getSDKVersion()
311 | + Constants.LINE_END + getString(R.string.mobile_type)
312 | + Constants.COMMA + memoryInfo.getPhoneType()
313 | + Constants.LINE_END + "UID" + Constants.COMMA + uid
314 | + Constants.LINE_END);
315 |
316 | if (isGrantedReadLogsPermission()) {
317 | bw.write(START_TIME);
318 | }
319 | if (isRoot) {
320 | heapData = getString(R.string.native_heap) + Constants.COMMA
321 | + getString(R.string.dalvik_heap) + Constants.COMMA;
322 | }
323 | bw.write(getString(R.string.timestamp) + Constants.COMMA
324 | + getString(R.string.top_activity) + Constants.COMMA
325 | + heapData + getString(R.string.used_mem_PSS)
326 | + Constants.COMMA + getString(R.string.used_mem_ratio)
327 | + Constants.COMMA + getString(R.string.mobile_free_mem)
328 | + Constants.COMMA + getString(R.string.app_used_cpu_ratio)
329 | + Constants.COMMA
330 | + getString(R.string.total_used_cpu_ratio) + multiCpuTitle
331 | + Constants.COMMA + getString(R.string.traffic)
332 | + Constants.COMMA + getString(R.string.battery)
333 | + Constants.COMMA + getString(R.string.current)
334 | + Constants.COMMA + getString(R.string.temperature)
335 | + Constants.COMMA + getString(R.string.voltage)
336 | + Constants.COMMA + getString(R.string.fps)
337 | + Constants.LINE_END);
338 | } catch (IOException e) {
339 | Log.e(LOG_TAG, e.getMessage());
340 | }
341 | }
342 |
343 | /**
344 | * create a floating window to show real-time data.
345 | */
346 | private void createFloatingWindow() {
347 | SharedPreferences shared = getSharedPreferences("float_flag",
348 | Activity.MODE_PRIVATE);
349 | SharedPreferences.Editor editor = shared.edit();
350 | editor.putInt("float", 1);
351 | editor.commit();
352 | windowManager = (WindowManager) getApplicationContext()
353 | .getSystemService("window");
354 | wmParams = ((MyApplication) getApplication()).getMywmParams();
355 | wmParams.type = 2002;
356 | wmParams.flags |= 8;
357 | wmParams.gravity = Gravity.LEFT | Gravity.TOP;
358 | wmParams.x = 0;
359 | wmParams.y = 0;
360 | wmParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
361 | wmParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
362 | wmParams.format = 1;
363 | windowManager.addView(viFloatingWindow, wmParams);
364 | viFloatingWindow.setOnTouchListener(new OnTouchListener() {
365 | public boolean onTouch(View v, MotionEvent event) {
366 | x = event.getRawX();
367 | y = event.getRawY() - statusBarHeight;
368 | switch (event.getAction()) {
369 | case MotionEvent.ACTION_DOWN:
370 | mTouchStartX = event.getX();
371 | mTouchStartY = event.getY();
372 | break;
373 | case MotionEvent.ACTION_MOVE:
374 | updateViewPosition();
375 | break;
376 | case MotionEvent.ACTION_UP:
377 | updateViewPosition();
378 | mTouchStartX = mTouchStartY = 0;
379 | break;
380 | }
381 | return true;
382 | }
383 | });
384 |
385 | btnWifi.setOnClickListener(new OnClickListener() {
386 | @Override
387 | public void onClick(View v) {
388 | try {
389 | btnWifi = (Button) viFloatingWindow.findViewById(R.id.wifi);
390 | String buttonText = (String) btnWifi.getText();
391 | String wifiText = getResources().getString(
392 | R.string.open_wifi);
393 | if (buttonText.equals(wifiText)) {
394 | wifiManager.setWifiEnabled(true);
395 | btnWifi.setText(R.string.close_wifi);
396 | } else {
397 | wifiManager.setWifiEnabled(false);
398 | btnWifi.setText(R.string.open_wifi);
399 | }
400 | } catch (Exception e) {
401 | Toast.makeText(viFloatingWindow.getContext(),
402 | getString(R.string.wifi_fail_toast),
403 | Toast.LENGTH_LONG).show();
404 | Log.e(LOG_TAG, e.toString());
405 | }
406 | }
407 | });
408 | }
409 |
410 | private Runnable task = new Runnable() {
411 |
412 | public void run() {
413 | if (!isServiceStop) {
414 | dataRefresh();
415 | handler.postDelayed(this, delaytime);
416 | if (isFloating && viFloatingWindow != null) {
417 | windowManager.updateViewLayout(viFloatingWindow, wmParams);
418 | }
419 | // get app start time from logcat on every task running
420 | getStartTimeFromLogcat();
421 | } else {
422 | Intent intent = new Intent();
423 | intent.putExtra("isServiceStop", true);
424 | intent.setAction(SERVICE_ACTION);
425 | sendBroadcast(intent);
426 | stopSelf();
427 | }
428 | }
429 | };
430 |
431 | /**
432 | * Try to get start time from logcat.
433 | */
434 | private void getStartTimeFromLogcat() {
435 | if (!isGetStartTime || getStartTimeCount >= MAX_START_TIME_COUNT) {
436 | return;
437 | }
438 | try {
439 | // filter logcat by Tag:ActivityManager and Level:Info
440 | String logcatCommand = "logcat -v time -d ActivityManager:I *:S";
441 | Process process = Runtime.getRuntime().exec(logcatCommand);
442 | BufferedReader bufferedReader = new BufferedReader(
443 | new InputStreamReader(process.getInputStream()));
444 | StringBuilder strBuilder = new StringBuilder();
445 | String line = BLANK_STRING;
446 |
447 | while ((line = bufferedReader.readLine()) != null) {
448 | strBuilder.append(line);
449 | strBuilder.append(Constants.LINE_END);
450 | String regex = ".*Displayed.*" + startActivity
451 | + ".*\\+(.*)ms.*";
452 | if (line.matches(regex)) {
453 | Log.w("my logs", line);
454 | if (line.contains("total")) {
455 | line = line.substring(0, line.indexOf("total"));
456 | }
457 | startTime = line.substring(line.lastIndexOf("+") + 1,
458 | line.lastIndexOf("ms") + 2);
459 | Toast.makeText(EmmageeService.this,
460 | getString(R.string.start_time) + startTime,
461 | Toast.LENGTH_LONG).show();
462 | isGetStartTime = false;
463 | break;
464 | }
465 | }
466 | getStartTimeCount++;
467 | } catch (IOException e) {
468 | Log.d(LOG_TAG, e.getMessage());
469 | }
470 | }
471 |
472 | /**
473 | * Above JellyBean, we cannot grant READ_LOGS permission...
474 | *
475 | * @return
476 | */
477 | private boolean isGrantedReadLogsPermission() {
478 | int permissionState = getPackageManager().checkPermission(
479 | android.Manifest.permission.READ_LOGS, getPackageName());
480 | return permissionState == PackageManager.PERMISSION_GRANTED;
481 | }
482 |
483 | /**
484 | * refresh the performance data showing in floating window.
485 | *
486 | * @throws FileNotFoundException
487 | *
488 | * @throws IOException
489 | */
490 | private void dataRefresh() {
491 | int pidMemory = memoryInfo.getPidMemorySize(pid, getBaseContext());
492 | long freeMemory = memoryInfo.getFreeMemorySize(getBaseContext());
493 | String freeMemoryKb = fomart.format((double) freeMemory / 1024);
494 | String processMemory = fomart.format((double) pidMemory / 1024);
495 | String currentBatt = String.valueOf(currentInfo.getCurrentValue());
496 | // 异常数据过滤
497 | try {
498 | if (Math.abs(Double.parseDouble(currentBatt)) >= 500) {
499 | currentBatt = Constants.NA;
500 | }
501 | } catch (Exception e) {
502 | currentBatt = Constants.NA;
503 | }
504 | ArrayList processInfo = cpuInfo.getCpuRatioInfo(totalBatt,
505 | currentBatt, temperature, voltage,
506 | String.valueOf(fpsInfo.fps()), isRoot);
507 | if (isFloating) {
508 | String processCpuRatio = "0.00";
509 | String totalCpuRatio = "0.00";
510 | String trafficSize = "0";
511 | long tempTraffic = 0L;
512 | double trafficMb = 0;
513 | boolean isMb = false;
514 | if (!processInfo.isEmpty()) {
515 | processCpuRatio = processInfo.get(0);
516 | totalCpuRatio = processInfo.get(1);
517 | trafficSize = processInfo.get(2);
518 | if (!(BLANK_STRING.equals(trafficSize))
519 | && !("-1".equals(trafficSize))) {
520 | tempTraffic = Long.parseLong(trafficSize);
521 | if (tempTraffic > 1024) {
522 | isMb = true;
523 | trafficMb = (double) tempTraffic / 1024;
524 | }
525 | }
526 | // 如果cpu使用率存在且都不小于0,则输出
527 | if (processCpuRatio != null && totalCpuRatio != null) {
528 | txtUnusedMem.setText(getString(R.string.process_free_mem)
529 | + processMemory + "/" + freeMemoryKb + "MB");
530 | txtTotalMem.setText(getString(R.string.process_overall_cpu)
531 | + processCpuRatio + "%/" + totalCpuRatio + "%");
532 | String batt = getString(R.string.current) + currentBatt;
533 | if ("-1".equals(trafficSize)) {
534 | txtTraffic.setText(batt + Constants.COMMA
535 | + getString(R.string.traffic) + Constants.NA);
536 | } else if (isMb)
537 | txtTraffic.setText(batt + Constants.COMMA
538 | + getString(R.string.traffic)
539 | + fomart.format(trafficMb) + "MB");
540 | else
541 | txtTraffic.setText(batt + Constants.COMMA
542 | + getString(R.string.traffic) + trafficSize
543 | + "KB");
544 | }
545 | // 当内存为0切cpu使用率为0时则是被测应用退出
546 | if ("0".equals(processMemory)) {
547 | if (isAutoStop) {
548 | closeOpenedStream();
549 | isServiceStop = true;
550 | return;
551 | } else {
552 | Log.i(LOG_TAG, "未设置自动停止测试,继续监听");
553 | // 如果设置应用退出后不自动停止,则需要每次监听时重新获取pid
554 | Programe programe = procInfo.getProgrameByPackageName(
555 | this, packageName);
556 | if (programe != null && programe.getPid() > 0) {
557 | pid = programe.getPid();
558 | uid = programe.getUid();
559 | cpuInfo = new CpuInfo(getBaseContext(), pid,
560 | Integer.toString(uid));
561 | }
562 | }
563 | }
564 | }
565 |
566 | }
567 | }
568 |
569 | /**
570 | * update the position of floating window.
571 | */
572 | private void updateViewPosition() {
573 | wmParams.x = (int) (x - mTouchStartX);
574 | wmParams.y = (int) (y - mTouchStartY);
575 | if (viFloatingWindow != null) {
576 | windowManager.updateViewLayout(viFloatingWindow, wmParams);
577 | }
578 | }
579 |
580 | /**
581 | * close all opened stream.
582 | */
583 | public void closeOpenedStream() {
584 | try {
585 | if (bw != null) {
586 | bw.close();
587 | }
588 | if (osw != null)
589 | osw.close();
590 | if (out != null)
591 | out.close();
592 | } catch (Exception e) {
593 | Log.d(LOG_TAG, e.getMessage());
594 | }
595 | }
596 |
597 | @Override
598 | public void onDestroy() {
599 | Log.i(LOG_TAG, "service onDestroy");
600 | if (windowManager != null) {
601 | windowManager.removeView(viFloatingWindow);
602 | viFloatingWindow = null;
603 | }
604 | handler.removeCallbacks(task);
605 | closeOpenedStream();
606 | // replace the start time in file
607 | if (!BLANK_STRING.equals(startTime)) {
608 | replaceFileString(resultFilePath, START_TIME,
609 | getString(R.string.start_time) + startTime
610 | + Constants.LINE_END);
611 | } else {
612 | replaceFileString(resultFilePath, START_TIME, BLANK_STRING);
613 | }
614 | isStop = true;
615 | unregisterReceiver(batteryBroadcast);
616 | boolean isSendSuccessfully = false;
617 | try {
618 | isSendSuccessfully = MailSender.sendTextMail(sender,
619 | des.decrypt(password), smtp,
620 | "Emmagee Performance Test Report", "see attachment",
621 | resultFilePath, receivers);
622 | } catch (Exception e) {
623 | isSendSuccessfully = false;
624 | }
625 | if (isSendSuccessfully) {
626 | Toast.makeText(this,
627 | getString(R.string.send_success_toast) + recipients,
628 | Toast.LENGTH_LONG).show();
629 | } else {
630 | Toast.makeText(
631 | this,
632 | getString(R.string.send_fail_toast)
633 | + EmmageeService.resultFilePath, Toast.LENGTH_LONG)
634 | .show();
635 | }
636 | super.onDestroy();
637 | stopForeground(true);
638 | }
639 |
640 | /**
641 | * Replaces all matches for replaceType within this replaceString in file on
642 | * the filePath
643 | *
644 | * @param filePath
645 | * @param replaceType
646 | * @param replaceString
647 | */
648 | private void replaceFileString(String filePath, String replaceType,
649 | String replaceString) {
650 | try {
651 | File file = new File(filePath);
652 | BufferedReader reader = new BufferedReader(new FileReader(file));
653 | String line = BLANK_STRING;
654 | String oldtext = BLANK_STRING;
655 | while ((line = reader.readLine()) != null) {
656 | oldtext += line + Constants.LINE_END;
657 | }
658 | reader.close();
659 | // replace a word in a file
660 | String newtext = oldtext.replaceAll(replaceType, replaceString);
661 | BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
662 | new FileOutputStream(filePath),
663 | getString(R.string.csv_encoding)));
664 | writer.write(newtext);
665 | writer.close();
666 | } catch (IOException e) {
667 | Log.d(LOG_TAG, e.getMessage());
668 | }
669 | }
670 |
671 | /**
672 | * get height of status bar
673 | *
674 | * @return height of status bar, if default method does not work, return 25
675 | */
676 | public int getStatusBarHeight() {
677 | // set status bar height to 25
678 | int barHeight = 25;
679 | int resourceId = getResources().getIdentifier("status_bar_height",
680 | "dimen", "android");
681 | if (resourceId > 0) {
682 | barHeight = getResources().getDimensionPixelSize(resourceId);
683 | }
684 | return barHeight;
685 | }
686 |
687 | @Override
688 | public IBinder onBind(Intent intent) {
689 | return null;
690 | }
691 | }
--------------------------------------------------------------------------------
/src/com/netease/qa/emmagee/utils/Constants.java:
--------------------------------------------------------------------------------
1 | package com.netease.qa.emmagee.utils;
2 |
3 | /**
4 | * Constant Class
5 | *
6 | * @author andrewleo
7 | *
8 | */
9 | public final class Constants {
10 |
11 | public static final String NA = "N/A";
12 | public static final String COMMA = ",";
13 | public static final String LINE_END = "\r\n";
14 | public static final String COLON = ":";
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/src/com/netease/qa/emmagee/utils/CpuInfo.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012-2013 NetEase, Inc. and other contributors
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 | package com.netease.qa.emmagee.utils;
18 |
19 | import java.io.File;
20 | import java.io.FileFilter;
21 | import java.io.FileNotFoundException;
22 | import java.io.IOException;
23 | import java.io.RandomAccessFile;
24 | import java.text.DecimalFormat;
25 | import java.text.DecimalFormatSymbols;
26 | import java.text.SimpleDateFormat;
27 | import java.util.ArrayList;
28 | import java.util.Calendar;
29 | import java.util.Locale;
30 | import java.util.regex.Pattern;
31 |
32 | import com.netease.qa.emmagee.R;
33 | import com.netease.qa.emmagee.service.EmmageeService;
34 |
35 | import android.content.Context;
36 | import android.os.Build;
37 | import android.util.Log;
38 |
39 | /**
40 | * operate CPU information
41 | *
42 | * @author andrewleo
43 | */
44 | public class CpuInfo {
45 |
46 | private static final String LOG_TAG = "Emmagee-" + CpuInfo.class.getSimpleName();
47 |
48 | private Context context;
49 | private long processCpu;
50 | private ArrayList idleCpu = new ArrayList();
51 | private ArrayList totalCpu = new ArrayList();
52 | private boolean isInitialStatics = true;
53 | private SimpleDateFormat formatterFile;
54 | private MemoryInfo mi;
55 | private long totalMemorySize;
56 | private long preTraffic;
57 | private long lastestTraffic;
58 | private long traffic;
59 | private TrafficInfo trafficInfo;
60 | private ArrayList cpuUsedRatio = new ArrayList();
61 | private ArrayList totalCpu2 = new ArrayList();
62 | private long processCpu2;
63 | private ArrayList idleCpu2 = new ArrayList();
64 | private String processCpuRatio = "";
65 | private ArrayList totalCpuRatio = new ArrayList();
66 | private int pid;
67 |
68 | private static final String INTEL_CPU_NAME = "model name";
69 | private static final String CPU_DIR_PATH = "/sys/devices/system/cpu/";
70 | private static final String CPU_X86 = "x86";
71 | private static final String CPU_INFO_PATH = "/proc/cpuinfo";
72 | private static final String CPU_STAT = "/proc/stat";
73 |
74 | public CpuInfo(Context context, int pid, String uid) {
75 | this.pid = pid;
76 | this.context = context;
77 | trafficInfo = new TrafficInfo(uid);
78 | formatterFile = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
79 | mi = new MemoryInfo();
80 | totalMemorySize = mi.getTotalMemory();
81 | cpuUsedRatio = new ArrayList();
82 | }
83 |
84 | /**
85 | * read the status of CPU.
86 | *
87 | * @throws FileNotFoundException
88 | */
89 | public void readCpuStat() {
90 | String processPid = Integer.toString(pid);
91 | String cpuStatPath = "/proc/" + processPid + "/stat";
92 | try {
93 | // monitor cpu stat of certain process
94 | RandomAccessFile processCpuInfo = new RandomAccessFile(cpuStatPath, "r");
95 | String line = "";
96 | StringBuffer stringBuffer = new StringBuffer();
97 | stringBuffer.setLength(0);
98 | while ((line = processCpuInfo.readLine()) != null) {
99 | stringBuffer.append(line + "\n");
100 | }
101 | String[] tok = stringBuffer.toString().split(" ");
102 | processCpu = Long.parseLong(tok[13]) + Long.parseLong(tok[14]);
103 | processCpuInfo.close();
104 | } catch (FileNotFoundException e) {
105 | Log.w(LOG_TAG, "FileNotFoundException: " + e.getMessage());
106 | } catch (IOException e) {
107 | e.printStackTrace();
108 | }
109 | readTotalCpuStat();
110 | }
111 |
112 | /**
113 | * read stat of each CPU cores
114 | */
115 | private void readTotalCpuStat() {
116 | try {
117 | // monitor total and idle cpu stat of certain process
118 | RandomAccessFile cpuInfo = new RandomAccessFile(CPU_STAT, "r");
119 | String line = "";
120 | while ((null != (line = cpuInfo.readLine())) && line.startsWith("cpu")) {
121 | String[] toks = line.split("\\s+");
122 | idleCpu.add(Long.parseLong(toks[4]));
123 | totalCpu.add(Long.parseLong(toks[1]) + Long.parseLong(toks[2]) + Long.parseLong(toks[3]) + Long.parseLong(toks[4])
124 | + Long.parseLong(toks[6]) + Long.parseLong(toks[5]) + Long.parseLong(toks[7]));
125 | }
126 | cpuInfo.close();
127 | } catch (FileNotFoundException e) {
128 | e.printStackTrace();
129 | } catch (IOException e) {
130 | e.printStackTrace();
131 | }
132 | }
133 |
134 | /**
135 | * get CPU name.
136 | *
137 | * @return CPU name
138 | */
139 | public String getCpuName() {
140 | try {
141 | RandomAccessFile cpuStat = new RandomAccessFile(CPU_INFO_PATH, "r");
142 | // check cpu type
143 | String line;
144 | while (null != (line = cpuStat.readLine())) {
145 | String[] values = line.split(":");
146 | if (values[0].contains(INTEL_CPU_NAME) || values[0].contains("Processor")) {
147 | cpuStat.close();
148 | Log.d(LOG_TAG, "CPU name="+values[1]);
149 | return values[1];
150 | }
151 | }
152 | } catch (IOException e) {
153 | Log.e(LOG_TAG, "IOException: " + e.getMessage());
154 | }
155 | return "";
156 | }
157 |
158 | /**
159 | * display directories naming with "cpu*"
160 | *
161 | * @author andrewleo
162 | */
163 | class CpuFilter implements FileFilter {
164 | @Override
165 | public boolean accept(File pathname) {
166 | // Check if filename matchs "cpu[0-9]"
167 | if (Pattern.matches("cpu[0-9]", pathname.getName())) {
168 | return true;
169 | }
170 | return false;
171 | }
172 | }
173 |
174 | /**
175 | * get CPU core numbers
176 | *
177 | * @return cpu core numbers
178 | */
179 | public int getCpuNum() {
180 | try {
181 | // Get directory containing CPU info
182 | File dir = new File(CPU_DIR_PATH);
183 | // Filter to only list the devices we care about
184 | File[] files = dir.listFiles(new CpuFilter());
185 | return files.length;
186 | } catch (Exception e) {
187 | e.printStackTrace();
188 | return 1;
189 | }
190 | }
191 |
192 | /**
193 | * get CPU core list
194 | *
195 | * @return cpu core list
196 | */
197 | public ArrayList getCpuList() {
198 | ArrayList cpuList = new ArrayList();
199 | try {
200 | // Get directory containing CPU info
201 | File dir = new File(CPU_DIR_PATH);
202 | // Filter to only list the devices we care about
203 | File[] files = dir.listFiles(new CpuFilter());
204 | for (int i = 0; i < files.length; i++) {
205 | cpuList.add(files[i].getName());
206 | }
207 | return cpuList;
208 | } catch (Exception e) {
209 | e.printStackTrace();
210 | cpuList.add("cpu0");
211 | return cpuList;
212 | }
213 | }
214 |
215 | /**
216 | * reserve used ratio of process CPU and total CPU, meanwhile collect
217 | * network traffic.
218 | *
219 | * @return network traffic ,used ratio of process CPU and total CPU in
220 | * certain interval
221 | */
222 | public ArrayList getCpuRatioInfo(String totalBatt, String currentBatt, String temperature, String voltage, String fps, boolean isRoot) {
223 |
224 | String heapData = "";
225 | DecimalFormat fomart = new DecimalFormat();
226 | fomart.setDecimalFormatSymbols(new DecimalFormatSymbols(Locale.US));
227 | fomart.setGroupingUsed(false);
228 | fomart.setMaximumFractionDigits(2);
229 | fomart.setMinimumFractionDigits(2);
230 |
231 | cpuUsedRatio.clear();
232 | idleCpu.clear();
233 | totalCpu.clear();
234 | totalCpuRatio.clear();
235 | readCpuStat();
236 |
237 | try {
238 | String mDateTime2;
239 | Calendar cal = Calendar.getInstance();
240 | if ((Build.MODEL.equals("sdk")) || (Build.MODEL.equals("google_sdk"))) {
241 | mDateTime2 = formatterFile.format(cal.getTime().getTime() + 8 * 60 * 60 * 1000);
242 | totalBatt = Constants.NA;
243 | currentBatt = Constants.NA;
244 | temperature = Constants.NA;
245 | voltage = Constants.NA;
246 | } else
247 | mDateTime2 = formatterFile.format(cal.getTime().getTime());
248 | if (isInitialStatics) {
249 | preTraffic = trafficInfo.getTrafficInfo();
250 | isInitialStatics = false;
251 | } else {
252 | lastestTraffic = trafficInfo.getTrafficInfo();
253 | if (preTraffic == -1)
254 | traffic = -1;
255 | else {
256 | if (lastestTraffic > preTraffic) {
257 | traffic += (lastestTraffic - preTraffic + 1023) / 1024;
258 | }
259 | }
260 | preTraffic = lastestTraffic;
261 | Log.d(LOG_TAG, "lastestTraffic===" + lastestTraffic);
262 | Log.d(LOG_TAG, "preTraffic===" + preTraffic);
263 | StringBuffer totalCpuBuffer = new StringBuffer();
264 | if (null != totalCpu2 && totalCpu2.size() > 0) {
265 | processCpuRatio = fomart.format(100 * ((double) (processCpu - processCpu2) / ((double) (totalCpu.get(0) - totalCpu2.get(0)))));
266 | for (int i = 0; i < (totalCpu.size() > totalCpu2.size() ? totalCpu2.size() : totalCpu.size()); i++) {
267 | String cpuRatio = "0.00";
268 | if (totalCpu.get(i) - totalCpu2.get(i) > 0) {
269 | cpuRatio = fomart
270 | .format(100 * ((double) ((totalCpu.get(i) - idleCpu.get(i)) - (totalCpu2.get(i) - idleCpu2.get(i))) / (double) (totalCpu
271 | .get(i) - totalCpu2.get(i))));
272 | }
273 | totalCpuRatio.add(cpuRatio);
274 | totalCpuBuffer.append(cpuRatio + Constants.COMMA);
275 | }
276 | } else {
277 | processCpuRatio = "0";
278 | totalCpuRatio.add("0");
279 | totalCpuBuffer.append("0,");
280 | totalCpu2 = (ArrayList) totalCpu.clone();
281 | processCpu2 = processCpu;
282 | idleCpu2 = (ArrayList) idleCpu.clone();
283 | }
284 | // 多核cpu的值写入csv文件中
285 | for (int i = 0; i < getCpuNum() - totalCpuRatio.size() + 1; i++) {
286 | totalCpuBuffer.append("0.00,");
287 | }
288 | long pidMemory = mi.getPidMemorySize(pid, context);
289 | String pMemory = fomart.format((double) pidMemory / 1024);
290 | long freeMemory = mi.getFreeMemorySize(context);
291 | String fMemory = fomart.format((double) freeMemory / 1024);
292 | String percent = context.getString(R.string.stat_error);
293 | if (totalMemorySize != 0) {
294 | percent = fomart.format(((double) pidMemory / (double) totalMemorySize) * 100);
295 | }
296 |
297 | if (isPositive(processCpuRatio) && isPositive(totalCpuRatio.get(0))) {
298 | String trafValue;
299 | // whether certain device supports traffic statics or not
300 | if (traffic == -1) {
301 | trafValue = Constants.NA;
302 | } else {
303 | trafValue = String.valueOf(traffic);
304 | }
305 | if(isRoot){
306 | String[][] heapArray = MemoryInfo.getHeapSize(pid, context);
307 | heapData = heapArray[0][1]+"/"+heapArray[0][0]+Constants.COMMA+heapArray[1][1]+"/"+heapArray[1][0]+Constants.COMMA;
308 | }
309 | EmmageeService.bw.write(mDateTime2 + Constants.COMMA + ProcessInfo.getTopActivity(context) + Constants.COMMA +heapData+ pMemory
310 | + Constants.COMMA + percent + Constants.COMMA + fMemory + Constants.COMMA + processCpuRatio + Constants.COMMA
311 | + totalCpuBuffer.toString() + trafValue + Constants.COMMA + totalBatt + Constants.COMMA + currentBatt + Constants.COMMA
312 | + temperature + Constants.COMMA + voltage + Constants.COMMA + fps + Constants.LINE_END);
313 | totalCpu2 = (ArrayList) totalCpu.clone();
314 | processCpu2 = processCpu;
315 | idleCpu2 = (ArrayList) idleCpu.clone();
316 | cpuUsedRatio.add(processCpuRatio);
317 | cpuUsedRatio.add(totalCpuRatio.get(0));
318 | cpuUsedRatio.add(String.valueOf(traffic));
319 | }
320 | }
321 | } catch (IOException e) {
322 | e.printStackTrace();
323 | }
324 | return cpuUsedRatio;
325 | }
326 |
327 | /**
328 | * is text a positive number
329 | *
330 | * @param text
331 | * @return
332 | */
333 | private boolean isPositive(String text) {
334 | Double num;
335 | try {
336 | num = Double.parseDouble(text);
337 | } catch (NumberFormatException e) {
338 | return false;
339 | }
340 | return num >= 0;
341 | }
342 |
343 | }
344 |
--------------------------------------------------------------------------------
/src/com/netease/qa/emmagee/utils/CurrentInfo.java:
--------------------------------------------------------------------------------
1 | package com.netease.qa.emmagee.utils;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.DataInputStream;
5 | import java.io.File;
6 | import java.io.FileInputStream;
7 | import java.io.FileReader;
8 | import java.util.Locale;
9 |
10 | import android.os.Build;
11 | import android.util.Log;
12 |
13 | /**
14 | * Current info
15 | *
16 | * @author andrewleo
17 | *
18 | */
19 | public class CurrentInfo {
20 | private static final String LOG_TAG = "Emmagee-CurrentInfo";
21 | private static final String BUILD_MODEL = Build.MODEL.toLowerCase(Locale.ENGLISH);
22 | private static final String I_MBAT = "I_MBAT: ";
23 | private static final String CURRENT_NOW = "/sys/class/power_supply/battery/current_now";
24 | private static final String BATT_CURRENT = "/sys/class/power_supply/battery/batt_current";
25 | private static final String SMEM_TEXT = "/sys/class/power_supply/battery/smem_text";
26 | private static final String BATT_CURRENT_ADC = "/sys/class/power_supply/battery/batt_current_adc";
27 | private static final String CURRENT_AVG = "/sys/class/power_supply/battery/current_avg";
28 |
29 | /**
30 | * read system file to get current value
31 | *
32 | * @return current value
33 | */
34 | public Long getCurrentValue() {
35 | File f = null;
36 | Log.d(LOG_TAG, BUILD_MODEL);
37 | // galaxy s4,oppo find,samgsung note2
38 | if (BUILD_MODEL.contains("sgh-i337") || BUILD_MODEL.contains("gt-i9505") || BUILD_MODEL.contains("sch-i545")
39 | || BUILD_MODEL.contains("find 5") || BUILD_MODEL.contains("sgh-m919") || BUILD_MODEL.contains("sgh-i537")
40 | || BUILD_MODEL.contains("x907") || BUILD_MODEL.contains("gt-n7100")) {
41 | f = new File(CURRENT_NOW);
42 | if (f.exists()) {
43 | return getCurrentValue(f, false);
44 | }
45 | }
46 |
47 | // samsung galaxy
48 | if (BUILD_MODEL.contains("gt-p31") || BUILD_MODEL.contains("gt-p51")) {
49 | f = new File(CURRENT_AVG);
50 | if (f.exists()) {
51 | return getCurrentValue(f, false);
52 | }
53 | }
54 |
55 | // htc desire hd ,desire z
56 | if (BUILD_MODEL.contains("desire hd") || BUILD_MODEL.contains("desire z")) {
57 | f = new File(BATT_CURRENT);
58 | if (f.exists())
59 | return getCurrentValue(f, false);
60 | }
61 |
62 | // htc sensation z710e
63 | f = new File(BATT_CURRENT);
64 | if (f.exists())
65 | return getCurrentValue(f, false);
66 |
67 | // htc one V
68 | f = new File(SMEM_TEXT);
69 | if (f.exists())
70 | return getSMemValue();
71 |
72 | // nexus one,meizu
73 | f = new File(CURRENT_NOW);
74 | if (f.exists())
75 | return getCurrentValue(f, true);
76 |
77 | // meizu pro 5
78 | f = new File("/sys/class/power_supply/bq2753x-0/current_now");
79 | if (f.exists())
80 | return getCurrentValue(f, true);
81 |
82 | // galaxy note, galaxy s2
83 | f = new File(BATT_CURRENT_ADC);
84 | if (f.exists())
85 | return getCurrentValue(f, false);
86 |
87 | // acer V360
88 | f = new File("/sys/class/power_supply/battery/BatteryAverageCurrent");
89 | if (f.exists())
90 | return getCurrentValue(f, false);
91 |
92 | // moto milestone,moto mb526
93 | f = new File("/sys/devices/platform/cpcap_battery/power_supply/usb/current_now");
94 | if (f.exists())
95 | return getCurrentValue(f, false);
96 |
97 | return null;
98 | }
99 |
100 | /**
101 | * get current value from smem_text
102 | *
103 | * @return current value
104 | */
105 | public Long getSMemValue() {
106 | boolean success = false;
107 | String text = null;
108 | Long value = null;
109 | try {
110 | FileReader fr = new FileReader(SMEM_TEXT);
111 | BufferedReader br = new BufferedReader(fr);
112 | String line = br.readLine();
113 | while (line != null) {
114 | if (line.contains(I_MBAT)) {
115 | text = line.substring(line.indexOf(I_MBAT) + 8);
116 | success = true;
117 | break;
118 | }
119 | line = br.readLine();
120 | }
121 | fr.close();
122 | br.close();
123 | } catch (Exception e) {
124 | e.printStackTrace();
125 | }
126 | if (success) {
127 | try {
128 | value = Long.parseLong(text);
129 | } catch (NumberFormatException nfe) {
130 | nfe.printStackTrace();
131 | value = null;
132 | }
133 | }
134 | return value;
135 | }
136 |
137 | /**
138 | * read system file to get current value
139 | *
140 | * @param file
141 | * @param convertToMillis
142 | * @return current value
143 | */
144 | public Long getCurrentValue(File file, boolean convertToMillis) {
145 | Log.d(LOG_TAG, "*** getCurrentValue ***");
146 | Log.d(LOG_TAG, "*** " + convertToMillis + " ***");
147 | String line = null;
148 | Long value = null;
149 | FileInputStream fs = null;
150 | DataInputStream ds = null;
151 | try {
152 | fs = new FileInputStream(file);
153 | ds = new DataInputStream(fs);
154 | line = ds.readLine();
155 | } catch (Exception e) {
156 | e.printStackTrace();
157 | } finally {
158 | try {
159 | fs.close();
160 | ds.close();
161 | } catch (Exception ex) {
162 | ex.printStackTrace();
163 | }
164 | }
165 | if (line != null) {
166 | try {
167 | value = Long.parseLong(line);
168 | } catch (NumberFormatException nfe) {
169 | value = null;
170 | }
171 | if (convertToMillis)
172 | value = value / 1000;
173 | }
174 | return value;
175 | }
176 | }
177 |
--------------------------------------------------------------------------------
/src/com/netease/qa/emmagee/utils/CustomizedAuthenticator.java:
--------------------------------------------------------------------------------
1 | package com.netease.qa.emmagee.utils;
2 |
3 | import javax.mail.Authenticator;
4 | import javax.mail.PasswordAuthentication;
5 |
6 | /**
7 | * Customized Authenticator
8 | *
9 | * @author andrewleo
10 | */
11 | public class CustomizedAuthenticator extends Authenticator {
12 | String userName = null;
13 | String password = null;
14 |
15 | public CustomizedAuthenticator() {
16 | }
17 |
18 | public CustomizedAuthenticator(String username, String password) {
19 | this.userName = username;
20 | this.password = password;
21 | }
22 |
23 | protected PasswordAuthentication getPasswordAuthentication() {
24 | return new PasswordAuthentication(userName, password);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/com/netease/qa/emmagee/utils/EncryptData.java:
--------------------------------------------------------------------------------
1 | package com.netease.qa.emmagee.utils;
2 |
3 | import java.security.Key;
4 |
5 | import javax.crypto.BadPaddingException;
6 | import javax.crypto.Cipher;
7 | import javax.crypto.IllegalBlockSizeException;
8 |
9 | /**
10 | * 提供加密算法,可以对输入的字符串进行加密、解密操作
11 | *
12 | * @author andrewleo
13 | */
14 | public class EncryptData {
15 | private static String strDefaultKey = "emmagee";
16 |
17 | private Cipher encryptCipher = null;
18 |
19 | private Cipher decryptCipher = null;
20 |
21 | /**
22 | * 将byte数组转换为表示16进制值的字符串, 如:byte[]{8,18}转换为:0813, 和public static byte[]
23 | * hexStr2ByteArr(String strIn) 互为可逆的转换过程
24 | *
25 | * @param arrB
26 | * 需要转换的byte数组
27 | * @return 转换后的字符串
28 | * @throws Exception
29 | * 本方法不处理任何异常,所有异常全部抛出
30 | */
31 | public static String byteArr2HexStr(byte[] arrB) throws Exception {
32 | int iLen = arrB.length;
33 | // 每个byte用两个字符才能表示,所以字符串的长度是数组长度的两倍
34 | StringBuffer sb = new StringBuffer(iLen * 2);
35 | for (int i = 0; i < iLen; i++) {
36 | int intTmp = arrB[i];
37 | // 把负数转换为正数
38 | while (intTmp < 0) {
39 | intTmp = intTmp + 256;
40 | }
41 | // 小于0F的数需要在前面补0
42 | if (intTmp < 16) {
43 | sb.append("0");
44 | }
45 | sb.append(Integer.toString(intTmp, 16));
46 | }
47 | return sb.toString();
48 | }
49 |
50 | /**
51 | * 将表示16进制值的字符串转换为byte数组, 和public static String byteArr2HexStr(byte[] arrB)
52 | * 互为可逆的转换过程
53 | *
54 | * @param strIn
55 | * 需要转换的字符串
56 | * @return 转换后的byte数组
57 | * @throws Exception
58 | * 本方法不处理任何异常,所有异常全部抛出
59 | * @author
60 | */
61 | public static byte[] hexStr2ByteArr(String strIn) throws Exception {
62 | byte[] arrB = strIn.getBytes();
63 | int iLen = arrB.length;
64 |
65 | // 两个字符表示一个字节,所以字节数组长度是字符串长度除以2
66 | byte[] arrOut = new byte[iLen / 2];
67 | for (int i = 0; i < iLen; i = i + 2) {
68 | String strTmp = new String(arrB, i, 2);
69 | arrOut[i / 2] = (byte) Integer.parseInt(strTmp, 16);
70 | }
71 | return arrOut;
72 | }
73 |
74 | /**
75 | * 默认构造方法,使用默认密钥
76 | *
77 | * @throws Exception
78 | */
79 | public EncryptData() throws Exception {
80 | this(strDefaultKey);
81 | }
82 |
83 | /**
84 | * 指定密钥构造方法
85 | *
86 | * @param strKey
87 | * 指定的密钥
88 | * @throws Exception
89 | */
90 | public EncryptData(String strKey) {
91 | try {
92 | Key key = getKey(strKey.getBytes());
93 |
94 | encryptCipher = Cipher.getInstance("DES");
95 | encryptCipher.init(Cipher.ENCRYPT_MODE, key);
96 |
97 | decryptCipher = Cipher.getInstance("DES");
98 | decryptCipher.init(Cipher.DECRYPT_MODE, key);
99 | } catch (Exception e) {
100 | e.printStackTrace();
101 | }
102 | }
103 |
104 | /**
105 | * 加密字节数组
106 | *
107 | * @param arrB
108 | * 需加密的字节数组
109 | * @return 加密后的字节数组
110 | * @throws BadPaddingException
111 | * @throws IllegalBlockSizeException
112 | * @throws Exception
113 | */
114 | public byte[] encrypt(byte[] arrB) throws Exception {
115 | return encryptCipher.doFinal(arrB);
116 | }
117 |
118 | /**
119 | * 加密字符串
120 | *
121 | * @param strIn
122 | * 需加密的字符串
123 | * @return 加密后的字符串
124 | * @throws Exception
125 | */
126 | public String encrypt(String strIn) throws Exception {
127 | if (strIn == null) {
128 | strIn = "";
129 | }
130 | return byteArr2HexStr(encrypt(strIn.getBytes("utf-8")));
131 | }
132 |
133 | /**
134 | * 解密字节数组
135 | *
136 | * @param arrB
137 | * 需解密的字节数组
138 | * @return 解密后的字节数组
139 | * @throws Exception
140 | */
141 | public byte[] decrypt(byte[] arrB) throws Exception {
142 | return decryptCipher.doFinal(arrB);
143 | }
144 |
145 | /**
146 | * 解密字符串
147 | *
148 | * @param strIn
149 | * 需解密的字符串
150 | * @return 解密后的字符串
151 | * @throws Exception
152 | */
153 | public String decrypt(String strIn) throws Exception {
154 | return new String(decrypt(hexStr2ByteArr(strIn)));
155 | }
156 |
157 | /**
158 | * 从指定字符串生成密钥,密钥所需的字节数组长度为8位 不足8位时后面补0,超出8位只取前8位
159 | *
160 | * @param arrBTmp
161 | * 构成该字符串的字节数组
162 | * @return 生成的密钥
163 | * @throws java.lang.Exception
164 | */
165 | private Key getKey(byte[] arrBTmp) throws Exception {
166 | // 创建一个空的8位字节数组(默认值为0)
167 | byte[] arrB = new byte[8];
168 |
169 | // 将原始字节数组转换为8位
170 | for (int i = 0; i < arrBTmp.length && i < arrB.length; i++) {
171 | arrB[i] = arrBTmp[i];
172 | }
173 |
174 | // 生成密钥
175 | Key key = new javax.crypto.spec.SecretKeySpec(arrB, "DES");
176 |
177 | return key;
178 | }
179 | }
180 |
--------------------------------------------------------------------------------
/src/com/netease/qa/emmagee/utils/FpsInfo.java:
--------------------------------------------------------------------------------
1 | package com.netease.qa.emmagee.utils;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.DataOutputStream;
5 | import java.io.IOException;
6 | import java.io.InputStreamReader;
7 |
8 | public class FpsInfo {
9 |
10 | private static Process process;
11 | private static BufferedReader ir;
12 | private static DataOutputStream os = null;
13 | private static long startTime = 0L;
14 | private static int lastFrameNum = 0;
15 | private static boolean ok = true;
16 |
17 | /**
18 | * get frame per second
19 | *
20 | * @return frame per second
21 | */
22 | public static float fps() {
23 | if (ok) {
24 | long nowTime = System.nanoTime();
25 | float f = (float) (nowTime - startTime) / 1000000.0F;
26 | startTime = nowTime;
27 | int nowFrameNum = getFrameNum();
28 | final float fps = Math.round((nowFrameNum - lastFrameNum) * 1000
29 | / f);
30 | lastFrameNum = nowFrameNum;
31 | return fps;
32 | } else {
33 | return -1;
34 | }
35 |
36 | }
37 |
38 | /**
39 | * get frame value
40 | *
41 | * @return frame value
42 | */
43 | public static final int getFrameNum() {
44 | try {
45 | if (process == null) {
46 | process = Runtime.getRuntime().exec("su");
47 | os = new DataOutputStream(process.getOutputStream());
48 | ir = new BufferedReader(new InputStreamReader(
49 | process.getInputStream()));
50 | }
51 | os.writeBytes("service call SurfaceFlinger 1013" + "\n");
52 | os.flush();
53 | String str1 = ir.readLine();
54 | if (str1 != null) {
55 | int start = str1.indexOf("(");
56 | int end = str1.indexOf(" ");
57 | if ((start != -1) & (end > start)) {
58 | String str2 = str1.substring(start + 1, end);
59 | return Integer.parseInt((String) str2, 16);
60 | }
61 | }
62 | } catch (IOException e) {
63 | e.printStackTrace();
64 | }
65 | ok = false;
66 | return -1;
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/com/netease/qa/emmagee/utils/MailSender.java:
--------------------------------------------------------------------------------
1 | package com.netease.qa.emmagee.utils;
2 |
3 | import java.io.File;
4 | import java.util.Date;
5 | import java.util.Properties;
6 |
7 | import javax.activation.CommandMap;
8 | import javax.activation.DataHandler;
9 | import javax.activation.DataSource;
10 | import javax.activation.FileDataSource;
11 | import javax.activation.MailcapCommandMap;
12 | import javax.mail.Address;
13 | import javax.mail.BodyPart;
14 | import javax.mail.Message;
15 | import javax.mail.MessagingException;
16 | import javax.mail.Multipart;
17 | import javax.mail.Session;
18 | import javax.mail.Transport;
19 | import javax.mail.internet.InternetAddress;
20 | import javax.mail.internet.MimeBodyPart;
21 | import javax.mail.internet.MimeMessage;
22 | import javax.mail.internet.MimeMultipart;
23 |
24 | /**
25 | * 发送邮件给多个接收者、抄送邮件
26 | *
27 | * @author andrewleo
28 | */
29 | public class MailSender {
30 |
31 | private static final String LOG_TAG = MailSender.class.getSimpleName();
32 | private static final int PORT = 25;
33 |
34 | /**
35 | * 以文本格式发送邮件
36 | *
37 | * 待发送的邮件的信息
38 | */
39 | public static boolean sendTextMail(String sender, String encryptPassword, String smtp, String subject, String content, String file,
40 | String[] maillists) {
41 | if (maillists == null || maillists.length == 0 || ("".equals(maillists[0].trim()))) {
42 | return false;
43 | } else {
44 | // Get system properties
45 | Properties props = new Properties();
46 |
47 | // Setup mail server
48 | props.put("mail.smtp.host", smtp);
49 | props.put("mail.smtp.port", PORT);
50 | // Get session
51 | props.put("mail.smtp.auth", "true"); // 如果需要密码验证,把这里的false改成true
52 |
53 | // 判断是否需要身份认证
54 | CustomizedAuthenticator authenticator = null;
55 | if (true) {
56 | // 如果需要身份认证,则创建一个密码验证器
57 | authenticator = new CustomizedAuthenticator(sender, encryptPassword);
58 | }
59 | // 根据邮件会话属性和密码验证器构造一个发送邮件的session
60 | Session sendMailSession = Session.getInstance(props, authenticator);
61 | try {
62 | // 根据session创建一个邮件消息
63 | Message mailMessage = new MimeMessage(sendMailSession);
64 | // 创建邮件发送者地址
65 | Address from = new InternetAddress(sender);
66 | // 设置邮件消息的发送者
67 | mailMessage.setFrom(from);
68 | // 创建邮件的接收者地址,并设置到邮件消息中
69 | for (int i = 0; i < maillists.length; i++) {
70 | // Message.RecipientType.TO属性表示接收者的类型为TO
71 | mailMessage.addRecipient(Message.RecipientType.TO, new InternetAddress(maillists[i]));
72 | }
73 |
74 | // 设置邮件消息的主题
75 | mailMessage.setSubject(subject);
76 | // 设置邮件消息发送的时间
77 | mailMessage.setSentDate(new Date());
78 |
79 | Multipart multipart = new MimeMultipart();
80 | BodyPart bodyPart = new MimeBodyPart();
81 | bodyPart.setText(content);
82 | multipart.addBodyPart(bodyPart);
83 |
84 | File attach = new File(file);
85 | if (attach.exists()) {
86 | MimeBodyPart attachPart = new MimeBodyPart();
87 | DataSource source = new FileDataSource(attach);
88 | attachPart.setDataHandler(new DataHandler(source));
89 | attachPart.setFileName(attach.getName());
90 |
91 | multipart.addBodyPart(attachPart);
92 | }
93 | mailMessage.setContent(multipart);
94 | MailcapCommandMap mc = (MailcapCommandMap) CommandMap.getDefaultCommandMap();
95 | mc.addMailcap("text/html;; x-java-content-handler=com.sun.mail.handlers.text_html");
96 | mc.addMailcap("text/xml;; x-java-content-handler=com.sun.mail.handlers.text_xml");
97 | mc.addMailcap("text/plain;; x-java-content-handler=com.sun.mail.handlers.text_plain");
98 | mc.addMailcap("multipart/*;; x-java-content-handler=com.sun.mail.handlers.multipart_mixed");
99 | mc.addMailcap("message/rfc822;; x-java-content-handler=com.sun.mail.handlers.message_rfc822");
100 | CommandMap.setDefaultCommandMap(mc);
101 | Transport.send(mailMessage);
102 | return true;
103 | } catch (MessagingException ex) {
104 | ex.printStackTrace();
105 | }
106 | }
107 | return false;
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/src/com/netease/qa/emmagee/utils/MemoryInfo.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012-2013 NetEase, Inc. and other contributors
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 | package com.netease.qa.emmagee.utils;
18 |
19 | import java.io.BufferedReader;
20 | import java.io.DataOutputStream;
21 | import java.io.FileReader;
22 | import java.io.IOException;
23 | import java.io.InputStreamReader;
24 |
25 | import android.app.ActivityManager;
26 | import android.content.Context;
27 | import android.os.Debug;
28 | import android.util.Log;
29 |
30 | /**
31 | * operate memory information
32 | *
33 | * @author andrewleo
34 | */
35 | public class MemoryInfo {
36 |
37 | private static final String LOG_TAG = "Emmagee-" + MemoryInfo.class.getSimpleName();
38 |
39 | private static Process process;
40 |
41 | /**
42 | * get total memory of certain device.
43 | *
44 | * @return total memory of device
45 | */
46 | public long getTotalMemory() {
47 | String memInfoPath = "/proc/meminfo";
48 | String readTemp = "";
49 | String memTotal = "";
50 | long memory = 0;
51 | try {
52 | FileReader fr = new FileReader(memInfoPath);
53 | BufferedReader localBufferedReader = new BufferedReader(fr, 8192);
54 | while ((readTemp = localBufferedReader.readLine()) != null) {
55 | if (readTemp.contains("MemTotal")) {
56 | String[] total = readTemp.split(":");
57 | memTotal = total[1].trim();
58 | }
59 | }
60 | localBufferedReader.close();
61 | String[] memKb = memTotal.split(" ");
62 | memTotal = memKb[0].trim();
63 | Log.d(LOG_TAG, "memTotal: " + memTotal);
64 | memory = Long.parseLong(memTotal);
65 | } catch (IOException e) {
66 | Log.e(LOG_TAG, "IOException: " + e.getMessage());
67 | }
68 | return memory;
69 | }
70 |
71 | /**
72 | * get free memory.
73 | *
74 | * @return free memory of device
75 | *
76 | */
77 | public long getFreeMemorySize(Context context) {
78 | ActivityManager.MemoryInfo outInfo = new ActivityManager.MemoryInfo();
79 | ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
80 | am.getMemoryInfo(outInfo);
81 | long avaliMem = outInfo.availMem;
82 | return avaliMem / 1024;
83 | }
84 |
85 | /**
86 | * get the memory of process with certain pid.
87 | *
88 | * @param pid
89 | * pid of process
90 | * @param context
91 | * context of certain activity
92 | * @return memory usage of certain process
93 | */
94 | public int getPidMemorySize(int pid, Context context) {
95 | ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
96 | int[] myMempid = new int[] { pid };
97 | Debug.MemoryInfo[] memoryInfo = am.getProcessMemoryInfo(myMempid);
98 | memoryInfo[0].getTotalSharedDirty();
99 | int memSize = memoryInfo[0].getTotalPss();
100 | return memSize;
101 | }
102 |
103 | /**
104 | * get the sdk version of phone.
105 | *
106 | * @return sdk version
107 | */
108 | public String getSDKVersion() {
109 | return android.os.Build.VERSION.RELEASE;
110 | }
111 |
112 | /**
113 | * get phone type.
114 | *
115 | * @return phone type
116 | */
117 | public String getPhoneType() {
118 | return android.os.Build.MODEL;
119 | }
120 |
121 | /**
122 | * get app heap size, it is more importance than total memory
123 | *
124 | * @return heap size
125 | */
126 | public static String[][] getHeapSize(int pid, Context context) {
127 | String[][] heapData = parseMeminfo(pid);
128 | return heapData;
129 | }
130 |
131 | /**
132 | * dumpsys meminfo, and parse the result to get native and heap data
133 | *
134 | * @param pid
135 | * process id
136 | * @return native and heap data
137 | */
138 | public static String[][] parseMeminfo(int pid) {
139 |
140 | boolean infoStart = false;
141 | // [][],00:native heap size,01:native heap alloc;10: dalvik heap
142 | // size,11: dalvik heap alloc
143 | String[][] heapData = new String[2][2];
144 |
145 | try {
146 | Runtime runtime = Runtime.getRuntime();
147 | process = runtime.exec("su");
148 | DataOutputStream os = new DataOutputStream(process.getOutputStream());
149 | os.writeBytes("dumpsys meminfo " + pid + "\n");
150 | os.writeBytes("exit\n");
151 | os.flush();
152 |
153 | BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
154 | String line = "";
155 |
156 | while ((line = bufferedReader.readLine()) != null) {
157 | line = line.trim();
158 | if (line.contains("Permission Denial")) {
159 | break;
160 | } else {
161 | // 当读取到MEMINFO in pid 这一行时,下一行就是需要获取的数据
162 | if (line.contains("MEMINFO in pid")) {
163 | infoStart = true;
164 | } else if (infoStart) {
165 | String[] lineItems = line.split("\\s+");
166 | int length = lineItems.length;
167 | if (line.startsWith("size")) {
168 | heapData[0][0] = lineItems[1];
169 | heapData[1][0] = lineItems[2];
170 | } else if (line.startsWith("allocated")) {
171 | heapData[0][1] = lineItems[1];
172 | heapData[1][1] = lineItems[2];
173 | break;
174 | } else if (line.startsWith("Native")) {
175 | Log.d(LOG_TAG, "Native");
176 | Log.d(LOG_TAG, "lineItems[4]=" + lineItems[4]);
177 | Log.d(LOG_TAG, "lineItems[5]=" + lineItems[5]);
178 | heapData[0][0] = lineItems[length-3];
179 | heapData[0][1] = lineItems[length-2];
180 | } else if (line.startsWith("Dalvik")) {
181 | Log.d(LOG_TAG, "Dalvik");
182 | Log.d(LOG_TAG, "lineItems[4]=" + lineItems[4]);
183 | Log.d(LOG_TAG, "lineItems[5]=" + lineItems[5]);
184 | heapData[1][0] = lineItems[length-3];
185 | heapData[1][1] = lineItems[length-2];
186 | break;
187 | }
188 | }
189 | }
190 | }
191 | } catch (IOException e) {
192 | e.printStackTrace();
193 | }
194 | return heapData;
195 | }
196 | }
197 |
--------------------------------------------------------------------------------
/src/com/netease/qa/emmagee/utils/MyApplication.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012-2013 NetEase, Inc. and other contributors
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 | package com.netease.qa.emmagee.utils;
18 |
19 | import java.io.File;
20 |
21 | import android.app.Application;
22 | import android.view.WindowManager;
23 |
24 | /**
25 | * my application class
26 | *
27 | * @author andrewleo
28 | */
29 | public class MyApplication extends Application {
30 |
31 | private WindowManager.LayoutParams wmParams = new WindowManager.LayoutParams();
32 |
33 | public WindowManager.LayoutParams getMywmParams() {
34 | return wmParams;
35 | }
36 |
37 | @Override
38 | public void onCreate() {
39 | initAppConfig();
40 | super.onCreate();
41 | }
42 |
43 | private void initAppConfig() {
44 | // create directory of emmagee
45 | File dir = new File(Settings.EMMAGEE_RESULT_DIR);
46 | if (!dir.exists()) {
47 | dir.mkdirs();
48 | }
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/src/com/netease/qa/emmagee/utils/ProcessInfo.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012-2013 NetEase, Inc. and other contributors
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 | package com.netease.qa.emmagee.utils;
18 |
19 | import java.io.BufferedReader;
20 | import java.io.IOException;
21 | import java.io.InputStreamReader;
22 | import java.util.ArrayList;
23 | import java.util.Collections;
24 | import java.util.List;
25 |
26 | import android.app.ActivityManager;
27 | import android.app.ActivityManager.RunningAppProcessInfo;
28 | import android.app.ActivityManager.RunningTaskInfo;
29 | import android.content.Context;
30 | import android.content.pm.ApplicationInfo;
31 | import android.content.pm.PackageManager;
32 | import android.os.Build;
33 | import android.util.Log;
34 |
35 | /**
36 | * get information of processes
37 | *
38 | * @author andrewleo
39 | */
40 | public class ProcessInfo {
41 |
42 | private static final String LOG_TAG = "Emmagee-"
43 | + ProcessInfo.class.getSimpleName();
44 |
45 | private static final String PACKAGE_NAME = "com.netease.qa.emmagee";
46 | private static final int ANDROID_M = 22;
47 |
48 | /**
49 | * get information of all running processes,including package name ,process
50 | * name ,icon ,pid and uid.
51 | *
52 | * @param context
53 | * context of activity
54 | * @return running processes list
55 | */
56 | public List getRunningProcess(Context context) {
57 | Log.i(LOG_TAG, "get running processes");
58 | List progressList = new ArrayList();
59 | PackageManager pm = context.getPackageManager();
60 |
61 | ActivityManager am = (ActivityManager) context
62 | .getSystemService(Context.ACTIVITY_SERVICE);
63 | List run = am.getRunningAppProcesses();
64 | for (ApplicationInfo appinfo : getPackagesInfo(context)) {
65 | Programe programe = new Programe();
66 | if (((appinfo.flags & ApplicationInfo.FLAG_SYSTEM) > 0)
67 | || ((appinfo.processName != null) && (appinfo.processName
68 | .equals(PACKAGE_NAME)))) {
69 | continue;
70 | }
71 | for (RunningAppProcessInfo runningProcess : run) {
72 | if ((runningProcess.processName != null)
73 | && runningProcess.processName
74 | .equals(appinfo.processName)) {
75 | programe.setPid(runningProcess.pid);
76 | programe.setUid(runningProcess.uid);
77 | break;
78 | }
79 | }
80 | programe.setPackageName(appinfo.processName);
81 | programe.setProcessName(appinfo.loadLabel(pm).toString());
82 | programe.setIcon(appinfo.loadIcon(pm));
83 | progressList.add(programe);
84 | }
85 | Collections.sort(progressList);
86 | return progressList;
87 | }
88 |
89 | /**
90 | * get pid by package name
91 | *
92 | * @param context
93 | * context of activity
94 | * @return pid
95 | */
96 | public int getPidByPackageName(Context context, String packageName) {
97 | Log.i(LOG_TAG, "start getLaunchedPid");
98 | ActivityManager am = (ActivityManager) context
99 | .getSystemService(Context.ACTIVITY_SERVICE);
100 | // Note: getRunningAppProcesses return itself in API 22
101 | if (Build.VERSION.SDK_INT < ANDROID_M) {
102 | List run = am.getRunningAppProcesses();
103 | for (RunningAppProcessInfo runningProcess : run) {
104 | if ((runningProcess.processName != null)
105 | && runningProcess.processName.equals(packageName)) {
106 | return runningProcess.pid;
107 | }
108 | }
109 | } else {
110 | Log.i(LOG_TAG, "use top command to get pid");
111 | try {
112 | Process p = Runtime.getRuntime().exec("top -m 100 -n 1");
113 | BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(
114 | p.getInputStream()));
115 | String line = "";
116 | while ((line = bufferedReader.readLine()) != null) {
117 | if (line.contains(packageName)) {
118 | line = line.trim();
119 | String[] splitLine = line.split("\\s+");
120 | if (packageName.equals(splitLine[splitLine.length - 1])) {
121 | return Integer.parseInt(splitLine[0]);
122 | }
123 | }
124 | }
125 | } catch (IOException e1) {
126 | e1.printStackTrace();
127 | }
128 | }
129 | return 0;
130 | }
131 |
132 | /**
133 | * get information of all installed packages
134 | *
135 | * @param context
136 | * context of activity
137 | * @return all installed packages
138 | */
139 | public List getAllPackages(Context context) {
140 | Log.i(LOG_TAG, "getAllPackages");
141 | List progressList = new ArrayList();
142 | PackageManager pm = context.getPackageManager();
143 |
144 | for (ApplicationInfo appinfo : getPackagesInfo(context)) {
145 | Programe programe = new Programe();
146 | if (((appinfo.flags & ApplicationInfo.FLAG_SYSTEM) > 0)
147 | || ((appinfo.processName != null) && (appinfo.processName
148 | .equals(PACKAGE_NAME)))) {
149 | continue;
150 | }
151 | programe.setPackageName(appinfo.processName);
152 | programe.setProcessName(appinfo.loadLabel(pm).toString());
153 | programe.setIcon(appinfo.loadIcon(pm));
154 | progressList.add(programe);
155 | }
156 | Collections.sort(progressList);
157 | return progressList;
158 | }
159 |
160 | /**
161 | * get information of all applications.
162 | *
163 | * @param context
164 | * context of activity
165 | * @return packages information of all applications
166 | */
167 | private List getPackagesInfo(Context context) {
168 | PackageManager pm = context.getApplicationContext().getPackageManager();
169 | List appList = pm
170 | .getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES);
171 | return appList;
172 | }
173 |
174 | /**
175 | * get pid by package name
176 | *
177 | * @param context
178 | * context of activity
179 | * @param packageName
180 | * package name of monitoring app
181 | * @return pid
182 | */
183 | public Programe getProgrameByPackageName(Context context, String packageName) {
184 | if (Build.VERSION.SDK_INT < ANDROID_M) {
185 | List processList = getRunningProcess(context);
186 | for (Programe programe : processList) {
187 | if ((programe.getPackageName() != null)
188 | && (programe.getPackageName().equals(packageName))) {
189 | return programe;
190 | }
191 | }
192 | } else {
193 | Programe programe = new Programe();
194 | int pid = getPidByPackageName(context, packageName);
195 | programe.setPid(pid);
196 | programe.setUid(0);
197 | return programe;
198 | }
199 | return null;
200 | }
201 |
202 | /**
203 | * get top activity name
204 | *
205 | * @param context
206 | * context of activity
207 | * @return top activity name
208 | */
209 | public static String getTopActivity(Context context) {
210 | ActivityManager manager = (ActivityManager) context
211 | .getSystemService(Context.ACTIVITY_SERVICE);
212 | // Note: getRunningTasks is deprecated in API 21(Official)
213 | if (Build.VERSION.SDK_INT >= 21) {
214 | return Constants.NA;
215 | }
216 | List runningTaskInfos = manager.getRunningTasks(1);
217 | if (runningTaskInfos != null)
218 | return (runningTaskInfos.get(0).topActivity).toString();
219 | else
220 | return null;
221 | }
222 | }
223 |
--------------------------------------------------------------------------------
/src/com/netease/qa/emmagee/utils/Programe.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012-2013 NetEase, Inc. and other contributors
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 | package com.netease.qa.emmagee.utils;
18 |
19 | import android.graphics.drawable.Drawable;
20 |
21 | /**
22 | * details of installed processes ,including
23 | * icon,packagename,pid,uid,processname
24 | *
25 | * @author andrewleo
26 | */
27 | public class Programe implements Comparable {
28 | private Drawable icon;
29 | private String processName;
30 | private String packageName;
31 | private int pid;
32 | private int uid;
33 |
34 | public int getUid() {
35 | return uid;
36 | }
37 |
38 | public void setUid(int uid) {
39 | this.uid = uid;
40 | }
41 |
42 | public Drawable getIcon() {
43 | return icon;
44 | }
45 |
46 | public void setIcon(Drawable icon) {
47 | this.icon = icon;
48 | }
49 |
50 | public String getProcessName() {
51 | return processName;
52 | }
53 |
54 | public void setProcessName(String processName) {
55 |
56 | this.processName = processName;
57 | }
58 |
59 | public String getPackageName() {
60 | return packageName;
61 | }
62 |
63 | public void setPackageName(String packageName) {
64 | this.packageName = packageName;
65 | }
66 |
67 | public int getPid() {
68 | return pid;
69 | }
70 |
71 | public void setPid(int pid) {
72 | this.pid = pid;
73 | }
74 |
75 | @Override
76 | public int compareTo(Programe arg0) {
77 | return (this.getProcessName().compareTo(arg0.getProcessName()));
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/com/netease/qa/emmagee/utils/Settings.java:
--------------------------------------------------------------------------------
1 | package com.netease.qa.emmagee.utils;
2 |
3 | import android.content.Context;
4 | import android.content.SharedPreferences;
5 | import android.preference.PreferenceManager;
6 |
7 | /**
8 | * Parameters in Setting Activity
9 | *
10 | * @author yrom
11 | *
12 | */
13 | public final class Settings {
14 |
15 | public static final String KEY_SENDER = "sender";
16 | public static final String KEY_PASSWORD = "password";
17 | public static final String KEY_RECIPIENTS = "recipients";
18 | public static final String KEY_SMTP = "smtp";
19 | public static final String KEY_ISFLOAT = "isfloat";
20 | public static final String KEY_INTERVAL = "interval";
21 | public static final String KEY_ROOT = "root";
22 | public static final String KEY_AUTO_STOP = "autoStop";
23 | public static final String KEY_WACK_LOCK = "wakeLock";
24 | public static final String EMMAGEE_RESULT_DIR = "/sdcard/Emmagee/";
25 | private static WakeLockHelper wakeLockHelper;
26 |
27 | public static SharedPreferences getDefaultSharedPreferences(Context context) {
28 | return PreferenceManager.getDefaultSharedPreferences(context);
29 | }
30 |
31 | public static WakeLockHelper getDefaultWakeLock(Context context) {
32 | if (wakeLockHelper == null) {
33 | wakeLockHelper = new WakeLockHelper(context);
34 | }
35 | return wakeLockHelper;
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/src/com/netease/qa/emmagee/utils/TrafficInfo.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012-2013 NetEase, Inc. and other contributors
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 | package com.netease.qa.emmagee.utils;
18 |
19 | import java.io.IOException;
20 | import java.io.RandomAccessFile;
21 |
22 | import android.net.TrafficStats;
23 | import android.util.Log;
24 |
25 | /**
26 | * information of network traffic
27 | *
28 | * @author andrewleo
29 | */
30 | public class TrafficInfo {
31 |
32 | private static final String LOG_TAG = "Emmagee-" + TrafficInfo.class.getSimpleName();
33 | private static final int UNSUPPORTED = -1;
34 |
35 | private String uid;
36 |
37 | public TrafficInfo(String uid) {
38 | this.uid = uid;
39 | }
40 |
41 | /**
42 | * get total network traffic, which is the sum of upload and download
43 | * traffic.
44 | *
45 | * @return total traffic include received and send traffic
46 | */
47 | public long getTrafficInfo() {
48 | Log.i(LOG_TAG, "get traffic information");
49 | Log.d(LOG_TAG, "uid = " + uid);
50 | long traffic = trafficFromApi();
51 | return traffic <= 0 ? trafficFromFiles() : traffic;
52 | }
53 |
54 | /**
55 | * Use TrafficStats getUidRxBytes and getUidTxBytes to get network
56 | * traffic,these API return both tcp and udp usage
57 | *
58 | * @return
59 | */
60 | private long trafficFromApi() {
61 | long rcvTraffic = UNSUPPORTED, sndTraffic = UNSUPPORTED;
62 | rcvTraffic = TrafficStats.getUidRxBytes(Integer.parseInt(uid));
63 | sndTraffic = TrafficStats.getUidTxBytes(Integer.parseInt(uid));
64 | return rcvTraffic + sndTraffic < 0 ? UNSUPPORTED : rcvTraffic + sndTraffic;
65 | }
66 |
67 | /**
68 | * read files in uid_stat to get traffic info
69 | *
70 | * @return
71 | */
72 | private long trafficFromFiles() {
73 | RandomAccessFile rafRcv = null, rafSnd = null;
74 | long rcvTraffic = UNSUPPORTED, sndTraffic = UNSUPPORTED;
75 | String rcvPath = "/proc/uid_stat/" + uid + "/tcp_rcv";
76 | String sndPath = "/proc/uid_stat/" + uid + "/tcp_snd";
77 | try {
78 | rafRcv = new RandomAccessFile(rcvPath, "r");
79 | rafSnd = new RandomAccessFile(sndPath, "r");
80 | rcvTraffic = Long.parseLong(rafRcv.readLine());
81 | sndTraffic = Long.parseLong(rafSnd.readLine());
82 | Log.d(LOG_TAG, String.format("rcvTraffic, sndTraffic = %s, %s", rcvTraffic, sndTraffic));
83 | } catch (Exception e) {
84 | }
85 | finally {
86 | try {
87 | if (rafRcv != null) {
88 | rafRcv.close();
89 | }
90 | if (rafSnd != null)
91 | rafSnd.close();
92 | } catch (IOException e) {}
93 | }
94 | return rcvTraffic + sndTraffic < 0 ? UNSUPPORTED : rcvTraffic + sndTraffic;
95 | }
96 |
97 | }
98 |
--------------------------------------------------------------------------------
/src/com/netease/qa/emmagee/utils/WakeLockHelper.java:
--------------------------------------------------------------------------------
1 | package com.netease.qa.emmagee.utils;
2 |
3 | import android.content.Context;
4 | import android.os.PowerManager;
5 | import android.os.PowerManager.WakeLock;
6 |
7 | public class WakeLockHelper {
8 | private WakeLock wakeLock = null;
9 | private Context context;
10 |
11 | public WakeLockHelper(Context context) {
12 | this.context = context;
13 | }
14 |
15 | public void acquireFullWakeLock() {
16 | if (null == wakeLock) {
17 | PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
18 | wakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, "WakeLock");
19 | }
20 | wakeLock.acquire();
21 | }
22 |
23 | public void releaseWakeLock() {
24 | if (null != wakeLock) {
25 | wakeLock.release();
26 | wakeLock = null;
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------