1 /* 2 * Copyright (C) 2018 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.releaseparser; 18 19 import com.android.cts.releaseparser.ReleaseProto.*; 20 import com.google.protobuf.TextFormat; 21 22 import java.io.BufferedReader; 23 import java.io.File; 24 import java.io.IOException; 25 import java.io.InputStreamReader; 26 import java.util.ArrayList; 27 import java.util.HashMap; 28 import java.util.List; 29 import java.util.Map; 30 import java.util.regex.Matcher; 31 import java.util.regex.Pattern; 32 33 public class AndroidManifestParser extends FileParser { 34 private static Pattern pattern_element = Pattern.compile("E: (\\S*) "); 35 private static Pattern pattern_item = 36 Pattern.compile("A: (?:android:)?(\\w*)(?:\\S*)?=(?:\\(\\S*\\s*\\S*\\))?(\\S*)"); 37 private AppInfo.Builder mAppInfoBuilder; 38 private String mElementName; 39 private BufferedReader mReader; 40 private Map<String, String> mProperties; 41 private UsesFeature.Builder mUsesFeatureBuilder; 42 private UsesLibrary.Builder mUsesLibraryBuilder; 43 AndroidManifestParser(File file)44 public AndroidManifestParser(File file) { 45 super(file); 46 } 47 48 @Override getType()49 public Entry.EntryType getType() { 50 return Entry.EntryType.APK; 51 } 52 getAppInfo()53 public AppInfo getAppInfo() { 54 return getAppInfoBuilder().build(); 55 } 56 getAppInfoBuilder()57 public AppInfo.Builder getAppInfoBuilder() { 58 if (mAppInfoBuilder == null) { 59 prase(); 60 } 61 return mAppInfoBuilder; 62 } 63 prase()64 private void prase() { 65 mAppInfoBuilder = AppInfo.newBuilder(); 66 processManifest(); 67 } 68 69 // build cmd list as: "aapt", "d", "xmltree", fileName, "AndroidManifest.xml" getAaptCmds(String file)70 private List<String> getAaptCmds(String file) { 71 List<String> cmds = new ArrayList<>(); 72 cmds.add("aapt"); 73 cmds.add("d"); 74 cmds.add("xmltree"); 75 cmds.add(file); 76 cmds.add("AndroidManifest.xml"); 77 return cmds; 78 } 79 processManifest()80 private void processManifest() { 81 try { 82 // ToDo prasing as android/frameworks/base/tools/aapt/Resource.cpp parsePackage() 83 Process process = new ProcessBuilder(getAaptCmds(mFile.getAbsolutePath())).start(); 84 mReader = new BufferedReader(new InputStreamReader(process.getInputStream())); 85 parseElement(); 86 } catch (Exception ex) { 87 System.err.println("Failed to aapt d badging " + mFile.getAbsolutePath()); 88 ex.printStackTrace(); 89 } 90 } 91 parseElement()92 private void parseElement() { 93 String eleName; 94 Matcher matcher; 95 String line; 96 mProperties = new HashMap<>(); 97 int indent; 98 99 try { 100 while ((line = mReader.readLine()) != null) { 101 eleName = getElementName(line); 102 if (eleName != null) { 103 mElementName = eleName; 104 parseElementItem(); 105 } 106 } 107 mAppInfoBuilder.putAllProperties(mProperties); 108 } catch (Exception ex) { 109 System.err.println( 110 "Failed to aapt & parse AndroidManifest.xml from: " + mFile.getAbsolutePath()); 111 ex.printStackTrace(); 112 } 113 } 114 parseElementItem()115 private void parseElementItem() throws IOException { 116 String eleName; 117 Matcher matcher; 118 String line; 119 int indent; 120 while ((line = mReader.readLine()) != null) { 121 indent = parseItem(line); 122 if (indent == -1) { 123 eleName = getElementName(line); 124 if (eleName == null) { 125 break; 126 } 127 128 switch (mElementName) { 129 case "uses-feature": 130 mAppInfoBuilder.addUsesFeatures(mUsesFeatureBuilder); 131 mUsesFeatureBuilder = null; 132 break; 133 case "uses-library": 134 mAppInfoBuilder.addUsesLibraries(mUsesLibraryBuilder); 135 mUsesLibraryBuilder = null; 136 break; 137 } 138 mElementName = eleName; 139 } 140 } 141 } 142 parseItem(String line)143 private int parseItem(String line) { 144 String key; 145 String value; 146 Matcher matcher; 147 int indent = line.indexOf("A: "); 148 if (indent != -1) { 149 matcher = pattern_item.matcher(line); 150 if (matcher.find()) { 151 int processed = 0; 152 key = matcher.group(1); 153 value = matcher.group(2).replace("\"", ""); 154 155 switch (mElementName) { 156 case "manifest": 157 if (key.equals("package")) { 158 mAppInfoBuilder.setPackageName(value); 159 processed = 1; 160 break; 161 } 162 case "uses-sdk": 163 case "application": 164 case "supports-screens": 165 mProperties.put(key, value); 166 processed = 1; 167 break; 168 case "original-package": 169 if (key.equals("name")) { 170 mProperties.put(mElementName, value); 171 processed = 1; 172 } 173 break; 174 case "uses-permission": 175 if (key.equals("name")) { 176 mAppInfoBuilder.addUsesPermissions(value); 177 processed = 1; 178 } 179 break; 180 case "activity": 181 if (key.equals("name")) { 182 mAppInfoBuilder.addActivities(value); 183 processed = 1; 184 } 185 break; 186 case "service": 187 if (key.equals("name")) { 188 mAppInfoBuilder.addServices(value); 189 processed = 1; 190 } 191 break; 192 case "provider": 193 if (key.equals("name")) { 194 mAppInfoBuilder.addProviders(value); 195 processed = 1; 196 } 197 break; 198 case "uses-feature": 199 putFeature(key, value); 200 processed = 1; 201 break; 202 case "uses-library": 203 putLibrary(key, value); 204 processed = 1; 205 break; 206 default: 207 } 208 System.out.println( 209 String.format("%s,%s,%s,%d", mElementName, key, value, processed)); 210 } 211 } 212 return indent; 213 } 214 putFeature(String key, String value)215 private void putFeature(String key, String value) { 216 if (mUsesFeatureBuilder == null) { 217 mUsesFeatureBuilder = UsesFeature.newBuilder(); 218 } 219 switch (key) { 220 case "name": 221 mUsesFeatureBuilder.setName(value); 222 case "required": 223 mUsesFeatureBuilder.setRequired(value); 224 } 225 } 226 putLibrary(String key, String value)227 private void putLibrary(String key, String value) { 228 if (mUsesLibraryBuilder == null) { 229 mUsesLibraryBuilder = UsesLibrary.newBuilder(); 230 } 231 switch (key) { 232 case "name": 233 mUsesLibraryBuilder.setName(value); 234 case "required": 235 mUsesLibraryBuilder.setRequired(value); 236 } 237 } 238 getElementName(String line)239 private String getElementName(String line) { 240 String name; 241 Matcher matcher; 242 int indent = line.indexOf("E: "); 243 if (indent != -1) { 244 matcher = pattern_element.matcher(line); 245 if (matcher.find()) { 246 name = matcher.group(1); 247 return name; 248 } 249 } 250 return null; 251 } 252 253 private static final String USAGE_MESSAGE = 254 "Usage: java -jar releaseparser.jar com.android.cts.releaseparser.ApkParser [-options] <path> [args...]\n" 255 + " to prase an APK for API\n" 256 + "Options:\n" 257 + "\t-i PATH\t APK path \n"; 258 259 /** Get the argument or print out the usage and exit. */ printUsage()260 private static void printUsage() { 261 System.out.printf(USAGE_MESSAGE); 262 System.exit(1); 263 } 264 265 /** Get the argument or print out the usage and exit. */ getExpectedArg(String[] args, int index)266 private static String getExpectedArg(String[] args, int index) { 267 if (index < args.length) { 268 return args[index]; 269 } else { 270 printUsage(); 271 return null; // Never will happen because printUsage will call exit(1) 272 } 273 } 274 main(String[] args)275 public static void main(String[] args) throws IOException { 276 String apkFileName = null; 277 278 for (int i = 0; i < args.length; i++) { 279 if (args[i].startsWith("-")) { 280 if ("-i".equals(args[i])) { 281 apkFileName = getExpectedArg(args, ++i); 282 } 283 } 284 } 285 286 if (apkFileName == null) { 287 printUsage(); 288 } 289 290 File apkFile = new File(apkFileName); 291 AndroidManifestParser manifestParser = new AndroidManifestParser(apkFile); 292 System.out.println(); 293 System.out.println(TextFormat.printToString(manifestParser.getAppInfo())); 294 } 295 } 296