1 /* 2 * Copyright (C) 2021 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.car; 18 19 import static android.car.builtin.content.pm.PackageManagerHelper.PROPERTY_CAR_SERVICE_PACKAGE_NAME; 20 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.car.builtin.content.pm.PackageManagerHelper; 24 import android.car.builtin.util.Slogf; 25 import android.content.Context; 26 import android.content.ContextWrapper; 27 import android.content.om.OverlayInfo; 28 import android.content.om.OverlayManager; 29 import android.content.pm.PackageInfo; 30 import android.content.pm.PackageManager; 31 import android.content.res.AssetManager; 32 import android.content.res.Resources; 33 import android.os.SystemProperties; 34 import android.os.UserHandle; 35 import android.text.TextUtils; 36 37 import java.util.ArrayList; 38 import java.util.HashSet; 39 import java.util.List; 40 import java.util.Set; 41 42 /** Context for updatable package */ 43 public class UpdatablePackageContext extends ContextWrapper { 44 private static final String TAG = UpdatablePackageContext.class.getSimpleName(); 45 46 // This is the package context of the com.android.car.updatable 47 private final Context mPackageContext; 48 49 /** Create context for updatable package */ create(Context baseContext)50 public static UpdatablePackageContext create(Context baseContext) { 51 Context packageContext; 52 try { 53 PackageInfo info = findUpdatableServicePackage(baseContext); 54 if (info == null || info.applicationInfo == null || !(info.applicationInfo.isSystemApp() 55 || info.applicationInfo.isUpdatedSystemApp())) { 56 throw new IllegalStateException( 57 "Updated car service package is not usable:" + ((info == null) 58 ? "do not exist" : info.applicationInfo)); 59 } 60 61 // Enable correct RRO package 62 enableRROForCarServiceUpdatable(baseContext); 63 64 // CONTEXT_IGNORE_SECURITY: UID is different but ok as the package is trustable system 65 // app 66 packageContext = baseContext.createPackageContext(info.packageName, 67 Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY); 68 } catch (Exception e) { 69 throw new RuntimeException("Cannot load updatable package code", e); 70 } 71 72 return new UpdatablePackageContext(baseContext, packageContext); 73 } 74 75 @Nullable findUpdatableServicePackage(Context baseContext)76 private static PackageInfo findUpdatableServicePackage(Context baseContext) { 77 PackageInfo info = null; 78 String packageName = SystemProperties.get( 79 PROPERTY_CAR_SERVICE_PACKAGE_NAME, /*def=*/null); 80 if (packageName == null) { 81 throw new IllegalStateException( 82 PROPERTY_CAR_SERVICE_PACKAGE_NAME + " property not defined"); 83 } 84 try { 85 info = baseContext.getPackageManager().getPackageInfo(packageName, /* flags= */ 0); 86 } catch (PackageManager.NameNotFoundException e) { 87 // Just log and move over. Caller will throw exception instead. 88 Slogf.e(TAG, e, "Cannot find updatable car service package:%s", packageName); 89 } 90 return info; 91 } 92 93 // TODO(b/198516172): Add detailed description for the priority of RROs, who will replace whom. enableRROForCarServiceUpdatable(Context baseContext)94 private static void enableRROForCarServiceUpdatable(Context baseContext) { 95 List<String> packages = getEligibleRROPackages(baseContext); 96 if (packages.isEmpty()) { 97 Slogf.d(TAG, "No eligible RRO package to enable."); 98 return; 99 } 100 101 OverlayManager manager = baseContext.getSystemService(OverlayManager.class); 102 UserHandle user = baseContext.getUser(); 103 for (int i = 0; i < packages.size(); i++) { 104 // This class is called for each user, so need to enable RRO for system and current user 105 // separately. 106 String rroPackageName = packages.get(i); 107 try { 108 manager.setEnabled(rroPackageName, /* enable= */true, user); 109 Slogf.d(TAG, "RRO package %s is enabled for User %s", rroPackageName, user); 110 } catch (Exception e) { 111 Slogf.w(TAG, e, "RRO package %s is NOT enabled for User %s", rroPackageName, user); 112 } 113 } 114 } 115 116 @NonNull getEligibleRROPackages(Context baseContext)117 private static List<String> getEligibleRROPackages(Context baseContext) { 118 List<String> eligiblePackages = new ArrayList<>(); 119 120 String packageNames = SystemProperties.get( 121 PackageManagerHelper.PROPERTY_CAR_SERVICE_OVERLAY_PACKAGES, 122 /* default= */ null); 123 if (TextUtils.isEmpty(packageNames)) { 124 // read only property not defined. No need to dynamically overlay resources. 125 Slogf.d(TAG, " %s is not set. No need to dynamically overlay resources.", 126 PackageManagerHelper.PROPERTY_CAR_SERVICE_OVERLAY_PACKAGES); 127 return eligiblePackages; 128 } 129 130 Set<String> installedRROPackages = getInstalledRROPackages(baseContext); 131 132 if (installedRROPackages.isEmpty()) { 133 return eligiblePackages; 134 } 135 136 String[] packages = packageNames.split(";"); 137 String rroPackageName; 138 for (int i = 0; i < packages.length; i++) { 139 rroPackageName = packages[i].trim(); 140 141 if (rroPackageName.isEmpty()) { 142 continue; 143 } 144 145 if (!installedRROPackages.contains(rroPackageName)) { 146 Slogf.d(TAG, "RRO package %s is not installed.", rroPackageName); 147 continue; 148 } 149 150 // Check that package is part of the original image. A third party RRO 151 // should not be enabled using this. 152 try { 153 PackageInfo info = baseContext.getPackageManager().getPackageInfo( 154 rroPackageName, 0); 155 // TODO(b/198516172): Move following logic to separate class and test it. 156 if (info == null || info.applicationInfo == null 157 || !(PackageManagerHelper.isSystemApp(info.applicationInfo) 158 || PackageManagerHelper.isUpdatedSystemApp(info.applicationInfo) 159 || PackageManagerHelper.isOemApp(info.applicationInfo) 160 || PackageManagerHelper.isOdmApp(info.applicationInfo) 161 || PackageManagerHelper.isVendorApp(info.applicationInfo) 162 || PackageManagerHelper.isProductApp(info.applicationInfo) 163 || PackageManagerHelper.isSystemExtApp(info.applicationInfo))) { 164 Slogf.d(TAG, "%s is not usable: %s", rroPackageName, ((info == null) 165 ? "package do not exist" 166 : info.applicationInfo)); 167 continue; 168 } 169 } catch (Exception e) { 170 Slogf.w(TAG, e, "couldn't find package: %s", rroPackageName); 171 continue; 172 } 173 174 // Add RRO package to the list. 175 Slogf.d(TAG, "RRO package %s is eligible for enabling.", rroPackageName); 176 eligiblePackages.add(rroPackageName); 177 } 178 return eligiblePackages; 179 } 180 181 @NonNull getInstalledRROPackages(Context baseContext)182 private static Set<String> getInstalledRROPackages(Context baseContext) { 183 Set<String> installedOverlayPackages = new HashSet<>(); 184 PackageInfo packageInfo = findUpdatableServicePackage(baseContext); 185 if (packageInfo == null) { 186 return installedOverlayPackages; 187 } 188 String updatablePackageName = packageInfo.packageName; 189 190 OverlayManager manager = baseContext.getSystemService(OverlayManager.class); 191 UserHandle user = baseContext.getUser(); 192 193 List<OverlayInfo> installedOverlays = manager.getOverlayInfosForTarget(updatablePackageName, 194 user); 195 196 if (installedOverlays == null || installedOverlays.isEmpty()) { 197 return installedOverlayPackages; 198 } 199 200 for (int i = 0; i < installedOverlays.size(); i++) { 201 OverlayInfo overlayInfo = installedOverlays.get(i); 202 installedOverlayPackages.add(overlayInfo.getPackageName()); 203 } 204 205 Slogf.d(TAG, "Total RROs packages for target package %s are %d.", updatablePackageName, 206 installedOverlayPackages.size()); 207 208 return installedOverlayPackages; 209 } 210 UpdatablePackageContext(Context baseContext, Context packageContext)211 private UpdatablePackageContext(Context baseContext, Context packageContext) { 212 super(baseContext); 213 mPackageContext = packageContext; 214 } 215 216 @Override getAssets()217 public AssetManager getAssets() { 218 return mPackageContext.getAssets(); 219 } 220 221 @Override getResources()222 public Resources getResources() { 223 return mPackageContext.getResources(); 224 } 225 226 @Override getClassLoader()227 public ClassLoader getClassLoader() { 228 // This context cannot load code from builtin any more. 229 return mPackageContext.getClassLoader(); 230 } 231 } 232