1 /* 2 * Copyright (C) 2022 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.ondevicepersonalization.libraries.plugin.internal; 18 19 import android.util.Log; 20 21 import com.android.ondevicepersonalization.libraries.plugin.Plugin; 22 import com.android.ondevicepersonalization.libraries.plugin.internal.util.ApkReader; 23 24 import dalvik.system.InMemoryDexClassLoader; 25 26 import com.google.common.collect.ImmutableList; 27 import com.google.common.collect.ImmutableSet; 28 29 import org.checkerframework.checker.nullness.qual.Nullable; 30 31 import java.io.FileInputStream; 32 import java.io.IOException; 33 import java.lang.reflect.InvocationTargetException; 34 import java.nio.ByteBuffer; 35 36 /** Implementation of PluginLoader */ 37 public final class PluginLoaderImpl implements PluginLoader { 38 private static final String TAG = "PluginLoaderImpl"; 39 loadPlugin( String className, ImmutableList<PluginCode> pluginCode, ClassLoader classLoader, ImmutableSet<String> containerClassesAllowlist, ImmutableSet<String> containerPackagesAllowlist)40 @Override public @Nullable Plugin loadPlugin( 41 String className, 42 ImmutableList<PluginCode> pluginCode, 43 ClassLoader classLoader, 44 ImmutableSet<String> containerClassesAllowlist, 45 ImmutableSet<String> containerPackagesAllowlist) { 46 47 try (CloseableList<FileInputStream> archiveList = 48 PluginCode.createFileInputStreamListFromNonNativeFds(pluginCode)) { 49 ByteBuffer[] dexes = ApkReader.loadPluginCode(archiveList.closeables()); 50 51 // Instantiating a ClassLoader and loading classes on Android would trigger dex-to-vdex 52 // generation to speed up class verification process at subsequent calls by verifying 53 // dex 54 // upfront and storing verified dex (vdex) to app's storage. 55 // On an isolated process, dex-to-vdex process would always fail due to denials of 56 // file/dir creation. It potentially hurts class loading performance but not impacts 57 // functionalities, worth further systracing/profiling to observe VerifyClass overhead. 58 IsolationClassLoader isolatedContainerClassLoader = 59 new IsolationClassLoader( 60 classLoader, containerClassesAllowlist, containerPackagesAllowlist); 61 ClassLoader pluginClassLoader; 62 if (dexes.length > 0) { 63 pluginClassLoader = new InMemoryDexClassLoader(dexes, isolatedContainerClassLoader); 64 } else { 65 // TODO(b/249345663): Remove this after we add tests for loading APKs. 66 // InMemoryDexClassLoader crashes if there are no dexes. 67 pluginClassLoader = isolatedContainerClassLoader; 68 } 69 70 Class<?> clazz = pluginClassLoader.loadClass(className); 71 Object instance = clazz.getDeclaredConstructor().newInstance(); 72 73 if (!(instance instanceof Plugin)) { 74 Log.e(TAG, "Instance not a Plugin"); 75 return null; 76 } 77 Plugin pluginInstance = (Plugin) instance; 78 pluginInstance.setClassLoader(pluginClassLoader); 79 return pluginInstance; 80 } catch (IOException e) { 81 Log.e(TAG, "Error loading dex files from archive", e); 82 } catch (ClassNotFoundException e) { 83 Log.e(TAG, String.format("Class %s not found", className), e); 84 } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { 85 Log.e(TAG, String.format("Error instantiating %s", className), e); 86 } catch (NoSuchMethodException e) { 87 Log.e(TAG, "Plugin's declared constructor not found", e); 88 } 89 90 return null; 91 } 92 } 93