1 /*
2  * Copyright (C) 2015 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 package com.android.car.cluster;
17 
18 import android.car.cluster.renderer.InstrumentClusterRenderer;
19 import android.content.Context;
20 import android.content.ContextWrapper;
21 import android.content.pm.PackageManager;
22 import android.content.pm.PackageManager.NameNotFoundException;
23 import android.util.Log;
24 
25 import com.android.car.CarLog;
26 import com.android.car.R;
27 
28 import dalvik.system.PathClassLoader;
29 
30 import java.lang.reflect.Method;
31 
32 /**
33  * Responsible for loading {@link InstrumentClusterRenderer} from separate android.car.cluster APK
34  * library.
35  */
36 public class InstrumentClusterRendererLoader {
37     private final static String TAG = CarLog.TAG_SERVICE;
38 
39     private final static String sCreateRendererMethod = "createRenderer";
40 
41     /**
42      * Returns true if instrument cluster renderer installed.
43      */
isRendererAvailable(Context context)44     public static boolean isRendererAvailable(Context context) {
45         PackageManager packageManager = context.getPackageManager();
46         try {
47             packageManager.getPackageInfo(getRendererPackageName(context), 0);
48             return true;
49         } catch (NameNotFoundException e) {
50             return false;
51         }
52     }
53 
54     /** Dynamically load renderer APK and creates {@link InstrumentClusterRenderer}. */
createRenderer(Context context)55     public static InstrumentClusterRenderer createRenderer(Context context) {
56         final String packageName = getRendererPackageName(context);
57         try {
58             return load(context, packageName, getRendererFactoryClassName(context));
59         } catch (Exception e) {
60             Log.e(TAG, "Failed to load renderer class: " + e.getMessage(), e);
61             throw new RuntimeException(e);
62         }
63     }
64 
createRendererPackageContext(Context context)65     public static Context createRendererPackageContext(Context context) {
66         return createPackageContext(context, getRendererPackageName(context));
67     }
68 
69     /** To prevent instantiation of a singleton class. */
InstrumentClusterRendererLoader()70     private InstrumentClusterRendererLoader() {}
71 
72     /**
73      * Creates package context for given package name. It is necessary to get renderer's context
74      * so appropriate resources will be loaded in the renderer code.
75      */
createPackageContext(Context currentContext, String packageName)76     private static Context createPackageContext(Context currentContext, String packageName) {
77         try {
78             return new PackageContextWrapper(currentContext.createPackageContext(packageName,
79                     Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY));
80         } catch (NameNotFoundException e) {
81             Log.e(TAG, "Package not found: " + packageName, e);
82             throw new IllegalStateException(e);
83         }
84     }
85 
86     /** Returns instrument cluster renderer or null if renderer package is not found */
load(Context context, String packageName, String factoryClassName)87     private static InstrumentClusterRenderer load(Context context, String packageName,
88             String factoryClassName) throws Exception {
89         PackageManager packageManager = context.getPackageManager();
90 
91         assertSignature(context.getApplicationContext(), packageManager, packageName);
92 
93         String clusterRendererApk = packageManager.getApplicationInfo(packageName, 0).sourceDir;
94 
95         PathClassLoader pathClassLoader = new dalvik.system.PathClassLoader(
96                 clusterRendererApk, ClassLoader.getSystemClassLoader());
97 
98         Class<?> factoryClass = Class.forName(factoryClassName, true, pathClassLoader);
99 
100         Method createRendererMethod = factoryClass.getMethod(sCreateRendererMethod);
101 
102         Object rendererObject = createRendererMethod.invoke(null /* static */);
103 
104         if (rendererObject == null) {
105             Log.e(TAG, factoryClassName + "#" + sCreateRendererMethod + " returned null.");
106             throw new IllegalStateException();
107         }
108 
109         if (!(rendererObject instanceof InstrumentClusterRenderer)) {
110             Log.e(TAG, factoryClassName + "#" + sCreateRendererMethod + " returned unexpected"
111                     + " object of class " + rendererObject.getClass().getCanonicalName());
112             throw new IllegalStateException();
113         }
114         return (InstrumentClusterRenderer) rendererObject;
115     }
116 
117     /** Asserts that signature of a given alienPackageName matches with the current application. */
assertSignature(Context applicationContext, PackageManager packageManager, String alienPackageName)118     private static void assertSignature(Context applicationContext, PackageManager packageManager,
119             String alienPackageName) {
120         String carServicePackage = applicationContext.getPackageName();
121         int signatureMatch = packageManager.checkSignatures(carServicePackage, alienPackageName);
122         if (signatureMatch != PackageManager.SIGNATURE_MATCH) {
123             throw new IllegalArgumentException(
124                     "Signature doesn't match for package: " + alienPackageName
125                             + ", signatureMatch: " + signatureMatch);
126         }
127     }
128 
getRendererFactoryClassName(Context context)129     private static String getRendererFactoryClassName(Context context) {
130         return context.getString(R.string.instrumentClusterRendererFactoryClass);
131     }
132 
getRendererPackageName(Context context)133     private static String getRendererPackageName(Context context) {
134         return context.getString(R.string.instrumentClusterRendererPackage);
135     }
136 
137     /**
138      * The context returned by Context.createPackageContext returns null in getApplicationContext
139      * method which causes some problems later, e.g. when CursorLoader class is being used.
140      */
141     private static class PackageContextWrapper extends ContextWrapper {
PackageContextWrapper(Context packageContext)142         PackageContextWrapper(Context packageContext) {
143             super(packageContext);
144         }
145 
146         @Override
getApplicationContext()147         public Context getApplicationContext() {
148             return this;
149         }
150     }
151 }
152