Skip to content

Commit 007c64f

Browse files
Maxim SiniavineAndroid (Google) Code Review
authored andcommitted
Merge "Added app launch test." into jb-mr1.1-dev
2 parents 3f64ede + f58e5b6 commit 007c64f

File tree

3 files changed

+246
-0
lines changed

3 files changed

+246
-0
lines changed

tests/AppLaunch/Android.mk

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
LOCAL_PATH:= $(call my-dir)
2+
include $(CLEAR_VARS)
3+
4+
LOCAL_MODULE_TAGS := tests
5+
6+
# Only compile source java files in this apk.
7+
LOCAL_SRC_FILES := $(call all-java-files-under, src)
8+
9+
LOCAL_PACKAGE_NAME := AppLaunch
10+
11+
LOCAL_CERTIFICATE := platform
12+
LOCAL_JAVA_LIBRARIES := android.test.runner
13+
14+
include $(BUILD_PACKAGE)
15+
16+
# Use the following include to make our test apk.
17+
include $(call all-makefiles-under,$(LOCAL_PATH))
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
3+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
4+
package="com.android.tests.applaunch"
5+
android:sharedUserId="android.uid.system" >
6+
<instrumentation android:label="Measure app start up time"
7+
android:name="android.test.InstrumentationTestRunner"
8+
android:targetPackage="com.android.tests.applaunch" />
9+
10+
<application android:label="App Launch Test">
11+
<uses-library android:name="android.test.runner" />
12+
</application>
13+
</manifest>
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
/*
2+
* Copyright (C) 2012 The Android Open Source Project
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+
package com.android.tests.applaunch;
17+
18+
import android.app.ActivityManager;
19+
import android.app.ActivityManager.ProcessErrorStateInfo;
20+
import android.app.ActivityManagerNative;
21+
import android.app.IActivityManager;
22+
import android.content.Context;
23+
import android.content.Intent;
24+
import android.content.pm.PackageManager;
25+
import android.content.pm.PackageManager.NameNotFoundException;
26+
import android.content.pm.ResolveInfo;
27+
import android.os.Bundle;
28+
import android.os.RemoteException;
29+
import android.os.UserHandle;
30+
import android.test.InstrumentationTestCase;
31+
import android.test.InstrumentationTestRunner;
32+
import android.util.Log;
33+
34+
import java.util.HashMap;
35+
import java.util.List;
36+
import java.util.Map;
37+
38+
/**
39+
* This test is intended to measure the time it takes for the apps to start.
40+
* Names of the applications are passed in command line, and the
41+
* test starts each application, and reports the start up time in milliseconds.
42+
* The instrumentation expects the following key to be passed on the command line:
43+
* apps - A list of applications to start and their corresponding result keys
44+
* in the following format:
45+
* -e apps <app name>^<result key>|<app name>^<result key>
46+
*/
47+
public class AppLaunch extends InstrumentationTestCase {
48+
49+
private static final int JOIN_TIMEOUT = 10000;
50+
private static final String TAG = "AppLaunch";
51+
private static final String KEY_APPS = "apps";
52+
53+
private Map<String, Intent> mNameToIntent;
54+
private Map<String, String> mNameToProcess;
55+
private Map<String, String> mNameToResultKey;
56+
57+
private IActivityManager mAm;
58+
59+
public void testMeasureStartUpTime() throws RemoteException {
60+
InstrumentationTestRunner instrumentation =
61+
(InstrumentationTestRunner)getInstrumentation();
62+
Bundle args = instrumentation.getBundle();
63+
mAm = ActivityManagerNative.getDefault();
64+
65+
createMappings();
66+
parseArgs(args);
67+
68+
Bundle results = new Bundle();
69+
for (String app : mNameToResultKey.keySet()) {
70+
try {
71+
startApp(app, results);
72+
closeApp();
73+
} catch (NameNotFoundException e) {
74+
Log.i(TAG, "Application " + app + " not found");
75+
}
76+
77+
}
78+
instrumentation.sendStatus(0, results);
79+
}
80+
81+
private void parseArgs(Bundle args) {
82+
mNameToResultKey = new HashMap<String, String>();
83+
String appList = args.getString(KEY_APPS);
84+
85+
if (appList == null)
86+
return;
87+
88+
String appNames[] = appList.split("\\|");
89+
for (String pair : appNames) {
90+
String[] parts = pair.split("\\^");
91+
if (parts.length != 2) {
92+
Log.e(TAG, "The apps key is incorectly formatted");
93+
fail();
94+
}
95+
96+
mNameToResultKey.put(parts[0], parts[1]);
97+
}
98+
}
99+
100+
private void createMappings() {
101+
mNameToIntent = new HashMap<String, Intent>();
102+
mNameToProcess = new HashMap<String, String>();
103+
104+
PackageManager pm = getInstrumentation().getContext()
105+
.getPackageManager();
106+
Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
107+
intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
108+
List<ResolveInfo> ris = pm.queryIntentActivities(intentToResolve, 0);
109+
if (ris == null || ris.isEmpty()) {
110+
Log.i(TAG, "Could not find any apps");
111+
} else {
112+
for (ResolveInfo ri : ris) {
113+
Intent startIntent = new Intent(intentToResolve);
114+
startIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
115+
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
116+
startIntent.setClassName(ri.activityInfo.packageName,
117+
ri.activityInfo.name);
118+
mNameToIntent.put(ri.loadLabel(pm).toString(), startIntent);
119+
mNameToProcess.put(ri.loadLabel(pm).toString(),
120+
ri.activityInfo.processName);
121+
}
122+
}
123+
}
124+
125+
private void startApp(String appName, Bundle results)
126+
throws NameNotFoundException, RemoteException {
127+
Log.i(TAG, "Starting " + appName);
128+
129+
Intent startIntent = mNameToIntent.get(appName);
130+
AppLaunchRunnable runnable = new AppLaunchRunnable(startIntent);
131+
Thread t = new Thread(runnable);
132+
long startTime = System.currentTimeMillis();
133+
t.start();
134+
try {
135+
t.join(JOIN_TIMEOUT);
136+
} catch (InterruptedException e) {
137+
// ignore
138+
}
139+
if(t.isAlive() || (runnable.getResult() != null &&
140+
runnable.getResult().result != ActivityManager.START_SUCCESS)) {
141+
Log.w(TAG, "Assuming app " + appName + " crashed.");
142+
reportError(appName, mNameToProcess.get(appName), results);
143+
return;
144+
}
145+
long startUpTime = System.currentTimeMillis() - startTime;
146+
results.putString(mNameToResultKey.get(appName), String.valueOf(startUpTime));
147+
sleep(5000);
148+
}
149+
150+
private void closeApp() {
151+
Intent homeIntent = new Intent(Intent.ACTION_MAIN);
152+
homeIntent.addCategory(Intent.CATEGORY_HOME);
153+
homeIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
154+
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
155+
getInstrumentation().getContext().startActivity(homeIntent);
156+
sleep(3000);
157+
}
158+
159+
private void sleep(int time) {
160+
try {
161+
Thread.sleep(time);
162+
} catch (InterruptedException e) {
163+
// ignore
164+
}
165+
}
166+
167+
private void reportError(String appName, String processName, Bundle results) {
168+
ActivityManager am = (ActivityManager) getInstrumentation()
169+
.getContext().getSystemService(Context.ACTIVITY_SERVICE);
170+
List<ProcessErrorStateInfo> crashes = am.getProcessesInErrorState();
171+
if (crashes != null) {
172+
for (ProcessErrorStateInfo crash : crashes) {
173+
if (!crash.processName.equals(processName))
174+
continue;
175+
176+
Log.w(TAG, appName + " crashed: " + crash.shortMsg);
177+
results.putString(mNameToResultKey.get(appName), crash.shortMsg);
178+
return;
179+
}
180+
}
181+
182+
results.putString(mNameToResultKey.get(appName),
183+
"Crashed for unknown reason");
184+
Log.w(TAG, appName
185+
+ " not found in process list, most likely it is crashed");
186+
}
187+
188+
private class AppLaunchRunnable implements Runnable {
189+
private Intent mLaunchIntent;
190+
private IActivityManager.WaitResult mResult;
191+
public AppLaunchRunnable(Intent intent) {
192+
mLaunchIntent = intent;
193+
}
194+
195+
public IActivityManager.WaitResult getResult() {
196+
return mResult;
197+
}
198+
199+
public void run() {
200+
try {
201+
String mimeType = mLaunchIntent.getType();
202+
if (mimeType == null && mLaunchIntent.getData() != null
203+
&& "content".equals(mLaunchIntent.getData().getScheme())) {
204+
mimeType = mAm.getProviderMimeType(mLaunchIntent.getData(),
205+
UserHandle.USER_CURRENT);
206+
}
207+
208+
mResult = mAm.startActivityAndWait(null, mLaunchIntent, mimeType,
209+
null, null, 0, mLaunchIntent.getFlags(), null, null, null,
210+
UserHandle.USER_CURRENT);
211+
} catch (RemoteException e) {
212+
Log.w(TAG, "Error launching app", e);
213+
}
214+
}
215+
}
216+
}

0 commit comments

Comments
 (0)