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