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 setUp()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 tearDown()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 */ testAppStability()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 */ getStackTrace(ProcessErrorStateInfo in)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 */ getProcessName(String packageName)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 */ launchActivity(String packageName)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 */ processStillUp(String packageName)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