1 /* 2 * Copyright (C) 2012 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 com.android.tradefed.device.DeviceNotAvailableException; 20 import com.android.tradefed.device.ITestDevice; 21 import com.android.tradefed.log.LogUtil.CLog; 22 23 import org.junit.Assert; 24 import org.w3c.dom.Document; 25 import org.w3c.dom.Node; 26 27 import java.io.File; 28 import java.util.regex.Matcher; 29 import java.util.regex.Pattern; 30 31 import javax.xml.parsers.DocumentBuilder; 32 import javax.xml.parsers.DocumentBuilderFactory; 33 import javax.xml.xpath.XPath; 34 import javax.xml.xpath.XPathConstants; 35 import javax.xml.xpath.XPathExpression; 36 import javax.xml.xpath.XPathFactory; 37 /** Utility method used for PackageMangerOTATests. Requires adb root. */ 38 public class PackageManagerOTATestUtils { 39 public static final String USERDATA_PARTITION = "userdata"; 40 public static final String PACKAGE_XML_FILE = "/data/system/packages.xml"; 41 42 private ITestDevice mDevice = null; 43 44 /** 45 * Constructor. 46 * 47 * @param device the {@link ITestDevice} to use when performing operations. 48 * @throws DeviceNotAvailableException 49 */ PackageManagerOTATestUtils(ITestDevice device)50 public PackageManagerOTATestUtils(ITestDevice device) throws DeviceNotAvailableException { 51 mDevice = device; 52 } 53 54 /** 55 * Wipe userdata partition on device. 56 * 57 * @throws DeviceNotAvailableException 58 */ wipeDevice()59 public void wipeDevice() throws DeviceNotAvailableException { 60 // Make sure to keep the local.prop file for testing purposes. 61 File prop = mDevice.pullFile("/data/local.prop"); 62 mDevice.rebootIntoBootloader(); 63 mDevice.fastbootWipePartition(USERDATA_PARTITION); 64 mDevice.rebootUntilOnline(); 65 if (prop != null) { 66 mDevice.pushFile(prop, "/data/local.prop"); 67 mDevice.executeShellCommand("chmod 644 /data/local.prop"); 68 mDevice.reboot(); 69 } 70 } 71 72 /** 73 * Remove a system app. 74 * 75 * @param systemApp {@link String} name for the application in the /system/app folder 76 * @param reboot set to <code>true</code> to optionally reboot device after app removal 77 * @throws DeviceNotAvailableException 78 */ removeSystemApp(String systemApp, boolean reboot)79 public void removeSystemApp(String systemApp, boolean reboot) 80 throws DeviceNotAvailableException { 81 mDevice.remountSystemWritable(); 82 String cmd = String.format("rm %s", systemApp); 83 mDevice.executeShellCommand(cmd); 84 if (reboot) { 85 mDevice.reboot(); 86 } 87 mDevice.waitForDeviceAvailable(); 88 } 89 90 /** 91 * Remove a system app and wipe the device. 92 * 93 * @param systemApp {@link String} name for the application in the /system/app folder 94 * @throws DeviceNotAvailableException 95 */ removeAndWipe(String systemApp)96 public void removeAndWipe(String systemApp) throws DeviceNotAvailableException { 97 removeSystemApp(systemApp, false); 98 wipeDevice(); 99 } 100 101 /** 102 * Expect that a given xpath exists in a given xml file. 103 * 104 * @param xmlFile {@link File} xml file to process 105 * @param xPathString {@link String} Xpath to look for 106 * @return true if the xpath is found 107 */ expectExists(File xmlFile, String xPathString)108 public boolean expectExists(File xmlFile, String xPathString) { 109 Node n = getNodeForXPath(xmlFile, xPathString); 110 if (n != null) { 111 CLog.d("Found node %s for xpath %s", n.getNodeName(), xPathString); 112 return true; 113 } 114 return false; 115 } 116 117 /** 118 * Expect that the value of a given xpath starts with a given string. 119 * 120 * @param xmlFile {@link File} the xml file in question 121 * @param xPathString {@link String} the xpath to look for 122 * @param value {@link String} the expected start string of the xpath 123 * @return true if the value for the xpath starts with value, false otherwise 124 */ expectStartsWith(File xmlFile, String xPathString, String value)125 public boolean expectStartsWith(File xmlFile, String xPathString, String value) { 126 Node n = getNodeForXPath(xmlFile, xPathString); 127 if (n == null) { 128 CLog.d("Failed to find node for xpath %s", xPathString); 129 return false; 130 } 131 CLog.d("Value of node %s: %s", xPathString, n.getNodeValue()); 132 return n.getNodeValue().toLowerCase().startsWith(value.toLowerCase()); 133 } 134 135 /** 136 * Expect that the value of a given xpath matches. 137 * 138 * @param xmlFile {@link File} the xml file in question 139 * @param xPathString {@link String} the xpath to look for 140 * @param value {@link String} the expected string value 141 * @return true if the value for the xpath matches, false otherwise 142 */ expectEquals(File xmlFile, String xPathString, String value)143 public boolean expectEquals(File xmlFile, String xPathString, String value) { 144 Node n = getNodeForXPath(xmlFile, xPathString); 145 if (n == null) { 146 CLog.d("Failed to find node for xpath %s", xPathString); 147 return false; 148 } 149 boolean result = n.getNodeValue().equalsIgnoreCase(value); 150 if (!result) { 151 CLog.v( 152 "Value of node %s: \"%s\", expected: \"%s\"", 153 xPathString, n.getNodeValue(), value); 154 } 155 return result; 156 } 157 getNodeForXPath(File xmlFile, String xPathString)158 public Node getNodeForXPath(File xmlFile, String xPathString) { 159 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 160 try { 161 DocumentBuilder documentBuilder = factory.newDocumentBuilder(); 162 Document doc = documentBuilder.parse(xmlFile); 163 XPathFactory xpFactory = XPathFactory.newInstance(); 164 XPath xpath = xpFactory.newXPath(); 165 XPathExpression expr = xpath.compile(xPathString); 166 Node node = (Node) expr.evaluate(doc, XPathConstants.NODE); 167 return node; 168 } catch (Exception e) { 169 CLog.e(e); 170 } 171 return null; 172 } 173 174 /** 175 * Check if a given package has the said permission. 176 * 177 * @param packageName {@link String} the package in question 178 * @param permission {@link String} the permission to look for 179 * @return true if the permission exists, false otherwise 180 * @throws DeviceNotAvailableException 181 */ packageHasPermission(String packageName, String permission)182 public boolean packageHasPermission(String packageName, String permission) 183 throws DeviceNotAvailableException { 184 String cmd = "dumpsys package " + packageName; 185 String res = mDevice.executeShellCommand(cmd); 186 if (res != null) { 187 if (res.contains("grantedPermissions:")) { 188 return res.contains(permission); 189 } else { 190 Pattern perm = 191 Pattern.compile( 192 String.format("^.*%s.*granted=true.*$", permission), 193 Pattern.MULTILINE); 194 Matcher m = perm.matcher(res); 195 return m.find(); 196 } 197 } 198 CLog.d("Failed to execute shell command: %s", cmd); 199 return false; 200 } 201 202 /** 203 * Check if a given package has the said permission. 204 * 205 * @param packageName {@link String} the package in question 206 * @param flag {@link String} the permission to look for 207 * @return true if the permission exists, false otherwise 208 * @throws DeviceNotAvailableException 209 */ packageHasFlag(String packageName, String flag)210 public boolean packageHasFlag(String packageName, String flag) 211 throws DeviceNotAvailableException { 212 String cmd = "dumpsys package " + packageName; 213 String res = mDevice.executeShellCommand(cmd); 214 if (res != null) { 215 Pattern flags = Pattern.compile("^.*flags=\\[(.*?)\\]$", Pattern.MULTILINE); 216 Matcher m = flags.matcher(res); 217 if (m.find()) { 218 return m.group(1).contains(flag); 219 } else { 220 CLog.d("Failed to find package flags record in dumpsys package output"); 221 } 222 } 223 CLog.d("Failed to execute shell command: %s", cmd); 224 return false; 225 } 226 227 /** 228 * Helper method to install a file 229 * 230 * @param localFile the {@link File} to install 231 * @param replace set to <code>true</code> if re-install of app should be performed 232 * @param extraArgs optional extra arguments to pass. See 'adb shell pm install --help' for 233 * available options. 234 * @throws DeviceNotAvailableException 235 */ installFile(final File localFile, final boolean replace, String... extraArgs)236 public void installFile(final File localFile, final boolean replace, String... extraArgs) 237 throws DeviceNotAvailableException { 238 String result = mDevice.installPackage(localFile, replace, extraArgs); 239 Assert.assertNull( 240 String.format( 241 "Failed to install file %s with result %s", 242 localFile.getAbsolutePath(), result), 243 result); 244 } 245 246 /** 247 * Helper method to stop system shell. 248 * 249 * @throws DeviceNotAvailableException 250 */ stopSystem()251 public void stopSystem() throws DeviceNotAvailableException { 252 mDevice.executeShellCommand("stop"); 253 } 254 255 /** 256 * Helper method to start system shell. It also reset the flag dev.bootcomplete to 0 to ensure 257 * that the package manager had a chance to finish. 258 * 259 * @throws DeviceNotAvailableException 260 */ startSystem()261 public void startSystem() throws DeviceNotAvailableException { 262 mDevice.executeShellCommand("setprop dev.bootcomplete 0"); 263 mDevice.executeShellCommand("start"); 264 mDevice.waitForDeviceAvailable(); 265 } 266 267 /** 268 * Convenience method to stop, then start the runtime. 269 * 270 * @throws DeviceNotAvailableException 271 */ restartSystem()272 public void restartSystem() throws DeviceNotAvailableException { 273 stopSystem(); 274 startSystem(); 275 } 276 277 /** 278 * Push apk to system app directory on the device. 279 * 280 * @param localFile {@link File} the local file to install 281 * @param deviceFilePath {@link String} the remote device path where to install the application 282 * @throws DeviceNotAvailableException 283 */ pushSystemApp(final File localFile, final String deviceFilePath)284 public void pushSystemApp(final File localFile, final String deviceFilePath) 285 throws DeviceNotAvailableException { 286 mDevice.remountSystemWritable(); 287 stopSystem(); 288 mDevice.pushFile(localFile, deviceFilePath); 289 startSystem(); 290 } 291 292 /** 293 * Pulls packages xml file from the device. 294 * 295 * @return {@link File} xml file for all packages on device. 296 * @throws DeviceNotAvailableException 297 */ pullPackagesXML()298 public File pullPackagesXML() throws DeviceNotAvailableException { 299 return mDevice.pullFile(PACKAGE_XML_FILE); 300 } 301 } 302