1 /* 2 * Copyright (C) 2023 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.server.sdksandbox.verifier; 18 19 import com.android.server.sdksandbox.verifier.SerialDexLoader.DexLoadResult; 20 import com.android.tools.smali.dexlib2.DexFileFactory; 21 import com.android.tools.smali.dexlib2.DexFileFactory.DexFileNotFoundException; 22 import com.android.tools.smali.dexlib2.Opcodes; 23 import com.android.tools.smali.dexlib2.dexbacked.DexBackedDexFile; 24 import com.android.tools.smali.dexlib2.dexbacked.reference.DexBackedMethodReference; 25 import com.android.tools.smali.dexlib2.iface.MultiDexContainer; 26 27 import java.io.File; 28 import java.io.IOException; 29 import java.util.ArrayList; 30 import java.util.Enumeration; 31 import java.util.HashMap; 32 import java.util.List; 33 import java.util.Map; 34 import java.util.zip.ZipEntry; 35 import java.util.zip.ZipFile; 36 37 /** 38 * DEX parser for SDK verification 39 * 40 * @hide 41 */ 42 public class DexParserImpl implements DexParser { 43 44 @Override getDexFilePaths(File apkPathFile)45 public Map<File, List<String>> getDexFilePaths(File apkPathFile) throws IOException { 46 ArrayList<File> apkList = new ArrayList<>(); 47 48 // If multi-apk directory, find a base apk and zero or more split apks 49 if (apkPathFile.isDirectory()) { 50 for (File apkFile : apkPathFile.listFiles()) { 51 if (apkFile.isFile() && apkFile.getName().endsWith(".apk")) { 52 apkList.add(apkFile); 53 } 54 } 55 } else { 56 apkList.add(apkPathFile); 57 } 58 59 HashMap<File, List<String>> dexLists = new HashMap<>(); 60 61 for (File apk : apkList) { 62 try (ZipFile apkZipFile = new ZipFile(apk)) { 63 Enumeration<? extends ZipEntry> entriesEnumeration = apkZipFile.entries(); 64 ArrayList<String> dexEntries = new ArrayList<>(); 65 66 while (entriesEnumeration.hasMoreElements()) { 67 String entryName = entriesEnumeration.nextElement().getName(); 68 if (entryName.endsWith(".dex")) { 69 dexEntries.add(entryName); 70 } 71 } 72 dexLists.put(apk, dexEntries); 73 } catch (IOException ex) { 74 throw new IOException(apk.getName() + " is not a valid DEX container file.", ex); 75 } 76 } 77 78 return (Map<File, List<String>>) dexLists; 79 } 80 81 @Override loadDexSymbols(File apkFile, String dexName, DexLoadResult dexLoadResult)82 public void loadDexSymbols(File apkFile, String dexName, DexLoadResult dexLoadResult) 83 throws IOException { 84 MultiDexContainer.DexEntry<? extends DexBackedDexFile> dexEntry; 85 try { 86 dexEntry = 87 DexFileFactory.loadDexEntry( 88 apkFile, dexName, /* exactMatch */ true, Opcodes.getDefault()); 89 } catch (DexFileNotFoundException e) { 90 throw new IOException(e); 91 } 92 dexLoadResult.clear(); 93 94 DexBackedDexFile dexFile = dexEntry.getDexFile(); 95 96 for (DexBackedMethodReference method : dexFile.getMethodSection()) { 97 String methodString = method.getDefiningClass() + method.getName() + ";"; 98 for (String param : method.getParameterTypes()) { 99 methodString = methodString + param; 100 } 101 methodString = methodString + method.getReturnType(); 102 dexLoadResult.addReferencedMethod(methodString); 103 } 104 } 105 } 106