1 /* 2 * Copyright (C) 2016 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.apptransition.tests; 18 19 import android.app.Instrumentation; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.pm.PackageManager; 23 import android.content.pm.ResolveInfo; 24 import android.os.Bundle; 25 import android.os.ParcelFileDescriptor; 26 import android.os.RemoteException; 27 import android.support.test.launcherhelper.ILauncherStrategy; 28 import android.support.test.launcherhelper.LauncherStrategyFactory; 29 import android.support.test.uiautomator.UiDevice; 30 import android.util.Log; 31 32 import androidx.test.InstrumentationRegistry; 33 import androidx.test.rule.logging.AtraceLogger; 34 35 import com.android.launcher3.tapl.LauncherInstrumentation; 36 import com.android.launcher3.tapl.Workspace; 37 38 import org.junit.After; 39 import org.junit.Assume; 40 import org.junit.Before; 41 import org.junit.Test; 42 43 import java.io.BufferedReader; 44 import java.io.File; 45 import java.io.FileInputStream; 46 import java.io.IOException; 47 import java.io.InputStream; 48 import java.io.InputStreamReader; 49 import java.util.HashMap; 50 import java.util.HashSet; 51 import java.util.LinkedHashMap; 52 import java.util.List; 53 import java.util.Map; 54 import java.util.Set; 55 56 public class AppTransitionTests extends Instrumentation { 57 58 private static final String TAG = AppTransitionTests.class.getSimpleName(); 59 private static final int JOIN_TIMEOUT = 10000; 60 private static final int DEFAULT_DROP_CACHE_DELAY = 2000; 61 private static final String DEFAULT_POST_LAUNCH_TIMEOUT = "5000"; 62 private static final String DEFAULT_LAUNCH_COUNT = "10"; 63 private static final String SUCCESS_MESSAGE = "Status: ok"; 64 private static final String HOT_LAUNCH_MESSAGE = "LaunchState: HOT"; 65 private static final String TOTAL_TIME_MESSAGE = "TotalTime:"; 66 private static final String DROP_CACHE_SCRIPT = "/data/local/tmp/dropCache.sh"; 67 private static final String APP_LAUNCH_CMD = "am start -W -n"; 68 private static final String FORCE_STOP = "am force-stop "; 69 private static final String PRE_LAUNCH_APPS = "pre_launch_apps"; 70 private static final String LAUNCH_APPS = "launch_apps"; 71 private static final String KEY_LAUNCH_ITERATIONS = "launch_iteration"; 72 private static final String KEY_POST_LAUNCH_TIMEOUT = "postlaunch_timeout"; 73 private static final String COLD_LAUNCH = "cold_launch"; 74 private static final String HOT_LAUNCH = "hot_launch"; 75 private static final String NOT_SURE = "not_sure"; 76 private static final String ACTIVITY = "Activity:"; 77 private static final String KEY_TRACE_DIRECTORY = "trace_directory"; 78 private static final String KEY_TRACE_CATEGORY = "trace_categories"; 79 private static final String KEY_TRACE_BUFFERSIZE = "trace_bufferSize"; 80 private static final String KEY_TRACE_DUMPINTERVAL = "tracedump_interval"; 81 private static final String DEFAULT_TRACE_CATEGORIES = "sched,freq,gfx,view,dalvik,webview," 82 + "input,wm,disk,am,wm"; 83 private static final String DEFAULT_TRACE_BUFFER_SIZE = "20000"; 84 private static final String DEFAULT_TRACE_DUMP_INTERVAL = "10"; 85 private static final String DELIMITER = ","; 86 private UiDevice mDevice; 87 private LauncherInstrumentation mLauncher; 88 private ILauncherStrategy mLauncherStrategy = null; 89 private Map<String, Intent> mAppLaunchIntentsMapping = null; 90 private String mTraceDirectoryStr = null; 91 private Bundle mResult = new Bundle(); 92 private Bundle mArgs; 93 private String mPreAppsList; 94 private int mLaunchIterations; 95 private int mPostLaunchTimeout; 96 private String[] mAppListArray; 97 private String[] mPreAppsListArray; 98 private File mRootTrace = null; 99 private File mRootTraceSubDir = null; 100 private int mTraceBufferSize = 0; 101 private int mTraceDumpInterval = 0; 102 private Set<String> mTraceCategoriesSet = null; 103 private AtraceLogger mAtraceLogger = null; 104 private String mComponentName = null; 105 private Map<String,String> mPreAppsComponentName = new HashMap<String, String>(); 106 private boolean mHasLeanback = false; 107 108 @Before setUp()109 public void setUp() throws Exception { 110 mArgs = InstrumentationRegistry.getArguments(); 111 mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); 112 LauncherStrategyFactory factory = LauncherStrategyFactory.getInstance(mDevice); 113 mLauncherStrategy = factory.getLauncherStrategy(); 114 mLauncher = new LauncherInstrumentation(getInstrumentation()); 115 mHasLeanback = hasLeanback(getInstrumentation().getTargetContext()); 116 117 // Inject an instance of instrumentation only if leanback. This enables to launch any app 118 // in the Apps and Games row on leanback launcher. 119 if (mHasLeanback) { 120 factory.getLeanbackLauncherStrategy().setInstrumentation(getInstrumentation()); 121 } 122 123 createLaunchIntentMappings(); 124 125 String appsList = mArgs.getString(LAUNCH_APPS, ""); 126 mPreAppsList = mArgs.getString(PRE_LAUNCH_APPS, ""); 127 mLaunchIterations = Integer.parseInt(mArgs.getString(KEY_LAUNCH_ITERATIONS, 128 DEFAULT_LAUNCH_COUNT)); 129 mPostLaunchTimeout = Integer.parseInt(mArgs.getString(KEY_POST_LAUNCH_TIMEOUT, 130 DEFAULT_POST_LAUNCH_TIMEOUT)); 131 if (null == appsList || appsList.isEmpty()) { 132 throw new IllegalArgumentException("Need atleast one app to do the" 133 + " app transition from launcher"); 134 } 135 mAppListArray = appsList.split(DELIMITER); 136 137 // Parse the trace parameters 138 mTraceDirectoryStr = mArgs.getString(KEY_TRACE_DIRECTORY); 139 if (isTracesEnabled()) { 140 String traceCategoriesStr = mArgs 141 .getString(KEY_TRACE_CATEGORY, DEFAULT_TRACE_CATEGORIES); 142 mTraceBufferSize = Integer.parseInt(mArgs.getString(KEY_TRACE_BUFFERSIZE, 143 DEFAULT_TRACE_BUFFER_SIZE)); 144 mTraceDumpInterval = Integer.parseInt(mArgs.getString(KEY_TRACE_DUMPINTERVAL, 145 DEFAULT_TRACE_DUMP_INTERVAL)); 146 mTraceCategoriesSet = new HashSet<String>(); 147 if (!traceCategoriesStr.isEmpty()) { 148 String[] traceCategoriesSplit = traceCategoriesStr.split(DELIMITER); 149 for (int i = 0; i < traceCategoriesSplit.length; i++) { 150 mTraceCategoriesSet.add(traceCategoriesSplit[i]); 151 } 152 } 153 } 154 mDevice.setOrientationNatural(); 155 sleep(mPostLaunchTimeout); 156 cleanTestApps(); 157 } 158 159 @After tearDown()160 public void tearDown() throws Exception{ 161 cleanTestApps(); 162 getInstrumentation().sendStatus(0, mResult); 163 } 164 165 /** 166 * Cold launch given list of apps for given launch count from the launcher screen. 167 * @throws IOException if there are issues in writing atrace file 168 * @throws InterruptedException if there are interrupt during the sleep 169 * @throws RemoteException if press home is not successful 170 */ 171 @Test testColdLaunchFromLauncher()172 public void testColdLaunchFromLauncher() throws IOException, InterruptedException, 173 RemoteException { 174 if (isTracesEnabled()) { 175 createTraceDirectory("testColdLaunchFromLauncher"); 176 } 177 // Perform cold app launch from launcher screen 178 for (int appCount = 0; appCount < mAppListArray.length; appCount++) { 179 String appName = mAppListArray[appCount]; 180 // Additional launch to account for cold launch 181 if (setupAppLaunch(appName) == ILauncherStrategy.LAUNCH_FAILED_TIMESTAMP) { 182 continue; 183 } 184 closeApps(new String[] { 185 appName 186 }); 187 getInstrumentation().getUiAutomation() 188 .executeShellCommand(DROP_CACHE_SCRIPT); 189 sleep(DEFAULT_DROP_CACHE_DELAY); 190 for (int launchCount = 0; launchCount <= mLaunchIterations; launchCount++) { 191 if (null != mAtraceLogger) { 192 mAtraceLogger.atraceStart(mTraceCategoriesSet, mTraceBufferSize, 193 mTraceDumpInterval, mRootTraceSubDir, 194 String.format("%s-%d", appName, launchCount)); 195 } 196 mLauncherStrategy.launch(appName, mComponentName.split("\\/")[0]); 197 if (null != mAtraceLogger) { 198 mAtraceLogger.atraceStop(); 199 } 200 sleep(mPostLaunchTimeout); 201 mDevice.pressHome(); 202 mDevice.waitForIdle(); 203 closeApps(new String[] { 204 appName 205 }); 206 sleep(mPostLaunchTimeout); 207 getInstrumentation().getUiAutomation() 208 .executeShellCommand(DROP_CACHE_SCRIPT); 209 sleep(DEFAULT_DROP_CACHE_DELAY); 210 } 211 mComponentName = null; 212 // Update the result with the component name 213 updateResult(appName); 214 } 215 } 216 217 /** 218 * Hot launch given list of apps for given launch count from the launcher screen. Same method can be 219 * used to test app to home transition delay information as well. 220 * @throws IOException if there are issues in writing atrace file 221 * @throws InterruptedException if there are interrupt during the sleep 222 * @throws RemoteException if press home is not successful 223 */ 224 @Test testHotLaunchFromLauncher()225 public void testHotLaunchFromLauncher() throws IOException, InterruptedException, 226 RemoteException { 227 if (isTracesEnabled()) { 228 createTraceDirectory("testHotLaunchFromLauncher"); 229 } 230 for (int appCount = 0; appCount < mAppListArray.length; appCount++) { 231 String appName = mAppListArray[appCount]; 232 // Additional launch to account for cold launch 233 if (setupAppLaunch(appName) == ILauncherStrategy.LAUNCH_FAILED_TIMESTAMP) { 234 continue; 235 } 236 // Hot app launch for given (launch iterations + 1) times. 237 for (int launchCount = 0; launchCount <= (mLaunchIterations); launchCount++) { 238 if (null != mAtraceLogger) { 239 mAtraceLogger.atraceStart(mTraceCategoriesSet, mTraceBufferSize, 240 mTraceDumpInterval, mRootTraceSubDir, 241 String.format("%s-%d", appName, (launchCount))); 242 } 243 mLauncherStrategy.launch(appName, mComponentName.split("\\/")[0]); 244 sleep(mPostLaunchTimeout); 245 mDevice.pressHome(); 246 sleep(mPostLaunchTimeout); 247 if (null != mAtraceLogger) { 248 mAtraceLogger.atraceStop(); 249 } 250 } 251 mComponentName = null; 252 // Update the result with the component name 253 updateResult(appName); 254 } 255 } 256 257 /** 258 * Launch an app and press recents for given list of apps for given launch counts. 259 * @throws IOException if there are issues in writing atrace file 260 * @throws InterruptedException if there are interrupt during the sleep 261 * @throws RemoteException if press recent apps is not successful 262 */ 263 @Test testAppToRecents()264 public void testAppToRecents() throws IOException, InterruptedException, RemoteException { 265 Assume.assumeFalse(mHasLeanback); 266 if (isTracesEnabled()) { 267 createTraceDirectory("testAppToRecents"); 268 } 269 if (null == mPreAppsList || mPreAppsList.isEmpty()) { 270 throw new IllegalArgumentException("Need atleast few apps in the " 271 + "recents before starting the test"); 272 } 273 mPreAppsListArray = mPreAppsList.split(DELIMITER); 274 mPreAppsComponentName.clear(); 275 populateRecentsList(); 276 for (int appCount = 0; appCount < mAppListArray.length; appCount++) { 277 String appName = mAppListArray[appCount]; 278 long appLaunchTime = -1L; 279 for (int launchCount = 0; launchCount <= mLaunchIterations; launchCount++) { 280 mLauncherStrategy.launch(appName, mPreAppsComponentName.get(appName).split( 281 "\\/")[0]); 282 sleep(mPostLaunchTimeout); 283 if (null != mAtraceLogger && launchCount > 0) { 284 mAtraceLogger.atraceStart(mTraceCategoriesSet, mTraceBufferSize, 285 mTraceDumpInterval, mRootTraceSubDir, 286 String.format("%s-%d", appName, launchCount - 1)); 287 } 288 mLauncher.getBackground().switchToOverview(); 289 sleep(mPostLaunchTimeout); 290 if (null != mAtraceLogger && launchCount > 0) { 291 mAtraceLogger.atraceStop(); 292 } 293 mDevice.pressHome(); 294 sleep(mPostLaunchTimeout); 295 } 296 updateResult(appName); 297 } 298 } 299 300 /** 301 * Hot launch an app from recents for given list of apps for given launch counts. 302 * @throws IOException if there are issues in writing atrace file 303 * @throws InterruptedException if there are interrupt during the sleep 304 * @throws RemoteException if press recent apps is not successful 305 */ 306 @Test testHotLaunchFromRecents()307 public void testHotLaunchFromRecents() throws IOException, InterruptedException, 308 RemoteException { 309 Assume.assumeFalse(mHasLeanback); 310 if (isTracesEnabled()) { 311 createTraceDirectory("testHotLaunchFromRecents"); 312 } 313 if (null == mPreAppsList || mPreAppsList.isEmpty()) { 314 throw new IllegalArgumentException("Need atleast few apps in the" 315 + " recents before starting the test"); 316 } 317 mPreAppsListArray = mPreAppsList.split(DELIMITER); 318 mPreAppsComponentName.clear(); 319 populateRecentsList(); 320 for (int appCount = 0; appCount < mAppListArray.length; appCount++) { 321 String appName = mAppListArray[appCount]; 322 // To bring the app to launch as first item from recents task. 323 mLauncherStrategy.launch(appName, mPreAppsComponentName.get(appName).split( 324 "\\/")[0]); 325 for (int launchCount = 0; launchCount <= mLaunchIterations; launchCount++) { 326 sleep(mPostLaunchTimeout); 327 final Workspace workspace = mLauncher.pressHome(); 328 if (null != mAtraceLogger) { 329 mAtraceLogger.atraceStart(mTraceCategoriesSet, mTraceBufferSize, 330 mTraceDumpInterval, mRootTraceSubDir, 331 String.format("%s-%d", appName, (launchCount))); 332 } 333 workspace.switchToOverview().getCurrentTask().open(); 334 sleep(mPostLaunchTimeout); 335 if (null != mAtraceLogger) { 336 mAtraceLogger.atraceStop(); 337 } 338 } 339 updateResult(appName); 340 } 341 } 342 343 /** 344 * Launch given app to account for the cold launch and track 345 * component name associated with the app. 346 * @throws RemoteException if press home is not successful 347 * @param appName 348 * @return 349 */ setupAppLaunch(String appName)350 public long setupAppLaunch(String appName) throws RemoteException { 351 long appLaunchTime = startApp(appName, NOT_SURE); 352 if (appLaunchTime == ILauncherStrategy.LAUNCH_FAILED_TIMESTAMP) { 353 return appLaunchTime; 354 } 355 sleep(mPostLaunchTimeout); 356 mDevice.pressHome(); 357 sleep(mPostLaunchTimeout); 358 return appLaunchTime; 359 } 360 361 /** 362 * Create sub directory under the trace root directory to store the trace files captured during 363 * the app transition. 364 * @param subDirectoryName 365 */ createTraceDirectory(String subDirectoryName)366 private void createTraceDirectory(String subDirectoryName) throws IOException { 367 mRootTrace = new File(mTraceDirectoryStr); 368 if (!mRootTrace.exists() && !mRootTrace.mkdirs()) { 369 throw new IOException("Unable to create the trace directory"); 370 } 371 mRootTraceSubDir = new File(mRootTrace, subDirectoryName); 372 if (!mRootTraceSubDir.exists() && !mRootTraceSubDir.mkdirs()) { 373 throw new IOException("Unable to create the trace sub directory"); 374 } 375 mAtraceLogger = AtraceLogger.getAtraceLoggerInstance(getInstrumentation()); 376 } 377 378 /** 379 * Force stop the given list of apps, clear the cache and return to home screen. 380 * @throws RemoteException if press home is not successful 381 */ cleanTestApps()382 private void cleanTestApps() throws RemoteException { 383 if (null != mPreAppsListArray && mPreAppsListArray.length > 0) { 384 closeApps(mPreAppsListArray); 385 } 386 closeApps(mAppListArray); 387 getInstrumentation().getUiAutomation() 388 .executeShellCommand(DROP_CACHE_SCRIPT); 389 sleep(DEFAULT_DROP_CACHE_DELAY); 390 mDevice.pressHome(); 391 sleep(mPostLaunchTimeout); 392 } 393 394 /** 395 * Populate the recents list with given list of apps. 396 * @throws RemoteException if press home is not successful 397 */ populateRecentsList()398 private void populateRecentsList() throws RemoteException { 399 for (int preAppCount = 0; preAppCount < mPreAppsListArray.length; preAppCount++) { 400 startApp(mPreAppsListArray[preAppCount], NOT_SURE); 401 mPreAppsComponentName.put(mPreAppsListArray[preAppCount], mComponentName); 402 sleep(mPostLaunchTimeout); 403 mDevice.pressHome(); 404 sleep(mPostLaunchTimeout); 405 } 406 mComponentName = null; 407 } 408 409 410 /** 411 * To obtain the app name and corresponding intent to launch the app. 412 */ createLaunchIntentMappings()413 private void createLaunchIntentMappings() { 414 mAppLaunchIntentsMapping = new LinkedHashMap<String, Intent>(); 415 PackageManager pm = getInstrumentation().getContext() 416 .getPackageManager(); 417 Intent intentToResolve = new Intent(Intent.ACTION_MAIN); 418 intentToResolve.addCategory(mHasLeanback ? 419 Intent.CATEGORY_LEANBACK_LAUNCHER : 420 Intent.CATEGORY_LAUNCHER); 421 List<ResolveInfo> ris = pm.queryIntentActivities(intentToResolve, 0); 422 resolveLoop(ris, intentToResolve, pm); 423 } 424 resolveLoop(List<ResolveInfo> ris, Intent intentToResolve, PackageManager pm)425 private void resolveLoop(List<ResolveInfo> ris, Intent intentToResolve, PackageManager pm) { 426 if (ris == null || ris.isEmpty()) { 427 Log.i(TAG, "Could not find any apps"); 428 } else { 429 for (ResolveInfo ri : ris) { 430 Intent startIntent = new Intent(intentToResolve); 431 startIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 432 | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 433 startIntent.setClassName(ri.activityInfo.packageName, 434 ri.activityInfo.name); 435 String appName = ri.loadLabel(pm).toString(); 436 if (appName != null) { 437 mAppLaunchIntentsMapping.put(appName, startIntent); 438 } 439 } 440 } 441 } 442 443 /** 444 * Launch an app using the app name and return the app launch time. If app launch time is -1 445 * then app launch is not successful. 446 * @param appName Name of an app as listed in the launcher 447 * @param launchMode Cold or Hot launch 448 * @return 449 */ startApp(String appName, String launchMode)450 private long startApp(String appName, String launchMode) { 451 Log.i(TAG, "Starting " + appName); 452 Intent startIntent = mAppLaunchIntentsMapping.get(appName); 453 if (startIntent == null) { 454 return -1L; 455 } 456 AppLaunchRunnable runnable = new AppLaunchRunnable(startIntent, launchMode); 457 Thread t = new Thread(runnable); 458 t.start(); 459 try { 460 t.join(JOIN_TIMEOUT); 461 } catch (InterruptedException e) { 462 // ignore 463 } 464 mComponentName = runnable.getCmpName(); 465 return runnable.getResult(); 466 } 467 468 private class AppLaunchRunnable implements Runnable { 469 private Intent mLaunchIntent; 470 private String mLaunchMode; 471 private Long mResult = -1L; 472 private String mCmpName; 473 AppLaunchRunnable(Intent intent, String launchMode)474 public AppLaunchRunnable(Intent intent, String launchMode) { 475 mLaunchIntent = intent; 476 mLaunchMode = launchMode; 477 } 478 getResult()479 public Long getResult() { 480 return mResult; 481 } 482 getCmpName()483 public String getCmpName() { 484 return mCmpName; 485 } 486 487 @Override run()488 public void run() { 489 String packageName = mLaunchIntent.getComponent().getPackageName(); 490 String componentName = mLaunchIntent.getComponent().flattenToString(); 491 String launchCmd = String.format("%s %s", APP_LAUNCH_CMD, componentName); 492 ParcelFileDescriptor parcelDesc = getInstrumentation().getUiAutomation() 493 .executeShellCommand(launchCmd); 494 mResult = Long.parseLong(parseLaunchTime(parcelDesc)); 495 } 496 497 /** 498 * Returns launch time if app launch is successful otherwise "-1" 499 * @param parcelDesc 500 * @return 501 */ parseLaunchTime(ParcelFileDescriptor parcelDesc)502 private String parseLaunchTime(ParcelFileDescriptor parcelDesc) { 503 String launchTime = "-1"; 504 boolean launchSuccess = false; 505 mCmpName = null; 506 try { 507 InputStream inputStream = new FileInputStream(parcelDesc.getFileDescriptor()); 508 /* SAMPLE OUTPUT : Cold launch 509 Starting: Intent { cmp=com.google.android.calculator/com.android.calculator2.Calculator } 510 Status: ok 511 LaunchState: COLD 512 Activity: com.google.android.calculator/com.android.calculator2.Calculator 513 TotalTime: 357 514 WaitTime: 377 515 Complete*/ 516 /* SAMPLE OUTPUT : Hot launch 517 Starting: Intent { cmp=com.google.android.calculator/com.android.calculator2.Calculator } 518 Warning: Activity not started, its current task has been brought to the front 519 Status: ok 520 LaunchState: HOT 521 Activity: com.google.android.calculator/com.android.calculator2.CalculatorGoogle 522 TotalTime: 60 523 WaitTime: 67 524 Complete*/ 525 StringBuilder appLaunchOuput = new StringBuilder(); 526 BufferedReader bufferedReader = new BufferedReader(new InputStreamReader( 527 inputStream)); 528 String line = null; 529 final boolean maybeHotLaunch = mLaunchMode.contains(HOT_LAUNCH) || 530 mLaunchMode.contains(NOT_SURE); 531 while ((line = bufferedReader.readLine()) != null) { 532 if (line.startsWith(SUCCESS_MESSAGE)) { 533 launchSuccess = true; 534 } 535 if (!launchSuccess) { 536 continue; 537 } 538 539 if (line.startsWith(HOT_LAUNCH_MESSAGE) && (!maybeHotLaunch)){ 540 Log.w(TAG, "Error did not expect a hot launch"); 541 break; 542 } 543 544 if (line.startsWith(TOTAL_TIME_MESSAGE)) { 545 String launchSplit[] = line.split(":"); 546 launchTime = launchSplit[1].trim(); 547 } 548 // Needed to update the component name if the very first launch activity 549 // is different from hot launch activity (i.e YouTube) 550 if (maybeHotLaunch && line.startsWith(ACTIVITY)) { 551 String activitySplit[] = line.split(":"); 552 mCmpName = activitySplit[1].trim(); 553 } 554 } 555 inputStream.close(); 556 } catch (IOException e) { 557 Log.w(TAG, "Error writing the launch file", e); 558 } 559 return launchTime; 560 } 561 } 562 563 /** 564 * To force stop the given list of apps based on the app name. 565 * @param appNames 566 */ closeApps(String[] appNames)567 private void closeApps(String[] appNames) { 568 int length = appNames == null ? 0 : appNames.length; 569 for (int i = 0; i < length; i++) { 570 Intent startIntent = mAppLaunchIntentsMapping.get(appNames[i]); 571 if (startIntent != null) { 572 String packageName = startIntent.getComponent().getPackageName(); 573 574 getInstrumentation().getUiAutomation().executeShellCommand( 575 FORCE_STOP + packageName); 576 } 577 sleep(1000); 578 } 579 sleep(mPostLaunchTimeout); 580 } 581 582 /** 583 * @return 584 */ isTracesEnabled()585 private boolean isTracesEnabled(){ 586 return (null != mTraceDirectoryStr && !mTraceDirectoryStr.isEmpty()); 587 } 588 589 /** 590 * Update the result status 591 * @param appName 592 */ updateResult(String appName)593 private void updateResult(String appName) { 594 // Component name needed for parsing the events log 595 if (null != mComponentName) { 596 mResult.putString(appName, mComponentName); 597 } else { 598 // Component name needed for parsing the events log 599 mResult.putString(appName, mAppLaunchIntentsMapping.get(appName). 600 getComponent().flattenToString()); 601 } 602 } 603 604 605 /** 606 * To sleep for given millisecs. 607 * @param time 608 */ sleep(int time)609 private void sleep(int time) { 610 try { 611 Thread.sleep(time); 612 } catch (InterruptedException e) { 613 // ignore 614 } 615 } 616 617 /** 618 * Return the instrumentation from the registry. 619 * @return 620 */ getInstrumentation()621 private Instrumentation getInstrumentation() { 622 return InstrumentationRegistry.getInstrumentation(); 623 } 624 625 /** 626 * @return True if we're running on Android TV. 627 */ hasLeanback(Context context)628 private boolean hasLeanback(Context context) { 629 return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK); 630 } 631 } 632 633