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.os.Handler;
20 
21 import java.io.File;
22 import java.io.IOException;
23 import java.util.ArrayList;
24 import java.util.List;
25 import java.util.Map;
26 
27 /**
28  * Handles the loading of dex files for multiple apks to be verified, ensures that a single dex file
29  * is loaded at any time.
30  */
31 public class SerialDexLoader {
32     private static final String TAG = "SdkSandboxVerifier";
33 
34     private DexParser mParser;
35     private Handler mHandler;
36     private DexLoadResult mDexLoadResult;
37 
SerialDexLoader(DexParser parser, Handler handler)38     public SerialDexLoader(DexParser parser, Handler handler) {
39         mParser = parser;
40         mHandler = handler;
41         mDexLoadResult = new DexLoadResult();
42     }
43 
44     /**
45      * Queues all dex files found for an apk for serially loading and analyzing.
46      *
47      * @param apkPathFile path to apk containing one or more dex files
48      * @param packagename packagename associated with the apk
49      * @param verificationHandler object to handle the verification of the loaded dex
50      */
queueApkToLoad( File apkPathFile, String packagename, VerificationHandler verificationHandler)51     public void queueApkToLoad(
52             File apkPathFile, String packagename, VerificationHandler verificationHandler) {
53 
54         mHandler.post(
55                 () -> {
56                     Map<File, List<String>> dexEntries;
57                     try {
58                         dexEntries = mParser.getDexFilePaths(apkPathFile);
59                     } catch (IOException e) {
60                         verificationHandler.onVerificationErrorForPackage(e);
61                         return;
62                     }
63 
64                     for (Map.Entry<File, List<String>> dexFileEntries : dexEntries.entrySet()) {
65                         for (String dexEntry : dexFileEntries.getValue()) {
66                             try {
67                                 mParser.loadDexSymbols(
68                                         dexFileEntries.getKey(), dexEntry, mDexLoadResult);
69                             } catch (IOException e) {
70                                 verificationHandler.onVerificationErrorForPackage(e);
71                                 return;
72                             }
73                             if (!verificationHandler.verify(mDexLoadResult)) {
74                                 verificationHandler.onVerificationCompleteForPackage(false);
75                                 return;
76                             }
77                         }
78                     }
79 
80                     verificationHandler.onVerificationCompleteForPackage(true);
81                 });
82     }
83 
84     /** Interface for handling processing of the loaded dex contents */
85     public interface VerificationHandler {
86 
87         /**
88          * Takes in the DexLoadResult and verifies its contents.
89          *
90          * @param result object contains the symbols parsed from the loaded dex file
91          */
verify(DexLoadResult result)92         boolean verify(DexLoadResult result);
93 
94         /**
95          * Called when all the loaded dex files have passed verification, or when one has failed.
96          *
97          * @param passed is false if the last loaded dex failed verification, or true if all dexes
98          *     passed.
99          */
onVerificationCompleteForPackage(boolean result)100         void onVerificationCompleteForPackage(boolean result);
101 
102         /**
103          * Error occurred on verifying.
104          *
105          * @param e exception thrown while attempting to load and verify the apk.
106          */
onVerificationErrorForPackage(Exception e)107         void onVerificationErrorForPackage(Exception e);
108     }
109 
110     /** Result class that contains symbols loaded from a DEX file */
111     public static class DexLoadResult {
112 
113         public static final int DEX_MAX_METHOD_COUNT = 65536;
114 
115         /** The table of methods referenced by the DEX file. */
116         private ArrayList<String> mReferencedMethods = new ArrayList<>(DEX_MAX_METHOD_COUNT);
117 
118         /** Adds a new method to the referencedMethods table */
addReferencedMethod(String method)119         public void addReferencedMethod(String method) {
120             mReferencedMethods.add(method);
121         }
122 
123         /** Returns true if the method string is present in the referencedMethods table */
hasReferencedMethod(String method)124         public boolean hasReferencedMethod(String method) {
125             return mReferencedMethods.contains(method);
126         }
127 
128         /** Clears the internal state of DexLoadResult to load next dex file. */
clear()129         public void clear() {
130             mReferencedMethods.clear();
131         }
132     }
133 }
134