FileDocCategorySizeDatePackage
AppLaunch.javaAPI DocAndroid 5.1 API14539Thu Mar 12 22:22:42 GMT 2015com.android.tests.applaunch

AppLaunch

public class AppLaunch extends android.test.InstrumentationTestCase
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 ^|^

Fields Summary
private static final int
JOIN_TIMEOUT
private static final String
TAG
private static final String
KEY_APPS
private static final String
KEY_LAUNCH_ITERATIONS
private static final String
KEY_REQUIRED_ACCOUNTS
private static final int
INITIAL_LAUNCH_IDLE_TIMEOUT
private static final int
POST_LAUNCH_IDLE_TIMEOUT
private static final int
BETWEEN_LAUNCH_SLEEP_TIMEOUT
private Map
mNameToIntent
private Map
mNameToProcess
private Map
mNameToResultKey
private Map
mNameToLaunchTime
private android.app.IActivityManager
mAm
private int
mLaunchIterations
private android.os.Bundle
mResult
private Set
mRequiredAccounts
Constructors Summary
Methods Summary
private voidcheckAccountSignIn()

        // ensure that the device has the required account types before starting test
        // e.g. device must have a valid Google account sign in to measure a meaningful launch time
        // for Gmail
        if (mRequiredAccounts == null || mRequiredAccounts.isEmpty()) {
            return;
        }
        final AccountManager am =
                (AccountManager) getInstrumentation().getTargetContext().getSystemService(
                        Context.ACCOUNT_SERVICE);
        Account[] accounts = am.getAccounts();
        // use set here in case device has multiple accounts of the same type
        Set<String> foundAccounts = new HashSet<String>();
        for (Account account : accounts) {
            if (mRequiredAccounts.contains(account.type)) {
                foundAccounts.add(account.type);
            }
        }
        // check if account type matches, if not, fail test with message on what account types
        // are missing
        if (mRequiredAccounts.size() != foundAccounts.size()) {
            mRequiredAccounts.removeAll(foundAccounts);
            StringBuilder sb = new StringBuilder("Device missing these accounts:");
            for (String account : mRequiredAccounts) {
                sb.append(' ");
                sb.append(account);
            }
            fail(sb.toString());
        }
    
private voidcloseApp(java.lang.String appName, boolean forceStopApp)

        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);
        sleep(POST_LAUNCH_IDLE_TIMEOUT);
        if (forceStopApp) {
            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 voidcreateMappings()

        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);
                String appName = ri.loadLabel(pm).toString();
                if (appName != null) {
                    mNameToIntent.put(appName, startIntent);
                    mNameToProcess.put(appName, ri.activityInfo.processName);
                }
            }
        }
    
private voidparseArgs(android.os.Bundle args)

        mNameToResultKey = new LinkedHashMap<String, String>();
        mNameToLaunchTime = new HashMap<String, Long>();
        String launchIterations = args.getString(KEY_LAUNCH_ITERATIONS);
        if (launchIterations != null) {
            mLaunchIterations = Integer.parseInt(launchIterations);
        }
        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]);
            mNameToLaunchTime.put(parts[0], 0L);
        }
        String requiredAccounts = args.getString(KEY_REQUIRED_ACCOUNTS);
        if (requiredAccounts != null) {
            mRequiredAccounts = new HashSet<String>();
            for (String accountType : requiredAccounts.split(",")) {
                mRequiredAccounts.add(accountType);
            }
        }
    
private voidreportError(java.lang.String appName, java.lang.String processName)

        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);
                mResult.putString(mNameToResultKey.get(appName), crash.shortMsg);
                return;
            }
        }

        mResult.putString(mNameToResultKey.get(appName),
                "Crashed for unknown reason");
        Log.w(TAG, appName
                + " not found in process list, most likely it is crashed");
    
protected voidsetUp()


    
         
        super.setUp();
        getInstrumentation().getUiAutomation().setRotation(UiAutomation.ROTATION_FREEZE_0);
    
private voidsleep(int time)

        try {
            Thread.sleep(time);
        } catch (InterruptedException e) {
            // ignore
        }
    
private longstartApp(java.lang.String appName, boolean forceStopBeforeLaunch)

        Log.i(TAG, "Starting " + appName);

        Intent startIntent = mNameToIntent.get(appName);
        if (startIntent == null) {
            Log.w(TAG, "App does not exist: " + appName);
            mResult.putString(mNameToResultKey.get(appName), "App does not exist");
            return -1;
        }
        AppLaunchRunnable runnable = new AppLaunchRunnable(startIntent, forceStopBeforeLaunch);
        Thread t = new Thread(runnable);
        t.start();
        try {
            t.join(JOIN_TIMEOUT);
        } 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_SUCESS
        //   * 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;
    
protected voidtearDown()

        getInstrumentation().getUiAutomation().setRotation(UiAutomation.ROTATION_UNFREEZE);
        super.tearDown();
    
public voidtestMeasureStartUpTime()

        InstrumentationTestRunner instrumentation =
                (InstrumentationTestRunner)getInstrumentation();
        Bundle args = instrumentation.getArguments();
        mAm = ActivityManagerNative.getDefault();

        createMappings();
        parseArgs(args);
        checkAccountSignIn();

        // 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);
        }
        // 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;
                }
                launchTime = startApp(app, true);
                if (launchTime <= 0) {
                    // if it fails once, skip the rest of the launches
                    mNameToLaunchTime.put(app, -1L);
                    continue;
                }
                // keep the min launch time
                if (launchTime < prevLaunchTime) {
                    mNameToLaunchTime.put(app, launchTime);
                }
                sleep(POST_LAUNCH_IDLE_TIMEOUT);
                closeApp(app, true);
                sleep(BETWEEN_LAUNCH_SLEEP_TIMEOUT);
            }
        }
        for (String app : mNameToResultKey.keySet()) {
            long launchTime = mNameToLaunchTime.get(app);
            if (launchTime != -1) {
                mResult.putLong(mNameToResultKey.get(app), launchTime);
            }
        }
        instrumentation.sendStatus(0, mResult);