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