1/* 2 * Copyright (C) 2016 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 */ 16package android.support.car.app.menu; 17 18import android.car.app.menu.CarUiEntry; 19import android.content.Context; 20import android.content.pm.ApplicationInfo; 21import android.content.pm.PackageManager; 22import android.graphics.Bitmap; 23import android.os.Bundle; 24import android.support.car.app.menu.compat.EmbeddedCarMenuCallbacksCompat; 25import android.support.car.app.menu.compat.EmbeddedSearchBoxEditListenerCompat; 26import android.view.View; 27import android.widget.EditText; 28import java.lang.reflect.InvocationTargetException; 29 30/** 31 * A {@link android.support.car.app.menu.CarUiController} that talks to embedded car ui provider. 32 * @hide 33 */ 34public class EmbeddedCarUiController extends CarUiController { 35 36 private static final String TAG = "EmbeddedCarUiController"; 37 // TODO: load the package name and class name from resources 38 private static final String UI_ENTRY_CLASS_NAME = ".CarUiEntry"; 39 private static final String CAR_UI_PROVIDER_PKG = "android.car.ui.provider"; 40 private static final String CAR_SERVICE_PKG = "com.android.car"; 41 42 private CarUiEntry mCarUiEntry; 43 private EmbeddedCarMenuCallbacksCompat mCallback; 44 45 public EmbeddedCarUiController(CarDrawerActivity activity) { 46 super(activity); 47 try { 48 Context carUiContext = mActivity.getContext().createPackageContext( 49 CAR_UI_PROVIDER_PKG, 50 Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY); 51 52 ClassLoader classLoader = carUiContext.getClassLoader(); 53 Class<?> loadedClass = classLoader.loadClass(CAR_UI_PROVIDER_PKG + UI_ENTRY_CLASS_NAME); 54 mCarUiEntry = (CarUiEntry) loadedClass.getConstructor(Context.class, Context.class) 55 .newInstance(carUiContext, mActivity.getContext()); 56 } catch (PackageManager.NameNotFoundException | ClassNotFoundException e) { 57 throw new RuntimeException("Cannot find CarUiEntry from " + CAR_UI_PROVIDER_PKG + "/" 58 + UI_ENTRY_CLASS_NAME, e); 59 } catch (IllegalAccessException | InvocationTargetException | InstantiationException 60 | NoSuchMethodException e) { 61 throw new RuntimeException("Cannot cast CarUiEntry.", e); 62 } 63 } 64 65 @Override 66 public void validateCarUiPackage() { 67 try { 68 PackageManager packageManager = mActivity.getContext().getPackageManager(); 69 int flag = packageManager.getApplicationInfo(CAR_UI_PROVIDER_PKG, 0).flags; 70 if ((flag & ApplicationInfo.FLAG_SYSTEM) == 0 71 && (flag & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 0) { 72 throw new SecurityException("CarUiProvider is not a system app!"); 73 } 74 75 // Do not change the order of the two packages as it need to be in sync with 76 // the error message. 77 int signatureMatchResult = 78 packageManager.checkSignatures(CAR_SERVICE_PKG, CAR_UI_PROVIDER_PKG); 79 if (signatureMatchResult != PackageManager.SIGNATURE_MATCH) { 80 throw new SecurityException("CarUiProvider and CarService signature check" + 81 " failed. " + getSignatureFailureMessage(signatureMatchResult)); 82 } 83 } catch (PackageManager.NameNotFoundException e) { 84 throw new RuntimeException("Cannot find CarUiProvider" + CAR_UI_PROVIDER_PKG, e); 85 } 86 } 87 88 @Override 89 public int getFragmentContainerId() { 90 return mCarUiEntry.getFragmentContainerId(); 91 } 92 93 @Override 94 public void setTitle(CharSequence title) { 95 mCarUiEntry.setTitle(title); 96 } 97 98 @Override 99 public void setScrimColor(int color) { 100 mCarUiEntry.setScrimColor(color); 101 } 102 103 @Override 104 public View getContentView() { 105 return mCarUiEntry.getContentView(); 106 } 107 108 @Override 109 public void registerCarMenuCallbacks(final CarMenuCallbacks callbacks) { 110 mCallback = new EmbeddedCarMenuCallbacksCompat(mActivity, callbacks); 111 mCarUiEntry.setCarMenuCallbacks(mCallback); 112 } 113 114 @Override 115 public void restoreMenuButtonDrawable() { 116 mCarUiEntry.restoreMenuDrawable(); 117 } 118 119 @Override 120 public void setMenuButtonBitmap(Bitmap bitmap) { 121 mCarUiEntry.setMenuButtonBitmap(bitmap); 122 } 123 124 @Override 125 public void setLightMode() { 126 mCarUiEntry.setLightMode(); 127 } 128 129 @Override 130 public void setDarkMode() { 131 mCarUiEntry.setDarkMode(); 132 } 133 134 @Override 135 public void setAutoLightDarkMode() { 136 mCarUiEntry.setAutoLightDarkMode(); 137 } 138 139 @Override 140 public void setBackground(Bitmap bitmap) { 141 mCarUiEntry.setBackground(bitmap); 142 } 143 144 @Override 145 public void onRestoreInstanceState(Bundle savedState) { 146 mCarUiEntry.onRestoreInstanceState(savedState); 147 } 148 149 @Override 150 public void onSaveInstanceState(Bundle outState) { 151 mCarUiEntry.onSaveInstanceState(outState); 152 } 153 154 @Override 155 public void closeDrawer() { 156 mCarUiEntry.closeDrawer(); 157 } 158 159 @Override 160 public void openDrawer() { 161 mCarUiEntry.openDrawer(); 162 } 163 164 @Override 165 public void showMenu(String id, String title) { 166 mCarUiEntry.showMenu(id, title); 167 } 168 169 @Override 170 public void onStart() { 171 mCarUiEntry.onStart(); 172 } 173 174 @Override 175 public void onResume() { 176 mCarUiEntry.onResume(); 177 } 178 179 @Override 180 public void onPause() { 181 mCarUiEntry.onPause(); 182 } 183 184 @Override 185 public void onStop() { 186 mCarUiEntry.onStop(); 187 } 188 189 @Override 190 public void showSearchBox(View.OnClickListener listener) { 191 mCarUiEntry.showSearchBox(listener); 192 } 193 194 @Override 195 public void setSearchBoxColors(int backgroundColor, int searchLogocolor, int textColor, int hintTextColor) { 196 mCarUiEntry.setSearchBoxColors(backgroundColor, searchLogocolor, textColor, hintTextColor); 197 } 198 199 @Override 200 public void setSearchBoxEditListener(SearchBoxEditListener listener) { 201 mCarUiEntry.setSearchBoxEditListener(new EmbeddedSearchBoxEditListenerCompat(listener)); 202 } 203 204 @Override 205 public CharSequence getText() { 206 return mCarUiEntry.getSearchBoxText(); 207 } 208 209 @Override 210 public void stopInput() { 211 mCarUiEntry.stopInput(); 212 } 213 214 @Override 215 public EditText startInput(String hint, View.OnClickListener listener) { 216 return mCarUiEntry.startInput(hint, listener); 217 } 218 219 @Override 220 public void setSearchBoxEndView(View view) { 221 mCarUiEntry.setSearchBoxEndView(view); 222 } 223 224 @Override 225 public void onChildChanged(String parentId, Bundle item) { 226 mCallback.onChildChanged(parentId, item); 227 } 228 229 @Override 230 public void onChildrenChanged(String parentId) { 231 mCallback.onChildrenChanged(parentId); 232 } 233 234 @Override 235 public void showToast(String msg, int duration) { 236 mCarUiEntry.showToast(msg, duration); 237 } 238 239 /** 240 * Return more informative error message from the PackageManager's signature check result. 241 */ 242 private static final String getSignatureFailureMessage(int code) { 243 switch (code) { 244 case PackageManager.SIGNATURE_NEITHER_SIGNED: 245 return "Both CarService and CarUiProvider are not signed"; 246 case PackageManager.SIGNATURE_FIRST_NOT_SIGNED: 247 return "CarService not signed"; 248 case PackageManager.SIGNATURE_SECOND_NOT_SIGNED: 249 return "CarUiProvider not signed"; 250 case PackageManager.SIGNATURE_NO_MATCH: 251 return "Signatures do not match"; 252 case PackageManager.SIGNATURE_UNKNOWN_PACKAGE: 253 return "CarService not found"; 254 default: 255 return "Unknown error code"; 256 } 257 } 258} 259