Home | History | Annotate | Download | only in compatibilitytest
      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  */
     16 
     17 package com.android.compatibilitytest;
     18 
     19 import android.app.ActivityManager;
     20 import android.app.ActivityManager.ProcessErrorStateInfo;
     21 import android.app.ActivityManager.RunningAppProcessInfo;
     22 import android.content.Context;
     23 import android.content.Intent;
     24 import android.content.pm.PackageInfo;
     25 import android.content.pm.PackageManager;
     26 import android.content.pm.PackageManager.NameNotFoundException;
     27 import android.os.Bundle;
     28 import android.test.InstrumentationTestCase;
     29 import android.util.Log;
     30 
     31 import junit.framework.Assert;
     32 
     33 import java.util.Collection;
     34 import java.util.List;
     35 
     36 /**
     37  * Application Compatibility Test that launches an application and detects
     38  * crashes.
     39  */
     40 public class AppCompatibility extends InstrumentationTestCase {
     41 
     42     private static final String TAG = "AppCompability";
     43     private static final String PACKAGE_TO_LAUNCH = "package_to_launch";
     44     private static final String APP_LAUNCH_TIMEOUT_MSECS = "app_launch_timeout_ms";
     45     private static final String WORKSPACE_LAUNCH_TIMEOUT_MSECS = "workspace_launch_timeout_ms";
     46 
     47     private int mAppLaunchTimeout = 7000;
     48     private int mWorkspaceLaunchTimeout = 2000;
     49 
     50     private Context mContext;
     51     private ActivityManager mActivityManager;
     52     private PackageManager mPackageManager;
     53     private AppCompatibilityRunner mRunner;
     54     private Bundle mArgs;
     55 
     56     @Override
     57     public void setUp() throws Exception {
     58         super.setUp();
     59         mRunner = (AppCompatibilityRunner) getInstrumentation();
     60         assertNotNull("Could not fetch InstrumentationTestRunner.", mRunner);
     61 
     62         mContext = mRunner.getTargetContext();
     63         Assert.assertNotNull("Could not get the Context", mContext);
     64 
     65         mActivityManager = (ActivityManager)
     66                 mContext.getSystemService(Context.ACTIVITY_SERVICE);
     67         Assert.assertNotNull("Could not get Activity Manager", mActivityManager);
     68 
     69         mPackageManager = mContext.getPackageManager();
     70         Assert.assertNotNull("Missing Package Manager", mPackageManager);
     71 
     72         mArgs = mRunner.getBundle();
     73 
     74         // Parse optional inputs.
     75         String appLaunchTimeoutMsecs = mArgs.getString(APP_LAUNCH_TIMEOUT_MSECS);
     76         if (appLaunchTimeoutMsecs != null) {
     77             mAppLaunchTimeout = Integer.parseInt(appLaunchTimeoutMsecs);
     78         }
     79         String workspaceLaunchTimeoutMsecs = mArgs.getString(WORKSPACE_LAUNCH_TIMEOUT_MSECS);
     80         if (workspaceLaunchTimeoutMsecs != null) {
     81             mWorkspaceLaunchTimeout = Integer.parseInt(workspaceLaunchTimeoutMsecs);
     82         }
     83     }
     84 
     85     @Override
     86     protected void tearDown() throws Exception {
     87         super.tearDown();
     88     }
     89 
     90     /**
     91      * Actual test case that launches the package and throws an exception on the
     92      * first error.
     93      *
     94      * @throws Exception
     95      */
     96     public void testAppStability() throws Exception {
     97         String packageName = mArgs.getString(PACKAGE_TO_LAUNCH);
     98         if (packageName != null) {
     99             Log.d(TAG, "Launching app " + packageName);
    100             ProcessErrorStateInfo err = launchActivity(packageName);
    101             // Make sure there are no errors when launching the application,
    102             // otherwise raise an
    103             // exception with the first error encountered.
    104             assertNull(getStackTrace(err), err);
    105             assertTrue("App crashed after launch.", processStillUp(packageName));
    106         } else {
    107             Log.d(TAG, "Missing argument, use " + PACKAGE_TO_LAUNCH +
    108                     " to specify the package to launch");
    109         }
    110     }
    111 
    112     /**
    113      * Gets the stack trace for the error.
    114      *
    115      * @param in {@link ProcessErrorStateInfo} to parse.
    116      * @return {@link String} the long message of the error.
    117      */
    118     private String getStackTrace(ProcessErrorStateInfo in) {
    119         if (in == null) {
    120             return null;
    121         } else {
    122             return in.stackTrace;
    123         }
    124     }
    125 
    126     /**
    127      * Returns the process name that the package is going to use.
    128      *
    129      * @param packageName name of the package
    130      * @return process name of the package
    131      */
    132     private String getProcessName(String packageName) {
    133         try {
    134             PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0);
    135             return pi.applicationInfo.processName;
    136         } catch (NameNotFoundException e) {
    137             return packageName;
    138         }
    139     }
    140 
    141     /**
    142      * Launches and activity and queries for errors.
    143      *
    144      * @param packageName {@link String} the package name of the application to
    145      *            launch.
    146      * @return {@link Collection} of {@link ProcessErrorStateInfo} detected
    147      *         during the app launch.
    148      */
    149     private ProcessErrorStateInfo launchActivity(String packageName) {
    150         // the recommended way to see if this is a tv or not.
    151         boolean isleanback = !mPackageManager.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN)
    152             && !mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
    153         Intent homeIntent = new Intent(Intent.ACTION_MAIN);
    154         homeIntent.addCategory(Intent.CATEGORY_HOME);
    155         homeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    156         Intent intent;
    157         if (isleanback) {
    158             Log.d(TAG, "Leanback and relax! " + packageName);
    159             intent = mPackageManager.getLeanbackLaunchIntentForPackage(packageName);
    160         } else {
    161             intent = mPackageManager.getLaunchIntentForPackage(packageName);
    162         }
    163         assertNotNull("Skipping " + packageName + "; missing launch intent", intent);
    164 
    165         String processName = getProcessName(packageName);
    166 
    167         // Launch Activity
    168         mContext.startActivity(intent);
    169 
    170         try {
    171             Thread.sleep(mAppLaunchTimeout);
    172         } catch (InterruptedException e) {
    173             // ignore
    174         }
    175 
    176         // Send the "home" intent and wait 2 seconds for us to get there
    177         mContext.startActivity(homeIntent);
    178         try {
    179             Thread.sleep(mWorkspaceLaunchTimeout);
    180         } catch (InterruptedException e) {
    181             // ignore
    182         }
    183 
    184         // See if there are any errors. We wait until down here to give ANRs as
    185         // much time as
    186         // possible to occur.
    187         final Collection<ProcessErrorStateInfo> postErr =
    188                 mActivityManager.getProcessesInErrorState();
    189 
    190         if (postErr == null) {
    191             return null;
    192         }
    193         for (ProcessErrorStateInfo error : postErr) {
    194             if (error.processName.equals(processName)) {
    195                 return error;
    196             }
    197         }
    198         return null;
    199     }
    200 
    201     /**
    202      * Determine if a given package is still running.
    203      *
    204      * @param packageName {@link String} package to look for
    205      * @return True if package is running, false otherwise.
    206      */
    207     private boolean processStillUp(String packageName) {
    208         String processName = getProcessName(packageName);
    209         List<RunningAppProcessInfo> runningApps = mActivityManager.getRunningAppProcesses();
    210         for (RunningAppProcessInfo app : runningApps) {
    211             if (app.processName.equalsIgnoreCase(processName)) {
    212                 Log.d(TAG, "Found process " + app.processName);
    213                 return true;
    214             }
    215             for (String relatedPackage : app.pkgList) {
    216                 if (relatedPackage.equalsIgnoreCase(processName)) {
    217                     Log.d(TAG, "Found process " + app.processName);
    218                     return true;
    219                 }
    220             }
    221         }
    222         Log.d(TAG, "Failed to find process " + processName + " with package name "
    223                 + packageName);
    224         return false;
    225     }
    226 }
    227