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