App launch - Test app changes

1) Drop cache between app launches Bug 29564205
2) Trace capture while launching app Bug 26779882
3) Simple Perf while launching app Bug 29249860
4) Launch order which could be cyclic or sequential

Change-Id: I0c883e7feb7e07474b045835fd56a97d8a126552
diff --git a/tests/AppLaunch/Android.mk b/tests/AppLaunch/Android.mk
index c0560fd..e6f6c39 100644
--- a/tests/AppLaunch/Android.mk
+++ b/tests/AppLaunch/Android.mk
@@ -11,7 +11,9 @@
 LOCAL_CERTIFICATE := platform
 LOCAL_JAVA_LIBRARIES := android.test.runner
 
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
 include $(BUILD_PACKAGE)
 
 # Use the following include to make our test apk.
-include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/AppLaunch/AndroidManifest.xml b/tests/AppLaunch/AndroidManifest.xml
index ac6760b..7dfd7ba 100644
--- a/tests/AppLaunch/AndroidManifest.xml
+++ b/tests/AppLaunch/AndroidManifest.xml
@@ -3,6 +3,14 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.tests.applaunch"
     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"
                      android:name="android.test.InstrumentationTestRunner"
                      android:targetPackage="com.android.tests.applaunch" />
@@ -10,4 +18,4 @@
     <application android:label="App Launch Test">
         <uses-library android:name="android.test.runner" />
     </application>
-</manifest>
\ No newline at end of file
+</manifest>
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index 085b7aa..2346f85 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -15,14 +15,13 @@
  */
 package com.android.tests.applaunch;
 
+import java.io.OutputStreamWriter;
+
 import android.accounts.Account;
 import android.accounts.AccountManager;
+import android.app.ActivityManagerNative;
 import android.app.ActivityManager;
 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.Intent;
 import android.content.pm.PackageManager;
@@ -31,16 +30,29 @@
 import android.os.Bundle;
 import android.os.RemoteException;
 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.InstrumentationTestRunner;
 import android.util.Log;
-
+import java.io.File;
+import java.io.IOException;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.ArrayList;
 import java.util.Map;
 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.
@@ -55,27 +67,66 @@
 
     private static final int JOIN_TIMEOUT = 10000;
     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
     // with the app launch
     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 =
             "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 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, String> mNameToProcess;
+    private List<LaunchOrder> mLaunchOrderList = new ArrayList<LaunchOrder>();
     private Map<String, String> mNameToResultKey;
-    private Map<String, Long> mNameToLaunchTime;
+    private Map<String, List<Long>> mNameToLaunchTime;
     private IActivityManager mAm;
+    private String mSimplePerfCmd = null;
+    private String mLaunchOrder = null;
+    private boolean mDropCache = false;
     private int mLaunchIterations = 10;
+    private int mTraceLaunchCount = 0;
+    private String mTraceDirectoryStr = null;
     private Bundle mResult = new Bundle();
     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
     protected void setUp() throws Exception {
@@ -89,69 +140,231 @@
         super.tearDown();
     }
 
-    public void testMeasureStartUpTime() throws RemoteException, NameNotFoundException {
+    public void testMeasureStartUpTime() throws RemoteException, NameNotFoundException,
+            IOException, InterruptedException {
         InstrumentationTestRunner instrumentation =
                 (InstrumentationTestRunner)getInstrumentation();
         Bundle args = instrumentation.getArguments();
         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();
         parseArgs(args);
         checkAccountSignIn();
 
-        if (!mSkipInitialLaunch) {
-            // do initial app launch, without force stopping
-            for (String app : mNameToResultKey.keySet()) {
-                long launchTime = startApp(app, false);
-                if (launchTime <= 0) {
-                    mNameToLaunchTime.put(app, -1L);
-                    // simply pass the app if launch isn't successful
-                    // 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);
+        // Root directory for applaunch file to log the app launch output
+        // Will be useful in case of simpleperf command is used
+        File launchRootDir = null;
+        if (null != launchDirectory && !launchDirectory.isEmpty()) {
+            launchRootDir = new File(launchDirectory);
+            if (!launchRootDir.exists() && !launchRootDir.mkdirs()) {
+                throw new IOException("Unable to create the destination directory");
             }
         }
-        // do the real app launch now
-        for (int i = 0; i < mLaunchIterations; i++) {
-            for (String app : mNameToResultKey.keySet()) {
-                long prevLaunchTime = mNameToLaunchTime.get(app);
-                long launchTime = 0;
-                if (prevLaunchTime < 0) {
-                    // skip if the app has previous failures
-                    continue;
+
+        try {
+            File launchSubDir = new File(launchRootDir, LAUNCH_SUB_DIRECTORY);
+            if (!launchSubDir.exists() && !launchSubDir.mkdirs()) {
+                throw new IOException("Unable to create the lauch file sub directory");
+            }
+            mFile = new File(launchSubDir, LAUNCH_FILE);
+            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);
-                if (launchTime <= 0) {
-                    // if it fails once, skip the rest of the launches
-                    mNameToLaunchTime.put(app, -1L);
-                    continue;
+                rootTraceSubDir = new File(rootTrace, TRACE_SUB_DIRECTORY);
+                if (!rootTraceSubDir.exists() && !rootTraceSubDir.mkdirs()) {
+                    throw new IOException("Unable to create the trace sub directory");
                 }
-                // keep the min launch time
-                if (launchTime < prevLaunchTime) {
-                    mNameToLaunchTime.put(app, launchTime);
+                assertNotNull("Trace iteration parameter is mandatory",
+                        args.getString(KEY_TRACE_ITERATIONS));
+                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()) {
-            long launchTime = mNameToLaunchTime.get(app);
-            if (launchTime != -1) {
-                mResult.putLong(mNameToResultKey.get(app), launchTime);
+            StringBuilder launchTimes = new StringBuilder();
+            for (Long launch : mNameToLaunchTime.get(app)) {
+                launchTimes.append(launch);
+                launchTimes.append(",");
             }
+            mResult.putString(mNameToResultKey.get(app), launchTimes.toString());
         }
         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) {
         mNameToResultKey = new LinkedHashMap<String, String>();
-        mNameToLaunchTime = new HashMap<String, Long>();
+        mNameToLaunchTime = new HashMap<String, List<Long>>();
         String launchIterations = args.getString(KEY_LAUNCH_ITERATIONS);
         if (launchIterations != null) {
             mLaunchIterations = Integer.parseInt(launchIterations);
@@ -169,7 +382,7 @@
             }
 
             mNameToResultKey.put(parts[0], parts[1]);
-            mNameToLaunchTime.put(parts[0], 0L);
+            mNameToLaunchTime.put(parts[0], null);
         }
         String requiredAccounts = args.getString(KEY_REQUIRED_ACCOUNTS);
         if (requiredAccounts != null) {
@@ -178,7 +391,7 @@
                 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) {
@@ -222,7 +435,7 @@
         }
     }
 
-    private long startApp(String appName, boolean forceStopBeforeLaunch)
+    private long startApp(String appName, boolean forceStopBeforeLaunch, String launchReason)
             throws NameNotFoundException, RemoteException {
         Log.i(TAG, "Starting " + appName);
 
@@ -230,9 +443,10 @@
         if (startIntent == null) {
             Log.w(TAG, "App does not exist: " + appName);
             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);
         t.start();
         try {
@@ -240,21 +454,7 @@
         } catch (InterruptedException e) {
             // ignore
         }
-        WaitResult result = 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;
+        return runnable.getResult();
     }
 
     private void checkAccountSignIn() {
@@ -337,39 +537,117 @@
                 + " not found in process list, most likely it is crashed");
     }
 
-    private class AppLaunchRunnable implements Runnable {
-        private Intent mLaunchIntent;
-        private IActivityManager.WaitResult mResult;
-        private boolean mForceStopBeforeLaunch;
+    private class LaunchOrder {
+        private String mApp;
+        private String mLaunchReason;
 
-        public AppLaunchRunnable(Intent intent, boolean forceStopBeforeLaunch) {
-            mLaunchIntent = intent;
-            mForceStopBeforeLaunch = forceStopBeforeLaunch;
+        LaunchOrder(String app,String launchReason){
+            mApp = app;
+            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;
         }
 
         public void run() {
             try {
                 String packageName = mLaunchIntent.getComponent().getPackageName();
+                String componentName = mLaunchIntent.getComponent().flattenToShortString();
                 if (mForceStopBeforeLaunch) {
                     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);
+                String launchCmd = String.format("%s %s", APP_LAUNCH_CMD, componentName);
+                if (null != mSimplePerfCmd) {
+                    launchCmd = String.format("%s %s", mSimplePerfCmd, launchCmd);
                 }
-
-                mResult = mAm.startActivityAndWait(null, null, mLaunchIntent, mimeType,
-                        null, null, 0, mLaunchIntent.getFlags(), null, null,
-                        UserHandle.USER_CURRENT);
+                Log.v(TAG, "Final launch cmd:" + launchCmd);
+                ParcelFileDescriptor parcelDesc = getInstrumentation().getUiAutomation()
+                        .executeShellCommand(launchCmd);
+                mResult = Long.parseLong(parseLaunchTimeAndWrite(parcelDesc, String.format
+                        ("App Launch :%s %s",
+                                componentName, mLaunchReason)), 10);
             } catch (RemoteException 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;
+        }
+
     }
 }