/* * 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 junit.framework.Assert; import org.w3c.dom.Document; import org.w3c.dom.Node; import java.io.File; 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 { remountSystemRW(); mDevice.waitForDeviceAvailable(); String cmd = String.format("rm /system/app/%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; } /** * Get int value for a xpath in a given xml file. * @param xmlFile {@link File} xml file to process. * @param xPathString {@link String} Xpath to look for * @return */ public int getIntForXPath(File xmlFile, String xPathString) { String tmp = getStringForXPath(xmlFile, xPathString); return Integer.parseInt(tmp); } /** * 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; } CLog.d("Value of node %s: %s", xPathString, n.getNodeValue()); return n.getNodeValue().equalsIgnoreCase(value); } /** * Get String value for a given xpath. * * @param xmlFile {@link File} the xml file in question * @param xPathString {@link String} the xpath to look for * * @return {@link String} value for a given xpath */ public String getStringForXPath(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); String node = (String) expr.evaluate(doc, XPathConstants.STRING); return node; } catch (Exception e) { CLog.e(e); } return null; } 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 && res.contains("grantedPermissions:")) { return res.contains(permission); } 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 remount system partition. * * @throws DeviceNotAvailableException */ public void remountSystemRW() throws DeviceNotAvailableException { mDevice.executeAdbCommand("remount"); } /** * 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(); } /** * 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 { remountSystemRW(); stopSystem(); mDevice.pushFile(localFile, deviceFilePath); startSystem(); mDevice.reboot(); } /** * 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); } }