blob: e2cb65d719fda465d859127bd56d26d71ae05171 [file] [log] [blame]
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tests.applaunch;
import android.app.ActivityManager;
import android.app.ActivityManager.ProcessErrorStateInfo;
import android.app.ActivityManagerNative;
import android.app.IActivityManager;
import android.app.IActivityManager.WaitResult;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserHandle;
import android.test.InstrumentationTestCase;
import android.test.InstrumentationTestRunner;
import android.util.Log;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* This test is intended to measure the time it takes for the apps to start.
* Names of the applications are passed in command line, and the
* test starts each application, and reports the start up time in milliseconds.
* The instrumentation expects the following key to be passed on the command line:
* apps - A list of applications to start and their corresponding result keys
* in the following format:
* -e apps <app name>^<result key>|<app name>^<result key>
*/
public class AppLaunch extends InstrumentationTestCase {
private static final int JOIN_TIMEOUT = 10000;
private static final String TAG = "AppLaunch";
private static final String KEY_APPS = "apps";
private Map<String, Intent> mNameToIntent;
private Map<String, String> mNameToProcess;
private Map<String, String> mNameToResultKey;
private IActivityManager mAm;
public void testMeasureStartUpTime() throws RemoteException {
InstrumentationTestRunner instrumentation =
(InstrumentationTestRunner)getInstrumentation();
Bundle args = instrumentation.getBundle();
mAm = ActivityManagerNative.getDefault();
createMappings();
parseArgs(args);
Bundle results = new Bundle();
for (String app : mNameToResultKey.keySet()) {
try {
startApp(app, results);
sleep(750);
closeApp(app);
sleep(2000);
} catch (NameNotFoundException e) {
Log.i(TAG, "Application " + app + " not found");
}
}
instrumentation.sendStatus(0, results);
}
private void parseArgs(Bundle args) {
mNameToResultKey = new LinkedHashMap<String, String>();
String appList = args.getString(KEY_APPS);
if (appList == null)
return;
String appNames[] = appList.split("\\|");
for (String pair : appNames) {
String[] parts = pair.split("\\^");
if (parts.length != 2) {
Log.e(TAG, "The apps key is incorectly formatted");
fail();
}
mNameToResultKey.put(parts[0], parts[1]);
}
}
private void createMappings() {
mNameToIntent = new LinkedHashMap<String, Intent>();
mNameToProcess = new LinkedHashMap<String, String>();
PackageManager pm = getInstrumentation().getContext()
.getPackageManager();
Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
List<ResolveInfo> ris = pm.queryIntentActivities(intentToResolve, 0);
if (ris == null || ris.isEmpty()) {
Log.i(TAG, "Could not find any apps");
} else {
for (ResolveInfo ri : ris) {
Intent startIntent = new Intent(intentToResolve);
startIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
startIntent.setClassName(ri.activityInfo.packageName,
ri.activityInfo.name);
mNameToIntent.put(ri.loadLabel(pm).toString(), startIntent);
mNameToProcess.put(ri.loadLabel(pm).toString(),
ri.activityInfo.processName);
}
}
}
private void startApp(String appName, Bundle results)
throws NameNotFoundException, RemoteException {
Log.i(TAG, "Starting " + appName);
Intent startIntent = mNameToIntent.get(appName);
if (startIntent == null) {
Log.w(TAG, "App does not exist: " + appName);
return;
}
AppLaunchRunnable runnable = new AppLaunchRunnable(startIntent);
Thread t = new Thread(runnable);
t.start();
try {
t.join(JOIN_TIMEOUT);
} catch (InterruptedException e) {
// ignore
}
WaitResult result = runnable.getResult();
if(t.isAlive() || (result != null && result.result != ActivityManager.START_SUCCESS)) {
Log.w(TAG, "Assuming app " + appName + " crashed.");
reportError(appName, mNameToProcess.get(appName), results);
return;
}
results.putString(mNameToResultKey.get(appName), String.valueOf(result.thisTime));
}
private void closeApp(String appName) {
Intent homeIntent = new Intent(Intent.ACTION_MAIN);
homeIntent.addCategory(Intent.CATEGORY_HOME);
homeIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
getInstrumentation().getContext().startActivity(homeIntent);
Intent startIntent = mNameToIntent.get(appName);
if (startIntent != null) {
String packageName = startIntent.getComponent().getPackageName();
try {
mAm.forceStopPackage(packageName, UserHandle.USER_CURRENT);
} catch (RemoteException e) {
Log.w(TAG, "Error closing app", e);
}
}
}
private void sleep(int time) {
try {
Thread.sleep(time);
} catch (InterruptedException e) {
// ignore
}
}
private void reportError(String appName, String processName, Bundle results) {
ActivityManager am = (ActivityManager) getInstrumentation()
.getContext().getSystemService(Context.ACTIVITY_SERVICE);
List<ProcessErrorStateInfo> crashes = am.getProcessesInErrorState();
if (crashes != null) {
for (ProcessErrorStateInfo crash : crashes) {
if (!crash.processName.equals(processName))
continue;
Log.w(TAG, appName + " crashed: " + crash.shortMsg);
results.putString(mNameToResultKey.get(appName), crash.shortMsg);
return;
}
}
results.putString(mNameToResultKey.get(appName),
"Crashed for unknown reason");
Log.w(TAG, appName
+ " not found in process list, most likely it is crashed");
}
private class AppLaunchRunnable implements Runnable {
private Intent mLaunchIntent;
private IActivityManager.WaitResult mResult;
public AppLaunchRunnable(Intent intent) {
mLaunchIntent = intent;
}
public IActivityManager.WaitResult getResult() {
return mResult;
}
public void run() {
try {
String packageName = mLaunchIntent.getComponent().getPackageName();
mAm.forceStopPackage(packageName, UserHandle.USER_CURRENT);
String mimeType = mLaunchIntent.getType();
if (mimeType == null && mLaunchIntent.getData() != null
&& "content".equals(mLaunchIntent.getData().getScheme())) {
mimeType = mAm.getProviderMimeType(mLaunchIntent.getData(),
UserHandle.USER_CURRENT);
}
mResult = mAm.startActivityAndWait(null, mLaunchIntent, mimeType,
null, null, 0, mLaunchIntent.getFlags(), null, null, null,
UserHandle.USER_CURRENT);
} catch (RemoteException e) {
Log.w(TAG, "Error launching app", e);
}
}
}
}