1 /* 2 * Copyright (C) 2016 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.cts.managedprofile; 18 19 import com.google.common.base.Strings; 20 import com.google.common.collect.ImmutableList; 21 import com.google.common.collect.ImmutableMap; 22 import com.google.common.primitives.Primitives; 23 24 import org.w3c.dom.Document; 25 import org.w3c.dom.Element; 26 import org.w3c.dom.NodeList; 27 28 import java.io.File; 29 import java.io.FileInputStream; 30 import java.io.InputStream; 31 import java.lang.reflect.Method; 32 import java.util.zip.GZIPInputStream; 33 34 import javax.xml.parsers.DocumentBuilder; 35 import javax.xml.parsers.DocumentBuilderFactory; 36 37 /** 38 * Helper class for retrieving the current list of API methods. 39 */ 40 public class CurrentApiHelper { 41 42 /** 43 * Location of the XML file that lists the current public APIs. 44 * 45 * <p><b>Note:</b> must be consistent with 46 * {@code cts/hostsidetests/devicepolicy/AndroidTest.xml} 47 */ 48 private static final String CURRENT_API_FILE = 49 "/data/local/tmp/device-policy-test/current.api.gz"; 50 51 private static final String LOG_TAG = "CurrentApiHelper"; 52 53 private static final ImmutableMap<String, Class> PRIMITIVE_TYPES = getPrimitiveTypes(); 54 private static final ImmutableMap<String, String> PRIMITIVE_ENCODINGS = 55 new ImmutableMap.Builder<String, String>() 56 .put("boolean", "Z") 57 .put("byte", "B") 58 .put("char", "C") 59 .put("double", "D") 60 .put("float", "F") 61 .put("int", "I") 62 .put("long", "J") 63 .put("short", "S") 64 .build(); 65 66 private static final String TAG_PACKAGE = "package"; 67 private static final String TAG_CLASS = "class"; 68 private static final String TAG_METHOD = "method"; 69 private static final String TAG_PARAMETER = "parameter"; 70 71 private static final String ATTRIBUTE_NAME = "name"; 72 private static final String ATTRIBUTE_TYPE = "type"; 73 74 /** 75 * Get public API methods of a specific class as defined in the API document. 76 * 77 * @param packageName The name of the package containing the class, e.g. {@code android.app}. 78 * @param className The name of the class, e.g. {@code Application}. 79 * @return an immutable list of {@link Method} instances. 80 */ getPublicApis(String packageName, String className)81 public static ImmutableList<Method> getPublicApis(String packageName, String className) 82 throws Exception { 83 Document apiDocument = parseXmlFile(CURRENT_API_FILE); 84 Element rootElement = apiDocument.getDocumentElement(); 85 Element packageElement = getChildElementByName(rootElement, TAG_PACKAGE, packageName); 86 Element classElement = getChildElementByName(packageElement, TAG_CLASS, className); 87 88 ImmutableList.Builder<Method> builder = new ImmutableList.Builder<>(); 89 90 NodeList nodes = classElement.getElementsByTagName(TAG_METHOD); 91 if (nodes != null && nodes.getLength() > 0) { 92 Class clazz = Class.forName(packageName + "." + className); 93 94 for (int i = 0; i < nodes.getLength(); ++i) { 95 Element element = (Element) nodes.item(i); 96 String name = element.getAttribute(ATTRIBUTE_NAME); 97 Class[] paramTypes = getParamTypes(element); 98 builder.add(clazz.getMethod(name, paramTypes)); 99 } 100 } 101 102 return builder.build(); 103 } 104 105 /** 106 * Given a {@link Class} object, get the default value if the {@link Class} refers to a 107 * primitive type, or null if it refers to an object. 108 * 109 * <p><ul> 110 * <li>For boolean type, return {@code false} 111 * <li>For other primitive types, return {@code 0} 112 * <li>For all other types, return {@code null} 113 * </ul> 114 * @param clazz The desired class to instantiate. 115 * @return Default instance as described above. 116 */ instantiate(Class clazz)117 public static Object instantiate(Class clazz) { 118 if (clazz.isPrimitive()) { 119 if (boolean.class.equals(clazz)) { 120 return false; 121 } else { 122 return 0; 123 } 124 } else { 125 return null; 126 } 127 } 128 parseXmlFile(String filePath)129 private static Document parseXmlFile(String filePath) throws Exception { 130 File apiFile = new File(filePath); 131 InputStream input = new GZIPInputStream(new FileInputStream(apiFile)); 132 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 133 DocumentBuilder db = dbf.newDocumentBuilder(); 134 Document dom = db.parse(input); 135 136 return dom; 137 } 138 getChildElementByName(Element parent, String childTag, String childName)139 private static Element getChildElementByName(Element parent, 140 String childTag, String childName) { 141 NodeList nodeList = parent.getElementsByTagName(childTag); 142 if (nodeList != null && nodeList.getLength() > 0) { 143 for (int i = 0; i < nodeList.getLength(); ++i) { 144 Element el = (Element) nodeList.item(i); 145 if (childName.equals(el.getAttribute(ATTRIBUTE_NAME))) { 146 return el; 147 } 148 } 149 } 150 return null; 151 } 152 getParamTypes(Element methodElement)153 private static Class[] getParamTypes(Element methodElement) throws Exception { 154 NodeList nodes = methodElement.getElementsByTagName(TAG_PARAMETER); 155 if (nodes != null && nodes.getLength() > 0) { 156 int paramCount = nodes.getLength(); 157 Class[] paramTypes = new Class[paramCount]; 158 for (int i = 0; i < paramCount; ++i) { 159 String typeName = ((Element) nodes.item(i)).getAttribute(ATTRIBUTE_TYPE); 160 paramTypes[i] = getClassByName(typeName); 161 } 162 return paramTypes; 163 } else { 164 return new Class[0]; 165 } 166 } 167 getClassByName(String typeName)168 private static Class getClassByName(String typeName) throws ClassNotFoundException { 169 // Check if typeName represents an array 170 int arrayDim = 0; 171 while (typeName.endsWith("[]")) { 172 arrayDim++; 173 typeName = typeName.substring(0, typeName.length() - 2); 174 } 175 176 // Resolve inner classes 177 typeName = typeName.replaceAll("([A-Z].*)\\.", "$1\\$"); 178 179 // Remove type parameters, if any 180 typeName = typeName.replaceAll("<.*>$", ""); 181 182 if (arrayDim == 0) { 183 if (isPrimitiveTypeName(typeName)) { 184 return PRIMITIVE_TYPES.get(typeName); 185 } else { 186 return Class.forName(typeName); 187 } 188 189 } else { 190 String prefix = Strings.repeat("[", arrayDim); 191 if (isPrimitiveTypeName(typeName)) { 192 return Class.forName(prefix + PRIMITIVE_ENCODINGS.get(typeName)); 193 } else { 194 return Class.forName(prefix + "L" + typeName + ";"); 195 } 196 } 197 } 198 getPrimitiveTypes()199 private static ImmutableMap<String, Class> getPrimitiveTypes() { 200 ImmutableMap.Builder<String, Class> builder = new ImmutableMap.Builder<>(); 201 for (Class type : Primitives.allPrimitiveTypes()) { 202 builder.put(type.getName(), type); 203 } 204 return builder.build(); 205 } 206 isPrimitiveTypeName(String typeName)207 private static boolean isPrimitiveTypeName(String typeName) { 208 return PRIMITIVE_TYPES.containsKey(typeName); 209 } 210 } 211