• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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