/* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.framework.tests; import com.android.tradefed.config.Option; import com.android.tradefed.config.Option.Importance; import com.android.tradefed.device.DeviceNotAvailableException; import com.android.tradefed.device.TestDeviceOptions; import com.android.tradefed.log.LogUtil.CLog; import com.android.tradefed.testtype.DeviceTestCase; import com.android.tradefed.util.FileUtil; import junit.framework.Assert; import java.io.File; /** * Set of tests that verify host side install cases for non-root adb. */ public class PackageManagerHostTestsNonRoot extends DeviceTestCase { private PackageManagerHostTestUtils mPMHostUtils = null; private static final String SIMPLE_PKG = "com.android.framework.simpletestapp"; private static final String SIMPLE_ENCRYPTED_APK = "SimpleTestApp-encrypted.apk"; private static final String ENCRYPTION_ALGO = "AES/CBC/PKCS7Padding"; // The following IV and Key were picked at random. private static final String ENCRYPTION_IV = "3dafba429d9eb430b422da802c9fac41"; private static final String ENCRYPTION_KEY = "06a9214036b8a15b512e03d534120006"; private static final String MAC_ALGO = "HmacSHA1"; private static final String MAC_KEY = "626c6168"; // 'blah' the mac key in // hex // The mac tag generated by the mac key "blah" for the encrypted apk. private static final String MAC_TAG = "d5140e260b7980850fc1ce044756a554b8471d07"; private static final String APP_PRIVATE_PATH = "/mnt/asec/"; private static final String ASEC_PRIVATE_PATH = "/data/app-asec/"; // TODO: consider fetching these files from build server instead. @Option(name = "app-repository-path", description = "path to the app repository containing large apks", importance = Importance.IF_UNSET) private File mAppRepositoryPath = null; @Override protected void setUp() throws Exception { super.setUp(); // Ensure apk path has been set before test is run assertNotNull("Missing --app-repository-path option", mAppRepositoryPath); // If device is currently in adb root, reboot. if (getDevice().isAdbRoot()) { TestDeviceOptions options = getDevice().getOptions(); options.setEnableAdbRoot(false); getDevice().setOptions(options); getDevice().reboot(); } // Ensure we are now running as non-root. assertFalse("Currently running as adb root, this test requires non-root access instead", getDevice().isAdbRoot()); // setup the PackageManager host tests utilities class, and get various // paths we'll need... mPMHostUtils = new PackageManagerHostTestUtils(getDevice()); // Ensure the default is set to let the system decide where to install // apps // (It's ok for individual tests to override and change this during // their test, but should // reset it back when they're done) mPMHostUtils.setDevicePreferredInstallLocation( PackageManagerHostTestUtils.InstallLocPreference.AUTO); } /** * Get the absolute file system location of test app with given filename * * @param fileName the file name of the test app apk * @return {@link String} of absolute file path */ public File getTestAppFilePath(String fileName) { return FileUtil.getFileForPath(mAppRepositoryPath, fileName); } /** * Ensure that normal install of apk on non-rooted device works. Also * ensures we are unable to grab the apk from the device if non-rooted. * Assumes that we are running adb as non root. * * @throws Exception */ public void testEncryptedApkForNonRoot() throws Exception { try { // cleanup test app just in case it was accidently installed getDevice().uninstallPackage(SIMPLE_PKG); mPMHostUtils.waitForPackageManager(); // Install app mPMHostUtils.installEncryptedAppAndVerifyExists( getTestAppFilePath(SIMPLE_ENCRYPTED_APK), SIMPLE_PKG, true, ENCRYPTION_ALGO, ENCRYPTION_IV, ENCRYPTION_KEY, MAC_ALGO, MAC_KEY, MAC_TAG); // Unable to ls /data/app-asec Assert.assertFalse(mPMHostUtils.doesRemoteFileExistContainingString(ASEC_PRIVATE_PATH, SIMPLE_PKG)); // Able to ls /mnt/asec/ Assert.assertTrue(mPMHostUtils.doesRemoteFileExistContainingString(APP_PRIVATE_PATH, SIMPLE_PKG)); // Able to pull the resource file from asec String remoteResourceFile = findRemoteFilePath(APP_PRIVATE_PATH, SIMPLE_PKG, "res.zip"); Assert.assertNotNull(remoteResourceFile); Assert.assertTrue(getDevice().doesFileExist(remoteResourceFile)); File res = getDevice().pullFile(remoteResourceFile); Assert.assertNotNull(res); // However, unable to pull apk file from asec String remoteApkFile = findRemoteFilePath(APP_PRIVATE_PATH, SIMPLE_PKG, ".apk"); Assert.assertNotNull(remoteApkFile); Assert.assertTrue(getDevice().doesFileExist(remoteApkFile)); File apk = null; try { apk = getDevice().pullFile(remoteApkFile); } catch (Exception e) { // Expect to fail. } Assert.assertNull(apk); } finally { mPMHostUtils.uninstallApp(SIMPLE_PKG); } } /** * Ensure that the permissions of the encrypted apk remains the same following a * reboot. * * @throws Exception */ public void testEncryptedApkForNonRootReboot() throws Exception { try { // cleanup test app just in case it was accidently installed getDevice().uninstallPackage(SIMPLE_PKG); mPMHostUtils.waitForPackageManager(); // Install app mPMHostUtils.installEncryptedAppAndVerifyExists( getTestAppFilePath(SIMPLE_ENCRYPTED_APK), SIMPLE_PKG, true, ENCRYPTION_ALGO, ENCRYPTION_IV, ENCRYPTION_KEY, MAC_ALGO, MAC_KEY, MAC_TAG); // Reboot device. getDevice().reboot(); // Unable to ls /data/app-asec Assert.assertFalse(mPMHostUtils.doesRemoteFileExistContainingString(ASEC_PRIVATE_PATH, SIMPLE_PKG)); // Able to ls /mnt/asec/ Assert.assertTrue(mPMHostUtils.doesRemoteFileExistContainingString(APP_PRIVATE_PATH, SIMPLE_PKG)); // Able to pull the resource file from asec String remoteResourceFile = findRemoteFilePath(APP_PRIVATE_PATH, SIMPLE_PKG, "res.zip"); Assert.assertNotNull(remoteResourceFile); Assert.assertTrue(getDevice().doesFileExist(remoteResourceFile)); File res = getDevice().pullFile(remoteResourceFile); Assert.assertNotNull(res); // However, unable to pull apk file from asec String remoteApkFile = findRemoteFilePath(APP_PRIVATE_PATH, SIMPLE_PKG, ".apk"); Assert.assertNotNull(remoteApkFile); Assert.assertTrue(getDevice().doesFileExist(remoteApkFile)); File apk = null; try { apk = getDevice().pullFile(remoteApkFile); } catch (Exception e) { // Expect to fail. } Assert.assertNull(apk); } finally { mPMHostUtils.uninstallApp(SIMPLE_PKG); } } /** * Helper method used to fetch the first element matching a given search * string on the device. * * @param remotePath {@link String} to search in the remote device. * @param packageName {@link String} package name under which to search in. * @param searchString {@link String} to search for. * @return {@link String} path of the first element that matches the search * criteria, or null if none found. * @throws DeviceNotAvailableException */ private String findRemoteFilePath(String remotePath, String packageName, String searchString) throws DeviceNotAvailableException { String packagePath = getDevice().executeShellCommand(String.format("ls %s", remotePath)); String[] packages = packagePath.split("\\r\\n"); for (String p : packages) { if (p.contains(packageName)) { String searchPath = new File(remotePath, p).getAbsolutePath(); CLog.d(searchPath); String lsOut = getDevice().executeShellCommand(String.format("ls %s", searchPath)); String[] files = lsOut.split("\\r\\n"); for (String f : files) { if (f.contains(searchString)) { return new File(searchPath, f).getAbsolutePath(); } } } } return null; } }