AppLaunch.java revision 13f77349778fbe95d665542be6f05006879854e2
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 */ 16package com.android.tests.applaunch; 17 18import android.app.ActivityManager; 19import android.app.ActivityManager.ProcessErrorStateInfo; 20import android.app.ActivityManagerNative; 21import android.app.IActivityManager; 22import android.app.IActivityManager.WaitResult; 23import android.content.Context; 24import android.content.Intent; 25import android.content.pm.PackageManager; 26import android.content.pm.PackageManager.NameNotFoundException; 27import android.content.pm.ResolveInfo; 28import android.os.Bundle; 29import android.os.RemoteException; 30import android.os.UserHandle; 31import android.test.InstrumentationTestCase; 32import android.test.InstrumentationTestRunner; 33import android.util.Log; 34 35import java.util.LinkedHashMap; 36import java.util.List; 37import java.util.Map; 38 39/** 40 * This test is intended to measure the time it takes for the apps to start. 41 * Names of the applications are passed in command line, and the 42 * test starts each application, and reports the start up time in milliseconds. 43 * The instrumentation expects the following key to be passed on the command line: 44 * apps - A list of applications to start and their corresponding result keys 45 * in the following format: 46 * -e apps <app name>^<result key>|<app name>^<result key> 47 */ 48public class AppLaunch extends InstrumentationTestCase { 49 50 private static final int JOIN_TIMEOUT = 10000; 51 private static final String TAG = "AppLaunch"; 52 private static final String KEY_APPS = "apps"; 53 54 private Map<String, Intent> mNameToIntent; 55 private Map<String, String> mNameToProcess; 56 private Map<String, String> mNameToResultKey; 57 58 private IActivityManager mAm; 59 60 public void testMeasureStartUpTime() throws RemoteException { 61 InstrumentationTestRunner instrumentation = 62 (InstrumentationTestRunner)getInstrumentation(); 63 Bundle args = instrumentation.getBundle(); 64 mAm = ActivityManagerNative.getDefault(); 65 66 createMappings(); 67 parseArgs(args); 68 69 Bundle results = new Bundle(); 70 for (String app : mNameToResultKey.keySet()) { 71 try { 72 startApp(app, results); 73 sleep(750); 74 closeApp(app); 75 sleep(2000); 76 } catch (NameNotFoundException e) { 77 Log.i(TAG, "Application " + app + " not found"); 78 } 79 80 } 81 instrumentation.sendStatus(0, results); 82 } 83 84 private void parseArgs(Bundle args) { 85 mNameToResultKey = new LinkedHashMap<String, String>(); 86 String appList = args.getString(KEY_APPS); 87 88 if (appList == null) 89 return; 90 91 String appNames[] = appList.split("\\|"); 92 for (String pair : appNames) { 93 String[] parts = pair.split("\\^"); 94 if (parts.length != 2) { 95 Log.e(TAG, "The apps key is incorectly formatted"); 96 fail(); 97 } 98 99 mNameToResultKey.put(parts[0], parts[1]); 100 } 101 } 102 103 private void createMappings() { 104 mNameToIntent = new LinkedHashMap<String, Intent>(); 105 mNameToProcess = new LinkedHashMap<String, String>(); 106 107 PackageManager pm = getInstrumentation().getContext() 108 .getPackageManager(); 109 Intent intentToResolve = new Intent(Intent.ACTION_MAIN); 110 intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER); 111 List<ResolveInfo> ris = pm.queryIntentActivities(intentToResolve, 0); 112 if (ris == null || ris.isEmpty()) { 113 Log.i(TAG, "Could not find any apps"); 114 } else { 115 for (ResolveInfo ri : ris) { 116 Intent startIntent = new Intent(intentToResolve); 117 startIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 118 | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 119 startIntent.setClassName(ri.activityInfo.packageName, 120 ri.activityInfo.name); 121 mNameToIntent.put(ri.loadLabel(pm).toString(), startIntent); 122 mNameToProcess.put(ri.loadLabel(pm).toString(), 123 ri.activityInfo.processName); 124 } 125 } 126 } 127 128 private void startApp(String appName, Bundle results) 129 throws NameNotFoundException, RemoteException { 130 Log.i(TAG, "Starting " + appName); 131 132 Intent startIntent = mNameToIntent.get(appName); 133 if (startIntent == null) { 134 Log.w(TAG, "App does not exist: " + appName); 135 return; 136 } 137 AppLaunchRunnable runnable = new AppLaunchRunnable(startIntent); 138 Thread t = new Thread(runnable); 139 t.start(); 140 try { 141 t.join(JOIN_TIMEOUT); 142 } catch (InterruptedException e) { 143 // ignore 144 } 145 WaitResult result = runnable.getResult(); 146 if(t.isAlive() || (result != null && result.result != ActivityManager.START_SUCCESS)) { 147 Log.w(TAG, "Assuming app " + appName + " crashed."); 148 reportError(appName, mNameToProcess.get(appName), results); 149 return; 150 } 151 results.putString(mNameToResultKey.get(appName), String.valueOf(result.thisTime)); 152 } 153 154 private void closeApp(String appName) { 155 Intent homeIntent = new Intent(Intent.ACTION_MAIN); 156 homeIntent.addCategory(Intent.CATEGORY_HOME); 157 homeIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 158 | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 159 getInstrumentation().getContext().startActivity(homeIntent); 160 Intent startIntent = mNameToIntent.get(appName); 161 if (startIntent != null) { 162 String packageName = startIntent.getComponent().getPackageName(); 163 try { 164 mAm.forceStopPackage(packageName, UserHandle.USER_CURRENT); 165 } catch (RemoteException e) { 166 Log.w(TAG, "Error closing app", e); 167 } 168 } 169 } 170 171 private void sleep(int time) { 172 try { 173 Thread.sleep(time); 174 } catch (InterruptedException e) { 175 // ignore 176 } 177 } 178 179 private void reportError(String appName, String processName, Bundle results) { 180 ActivityManager am = (ActivityManager) getInstrumentation() 181 .getContext().getSystemService(Context.ACTIVITY_SERVICE); 182 List<ProcessErrorStateInfo> crashes = am.getProcessesInErrorState(); 183 if (crashes != null) { 184 for (ProcessErrorStateInfo crash : crashes) { 185 if (!crash.processName.equals(processName)) 186 continue; 187 188 Log.w(TAG, appName + " crashed: " + crash.shortMsg); 189 results.putString(mNameToResultKey.get(appName), crash.shortMsg); 190 return; 191 } 192 } 193 194 results.putString(mNameToResultKey.get(appName), 195 "Crashed for unknown reason"); 196 Log.w(TAG, appName 197 + " not found in process list, most likely it is crashed"); 198 } 199 200 private class AppLaunchRunnable implements Runnable { 201 private Intent mLaunchIntent; 202 private IActivityManager.WaitResult mResult; 203 public AppLaunchRunnable(Intent intent) { 204 mLaunchIntent = intent; 205 } 206 207 public IActivityManager.WaitResult getResult() { 208 return mResult; 209 } 210 211 public void run() { 212 try { 213 String packageName = mLaunchIntent.getComponent().getPackageName(); 214 mAm.forceStopPackage(packageName, UserHandle.USER_CURRENT); 215 String mimeType = mLaunchIntent.getType(); 216 if (mimeType == null && mLaunchIntent.getData() != null 217 && "content".equals(mLaunchIntent.getData().getScheme())) { 218 mimeType = mAm.getProviderMimeType(mLaunchIntent.getData(), 219 UserHandle.USER_CURRENT); 220 } 221 222 mResult = mAm.startActivityAndWait(null, mLaunchIntent, mimeType, 223 null, null, 0, mLaunchIntent.getFlags(), null, null, null, 224 UserHandle.USER_CURRENT); 225 } catch (RemoteException e) { 226 Log.w(TAG, "Error launching app", e); 227 } 228 } 229 } 230} 231