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