1 /* 2 * Copyright (C) 2010 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.framework.tests; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertFalse; 21 import static org.junit.Assert.assertTrue; 22 23 import com.android.ddmlib.testrunner.IRemoteAndroidTestRunner; 24 import com.android.ddmlib.testrunner.RemoteAndroidTestRunner; 25 import com.android.tradefed.device.DeviceNotAvailableException; 26 import com.android.tradefed.device.IFileEntry; 27 import com.android.tradefed.device.ITestDevice; 28 import com.android.tradefed.log.LogUtil.CLog; 29 import com.android.tradefed.result.CollectingTestListener; 30 31 import java.io.File; 32 import java.util.Map; 33 import java.util.Map.Entry; 34 import java.util.regex.Matcher; 35 import java.util.regex.Pattern; 36 37 /** Set of tests that verify host side install cases */ 38 public class PackageManagerHostTestUtils { 39 private ITestDevice mDevice = null; 40 private boolean mEmulatedExternalStorage = false; 41 42 // TODO: get this value from Android Environment instead of hard coding 43 public static final String JB_APP_PRIVATE_PATH = "/mnt/asec/"; 44 public static final String DEVICE_APP_PATH = "/data/app/"; 45 public static final String SDCARD_APP_PATH = "/mnt/asec/"; 46 47 private static String mAppPrivatePath = JB_APP_PRIVATE_PATH; 48 49 private static final int MAX_WAIT_FOR_DEVICE_TIME = 120 * 1000; 50 51 // Install preference on the device-side 52 public static enum InstallLocPreference { 53 AUTO, 54 INTERNAL, 55 EXTERNAL 56 } 57 58 // Actual install location 59 public static enum InstallLocation { 60 DEVICE, 61 SDCARD 62 } 63 64 /** 65 * Constructor. 66 * 67 * @param device the {@link ITestDevice} to use when performing operations. 68 * @throws DeviceNotAvailableException 69 */ PackageManagerHostTestUtils(ITestDevice device)70 public PackageManagerHostTestUtils(ITestDevice device) throws DeviceNotAvailableException { 71 mDevice = device; 72 determineExternalStorageEmulation(); 73 } 74 75 /** 76 * Returns the path on the device of forward-locked apps. 77 * 78 * @return path of forward-locked apps on the device 79 */ getAppPrivatePath()80 public static String getAppPrivatePath() { 81 return mAppPrivatePath; 82 } 83 setAppPrivatePath(String path)84 public static void setAppPrivatePath(String path) { 85 mAppPrivatePath = path; 86 } 87 88 /** 89 * Returns the path on the device of normal apps. 90 * 91 * @return path of forward-locked apps on the device 92 */ getDeviceAppPath()93 public static String getDeviceAppPath() { 94 return DEVICE_APP_PATH; 95 } 96 97 /** 98 * Returns the path of apps installed on the SD card. 99 * 100 * @return path of forward-locked apps on the device 101 */ getSDCardAppPath()102 public static String getSDCardAppPath() { 103 return SDCARD_APP_PATH; 104 } 105 106 /** 107 * Helper method to run tests and return the listener that collected the results. For the 108 * optional params, pass null to use the default values. 109 * 110 * @param pkgName Android application package for tests 111 * @param className (optional) The class containing the method to test 112 * @param methodName (optional) The method in the class of which to test 113 * @param runnerName (optional) The name of the TestRunner of the test on the device to be run 114 * @param params (optional) Any additional parameters to pass into the Test Runner 115 * @return the {@link CollectingTestListener} 116 * @throws DeviceNotAvailableException 117 */ doRunTests( String pkgName, String className, String methodName, String runnerName, Map<String, String> params)118 private CollectingTestListener doRunTests( 119 String pkgName, 120 String className, 121 String methodName, 122 String runnerName, 123 Map<String, String> params) 124 throws DeviceNotAvailableException { 125 IRemoteAndroidTestRunner testRunner = 126 new RemoteAndroidTestRunner(pkgName, runnerName, mDevice.getIDevice()); 127 128 if (className != null && methodName != null) { 129 testRunner.setMethodName(className, methodName); 130 } 131 132 // Add in any additional args to pass into the test 133 if (params != null) { 134 for (Entry<String, String> argPair : params.entrySet()) { 135 testRunner.addInstrumentationArg(argPair.getKey(), argPair.getValue()); 136 } 137 } 138 139 CollectingTestListener listener = new CollectingTestListener(); 140 mDevice.runInstrumentationTests(testRunner, listener); 141 return listener; 142 } 143 144 /** 145 * Runs the specified packages tests, and returns whether all tests passed or not. 146 * 147 * @param pkgName Android application package for tests 148 * @param className The class containing the method to test 149 * @param methodName The method in the class of which to test 150 * @param runnerName The name of the TestRunner of the test on the device to be run 151 * @param params Any additional parameters to pass into the Test Runner 152 * @return true if test passed, false otherwise. 153 * @throws DeviceNotAvailableException 154 */ runDeviceTestsDidAllTestsPass( String pkgName, String className, String methodName, String runnerName, Map<String, String> params)155 public boolean runDeviceTestsDidAllTestsPass( 156 String pkgName, 157 String className, 158 String methodName, 159 String runnerName, 160 Map<String, String> params) 161 throws DeviceNotAvailableException { 162 CollectingTestListener listener = 163 doRunTests(pkgName, className, methodName, runnerName, params); 164 return !listener.hasFailedTests(); 165 } 166 167 /** 168 * Runs the specified packages tests, and returns whether all tests passed or not. 169 * 170 * @param pkgName Android application package for tests 171 * @return true if every test passed, false otherwise. 172 * @throws DeviceNotAvailableException 173 */ runDeviceTestsDidAllTestsPass(String pkgName)174 public boolean runDeviceTestsDidAllTestsPass(String pkgName) 175 throws DeviceNotAvailableException { 176 CollectingTestListener listener = doRunTests(pkgName, null, null, null, null); 177 return !listener.hasFailedTests(); 178 } 179 180 /** 181 * Helper method to install a file 182 * 183 * @param localFile the {@link File} to install 184 * @param replace set to <code>true</code> if re-install of app should be performed 185 * @throws DeviceNotAvailableException 186 */ installFile(final File localFile, final boolean replace)187 public void installFile(final File localFile, final boolean replace) 188 throws DeviceNotAvailableException { 189 String result = mDevice.installPackage(localFile, replace); 190 assertEquals(null, result); 191 } 192 193 /** 194 * Helper method to install a file to device as forward locked. 195 * 196 * @param apkFile the {@link File} to install 197 * @param replace set to <code>true</code> if re-install of app should be performed 198 * @throws DeviceNotAvailableException if communication with device is lost 199 */ installFileForwardLocked(final File apkFile, final boolean replace)200 public String installFileForwardLocked(final File apkFile, final boolean replace) 201 throws DeviceNotAvailableException { 202 return mDevice.installPackage(apkFile, replace, "-l"); 203 } 204 205 /** 206 * Helper method to determine if file exists on the device containing a given string. 207 * 208 * @param destPath the absolute path of the file 209 * @return <code>true</code> if file exists containing given string, <code>false</code> 210 * otherwise. 211 * @throws DeviceNotAvailableException 212 */ doesRemoteFileExistContainingString(String destPath, String searchString)213 public boolean doesRemoteFileExistContainingString(String destPath, String searchString) 214 throws DeviceNotAvailableException { 215 String lsResult = mDevice.executeShellCommand(String.format("ls %s", destPath)); 216 return lsResult.contains(searchString); 217 } 218 219 /** 220 * Helper method to determine if package on device exists. 221 * 222 * @param packageName the Android manifest package to check. 223 * @return <code>true</code> if package exists, <code>false</code> otherwise 224 * @throws DeviceNotAvailableException 225 */ doesPackageExist(String packageName)226 public boolean doesPackageExist(String packageName) throws DeviceNotAvailableException { 227 String pkgGrep = mDevice.executeShellCommand(String.format("pm path %s", packageName)); 228 return pkgGrep.contains("package:"); 229 } 230 231 /** 232 * Determines if app was installed on device. 233 * 234 * @param packageName package name to check for 235 * @return <code>true</code> if file exists, <code>false</code> otherwise. 236 * @throws DeviceNotAvailableException 237 */ doesAppExistOnDevice(String packageName)238 public boolean doesAppExistOnDevice(String packageName) throws DeviceNotAvailableException { 239 return doesRemoteFileExistContainingString(DEVICE_APP_PATH, packageName); 240 } 241 242 /** 243 * Determines if app was installed on SD card. 244 * 245 * @param packageName package name to check for 246 * @return <code>true</code> if file exists, <code>false</code> otherwise. 247 * @throws DeviceNotAvailableException 248 */ doesAppExistOnSDCard(String packageName)249 public boolean doesAppExistOnSDCard(String packageName) throws DeviceNotAvailableException { 250 251 // if we're using emulated storage, the SDcard path is actually the 252 // device's normal app path 253 if (getIsExternalStorageEmulated()) { 254 return doesRemoteFileExistContainingString(DEVICE_APP_PATH, packageName); 255 } else { 256 return doesRemoteFileExistContainingString(SDCARD_APP_PATH, packageName); 257 } 258 } 259 260 /** 261 * Helper method to determine if app was installed as forward locked. 262 * 263 * @param packageName package name to check for 264 * @return <code>true</code> if file exists, <code>false</code> otherwise. 265 * @throws DeviceNotAvailableException 266 */ doesAppExistAsForwardLocked(String packageName)267 public boolean doesAppExistAsForwardLocked(String packageName) 268 throws DeviceNotAvailableException { 269 return doesRemoteFileExistContainingString(mAppPrivatePath, packageName); 270 } 271 272 /** 273 * Waits for device's package manager to respond. 274 * 275 * @throws DeviceNotAvailableException 276 */ waitForPackageManager()277 public void waitForPackageManager() throws DeviceNotAvailableException { 278 CLog.i("waiting for device"); 279 mDevice.waitForDeviceAvailable(MAX_WAIT_FOR_DEVICE_TIME); 280 } 281 282 /** 283 * Helper method for installing an app to wherever is specified in its manifest, and then 284 * verifying the app was installed onto SD Card. 285 * 286 * <p>Assumes adb is running as root in device under test. 287 * 288 * @param apkPath the path of the apk to install 289 * @param pkgName the name of the package 290 * @param overwrite <code>true</code> if the app should be overwritten, <code>false</code> 291 * otherwise 292 * @throws DeviceNotAvailableException 293 */ installAppAndVerifyExistsOnSDCard(File apkPath, String pkgName, boolean overwrite)294 public void installAppAndVerifyExistsOnSDCard(File apkPath, String pkgName, boolean overwrite) 295 throws DeviceNotAvailableException { 296 // Start with a clean slate if we're not overwriting 297 if (!overwrite) { 298 // cleanup test app just in case it already exists 299 mDevice.uninstallPackage(pkgName); 300 // grep for package to make sure its not installed 301 assertFalse(doesPackageExist(pkgName)); 302 } 303 304 installFile(apkPath, overwrite); 305 assertTrue(doesAppExistOnSDCard(pkgName)); 306 // TODO: is this necessary? 307 waitForPackageManager(); 308 309 // grep for package to make sure it is installed 310 assertTrue(doesPackageExist(pkgName)); 311 } 312 313 /** 314 * Helper method for installing an app to wherever is specified in its manifest, and then 315 * verifying the app was installed onto device. 316 * 317 * <p>Assumes adb is running as root in device under test. 318 * 319 * @param apkFile the {@link File} of the apk to install 320 * @param pkgName the name of the package 321 * @param overwrite <code>true</code> if the app should be overwritten, <code>false</code> 322 * otherwise 323 * @throws DeviceNotAvailableException 324 */ installAppAndVerifyExistsOnDevice(File apkFile, String pkgName, boolean overwrite)325 public void installAppAndVerifyExistsOnDevice(File apkFile, String pkgName, boolean overwrite) 326 throws DeviceNotAvailableException { 327 // Start with a clean slate if we're not overwriting 328 if (!overwrite) { 329 // cleanup test app just in case it already exists 330 mDevice.uninstallPackage(pkgName); 331 // grep for package to make sure its not installed 332 assertFalse(doesPackageExist(pkgName)); 333 } 334 335 installFile(apkFile, overwrite); 336 assertTrue(doesAppExistOnDevice(pkgName)); 337 // TODO: is this necessary? 338 waitForPackageManager(); 339 340 // grep for package to make sure it is installed 341 assertTrue(doesPackageExist(pkgName)); 342 } 343 344 /** 345 * Helper method for installing an app as forward-locked, and then verifying the app was 346 * installed in the proper forward-locked location. 347 * 348 * <p>Assumes adb is running as root in device under test. 349 * 350 * @param apkFile the {@link File} of the apk to install 351 * @param pkgName the name of the package 352 * @param overwrite <code>true</code> if the app should be overwritten, <code>false</code> 353 * otherwise 354 * @throws Exception if failed to install app 355 */ installFwdLockedAppAndVerifyExists(File apkFile, String pkgName, boolean overwrite)356 public void installFwdLockedAppAndVerifyExists(File apkFile, String pkgName, boolean overwrite) 357 throws Exception { 358 // Start with a clean slate if we're not overwriting 359 if (!overwrite) { 360 // cleanup test app just in case it already exists 361 mDevice.uninstallPackage(pkgName); 362 // grep for package to make sure its not installed 363 assertFalse(doesPackageExist(pkgName)); 364 } 365 366 String result = installFileForwardLocked(apkFile, overwrite); 367 assertEquals(null, result); 368 assertTrue(doesAppExistAsForwardLocked(pkgName)); 369 waitForPackageManager(); 370 371 // grep for package to make sure it is installed 372 assertTrue(doesPackageExist(pkgName)); 373 } 374 375 /** 376 * Helper method for uninstalling an app. 377 * 378 * <p>Assumes adb is running as root in device under test. 379 * 380 * @param pkgName package name to uninstall 381 * @throws DeviceNotAvailableException 382 */ uninstallApp(String pkgName)383 public void uninstallApp(String pkgName) throws DeviceNotAvailableException { 384 mDevice.uninstallPackage(pkgName); 385 waitForPackageManager(); 386 // make sure its not installed anymore 387 assertFalse(doesPackageExist(pkgName)); 388 } 389 390 /** 391 * Sets the device's install location preference. 392 * 393 * <p>Assumes adb is running as root in device under test. 394 * 395 * @throws DeviceNotAvailableException 396 */ setDevicePreferredInstallLocation(InstallLocPreference pref)397 public void setDevicePreferredInstallLocation(InstallLocPreference pref) 398 throws DeviceNotAvailableException { 399 String command = "pm set-install-location %d"; 400 int locValue = 0; 401 switch (pref) { 402 case INTERNAL: 403 locValue = 1; 404 break; 405 case EXTERNAL: 406 locValue = 2; 407 break; 408 default: // AUTO 409 locValue = 0; 410 break; 411 } 412 mDevice.executeShellCommand(String.format(command, locValue)); 413 } 414 415 /** 416 * Gets the device's install location preference. 417 * 418 * <p>Assumes adb is running as root in device under test. 419 * 420 * @throws DeviceNotAvailableException 421 */ getDevicePreferredInstallLocation()422 public InstallLocPreference getDevicePreferredInstallLocation() 423 throws DeviceNotAvailableException { 424 String result = mDevice.executeShellCommand("pm get-install-location"); 425 if (result.indexOf('0') != -1) { 426 return InstallLocPreference.AUTO; 427 } else if (result.indexOf('1') != -1) { 428 return InstallLocPreference.INTERNAL; 429 } else { 430 return InstallLocPreference.EXTERNAL; 431 } 432 } 433 434 /** 435 * Determines whether the device is using emulated external storage. 436 * 437 * <p>Sets mEmulatedExternalStorage based on the result Assumes adb is running as root in device 438 * under test. 439 * 440 * @throws DeviceNotAvailableException 441 */ determineExternalStorageEmulation()442 private void determineExternalStorageEmulation() throws DeviceNotAvailableException { 443 String result = mDevice.executeShellCommand("sm get-primary-storage-uuid"); 444 if (result.trim().equalsIgnoreCase("null")) { 445 CLog.i("Device is using emulated external storage."); 446 mEmulatedExternalStorage = true; 447 } else if (result.equals("primary_physical")) { 448 CLog.i("Device is using actual external storage."); 449 mEmulatedExternalStorage = false; 450 } else { 451 // older devices will not have mEmulated flag in the output 452 CLog.i("Unable to precisely determine external storage emulation; assuming false."); 453 mEmulatedExternalStorage = false; 454 } 455 } 456 457 /** 458 * Determine the location of the app private path. 459 * 460 * @param apkFile the {@link File} of test apk to determine packages' install path. 461 * @param pkgName the {@link String} pkgName of the test apk. 462 * @throws DeviceNotAvailableException 463 */ determinePrivateAppPath(File apkFile, String pkgName)464 public void determinePrivateAppPath(File apkFile, String pkgName) 465 throws DeviceNotAvailableException { 466 setAppPrivatePath(JB_APP_PRIVATE_PATH); 467 } 468 469 /** 470 * Returns whether the external storage is emulated or not. 471 * 472 * @return <code>true</code> if external storage is emulated, <code>false</code> otherwise. 473 */ getIsExternalStorageEmulated()474 public boolean getIsExternalStorageEmulated() { 475 return mEmulatedExternalStorage; 476 } 477 478 /** 479 * Connect device to wifi. 480 * 481 * @param device 482 * @param wifiNetwork 483 * @param wifiPsk 484 * @param connectionAttempts 485 * @return true if able to connect to wifi. 486 * @throws DeviceNotAvailableException 487 */ connectToWifi( ITestDevice device, String wifiNetwork, String wifiPsk, int connectionAttempts)488 public static boolean connectToWifi( 489 ITestDevice device, String wifiNetwork, String wifiPsk, int connectionAttempts) 490 throws DeviceNotAvailableException { 491 if (wifiNetwork != null) { 492 for (int i = 0; i < connectionAttempts; i++) { 493 device.disconnectFromWifi(); 494 if (device.connectToWifiNetwork(wifiNetwork, wifiPsk)) { 495 CLog.i("Connected to wifi network %s", wifiNetwork); 496 return true; 497 } 498 } 499 } 500 return false; 501 } 502 503 /** 504 * Ensure that the file's permissions matches expectation. 505 * 506 * @param remoteFilePath {@link String} the remote path for the file to check. 507 * @param expectedPerms {@link String} expected permissions. 508 * @return true if the permissions for a given file matches, false otherwise. 509 * @throws DeviceNotAvailableException 510 */ checkFilePermissions(String remoteFilePath, String expectedPerms)511 public boolean checkFilePermissions(String remoteFilePath, String expectedPerms) 512 throws DeviceNotAvailableException { 513 IFileEntry file = mDevice.getFileEntry(remoteFilePath); 514 return file.getPermissions().equals(expectedPerms); 515 } 516 517 /** 518 * Ensure that the file's owner matches expectation. 519 * 520 * @param remoteFilePath {@link String} the remote path for the file to check. 521 * @param expectedOwner {@link String} the expected owner. 522 * @return true if the owner for a given file matches, false otherwise. 523 * @throws DeviceNotAvailableException 524 */ checkFileOwnerName(String remoteFilePath, String expectedOwner)525 public boolean checkFileOwnerName(String remoteFilePath, String expectedOwner) 526 throws DeviceNotAvailableException { 527 // TODO: uncomment this, when we have support from ddmlib. 528 // IFileEntry file = mDevice.getFileEntry(remoteFilePath); 529 // return file.getOwner().equals(expectedOwner) 530 return true; 531 } 532 533 /** 534 * Ensure that the file's group matches expectation 535 * 536 * @param remoteFilePath {@link String} the remote path for the file to check. 537 * @param expectedGroup {@link String} the expected group. 538 * @return true if the group for a given file matches, false otherwise. 539 * @throws DeviceNotAvailableException 540 */ checkFileGroupName(String remoteFilePath, String expectedGroup)541 public boolean checkFileGroupName(String remoteFilePath, String expectedGroup) 542 throws DeviceNotAvailableException { 543 // TODO: uncomment this, when we have support from ddmlib. 544 // IFileEntry file = mDevice.getFileEntry(remoteFilePath); 545 // return file.getGroup().equals(expectedOwner) 546 return true; 547 } 548 549 /** 550 * Returns the uid of the installed package. 551 * 552 * @param pkgName package name of the test apk. 553 * @return uid of the installed package 554 * @throws DeviceNotAvailableException 555 */ getUid(String pkgName)556 public Integer getUid(String pkgName) throws DeviceNotAvailableException { 557 String out = mDevice.executeShellCommand(String.format("dumpsys package %s", pkgName)); 558 Matcher m = Pattern.compile("userId=(\\d+)").matcher(out); 559 assertTrue(m.find()); 560 561 Integer uid = Integer.parseInt(m.group(1)); 562 CLog.v("package %s has uid %d", pkgName, uid); 563 return uid; 564 } 565 getAbi(String pkgName)566 public String getAbi(String pkgName) throws DeviceNotAvailableException { 567 String out = mDevice.executeShellCommand(String.format("dumpsys package %s", pkgName)); 568 Matcher m = Pattern.compile("primaryCpuAbi=(.+)").matcher(out); 569 assertTrue(m.find()); 570 571 String abi = m.group(1); 572 CLog.i("package %s has abi %s", pkgName, abi); 573 return abi; 574 } 575 } 576