Merge "App launch - Test app changes" into nyc-dev

This commit is contained in:
Gopinath Elanchezhian
2016-07-12 16:02:42 +00:00
committed by Android (Google) Code Review
3 changed files with 378 additions and 90 deletions

View File

@@ -11,6 +11,8 @@ LOCAL_PACKAGE_NAME := AppLaunch
LOCAL_CERTIFICATE := platform LOCAL_CERTIFICATE := platform
LOCAL_JAVA_LIBRARIES := android.test.runner LOCAL_JAVA_LIBRARIES := android.test.runner
LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
include $(BUILD_PACKAGE) include $(BUILD_PACKAGE)
# Use the following include to make our test apk. # Use the following include to make our test apk.

View File

@@ -3,6 +3,14 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.tests.applaunch" package="com.android.tests.applaunch"
android:sharedUserId="android.uid.system" > android:sharedUserId="android.uid.system" >
<uses-permission android:name="android.permission.REAL_GET_TASKS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-sdk
android:minSdkVersion="22"
android:targetSdkVersion="24" />
<instrumentation android:label="Measure app start up time" <instrumentation android:label="Measure app start up time"
android:name="android.test.InstrumentationTestRunner" android:name="android.test.InstrumentationTestRunner"
android:targetPackage="com.android.tests.applaunch" /> android:targetPackage="com.android.tests.applaunch" />

View File

@@ -15,14 +15,13 @@
*/ */
package com.android.tests.applaunch; package com.android.tests.applaunch;
import java.io.OutputStreamWriter;
import android.accounts.Account; import android.accounts.Account;
import android.accounts.AccountManager; import android.accounts.AccountManager;
import android.app.ActivityManagerNative;
import android.app.ActivityManager; import android.app.ActivityManager;
import android.app.ActivityManager.ProcessErrorStateInfo; import android.app.ActivityManager.ProcessErrorStateInfo;
import android.app.ActivityManagerNative;
import android.app.IActivityManager;
import android.app.IActivityManager.WaitResult;
import android.app.UiAutomation;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
@@ -31,16 +30,29 @@ import android.content.pm.ResolveInfo;
import android.os.Bundle; import android.os.Bundle;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.UserHandle; import android.os.UserHandle;
import android.app.UiAutomation;
import android.app.IActivityManager;
import android.app.IActivityManager.WaitResult;
import android.support.test.rule.logging.AtraceLogger;
import android.test.InstrumentationTestCase; import android.test.InstrumentationTestCase;
import android.test.InstrumentationTestRunner; import android.test.InstrumentationTestRunner;
import android.util.Log; import android.util.Log;
import java.io.File;
import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.ArrayList;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import android.os.ParcelFileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
/** /**
* This test is intended to measure the time it takes for the apps to start. * This test is intended to measure the time it takes for the apps to start.
@@ -55,27 +67,66 @@ public class AppLaunch extends InstrumentationTestCase {
private static final int JOIN_TIMEOUT = 10000; private static final int JOIN_TIMEOUT = 10000;
private static final String TAG = AppLaunch.class.getSimpleName(); private static final String TAG = AppLaunch.class.getSimpleName();
private static final String KEY_APPS = "apps";
private static final String KEY_LAUNCH_ITERATIONS = "launch_iterations";
// optional parameter: comma separated list of required account types before proceeding // optional parameter: comma separated list of required account types before proceeding
// with the app launch // with the app launch
private static final String KEY_REQUIRED_ACCOUNTS = "required_accounts"; private static final String KEY_REQUIRED_ACCOUNTS = "required_accounts";
private static final String KEY_SKIP_INITIAL_LAUNCH = "skip_initial_launch"; private static final String KEY_APPS = "apps";
private static final String KEY_TRIAL_LAUNCH = "trial_launch";
private static final String KEY_LAUNCH_ITERATIONS = "launch_iterations";
private static final String KEY_LAUNCH_ORDER = "launch_order";
private static final String KEY_DROP_CACHE = "drop_cache";
private static final String KEY_SIMPLEPPERF_CMD = "simpleperf_cmd";
private static final String KEY_TRACE_ITERATIONS = "trace_iterations";
private static final String KEY_LAUNCH_DIRECTORY = "launch_directory";
private static final String KEY_TRACE_DIRECTORY = "trace_directory";
private static final String KEY_TRACE_CATEGORY = "trace_categories";
private static final String KEY_TRACE_BUFFERSIZE = "trace_bufferSize";
private static final String KEY_TRACE_DUMPINTERVAL = "tracedump_interval";
private static final String WEARABLE_ACTION_GOOGLE = private static final String WEARABLE_ACTION_GOOGLE =
"com.google.android.wearable.action.GOOGLE"; "com.google.android.wearable.action.GOOGLE";
private static final int INITIAL_LAUNCH_IDLE_TIMEOUT = 60000; //60s to allow app to idle private static final int INITIAL_LAUNCH_IDLE_TIMEOUT = 60000; //60s to allow app to idle
private static final int POST_LAUNCH_IDLE_TIMEOUT = 750; //750ms idle for non initial launches private static final int POST_LAUNCH_IDLE_TIMEOUT = 750; //750ms idle for non initial launches
private static final int BETWEEN_LAUNCH_SLEEP_TIMEOUT = 2000; //2s between launching apps private static final int BETWEEN_LAUNCH_SLEEP_TIMEOUT = 5000; //5s between launching apps
private static final String LAUNCH_SUB_DIRECTORY = "launch_logs";
private static final String LAUNCH_FILE = "applaunch.txt";
private static final String TRACE_SUB_DIRECTORY = "atrace_logs";
private static final String DEFAULT_TRACE_CATEGORIES = "sched,freq,gfx,view,dalvik,webview,"
+ "input,wm,disk,am,wm";
private static final String DEFAULT_TRACE_BUFFER_SIZE = "20000";
private static final String DEFAULT_TRACE_DUMP_INTERVAL = "10";
private static final String TRIAL_LAUNCH = "TRAIL_LAUNCH";
private static final String DELIMITER = ",";
private static final String DROP_CACHE_SCRIPT = "/data/local/tmp/dropCache.sh";
private static final String APP_LAUNCH_CMD = "am start -W -n";
private static final String SUCCESS_MESSAGE = "Status: ok";
private static final String THIS_TIME = "ThisTime:";
private static final String LAUNCH_ITERATION = "LAUNCH_ITERATION - %d";
private static final String TRACE_ITERATION = "TRACE_ITERATION - %d";
private static final String LAUNCH_ITERATION_PREFIX = "LAUNCH_ITERATION";
private static final String TRACE_ITERATION_PREFIX = "TRACE_ITERATION";
private static final String LAUNCH_ORDER_CYCLIC = "cyclic";
private static final String LAUNCH_ORDER_SEQUENTIAL = "sequential";
private Map<String, Intent> mNameToIntent; private Map<String, Intent> mNameToIntent;
private Map<String, String> mNameToProcess; private Map<String, String> mNameToProcess;
private List<LaunchOrder> mLaunchOrderList = new ArrayList<LaunchOrder>();
private Map<String, String> mNameToResultKey; private Map<String, String> mNameToResultKey;
private Map<String, Long> mNameToLaunchTime; private Map<String, List<Long>> mNameToLaunchTime;
private IActivityManager mAm; private IActivityManager mAm;
private String mSimplePerfCmd = null;
private String mLaunchOrder = null;
private boolean mDropCache = false;
private int mLaunchIterations = 10; private int mLaunchIterations = 10;
private int mTraceLaunchCount = 0;
private String mTraceDirectoryStr = null;
private Bundle mResult = new Bundle(); private Bundle mResult = new Bundle();
private Set<String> mRequiredAccounts; private Set<String> mRequiredAccounts;
private boolean mSkipInitialLaunch = false; private boolean mTrailLaunch = true;
private File mFile = null;
private FileOutputStream mOutputStream = null;
private BufferedWriter mBufferedWriter = null;
@Override @Override
protected void setUp() throws Exception { protected void setUp() throws Exception {
@@ -89,69 +140,231 @@ public class AppLaunch extends InstrumentationTestCase {
super.tearDown(); super.tearDown();
} }
public void testMeasureStartUpTime() throws RemoteException, NameNotFoundException { public void testMeasureStartUpTime() throws RemoteException, NameNotFoundException,
IOException, InterruptedException {
InstrumentationTestRunner instrumentation = InstrumentationTestRunner instrumentation =
(InstrumentationTestRunner)getInstrumentation(); (InstrumentationTestRunner)getInstrumentation();
Bundle args = instrumentation.getArguments(); Bundle args = instrumentation.getArguments();
mAm = ActivityManagerNative.getDefault(); mAm = ActivityManagerNative.getDefault();
String launchDirectory = args.getString(KEY_LAUNCH_DIRECTORY);
mTraceDirectoryStr = args.getString(KEY_TRACE_DIRECTORY);
mDropCache = Boolean.parseBoolean(args.getString(KEY_DROP_CACHE));
mSimplePerfCmd = args.getString(KEY_SIMPLEPPERF_CMD);
mLaunchOrder = args.getString(KEY_LAUNCH_ORDER, LAUNCH_ORDER_CYCLIC);
createMappings(); createMappings();
parseArgs(args); parseArgs(args);
checkAccountSignIn(); checkAccountSignIn();
if (!mSkipInitialLaunch) { // Root directory for applaunch file to log the app launch output
// do initial app launch, without force stopping // Will be useful in case of simpleperf command is used
for (String app : mNameToResultKey.keySet()) { File launchRootDir = null;
long launchTime = startApp(app, false); if (null != launchDirectory && !launchDirectory.isEmpty()) {
if (launchTime <= 0) { launchRootDir = new File(launchDirectory);
mNameToLaunchTime.put(app, -1L); if (!launchRootDir.exists() && !launchRootDir.mkdirs()) {
// simply pass the app if launch isn't successful throw new IOException("Unable to create the destination directory");
// error should have already been logged by startApp
continue;
} else {
mNameToLaunchTime.put(app, launchTime);
}
sleep(INITIAL_LAUNCH_IDLE_TIMEOUT);
closeApp(app, false);
sleep(BETWEEN_LAUNCH_SLEEP_TIMEOUT);
} }
} }
// do the real app launch now
for (int i = 0; i < mLaunchIterations; i++) { try {
for (String app : mNameToResultKey.keySet()) { File launchSubDir = new File(launchRootDir, LAUNCH_SUB_DIRECTORY);
long prevLaunchTime = mNameToLaunchTime.get(app); if (!launchSubDir.exists() && !launchSubDir.mkdirs()) {
long launchTime = 0; throw new IOException("Unable to create the lauch file sub directory");
if (prevLaunchTime < 0) { }
// skip if the app has previous failures mFile = new File(launchSubDir, LAUNCH_FILE);
continue; mOutputStream = new FileOutputStream(mFile);
mBufferedWriter = new BufferedWriter(new OutputStreamWriter(
mOutputStream));
// Root directory for trace file during the launches
File rootTrace = null;
File rootTraceSubDir = null;
int traceBufferSize = 0;
int traceDumpInterval = 0;
Set<String> traceCategoriesSet = null;
if (null != mTraceDirectoryStr && !mTraceDirectoryStr.isEmpty()) {
rootTrace = new File(mTraceDirectoryStr);
if (!rootTrace.exists() && !rootTrace.mkdirs()) {
throw new IOException("Unable to create the trace directory");
} }
launchTime = startApp(app, true); rootTraceSubDir = new File(rootTrace, TRACE_SUB_DIRECTORY);
if (launchTime <= 0) { if (!rootTraceSubDir.exists() && !rootTraceSubDir.mkdirs()) {
// if it fails once, skip the rest of the launches throw new IOException("Unable to create the trace sub directory");
mNameToLaunchTime.put(app, -1L);
continue;
} }
// keep the min launch time assertNotNull("Trace iteration parameter is mandatory",
if (launchTime < prevLaunchTime) { args.getString(KEY_TRACE_ITERATIONS));
mNameToLaunchTime.put(app, launchTime); mTraceLaunchCount = Integer.parseInt(args.getString(KEY_TRACE_ITERATIONS));
String traceCategoriesStr = args
.getString(KEY_TRACE_CATEGORY, DEFAULT_TRACE_CATEGORIES);
traceBufferSize = Integer.parseInt(args.getString(KEY_TRACE_BUFFERSIZE,
DEFAULT_TRACE_BUFFER_SIZE));
traceDumpInterval = Integer.parseInt(args.getString(KEY_TRACE_DUMPINTERVAL,
DEFAULT_TRACE_DUMP_INTERVAL));
traceCategoriesSet = new HashSet<String>();
if (!traceCategoriesStr.isEmpty()) {
String[] traceCategoriesSplit = traceCategoriesStr.split(DELIMITER);
for (int i = 0; i < traceCategoriesSplit.length; i++) {
traceCategoriesSet.add(traceCategoriesSplit[i]);
}
} }
sleep(POST_LAUNCH_IDLE_TIMEOUT); }
closeApp(app, true);
sleep(BETWEEN_LAUNCH_SLEEP_TIMEOUT); // Get the app launch order based on launch order, trial launch,
// launch iterations and trace iterations
setLaunchOrder();
for (LaunchOrder launch : mLaunchOrderList) {
// App launch times for trial launch will not be used for final
// launch time calculations.
if (launch.getLaunchReason().equals(TRIAL_LAUNCH)) {
// In the "applaunch.txt" file, trail launches is referenced using
// "TRIAL_LAUNCH"
long launchTime = startApp(launch.getApp(), true, launch.getLaunchReason());
if (launchTime < 0) {
List<Long> appLaunchList = new ArrayList<Long>();
appLaunchList.add(-1L);
mNameToLaunchTime.put(launch.getApp(), appLaunchList);
// simply pass the app if launch isn't successful
// error should have already been logged by startApp
continue;
}
sleep(INITIAL_LAUNCH_IDLE_TIMEOUT);
closeApp(launch.getApp(), true);
dropCache();
sleep(BETWEEN_LAUNCH_SLEEP_TIMEOUT);
}
// App launch times used for final calculation
if (launch.getLaunchReason().contains(LAUNCH_ITERATION_PREFIX)) {
long launchTime = -1;
if (null != mNameToLaunchTime.get(launch.getApp())) {
long firstLaunchTime = mNameToLaunchTime.get(launch.getApp()).get(0);
if (firstLaunchTime < 0) {
// skip if the app has failures while launched first
continue;
}
}
// In the "applaunch.txt" file app launches are referenced using
// "LAUNCH_ITERATION - ITERATION NUM"
launchTime = startApp(launch.getApp(), true, launch.getLaunchReason());
if (launchTime < 0) {
// if it fails once, skip the rest of the launches
List<Long> appLaunchList = new ArrayList<Long>();
appLaunchList.add(-1L);
mNameToLaunchTime.put(launch.getApp(), appLaunchList);
continue;
} else {
if (null != mNameToLaunchTime.get(launch.getApp())) {
mNameToLaunchTime.get(launch.getApp()).add(launchTime);
} else {
List<Long> appLaunchList = new ArrayList<Long>();
appLaunchList.add(launchTime);
mNameToLaunchTime.put(launch.getApp(), appLaunchList);
}
}
sleep(POST_LAUNCH_IDLE_TIMEOUT);
closeApp(launch.getApp(), true);
dropCache();
sleep(BETWEEN_LAUNCH_SLEEP_TIMEOUT);
}
// App launch times for trace launch will not be used for final
// launch time calculations.
if (launch.getLaunchReason().contains(TRACE_ITERATION_PREFIX)) {
AtraceLogger atraceLogger = AtraceLogger
.getAtraceLoggerInstance(getInstrumentation());
// Start the trace
try {
atraceLogger.atraceStart(traceCategoriesSet, traceBufferSize,
traceDumpInterval, rootTraceSubDir,
String.format("%s-%s", launch.getApp(), launch.getLaunchReason()));
startApp(launch.getApp(), true, launch.getLaunchReason());
sleep(POST_LAUNCH_IDLE_TIMEOUT);
} finally {
// Stop the trace
atraceLogger.atraceStop();
closeApp(launch.getApp(), true);
dropCache();
sleep(BETWEEN_LAUNCH_SLEEP_TIMEOUT);
}
}
}
} finally {
if (null != mBufferedWriter) {
mBufferedWriter.close();
} }
} }
for (String app : mNameToResultKey.keySet()) { for (String app : mNameToResultKey.keySet()) {
long launchTime = mNameToLaunchTime.get(app); StringBuilder launchTimes = new StringBuilder();
if (launchTime != -1) { for (Long launch : mNameToLaunchTime.get(app)) {
mResult.putLong(mNameToResultKey.get(app), launchTime); launchTimes.append(launch);
launchTimes.append(",");
} }
mResult.putString(mNameToResultKey.get(app), launchTimes.toString());
} }
instrumentation.sendStatus(0, mResult); instrumentation.sendStatus(0, mResult);
} }
/**
* If launch order is "cyclic" then apps will be launched one after the
* other for each iteration count.
* If launch order is "sequential" then each app will be launched for given number
* iterations at once before launching the other apps.
*/
private void setLaunchOrder() {
if (LAUNCH_ORDER_CYCLIC.equalsIgnoreCase(mLaunchOrder)) {
if (mTrailLaunch) {
for (String app : mNameToResultKey.keySet()) {
mLaunchOrderList.add(new LaunchOrder(app, TRIAL_LAUNCH));
}
}
for (int launchCount = 0; launchCount < mLaunchIterations; launchCount++) {
for (String app : mNameToResultKey.keySet()) {
mLaunchOrderList.add(new LaunchOrder(app,
String.format(LAUNCH_ITERATION, launchCount)));
}
}
if (mTraceDirectoryStr != null && !mTraceDirectoryStr.isEmpty()) {
for (int traceCount = 0; traceCount < mTraceLaunchCount; traceCount++) {
for (String app : mNameToResultKey.keySet()) {
mLaunchOrderList.add(new LaunchOrder(app,
String.format(TRACE_ITERATION, traceCount)));
}
}
}
} else if (LAUNCH_ORDER_SEQUENTIAL.equalsIgnoreCase(mLaunchOrder)) {
for (String app : mNameToResultKey.keySet()) {
if (mTrailLaunch) {
mLaunchOrderList.add(new LaunchOrder(app, TRIAL_LAUNCH));
}
for (int launchCount = 0; launchCount < mLaunchIterations; launchCount++) {
mLaunchOrderList.add(new LaunchOrder(app,
String.format(LAUNCH_ITERATION, launchCount)));
}
if (mTraceDirectoryStr != null && !mTraceDirectoryStr.isEmpty()) {
for (int traceCount = 0; traceCount < mTraceLaunchCount; traceCount++) {
mLaunchOrderList.add(new LaunchOrder(app,
String.format(TRACE_ITERATION, traceCount)));
}
}
}
} else {
assertTrue("Launch order is not valid parameter", false);
}
}
private void dropCache() {
if (true == mDropCache) {
assertNotNull("Issue in dropping the cache",
getInstrumentation().getUiAutomation()
.executeShellCommand(DROP_CACHE_SCRIPT));
}
}
private void parseArgs(Bundle args) { private void parseArgs(Bundle args) {
mNameToResultKey = new LinkedHashMap<String, String>(); mNameToResultKey = new LinkedHashMap<String, String>();
mNameToLaunchTime = new HashMap<String, Long>(); mNameToLaunchTime = new HashMap<String, List<Long>>();
String launchIterations = args.getString(KEY_LAUNCH_ITERATIONS); String launchIterations = args.getString(KEY_LAUNCH_ITERATIONS);
if (launchIterations != null) { if (launchIterations != null) {
mLaunchIterations = Integer.parseInt(launchIterations); mLaunchIterations = Integer.parseInt(launchIterations);
@@ -169,7 +382,7 @@ public class AppLaunch extends InstrumentationTestCase {
} }
mNameToResultKey.put(parts[0], parts[1]); mNameToResultKey.put(parts[0], parts[1]);
mNameToLaunchTime.put(parts[0], 0L); mNameToLaunchTime.put(parts[0], null);
} }
String requiredAccounts = args.getString(KEY_REQUIRED_ACCOUNTS); String requiredAccounts = args.getString(KEY_REQUIRED_ACCOUNTS);
if (requiredAccounts != null) { if (requiredAccounts != null) {
@@ -178,7 +391,7 @@ public class AppLaunch extends InstrumentationTestCase {
mRequiredAccounts.add(accountType); mRequiredAccounts.add(accountType);
} }
} }
mSkipInitialLaunch = "true".equals(args.getString(KEY_SKIP_INITIAL_LAUNCH)); mTrailLaunch = "true".equals(args.getString(KEY_TRIAL_LAUNCH));
} }
private boolean hasLeanback(Context context) { private boolean hasLeanback(Context context) {
@@ -222,7 +435,7 @@ public class AppLaunch extends InstrumentationTestCase {
} }
} }
private long startApp(String appName, boolean forceStopBeforeLaunch) private long startApp(String appName, boolean forceStopBeforeLaunch, String launchReason)
throws NameNotFoundException, RemoteException { throws NameNotFoundException, RemoteException {
Log.i(TAG, "Starting " + appName); Log.i(TAG, "Starting " + appName);
@@ -230,9 +443,10 @@ public class AppLaunch extends InstrumentationTestCase {
if (startIntent == null) { if (startIntent == null) {
Log.w(TAG, "App does not exist: " + appName); Log.w(TAG, "App does not exist: " + appName);
mResult.putString(mNameToResultKey.get(appName), "App does not exist"); mResult.putString(mNameToResultKey.get(appName), "App does not exist");
return -1; return -1L;
} }
AppLaunchRunnable runnable = new AppLaunchRunnable(startIntent, forceStopBeforeLaunch); AppLaunchRunnable runnable = new AppLaunchRunnable(startIntent, forceStopBeforeLaunch ,
launchReason);
Thread t = new Thread(runnable); Thread t = new Thread(runnable);
t.start(); t.start();
try { try {
@@ -240,21 +454,7 @@ public class AppLaunch extends InstrumentationTestCase {
} catch (InterruptedException e) { } catch (InterruptedException e) {
// ignore // ignore
} }
WaitResult result = runnable.getResult(); return runnable.getResult();
// report error if any of the following is true:
// * launch thread is alive
// * result is not null, but:
// * result is not START_SUCCESS
// * or in case of no force stop, result is not TASK_TO_FRONT either
if (t.isAlive() || (result != null
&& ((result.result != ActivityManager.START_SUCCESS)
&& (!forceStopBeforeLaunch
&& result.result != ActivityManager.START_TASK_TO_FRONT)))) {
Log.w(TAG, "Assuming app " + appName + " crashed.");
reportError(appName, mNameToProcess.get(appName));
return -1;
}
return result.thisTime;
} }
private void checkAccountSignIn() { private void checkAccountSignIn() {
@@ -337,39 +537,117 @@ public class AppLaunch extends InstrumentationTestCase {
+ " not found in process list, most likely it is crashed"); + " not found in process list, most likely it is crashed");
} }
private class AppLaunchRunnable implements Runnable { private class LaunchOrder {
private Intent mLaunchIntent; private String mApp;
private IActivityManager.WaitResult mResult; private String mLaunchReason;
private boolean mForceStopBeforeLaunch;
public AppLaunchRunnable(Intent intent, boolean forceStopBeforeLaunch) { LaunchOrder(String app,String launchReason){
mLaunchIntent = intent; mApp = app;
mForceStopBeforeLaunch = forceStopBeforeLaunch; mLaunchReason = launchReason;
} }
public IActivityManager.WaitResult getResult() { public String getApp() {
return mApp;
}
public void setApp(String app) {
mApp = app;
}
public String getLaunchReason() {
return mLaunchReason;
}
public void setLaunchReason(String launchReason) {
mLaunchReason = launchReason;
}
}
private class AppLaunchRunnable implements Runnable {
private Intent mLaunchIntent;
private Long mResult;
private boolean mForceStopBeforeLaunch;
private String mLaunchReason;
public AppLaunchRunnable(Intent intent, boolean forceStopBeforeLaunch,
String launchReason) {
mLaunchIntent = intent;
mForceStopBeforeLaunch = forceStopBeforeLaunch;
mLaunchReason = launchReason;
}
public Long getResult() {
return mResult; return mResult;
} }
public void run() { public void run() {
try { try {
String packageName = mLaunchIntent.getComponent().getPackageName(); String packageName = mLaunchIntent.getComponent().getPackageName();
String componentName = mLaunchIntent.getComponent().flattenToShortString();
if (mForceStopBeforeLaunch) { if (mForceStopBeforeLaunch) {
mAm.forceStopPackage(packageName, UserHandle.USER_CURRENT); mAm.forceStopPackage(packageName, UserHandle.USER_CURRENT);
} }
String mimeType = mLaunchIntent.getType(); String launchCmd = String.format("%s %s", APP_LAUNCH_CMD, componentName);
if (mimeType == null && mLaunchIntent.getData() != null if (null != mSimplePerfCmd) {
&& "content".equals(mLaunchIntent.getData().getScheme())) { launchCmd = String.format("%s %s", mSimplePerfCmd, launchCmd);
mimeType = mAm.getProviderMimeType(mLaunchIntent.getData(),
UserHandle.USER_CURRENT);
} }
Log.v(TAG, "Final launch cmd:" + launchCmd);
mResult = mAm.startActivityAndWait(null, null, mLaunchIntent, mimeType, ParcelFileDescriptor parcelDesc = getInstrumentation().getUiAutomation()
null, null, 0, mLaunchIntent.getFlags(), null, null, .executeShellCommand(launchCmd);
UserHandle.USER_CURRENT); mResult = Long.parseLong(parseLaunchTimeAndWrite(parcelDesc, String.format
("App Launch :%s %s",
componentName, mLaunchReason)), 10);
} catch (RemoteException e) { } catch (RemoteException e) {
Log.w(TAG, "Error launching app", e); Log.w(TAG, "Error launching app", e);
} }
} }
/**
* Method to parse the launch time info and write the result to file
*
* @param parcelDesc
* @return
*/
private String parseLaunchTimeAndWrite(ParcelFileDescriptor parcelDesc, String headerInfo) {
String launchTime = "-1";
boolean launchSuccess = false;
try {
InputStream inputStream = new FileInputStream(parcelDesc.getFileDescriptor());
StringBuilder appLaunchOuput = new StringBuilder();
/* SAMPLE OUTPUT :
Starting: Intent { cmp=com.google.android.calculator/com.android.calculator2.Calculator }
Status: ok
Activity: com.google.android.calculator/com.android.calculator2.Calculator
ThisTime: 357
TotalTime: 357
WaitTime: 377
Complete*/
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(
inputStream));
String line = null;
int lineCount = 1;
mBufferedWriter.newLine();
mBufferedWriter.write(headerInfo);
mBufferedWriter.newLine();
while ((line = bufferedReader.readLine()) != null) {
if (lineCount == 2 && line.contains(SUCCESS_MESSAGE)) {
launchSuccess = true;
}
if (launchSuccess && lineCount == 4) {
String launchSplit[] = line.split(":");
launchTime = launchSplit[1].trim();
}
mBufferedWriter.write(line);
mBufferedWriter.newLine();
lineCount++;
}
mBufferedWriter.flush();
inputStream.close();
} catch (IOException e) {
Log.w(TAG, "Error writing the launch file", e);
}
return launchTime;
}
} }
} }