blob: 005ef410a001c3074f4c6603cf10ae8c5bed229a [file] [log] [blame]
/*
* 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.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.log.LogUtil.CLog;
import org.junit.Assert;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import java.io.File;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;
/** Utility method used for PackageMangerOTATests. Requires adb root. */
public class PackageManagerOTATestUtils {
public static final String USERDATA_PARTITION = "userdata";
public static final String PACKAGE_XML_FILE = "/data/system/packages.xml";
private ITestDevice mDevice = null;
/**
* Constructor.
*
* @param device the {@link ITestDevice} to use when performing operations.
* @throws DeviceNotAvailableException
*/
public PackageManagerOTATestUtils(ITestDevice device) throws DeviceNotAvailableException {
mDevice = device;
}
/**
* Wipe userdata partition on device.
*
* @throws DeviceNotAvailableException
*/
public void wipeDevice() throws DeviceNotAvailableException {
// Make sure to keep the local.prop file for testing purposes.
File prop = mDevice.pullFile("/data/local.prop");
mDevice.rebootIntoBootloader();
mDevice.fastbootWipePartition(USERDATA_PARTITION);
mDevice.rebootUntilOnline();
if (prop != null) {
mDevice.pushFile(prop, "/data/local.prop");
mDevice.executeShellCommand("chmod 644 /data/local.prop");
mDevice.reboot();
}
}
/**
* Remove a system app.
*
* @param systemApp {@link String} name for the application in the /system/app folder
* @param reboot set to <code>true</code> to optionally reboot device after app removal
* @throws DeviceNotAvailableException
*/
public void removeSystemApp(String systemApp, boolean reboot)
throws DeviceNotAvailableException {
mDevice.remountSystemWritable();
String cmd = String.format("rm %s", systemApp);
mDevice.executeShellCommand(cmd);
if (reboot) {
mDevice.reboot();
}
mDevice.waitForDeviceAvailable();
}
/**
* Remove a system app and wipe the device.
*
* @param systemApp {@link String} name for the application in the /system/app folder
* @throws DeviceNotAvailableException
*/
public void removeAndWipe(String systemApp) throws DeviceNotAvailableException {
removeSystemApp(systemApp, false);
wipeDevice();
}
/**
* Expect that a given xpath exists in a given xml file.
*
* @param xmlFile {@link File} xml file to process
* @param xPathString {@link String} Xpath to look for
* @return true if the xpath is found
*/
public boolean expectExists(File xmlFile, String xPathString) {
Node n = getNodeForXPath(xmlFile, xPathString);
if (n != null) {
CLog.d("Found node %s for xpath %s", n.getNodeName(), xPathString);
return true;
}
return false;
}
/**
* Expect that the value of a given xpath starts with a given string.
*
* @param xmlFile {@link File} the xml file in question
* @param xPathString {@link String} the xpath to look for
* @param value {@link String} the expected start string of the xpath
* @return true if the value for the xpath starts with value, false otherwise
*/
public boolean expectStartsWith(File xmlFile, String xPathString, String value) {
Node n = getNodeForXPath(xmlFile, xPathString);
if (n == null) {
CLog.d("Failed to find node for xpath %s", xPathString);
return false;
}
CLog.d("Value of node %s: %s", xPathString, n.getNodeValue());
return n.getNodeValue().toLowerCase().startsWith(value.toLowerCase());
}
/**
* Expect that the value of a given xpath matches.
*
* @param xmlFile {@link File} the xml file in question
* @param xPathString {@link String} the xpath to look for
* @param value {@link String} the expected string value
* @return true if the value for the xpath matches, false otherwise
*/
public boolean expectEquals(File xmlFile, String xPathString, String value) {
Node n = getNodeForXPath(xmlFile, xPathString);
if (n == null) {
CLog.d("Failed to find node for xpath %s", xPathString);
return false;
}
boolean result = n.getNodeValue().equalsIgnoreCase(value);
if (!result) {
CLog.v(
"Value of node %s: \"%s\", expected: \"%s\"",
xPathString, n.getNodeValue(), value);
}
return result;
}
public Node getNodeForXPath(File xmlFile, String xPathString) {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
try {
DocumentBuilder documentBuilder = factory.newDocumentBuilder();
Document doc = documentBuilder.parse(xmlFile);
XPathFactory xpFactory = XPathFactory.newInstance();
XPath xpath = xpFactory.newXPath();
XPathExpression expr = xpath.compile(xPathString);
Node node = (Node) expr.evaluate(doc, XPathConstants.NODE);
return node;
} catch (Exception e) {
CLog.e(e);
}
return null;
}
/**
* Check if a given package has the said permission.
*
* @param packageName {@link String} the package in question
* @param permission {@link String} the permission to look for
* @return true if the permission exists, false otherwise
* @throws DeviceNotAvailableException
*/
public boolean packageHasPermission(String packageName, String permission)
throws DeviceNotAvailableException {
String cmd = "dumpsys package " + packageName;
String res = mDevice.executeShellCommand(cmd);
if (res != null) {
if (res.contains("grantedPermissions:")) {
return res.contains(permission);
} else {
Pattern perm =
Pattern.compile(
String.format("^.*%s.*granted=true.*$", permission),
Pattern.MULTILINE);
Matcher m = perm.matcher(res);
return m.find();
}
}
CLog.d("Failed to execute shell command: %s", cmd);
return false;
}
/**
* Check if a given package has the said permission.
*
* @param packageName {@link String} the package in question
* @param flag {@link String} the permission to look for
* @return true if the permission exists, false otherwise
* @throws DeviceNotAvailableException
*/
public boolean packageHasFlag(String packageName, String flag)
throws DeviceNotAvailableException {
String cmd = "dumpsys package " + packageName;
String res = mDevice.executeShellCommand(cmd);
if (res != null) {
Pattern flags = Pattern.compile("^.*flags=\\[(.*?)\\]$", Pattern.MULTILINE);
Matcher m = flags.matcher(res);
if (m.find()) {
return m.group(1).contains(flag);
} else {
CLog.d("Failed to find package flags record in dumpsys package output");
}
}
CLog.d("Failed to execute shell command: %s", cmd);
return false;
}
/**
* Helper method to install a file
*
* @param localFile the {@link File} to install
* @param replace set to <code>true</code> if re-install of app should be performed
* @param extraArgs optional extra arguments to pass. See 'adb shell pm install --help' for
* available options.
* @throws DeviceNotAvailableException
*/
public void installFile(final File localFile, final boolean replace, String... extraArgs)
throws DeviceNotAvailableException {
String result = mDevice.installPackage(localFile, replace, extraArgs);
Assert.assertNull(
String.format(
"Failed to install file %s with result %s",
localFile.getAbsolutePath(), result),
result);
}
/**
* Helper method to stop system shell.
*
* @throws DeviceNotAvailableException
*/
public void stopSystem() throws DeviceNotAvailableException {
mDevice.executeShellCommand("stop");
}
/**
* Helper method to start system shell. It also reset the flag dev.bootcomplete to 0 to ensure
* that the package manager had a chance to finish.
*
* @throws DeviceNotAvailableException
*/
public void startSystem() throws DeviceNotAvailableException {
mDevice.executeShellCommand("setprop dev.bootcomplete 0");
mDevice.executeShellCommand("start");
mDevice.waitForDeviceAvailable();
}
/**
* Convenience method to stop, then start the runtime.
*
* @throws DeviceNotAvailableException
*/
public void restartSystem() throws DeviceNotAvailableException {
stopSystem();
startSystem();
}
/**
* Push apk to system app directory on the device.
*
* @param localFile {@link File} the local file to install
* @param deviceFilePath {@link String} the remote device path where to install the application
* @throws DeviceNotAvailableException
*/
public void pushSystemApp(final File localFile, final String deviceFilePath)
throws DeviceNotAvailableException {
mDevice.remountSystemWritable();
stopSystem();
mDevice.pushFile(localFile, deviceFilePath);
startSystem();
}
/**
* Pulls packages xml file from the device.
*
* @return {@link File} xml file for all packages on device.
* @throws DeviceNotAvailableException
*/
public File pullPackagesXML() throws DeviceNotAvailableException {
return mDevice.pullFile(PACKAGE_XML_FILE);
}
}