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