blob: 6af1c3b20df29743bb6f04d921c78a9454b35319 [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.
105 if (mDefaultOverlays.contains(overlayPackage.packageName)) {
106 // Enable this overlay by default.
107 mSettings.setEnabled(overlayPackage.packageName, newUserId, true);
108 }
109 } else {
110 // The targetPackageName we have stored doesn't match the overlay's target.
111 // Queue the old target for an update as well.
112 packagesToUpdateAssets.add(oi.targetPackageName);
113 }
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100114 }
115
116 try {
117 final PackageInfo targetPackage =
118 mPackageManager.getPackageInfo(overlayPackage.overlayTarget, newUserId);
119 updateState(targetPackage, overlayPackage, newUserId);
120 } catch (OverlayManagerSettings.BadKeyException e) {
121 Slog.e(TAG, "failed to update settings", e);
122 mSettings.remove(overlayPackage.packageName, newUserId);
123 }
124
125 packagesToUpdateAssets.add(overlayPackage.overlayTarget);
126 storedOverlayInfos.remove(overlayPackage.packageName);
127 }
128
129 // any OverlayInfo left in storedOverlayInfos is no longer
130 // installed and should be removed
131 final int storedOverlayInfosSize = storedOverlayInfos.size();
132 for (int i = 0; i < storedOverlayInfosSize; i++) {
133 final OverlayInfo oi = storedOverlayInfos.get(i);
134 mSettings.remove(oi.packageName, oi.userId);
135 removeIdmapIfPossible(oi);
136 packagesToUpdateAssets.add(oi.targetPackageName);
137 }
138
139 // remove target packages that are not installed
140 final Iterator<String> iter = packagesToUpdateAssets.iterator();
141 while (iter.hasNext()) {
142 String targetPackageName = iter.next();
143 if (mPackageManager.getPackageInfo(targetPackageName, newUserId) == null) {
144 iter.remove();
145 }
146 }
147
Adam Lesinskia5ca6242017-03-01 15:45:12 -0800148 return new ArrayList<>(packagesToUpdateAssets);
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100149 }
150
151 void onUserRemoved(final int userId) {
152 if (DEBUG) {
153 Slog.d(TAG, "onUserRemoved userId=" + userId);
154 }
155 mSettings.removeUser(userId);
156 }
157
158 void onTargetPackageAdded(@NonNull final String packageName, final int userId) {
159 if (DEBUG) {
160 Slog.d(TAG, "onTargetPackageAdded packageName=" + packageName + " userId=" + userId);
161 }
162
163 final PackageInfo targetPackage = mPackageManager.getPackageInfo(packageName, userId);
164 updateAllOverlaysForTarget(packageName, userId, targetPackage);
165 }
166
167 void onTargetPackageChanged(@NonNull final String packageName, final int userId) {
168 if (DEBUG) {
169 Slog.d(TAG, "onTargetPackageChanged packageName=" + packageName + " userId=" + userId);
170 }
171
172 final PackageInfo targetPackage = mPackageManager.getPackageInfo(packageName, userId);
173 updateAllOverlaysForTarget(packageName, userId, targetPackage);
174 }
175
176 void onTargetPackageUpgrading(@NonNull final String packageName, final int userId) {
177 if (DEBUG) {
178 Slog.d(TAG, "onTargetPackageUpgrading packageName=" + packageName + " userId=" + userId);
179 }
180
181 updateAllOverlaysForTarget(packageName, userId, null);
182 }
183
184 void onTargetPackageUpgraded(@NonNull final String packageName, final int userId) {
185 if (DEBUG) {
186 Slog.d(TAG, "onTargetPackageUpgraded packageName=" + packageName + " userId=" + userId);
187 }
188
189 final PackageInfo targetPackage = mPackageManager.getPackageInfo(packageName, userId);
190 updateAllOverlaysForTarget(packageName, userId, targetPackage);
191 }
192
193 void onTargetPackageRemoved(@NonNull final String packageName, final int userId) {
194 if (DEBUG) {
195 Slog.d(TAG, "onTargetPackageRemoved packageName=" + packageName + " userId=" + userId);
196 }
197
198 updateAllOverlaysForTarget(packageName, userId, null);
199 }
200
201 private void updateAllOverlaysForTarget(@NonNull final String packageName, final int userId,
202 @Nullable final PackageInfo targetPackage) {
203 final List<OverlayInfo> ois = mSettings.getOverlaysForTarget(packageName, userId);
204 final int N = ois.size();
205 for (int i = 0; i < N; i++) {
206 final OverlayInfo oi = ois.get(i);
207 final PackageInfo overlayPackage = mPackageManager.getPackageInfo(oi.packageName, userId);
208 if (overlayPackage == null) {
209 mSettings.remove(oi.packageName, oi.userId);
210 removeIdmapIfPossible(oi);
211 } else {
212 try {
213 updateState(targetPackage, overlayPackage, userId);
214 } catch (OverlayManagerSettings.BadKeyException e) {
215 Slog.e(TAG, "failed to update settings", e);
216 mSettings.remove(oi.packageName, userId);
217 }
218 }
219 }
220 }
221
222 void onOverlayPackageAdded(@NonNull final String packageName, final int userId) {
223 if (DEBUG) {
224 Slog.d(TAG, "onOverlayPackageAdded packageName=" + packageName + " userId=" + userId);
225 }
226
227 final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
228 if (overlayPackage == null) {
229 Slog.w(TAG, "overlay package " + packageName + " was added, but couldn't be found");
230 onOverlayPackageRemoved(packageName, userId);
231 return;
232 }
233
234 final PackageInfo targetPackage =
235 mPackageManager.getPackageInfo(overlayPackage.overlayTarget, userId);
236
237 mSettings.init(packageName, userId, overlayPackage.overlayTarget,
238 overlayPackage.applicationInfo.getBaseCodePath());
239 try {
240 updateState(targetPackage, overlayPackage, userId);
241 } catch (OverlayManagerSettings.BadKeyException e) {
242 Slog.e(TAG, "failed to update settings", e);
243 mSettings.remove(packageName, userId);
244 }
245 }
246
247 void onOverlayPackageChanged(@NonNull final String packageName, final int userId) {
248 Slog.wtf(TAG, "onOverlayPackageChanged called, but only pre-installed overlays supported");
249 }
250
251 void onOverlayPackageUpgrading(@NonNull final String packageName, final int userId) {
252 Slog.wtf(TAG, "onOverlayPackageUpgrading called, but only pre-installed overlays supported");
253 }
254
255 void onOverlayPackageUpgraded(@NonNull final String packageName, final int userId) {
256 Slog.wtf(TAG, "onOverlayPackageUpgraded called, but only pre-installed overlays supported");
257 }
258
259 void onOverlayPackageRemoved(@NonNull final String packageName, final int userId) {
260 Slog.wtf(TAG, "onOverlayPackageRemoved called, but only pre-installed overlays supported");
261 }
262
263 OverlayInfo getOverlayInfo(@NonNull final String packageName, final int userId) {
264 try {
265 return mSettings.getOverlayInfo(packageName, userId);
266 } catch (OverlayManagerSettings.BadKeyException e) {
267 return null;
268 }
269 }
270
271 List<OverlayInfo> getOverlayInfosForTarget(@NonNull final String targetPackageName,
272 final int userId) {
273 return mSettings.getOverlaysForTarget(targetPackageName, userId);
274 }
275
276 Map<String, List<OverlayInfo>> getOverlaysForUser(final int userId) {
277 return mSettings.getOverlaysForUser(userId);
278 }
279
280 boolean setEnabled(@NonNull final String packageName, final boolean enable,
281 final int userId) {
282 if (DEBUG) {
283 Slog.d(TAG, String.format("setEnabled packageName=%s enable=%s userId=%d",
284 packageName, enable, userId));
285 }
286
287 final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
288 if (overlayPackage == null) {
289 return false;
290 }
Jaekyun Seok04342892017-03-02 15:24:19 +0900291 // Static overlay is always being enabled.
292 if (!enable && overlayPackage.isStaticOverlay) {
293 return false;
294 }
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100295
296 try {
297 final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
298 final PackageInfo targetPackage =
299 mPackageManager.getPackageInfo(oi.targetPackageName, userId);
300 mSettings.setEnabled(packageName, userId, enable);
301 updateState(targetPackage, overlayPackage, userId);
302 return true;
303 } catch (OverlayManagerSettings.BadKeyException e) {
304 return false;
305 }
306 }
307
Jason Monk929ed8d2017-03-07 16:01:20 -0500308 boolean setEnabledExclusive(@NonNull final String packageName, final boolean enable,
309 final int userId) {
310 if (DEBUG) {
311 Slog.d(TAG, String.format("setEnabled packageName=%s enable=%s userId=%d",
312 packageName, enable, userId));
313 }
314
315 final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
316 if (overlayPackage == null) {
317 return false;
318 }
319
320 try {
321 final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
322 List<OverlayInfo> allOverlays = getOverlayInfosForTarget(oi.targetPackageName, userId);
323
324 // Disable all other overlays.
325 allOverlays.remove(oi);
326 for (int i = 0; i < allOverlays.size(); i++) {
327 mSettings.setEnabled(allOverlays.get(i).packageName, userId, false);
328 }
329
330 final PackageInfo targetPackage =
331 mPackageManager.getPackageInfo(oi.targetPackageName, userId);
332 mSettings.setEnabled(packageName, userId, enable);
333 updateState(targetPackage, overlayPackage, userId);
334 return true;
335 } catch (OverlayManagerSettings.BadKeyException e) {
336 return false;
337 }
338 }
339
Jaekyun Seok04342892017-03-02 15:24:19 +0900340 boolean isPackageUpdatableOverlay(@NonNull final String packageName, final int userId) {
341 final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
342 if (overlayPackage == null || overlayPackage.isStaticOverlay) {
343 return false;
344 }
345 return true;
346 }
347
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100348 boolean setPriority(@NonNull final String packageName,
349 @NonNull final String newParentPackageName, final int userId) {
Jaekyun Seok04342892017-03-02 15:24:19 +0900350 return isPackageUpdatableOverlay(packageName, userId) &&
351 mSettings.setPriority(packageName, newParentPackageName, userId);
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100352 }
353
354 boolean setHighestPriority(@NonNull final String packageName, final int userId) {
Jaekyun Seok04342892017-03-02 15:24:19 +0900355 return isPackageUpdatableOverlay(packageName, userId) &&
356 mSettings.setHighestPriority(packageName, userId);
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100357 }
358
359 boolean setLowestPriority(@NonNull final String packageName, final int userId) {
Jaekyun Seok04342892017-03-02 15:24:19 +0900360 return isPackageUpdatableOverlay(packageName, userId) &&
361 mSettings.setLowestPriority(packageName, userId);
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100362 }
363
364 void onDump(@NonNull final PrintWriter pw) {
365 mSettings.dump(pw);
Adam Lesinskia5ca6242017-03-01 15:45:12 -0800366 pw.println("Default overlays: " + TextUtils.join(";", mDefaultOverlays));
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100367 }
368
369 List<String> getEnabledOverlayPackageNames(@NonNull final String targetPackageName,
370 final int userId) {
371 final List<OverlayInfo> overlays = mSettings.getOverlaysForTarget(targetPackageName, userId);
372 final List<String> paths = new ArrayList<>(overlays.size());
373 final int N = overlays.size();
374 for (int i = 0; i < N; i++) {
375 final OverlayInfo oi = overlays.get(i);
376 if (oi.isEnabled()) {
377 paths.add(oi.packageName);
378 }
379 }
380 return paths;
381 }
382
383 private void updateState(@Nullable final PackageInfo targetPackage,
384 @NonNull final PackageInfo overlayPackage, final int userId)
385 throws OverlayManagerSettings.BadKeyException {
Jaekyun Seok04342892017-03-02 15:24:19 +0900386 // Static RROs targeting to "android", ie framework-res.apk, are handled by native layers.
387 if (targetPackage != null &&
388 !("android".equals(targetPackage.packageName) && overlayPackage.isStaticOverlay)) {
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100389 mIdmapManager.createIdmap(targetPackage, overlayPackage, userId);
390 }
391
392 mSettings.setBaseCodePath(overlayPackage.packageName, userId,
393 overlayPackage.applicationInfo.getBaseCodePath());
394
395 final int currentState = mSettings.getState(overlayPackage.packageName, userId);
396 final int newState = calculateNewState(targetPackage, overlayPackage, userId);
397 if (currentState != newState) {
398 if (DEBUG) {
399 Slog.d(TAG, String.format("%s:%d: %s -> %s",
400 overlayPackage.packageName, userId,
401 OverlayInfo.stateToString(currentState),
402 OverlayInfo.stateToString(newState)));
403 }
404 mSettings.setState(overlayPackage.packageName, userId, newState);
405 }
406 }
407
408 private int calculateNewState(@Nullable final PackageInfo targetPackage,
409 @NonNull final PackageInfo overlayPackage, final int userId)
410 throws OverlayManagerSettings.BadKeyException {
411 if (targetPackage == null) {
412 return STATE_MISSING_TARGET;
413 }
414
415 if (!mIdmapManager.idmapExists(overlayPackage, userId)) {
416 return STATE_NO_IDMAP;
417 }
418
419 final boolean enabled = mSettings.getEnabled(overlayPackage.packageName, userId);
420 return enabled ? STATE_ENABLED : STATE_DISABLED;
421 }
422
423 private void removeIdmapIfPossible(@NonNull final OverlayInfo oi) {
424 // For a given package, all Android users share the same idmap file.
425 // This works because Android currently does not support users to
426 // install different versions of the same package. It also means we
427 // cannot remove an idmap file if any user still needs it.
428 //
429 // When/if the Android framework allows different versions of the same
430 // package to be installed for different users, idmap file handling
431 // should be revised:
432 //
433 // - an idmap file should be unique for each {user, package} pair
434 //
435 // - the path to the idmap file should be passed to the native Asset
436 // Manager layers, just like the path to the apk is passed today
437 //
438 // As part of that change, calls to this method should be replaced by
439 // direct calls to IdmapManager.removeIdmap, without looping over all
440 // users.
441
442 if (!mIdmapManager.idmapExists(oi)) {
443 return;
444 }
445 final List<Integer> userIds = mSettings.getUsers();
446 final int N = userIds.size();
447 for (int i = 0; i < N; i++) {
448 final int userId = userIds.get(i);
449 try {
450 final OverlayInfo tmp = mSettings.getOverlayInfo(oi.packageName, userId);
451 if (tmp != null && tmp.isEnabled()) {
452 // someone is still using the idmap file -> we cannot remove it
453 return;
454 }
455 } catch (OverlayManagerSettings.BadKeyException e) {
456 // intentionally left empty
457 }
458 }
459 mIdmapManager.removeIdmap(oi, oi.userId);
460 }
461
462 interface PackageManagerHelper {
463 PackageInfo getPackageInfo(@NonNull String packageName, int userId);
464 boolean signaturesMatching(@NonNull String packageName1, @NonNull String packageName2,
465 int userId);
466 List<PackageInfo> getOverlayPackages(int userId);
467 }
468}