blob: 5bdef9ea7d493596a16bbfc92be047e39cdff40d [file] [log] [blame]
Mårten Kongstadeabc9e92015-12-15 16:40:23 +01001/*
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 */
16
17package com.android.server.om;
18
19import static android.content.om.OverlayInfo.STATE_DISABLED;
20import static android.content.om.OverlayInfo.STATE_ENABLED;
21import static android.content.om.OverlayInfo.STATE_MISSING_TARGET;
22import static android.content.om.OverlayInfo.STATE_NO_IDMAP;
23
24import static com.android.server.om.OverlayManagerService.DEBUG;
25import static com.android.server.om.OverlayManagerService.TAG;
26
27import android.annotation.NonNull;
28import android.annotation.Nullable;
29import android.content.om.OverlayInfo;
Mårten Kongstadeabc9e92015-12-15 16:40:23 +010030import android.content.pm.PackageInfo;
Adam Lesinskia5ca6242017-03-01 15:45:12 -080031import android.text.TextUtils;
Mårten Kongstadeabc9e92015-12-15 16:40:23 +010032import android.util.ArrayMap;
33import android.util.ArraySet;
34import android.util.Slog;
35
36import java.io.PrintWriter;
37import java.util.ArrayList;
38import java.util.Iterator;
39import java.util.List;
40import java.util.Map;
41import java.util.Set;
42
43/**
44 * Internal implementation of OverlayManagerService.
45 *
46 * Methods in this class should only be called by the OverlayManagerService.
47 * This class is not thread-safe; the caller is expected to ensure the
48 * necessary thread synchronization.
49 *
50 * @see OverlayManagerService
51 */
52final class OverlayManagerServiceImpl {
53 private final PackageManagerHelper mPackageManager;
54 private final IdmapManager mIdmapManager;
55 private final OverlayManagerSettings mSettings;
Adam Lesinskia5ca6242017-03-01 15:45:12 -080056 private final Set<String> mDefaultOverlays;
Mårten Kongstadeabc9e92015-12-15 16:40:23 +010057
58 OverlayManagerServiceImpl(@NonNull final PackageManagerHelper packageManager,
59 @NonNull final IdmapManager idmapManager,
Adam Lesinskia5ca6242017-03-01 15:45:12 -080060 @NonNull final OverlayManagerSettings settings,
61 @NonNull final Set<String> defaultOverlays) {
Mårten Kongstadeabc9e92015-12-15 16:40:23 +010062 mPackageManager = packageManager;
63 mIdmapManager = idmapManager;
64 mSettings = settings;
Adam Lesinskia5ca6242017-03-01 15:45:12 -080065 mDefaultOverlays = defaultOverlays;
Mårten Kongstadeabc9e92015-12-15 16:40:23 +010066 }
67
68 /*
69 * Call this when switching to a new Android user. Will return a list of
70 * target packages that must refresh their overlays. This list is the union
71 * of two sets: the set of targets with currently active overlays, and the
72 * set of targets that had, but no longer have, active overlays.
73 */
74 List<String> onSwitchUser(final int newUserId) {
75 if (DEBUG) {
76 Slog.d(TAG, "onSwitchUser newUserId=" + newUserId);
77 }
78
79 final Set<String> packagesToUpdateAssets = new ArraySet<>();
80 final ArrayMap<String, List<OverlayInfo>> tmp = mSettings.getOverlaysForUser(newUserId);
81 final int tmpSize = tmp.size();
82 final ArrayMap<String, OverlayInfo> storedOverlayInfos = new ArrayMap<>(tmpSize);
83 for (int i = 0; i < tmpSize; i++) {
84 final List<OverlayInfo> chunk = tmp.valueAt(i);
85 final int chunkSize = chunk.size();
86 for (int j = 0; j < chunkSize; j++) {
87 final OverlayInfo oi = chunk.get(j);
88 storedOverlayInfos.put(oi.packageName, oi);
89 }
90 }
91
92 List<PackageInfo> overlayPackages = mPackageManager.getOverlayPackages(newUserId);
93 final int overlayPackagesSize = overlayPackages.size();
94 for (int i = 0; i < overlayPackagesSize; i++) {
95 final PackageInfo overlayPackage = overlayPackages.get(i);
96 final OverlayInfo oi = storedOverlayInfos.get(overlayPackage.packageName);
97 if (oi == null || !oi.targetPackageName.equals(overlayPackage.overlayTarget)) {
Adam Lesinskia5ca6242017-03-01 15:45:12 -080098 // Update the overlay if it didn't exist or had the wrong target package.
Mårten Kongstadeabc9e92015-12-15 16:40:23 +010099 mSettings.init(overlayPackage.packageName, newUserId,
100 overlayPackage.overlayTarget,
101 overlayPackage.applicationInfo.getBaseCodePath());
Adam Lesinskia5ca6242017-03-01 15:45:12 -0800102
103 if (oi == null) {
104 // This overlay does not exist in our settings.
Jaekyun Seok84d7f1e2017-03-29 11:02:15 +0900105 if (overlayPackage.isStaticOverlay ||
106 mDefaultOverlays.contains(overlayPackage.packageName)) {
Adam Lesinskia5ca6242017-03-01 15:45:12 -0800107 // Enable this overlay by default.
108 mSettings.setEnabled(overlayPackage.packageName, newUserId, true);
109 }
110 } else {
111 // The targetPackageName we have stored doesn't match the overlay's target.
112 // Queue the old target for an update as well.
113 packagesToUpdateAssets.add(oi.targetPackageName);
114 }
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100115 }
116
117 try {
118 final PackageInfo targetPackage =
119 mPackageManager.getPackageInfo(overlayPackage.overlayTarget, newUserId);
120 updateState(targetPackage, overlayPackage, newUserId);
121 } catch (OverlayManagerSettings.BadKeyException e) {
122 Slog.e(TAG, "failed to update settings", e);
123 mSettings.remove(overlayPackage.packageName, newUserId);
124 }
125
126 packagesToUpdateAssets.add(overlayPackage.overlayTarget);
127 storedOverlayInfos.remove(overlayPackage.packageName);
128 }
129
130 // any OverlayInfo left in storedOverlayInfos is no longer
131 // installed and should be removed
132 final int storedOverlayInfosSize = storedOverlayInfos.size();
133 for (int i = 0; i < storedOverlayInfosSize; i++) {
Mårten Kongstadc0bba8b2017-01-27 10:15:49 +0100134 final OverlayInfo oi = storedOverlayInfos.valueAt(i);
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100135 mSettings.remove(oi.packageName, oi.userId);
136 removeIdmapIfPossible(oi);
137 packagesToUpdateAssets.add(oi.targetPackageName);
138 }
139
140 // remove target packages that are not installed
141 final Iterator<String> iter = packagesToUpdateAssets.iterator();
142 while (iter.hasNext()) {
143 String targetPackageName = iter.next();
144 if (mPackageManager.getPackageInfo(targetPackageName, newUserId) == null) {
145 iter.remove();
146 }
147 }
148
Adam Lesinskia5ca6242017-03-01 15:45:12 -0800149 return new ArrayList<>(packagesToUpdateAssets);
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100150 }
151
152 void onUserRemoved(final int userId) {
153 if (DEBUG) {
154 Slog.d(TAG, "onUserRemoved userId=" + userId);
155 }
156 mSettings.removeUser(userId);
157 }
158
159 void onTargetPackageAdded(@NonNull final String packageName, final int userId) {
160 if (DEBUG) {
161 Slog.d(TAG, "onTargetPackageAdded packageName=" + packageName + " userId=" + userId);
162 }
163
164 final PackageInfo targetPackage = mPackageManager.getPackageInfo(packageName, userId);
165 updateAllOverlaysForTarget(packageName, userId, targetPackage);
166 }
167
168 void onTargetPackageChanged(@NonNull final String packageName, final int userId) {
169 if (DEBUG) {
170 Slog.d(TAG, "onTargetPackageChanged packageName=" + packageName + " userId=" + userId);
171 }
172
173 final PackageInfo targetPackage = mPackageManager.getPackageInfo(packageName, userId);
174 updateAllOverlaysForTarget(packageName, userId, targetPackage);
175 }
176
177 void onTargetPackageUpgrading(@NonNull final String packageName, final int userId) {
178 if (DEBUG) {
179 Slog.d(TAG, "onTargetPackageUpgrading packageName=" + packageName + " userId=" + userId);
180 }
181
182 updateAllOverlaysForTarget(packageName, userId, null);
183 }
184
185 void onTargetPackageUpgraded(@NonNull final String packageName, final int userId) {
186 if (DEBUG) {
187 Slog.d(TAG, "onTargetPackageUpgraded packageName=" + packageName + " userId=" + userId);
188 }
189
190 final PackageInfo targetPackage = mPackageManager.getPackageInfo(packageName, userId);
191 updateAllOverlaysForTarget(packageName, userId, targetPackage);
192 }
193
194 void onTargetPackageRemoved(@NonNull final String packageName, final int userId) {
195 if (DEBUG) {
196 Slog.d(TAG, "onTargetPackageRemoved packageName=" + packageName + " userId=" + userId);
197 }
198
199 updateAllOverlaysForTarget(packageName, userId, null);
200 }
201
202 private void updateAllOverlaysForTarget(@NonNull final String packageName, final int userId,
203 @Nullable final PackageInfo targetPackage) {
204 final List<OverlayInfo> ois = mSettings.getOverlaysForTarget(packageName, userId);
205 final int N = ois.size();
206 for (int i = 0; i < N; i++) {
207 final OverlayInfo oi = ois.get(i);
208 final PackageInfo overlayPackage = mPackageManager.getPackageInfo(oi.packageName, userId);
209 if (overlayPackage == null) {
210 mSettings.remove(oi.packageName, oi.userId);
211 removeIdmapIfPossible(oi);
212 } else {
213 try {
214 updateState(targetPackage, overlayPackage, userId);
215 } catch (OverlayManagerSettings.BadKeyException e) {
216 Slog.e(TAG, "failed to update settings", e);
217 mSettings.remove(oi.packageName, userId);
218 }
219 }
220 }
221 }
222
223 void onOverlayPackageAdded(@NonNull final String packageName, final int userId) {
224 if (DEBUG) {
225 Slog.d(TAG, "onOverlayPackageAdded packageName=" + packageName + " userId=" + userId);
226 }
227
228 final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
229 if (overlayPackage == null) {
230 Slog.w(TAG, "overlay package " + packageName + " was added, but couldn't be found");
231 onOverlayPackageRemoved(packageName, userId);
232 return;
233 }
234
235 final PackageInfo targetPackage =
236 mPackageManager.getPackageInfo(overlayPackage.overlayTarget, userId);
237
238 mSettings.init(packageName, userId, overlayPackage.overlayTarget,
239 overlayPackage.applicationInfo.getBaseCodePath());
240 try {
241 updateState(targetPackage, overlayPackage, userId);
242 } catch (OverlayManagerSettings.BadKeyException e) {
243 Slog.e(TAG, "failed to update settings", e);
244 mSettings.remove(packageName, userId);
245 }
246 }
247
248 void onOverlayPackageChanged(@NonNull final String packageName, final int userId) {
249 Slog.wtf(TAG, "onOverlayPackageChanged called, but only pre-installed overlays supported");
250 }
251
252 void onOverlayPackageUpgrading(@NonNull final String packageName, final int userId) {
253 Slog.wtf(TAG, "onOverlayPackageUpgrading called, but only pre-installed overlays supported");
254 }
255
256 void onOverlayPackageUpgraded(@NonNull final String packageName, final int userId) {
257 Slog.wtf(TAG, "onOverlayPackageUpgraded called, but only pre-installed overlays supported");
258 }
259
260 void onOverlayPackageRemoved(@NonNull final String packageName, final int userId) {
261 Slog.wtf(TAG, "onOverlayPackageRemoved called, but only pre-installed overlays supported");
262 }
263
264 OverlayInfo getOverlayInfo(@NonNull final String packageName, final int userId) {
265 try {
266 return mSettings.getOverlayInfo(packageName, userId);
267 } catch (OverlayManagerSettings.BadKeyException e) {
268 return null;
269 }
270 }
271
272 List<OverlayInfo> getOverlayInfosForTarget(@NonNull final String targetPackageName,
273 final int userId) {
274 return mSettings.getOverlaysForTarget(targetPackageName, userId);
275 }
276
277 Map<String, List<OverlayInfo>> getOverlaysForUser(final int userId) {
278 return mSettings.getOverlaysForUser(userId);
279 }
280
281 boolean setEnabled(@NonNull final String packageName, final boolean enable,
282 final int userId) {
283 if (DEBUG) {
284 Slog.d(TAG, String.format("setEnabled packageName=%s enable=%s userId=%d",
285 packageName, enable, userId));
286 }
287
288 final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
289 if (overlayPackage == null) {
290 return false;
291 }
Jaekyun Seok04342892017-03-02 15:24:19 +0900292 // Static overlay is always being enabled.
293 if (!enable && overlayPackage.isStaticOverlay) {
294 return false;
295 }
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100296
297 try {
298 final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
299 final PackageInfo targetPackage =
300 mPackageManager.getPackageInfo(oi.targetPackageName, userId);
301 mSettings.setEnabled(packageName, userId, enable);
302 updateState(targetPackage, overlayPackage, userId);
303 return true;
304 } catch (OverlayManagerSettings.BadKeyException e) {
305 return false;
306 }
307 }
308
Jason Monk929ed8d2017-03-07 16:01:20 -0500309 boolean setEnabledExclusive(@NonNull final String packageName, final boolean enable,
310 final int userId) {
311 if (DEBUG) {
312 Slog.d(TAG, String.format("setEnabled packageName=%s enable=%s userId=%d",
313 packageName, enable, userId));
314 }
315
316 final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
317 if (overlayPackage == null) {
318 return false;
319 }
320
321 try {
322 final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
323 List<OverlayInfo> allOverlays = getOverlayInfosForTarget(oi.targetPackageName, userId);
324
325 // Disable all other overlays.
326 allOverlays.remove(oi);
327 for (int i = 0; i < allOverlays.size(); i++) {
328 mSettings.setEnabled(allOverlays.get(i).packageName, userId, false);
329 }
330
331 final PackageInfo targetPackage =
332 mPackageManager.getPackageInfo(oi.targetPackageName, userId);
333 mSettings.setEnabled(packageName, userId, enable);
334 updateState(targetPackage, overlayPackage, userId);
335 return true;
336 } catch (OverlayManagerSettings.BadKeyException e) {
337 return false;
338 }
339 }
340
Jaekyun Seok04342892017-03-02 15:24:19 +0900341 boolean isPackageUpdatableOverlay(@NonNull final String packageName, final int userId) {
342 final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
343 if (overlayPackage == null || overlayPackage.isStaticOverlay) {
344 return false;
345 }
346 return true;
347 }
348
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100349 boolean setPriority(@NonNull final String packageName,
350 @NonNull final String newParentPackageName, final int userId) {
Jaekyun Seok04342892017-03-02 15:24:19 +0900351 return isPackageUpdatableOverlay(packageName, userId) &&
352 mSettings.setPriority(packageName, newParentPackageName, userId);
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100353 }
354
355 boolean setHighestPriority(@NonNull final String packageName, final int userId) {
Jaekyun Seok04342892017-03-02 15:24:19 +0900356 return isPackageUpdatableOverlay(packageName, userId) &&
357 mSettings.setHighestPriority(packageName, userId);
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100358 }
359
360 boolean setLowestPriority(@NonNull final String packageName, final int userId) {
Jaekyun Seok04342892017-03-02 15:24:19 +0900361 return isPackageUpdatableOverlay(packageName, userId) &&
362 mSettings.setLowestPriority(packageName, userId);
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100363 }
364
365 void onDump(@NonNull final PrintWriter pw) {
366 mSettings.dump(pw);
Adam Lesinskia5ca6242017-03-01 15:45:12 -0800367 pw.println("Default overlays: " + TextUtils.join(";", mDefaultOverlays));
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100368 }
369
370 List<String> getEnabledOverlayPackageNames(@NonNull final String targetPackageName,
371 final int userId) {
372 final List<OverlayInfo> overlays = mSettings.getOverlaysForTarget(targetPackageName, userId);
373 final List<String> paths = new ArrayList<>(overlays.size());
374 final int N = overlays.size();
375 for (int i = 0; i < N; i++) {
376 final OverlayInfo oi = overlays.get(i);
377 if (oi.isEnabled()) {
378 paths.add(oi.packageName);
379 }
380 }
381 return paths;
382 }
383
384 private void updateState(@Nullable final PackageInfo targetPackage,
385 @NonNull final PackageInfo overlayPackage, final int userId)
386 throws OverlayManagerSettings.BadKeyException {
Jaekyun Seok04342892017-03-02 15:24:19 +0900387 // Static RROs targeting to "android", ie framework-res.apk, are handled by native layers.
388 if (targetPackage != null &&
389 !("android".equals(targetPackage.packageName) && overlayPackage.isStaticOverlay)) {
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100390 mIdmapManager.createIdmap(targetPackage, overlayPackage, userId);
391 }
392
393 mSettings.setBaseCodePath(overlayPackage.packageName, userId,
394 overlayPackage.applicationInfo.getBaseCodePath());
395
396 final int currentState = mSettings.getState(overlayPackage.packageName, userId);
397 final int newState = calculateNewState(targetPackage, overlayPackage, userId);
398 if (currentState != newState) {
399 if (DEBUG) {
400 Slog.d(TAG, String.format("%s:%d: %s -> %s",
401 overlayPackage.packageName, userId,
402 OverlayInfo.stateToString(currentState),
403 OverlayInfo.stateToString(newState)));
404 }
405 mSettings.setState(overlayPackage.packageName, userId, newState);
406 }
407 }
408
409 private int calculateNewState(@Nullable final PackageInfo targetPackage,
410 @NonNull final PackageInfo overlayPackage, final int userId)
411 throws OverlayManagerSettings.BadKeyException {
412 if (targetPackage == null) {
413 return STATE_MISSING_TARGET;
414 }
415
416 if (!mIdmapManager.idmapExists(overlayPackage, userId)) {
417 return STATE_NO_IDMAP;
418 }
419
420 final boolean enabled = mSettings.getEnabled(overlayPackage.packageName, userId);
421 return enabled ? STATE_ENABLED : STATE_DISABLED;
422 }
423
424 private void removeIdmapIfPossible(@NonNull final OverlayInfo oi) {
425 // For a given package, all Android users share the same idmap file.
426 // This works because Android currently does not support users to
427 // install different versions of the same package. It also means we
428 // cannot remove an idmap file if any user still needs it.
429 //
430 // When/if the Android framework allows different versions of the same
431 // package to be installed for different users, idmap file handling
432 // should be revised:
433 //
434 // - an idmap file should be unique for each {user, package} pair
435 //
436 // - the path to the idmap file should be passed to the native Asset
437 // Manager layers, just like the path to the apk is passed today
438 //
439 // As part of that change, calls to this method should be replaced by
440 // direct calls to IdmapManager.removeIdmap, without looping over all
441 // users.
442
443 if (!mIdmapManager.idmapExists(oi)) {
444 return;
445 }
446 final List<Integer> userIds = mSettings.getUsers();
447 final int N = userIds.size();
448 for (int i = 0; i < N; i++) {
449 final int userId = userIds.get(i);
450 try {
451 final OverlayInfo tmp = mSettings.getOverlayInfo(oi.packageName, userId);
452 if (tmp != null && tmp.isEnabled()) {
453 // someone is still using the idmap file -> we cannot remove it
454 return;
455 }
456 } catch (OverlayManagerSettings.BadKeyException e) {
457 // intentionally left empty
458 }
459 }
460 mIdmapManager.removeIdmap(oi, oi.userId);
461 }
462
463 interface PackageManagerHelper {
464 PackageInfo getPackageInfo(@NonNull String packageName, int userId);
465 boolean signaturesMatching(@NonNull String packageName1, @NonNull String packageName2,
466 int userId);
467 List<PackageInfo> getOverlayPackages(int userId);
468 }
469}