/*
* 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;
}
}