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 */ 16 17 package com.android.server.om; 18 19 import static android.content.om.OverlayInfo.STATE_DISABLED; 20 import static android.content.om.OverlayInfo.STATE_ENABLED; 21 import static android.content.om.OverlayInfo.STATE_MISSING_TARGET; 22 import static android.content.om.OverlayInfo.STATE_NO_IDMAP; 23 24 import static com.android.server.om.OverlayManagerService.DEBUG; 25 import static com.android.server.om.OverlayManagerService.TAG; 26 27 import android.annotation.NonNull; 28 import android.annotation.Nullable; 29 import android.content.om.OverlayInfo; 30 import android.content.pm.PackageInfo; 31 import android.text.TextUtils; 32 import android.util.ArrayMap; 33 import android.util.ArraySet; 34 import android.util.Slog; 35 36 import java.io.PrintWriter; 37 import java.util.ArrayList; 38 import java.util.Iterator; 39 import java.util.List; 40 import java.util.Map; 41 import 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 */ 52 final class OverlayManagerServiceImpl { 53 private final PackageManagerHelper mPackageManager; 54 private final IdmapManager mIdmapManager; 55 private final OverlayManagerSettings mSettings; 56 private final Set<String> mDefaultOverlays; 57 private final OverlayChangeListener mListener; 58 59 OverlayManagerServiceImpl(@NonNull final PackageManagerHelper packageManager, 60 @NonNull final IdmapManager idmapManager, 61 @NonNull final OverlayManagerSettings settings, 62 @NonNull final Set<String> defaultOverlays, 63 @NonNull final OverlayChangeListener listener) { 64 mPackageManager = packageManager; 65 mIdmapManager = idmapManager; 66 mSettings = settings; 67 mDefaultOverlays = defaultOverlays; 68 mListener = listener; 69 } 70 71 /** 72 * Call this to synchronize the Settings for a user with what PackageManager knows about a user. 73 * Returns a list of target packages that must refresh their overlays. This list is the union 74 * of two sets: the set of targets with currently active overlays, and the 75 * set of targets that had, but no longer have, active overlays. 76 */ 77 ArrayList<String> updateOverlaysForUser(final int newUserId) { 78 if (DEBUG) { 79 Slog.d(TAG, "updateOverlaysForUser newUserId=" + newUserId); 80 } 81 82 final Set<String> packagesToUpdateAssets = new ArraySet<>(); 83 final ArrayMap<String, List<OverlayInfo>> tmp = mSettings.getOverlaysForUser(newUserId); 84 final int tmpSize = tmp.size(); 85 final ArrayMap<String, OverlayInfo> storedOverlayInfos = new ArrayMap<>(tmpSize); 86 for (int i = 0; i < tmpSize; i++) { 87 final List<OverlayInfo> chunk = tmp.valueAt(i); 88 final int chunkSize = chunk.size(); 89 for (int j = 0; j < chunkSize; j++) { 90 final OverlayInfo oi = chunk.get(j); 91 storedOverlayInfos.put(oi.packageName, oi); 92 } 93 } 94 95 List<PackageInfo> overlayPackages = mPackageManager.getOverlayPackages(newUserId); 96 final int overlayPackagesSize = overlayPackages.size(); 97 for (int i = 0; i < overlayPackagesSize; i++) { 98 final PackageInfo overlayPackage = overlayPackages.get(i); 99 final OverlayInfo oi = storedOverlayInfos.get(overlayPackage.packageName); 100 if (oi == null || !oi.targetPackageName.equals(overlayPackage.overlayTarget)) { 101 // Update the overlay if it didn't exist or had the wrong target package. 102 mSettings.init(overlayPackage.packageName, newUserId, 103 overlayPackage.overlayTarget, 104 overlayPackage.applicationInfo.getBaseCodePath(), 105 overlayPackage.isStaticOverlay, overlayPackage.overlayPriority); 106 107 if (oi == null) { 108 // This overlay does not exist in our settings. 109 if (overlayPackage.isStaticOverlay || 110 mDefaultOverlays.contains(overlayPackage.packageName)) { 111 // Enable this overlay by default. 112 if (DEBUG) { 113 Slog.d(TAG, "Enabling overlay " + overlayPackage.packageName 114 + " for user " + newUserId + " by default"); 115 } 116 mSettings.setEnabled(overlayPackage.packageName, newUserId, true); 117 } 118 } else { 119 // The targetPackageName we have stored doesn't match the overlay's target. 120 // Queue the old target for an update as well. 121 packagesToUpdateAssets.add(oi.targetPackageName); 122 } 123 } 124 125 try { 126 final PackageInfo targetPackage = 127 mPackageManager.getPackageInfo(overlayPackage.overlayTarget, newUserId); 128 updateState(targetPackage, overlayPackage, newUserId); 129 } catch (OverlayManagerSettings.BadKeyException e) { 130 Slog.e(TAG, "failed to update settings", e); 131 mSettings.remove(overlayPackage.packageName, newUserId); 132 } 133 134 packagesToUpdateAssets.add(overlayPackage.overlayTarget); 135 storedOverlayInfos.remove(overlayPackage.packageName); 136 } 137 138 // any OverlayInfo left in storedOverlayInfos is no longer 139 // installed and should be removed 140 final int storedOverlayInfosSize = storedOverlayInfos.size(); 141 for (int i = 0; i < storedOverlayInfosSize; i++) { 142 final OverlayInfo oi = storedOverlayInfos.valueAt(i); 143 mSettings.remove(oi.packageName, oi.userId); 144 removeIdmapIfPossible(oi); 145 packagesToUpdateAssets.add(oi.targetPackageName); 146 } 147 148 // remove target packages that are not installed 149 final Iterator<String> iter = packagesToUpdateAssets.iterator(); 150 while (iter.hasNext()) { 151 String targetPackageName = iter.next(); 152 if (mPackageManager.getPackageInfo(targetPackageName, newUserId) == null) { 153 iter.remove(); 154 } 155 } 156 return new ArrayList<>(packagesToUpdateAssets); 157 } 158 159 void onUserRemoved(final int userId) { 160 if (DEBUG) { 161 Slog.d(TAG, "onUserRemoved userId=" + userId); 162 } 163 mSettings.removeUser(userId); 164 } 165 166 void onTargetPackageAdded(@NonNull final String packageName, final int userId) { 167 if (DEBUG) { 168 Slog.d(TAG, "onTargetPackageAdded packageName=" + packageName + " userId=" + userId); 169 } 170 171 final PackageInfo targetPackage = mPackageManager.getPackageInfo(packageName, userId); 172 updateAllOverlaysForTarget(packageName, userId, targetPackage); 173 mListener.onOverlaysChanged(packageName, userId); 174 } 175 176 void onTargetPackageChanged(@NonNull final String packageName, final int userId) { 177 if (DEBUG) { 178 Slog.d(TAG, "onTargetPackageChanged packageName=" + packageName + " userId=" + userId); 179 } 180 181 final PackageInfo targetPackage = mPackageManager.getPackageInfo(packageName, userId); 182 if (updateAllOverlaysForTarget(packageName, userId, targetPackage)) { 183 mListener.onOverlaysChanged(packageName, userId); 184 } 185 } 186 187 void onTargetPackageUpgrading(@NonNull final String packageName, final int userId) { 188 if (DEBUG) { 189 Slog.d(TAG, "onTargetPackageUpgrading packageName=" + packageName + " userId=" + userId); 190 } 191 192 if (updateAllOverlaysForTarget(packageName, userId, null)) { 193 mListener.onOverlaysChanged(packageName, userId); 194 } 195 } 196 197 void onTargetPackageUpgraded(@NonNull final String packageName, final int userId) { 198 if (DEBUG) { 199 Slog.d(TAG, "onTargetPackageUpgraded packageName=" + packageName + " userId=" + userId); 200 } 201 202 final PackageInfo targetPackage = mPackageManager.getPackageInfo(packageName, userId); 203 if (updateAllOverlaysForTarget(packageName, userId, targetPackage)) { 204 mListener.onOverlaysChanged(packageName, userId); 205 } 206 } 207 208 void onTargetPackageRemoved(@NonNull final String packageName, final int userId) { 209 if (DEBUG) { 210 Slog.d(TAG, "onTargetPackageRemoved packageName=" + packageName + " userId=" + userId); 211 } 212 213 updateAllOverlaysForTarget(packageName, userId, null); 214 } 215 216 /** 217 * Returns true if the settings were modified for this target. 218 */ 219 private boolean updateAllOverlaysForTarget(@NonNull final String packageName, final int userId, 220 @Nullable final PackageInfo targetPackage) { 221 boolean modified = false; 222 final List<OverlayInfo> ois = mSettings.getOverlaysForTarget(packageName, userId); 223 final int N = ois.size(); 224 for (int i = 0; i < N; i++) { 225 final OverlayInfo oi = ois.get(i); 226 final PackageInfo overlayPackage = mPackageManager.getPackageInfo(oi.packageName, userId); 227 if (overlayPackage == null) { 228 modified |= mSettings.remove(oi.packageName, oi.userId); 229 removeIdmapIfPossible(oi); 230 } else { 231 try { 232 modified |= updateState(targetPackage, overlayPackage, userId); 233 } catch (OverlayManagerSettings.BadKeyException e) { 234 Slog.e(TAG, "failed to update settings", e); 235 modified |= mSettings.remove(oi.packageName, userId); 236 } 237 } 238 } 239 return modified; 240 } 241 242 void onOverlayPackageAdded(@NonNull final String packageName, final int userId) { 243 if (DEBUG) { 244 Slog.d(TAG, "onOverlayPackageAdded packageName=" + packageName + " userId=" + userId); 245 } 246 247 final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); 248 if (overlayPackage == null) { 249 Slog.w(TAG, "overlay package " + packageName + " was added, but couldn't be found"); 250 onOverlayPackageRemoved(packageName, userId); 251 return; 252 } 253 254 final PackageInfo targetPackage = 255 mPackageManager.getPackageInfo(overlayPackage.overlayTarget, userId); 256 257 mSettings.init(packageName, userId, overlayPackage.overlayTarget, 258 overlayPackage.applicationInfo.getBaseCodePath(), overlayPackage.isStaticOverlay, 259 overlayPackage.overlayPriority); 260 try { 261 if (updateState(targetPackage, overlayPackage, userId)) { 262 mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId); 263 } 264 } catch (OverlayManagerSettings.BadKeyException e) { 265 Slog.e(TAG, "failed to update settings", e); 266 mSettings.remove(packageName, userId); 267 } 268 } 269 270 void onOverlayPackageChanged(@NonNull final String packageName, final int userId) { 271 Slog.wtf(TAG, "onOverlayPackageChanged called, but only pre-installed overlays supported"); 272 } 273 274 void onOverlayPackageUpgrading(@NonNull final String packageName, final int userId) { 275 Slog.wtf(TAG, "onOverlayPackageUpgrading called, but only pre-installed overlays supported"); 276 } 277 278 void onOverlayPackageUpgraded(@NonNull final String packageName, final int userId) { 279 Slog.wtf(TAG, "onOverlayPackageUpgraded called, but only pre-installed overlays supported"); 280 } 281 282 void onOverlayPackageRemoved(@NonNull final String packageName, final int userId) { 283 Slog.wtf(TAG, "onOverlayPackageRemoved called, but only pre-installed overlays supported"); 284 } 285 286 OverlayInfo getOverlayInfo(@NonNull final String packageName, final int userId) { 287 try { 288 return mSettings.getOverlayInfo(packageName, userId); 289 } catch (OverlayManagerSettings.BadKeyException e) { 290 return null; 291 } 292 } 293 294 List<OverlayInfo> getOverlayInfosForTarget(@NonNull final String targetPackageName, 295 final int userId) { 296 return mSettings.getOverlaysForTarget(targetPackageName, userId); 297 } 298 299 Map<String, List<OverlayInfo>> getOverlaysForUser(final int userId) { 300 return mSettings.getOverlaysForUser(userId); 301 } 302 303 boolean setEnabled(@NonNull final String packageName, final boolean enable, 304 final int userId) { 305 if (DEBUG) { 306 Slog.d(TAG, String.format("setEnabled packageName=%s enable=%s userId=%d", 307 packageName, enable, userId)); 308 } 309 310 final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); 311 if (overlayPackage == null) { 312 return false; 313 } 314 315 // Ignore static overlays. 316 if (overlayPackage.isStaticOverlay) { 317 return false; 318 } 319 320 try { 321 final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId); 322 final PackageInfo targetPackage = 323 mPackageManager.getPackageInfo(oi.targetPackageName, userId); 324 boolean modified = mSettings.setEnabled(packageName, userId, enable); 325 modified |= updateState(targetPackage, overlayPackage, userId); 326 327 if (modified) { 328 mListener.onOverlaysChanged(oi.targetPackageName, userId); 329 } 330 return true; 331 } catch (OverlayManagerSettings.BadKeyException e) { 332 return false; 333 } 334 } 335 336 boolean setEnabledExclusive(@NonNull final String packageName, final int userId) { 337 if (DEBUG) { 338 Slog.d(TAG, String.format("setEnabledExclusive packageName=%s userId=%d", packageName, userId)); 339 } 340 341 final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); 342 if (overlayPackage == null) { 343 return false; 344 } 345 346 try { 347 final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId); 348 final PackageInfo targetPackage = 349 mPackageManager.getPackageInfo(oi.targetPackageName, userId); 350 351 List<OverlayInfo> allOverlays = getOverlayInfosForTarget(oi.targetPackageName, userId); 352 353 boolean modified = false; 354 355 // Disable all other overlays. 356 allOverlays.remove(oi); 357 for (int i = 0; i < allOverlays.size(); i++) { 358 final String disabledOverlayPackageName = allOverlays.get(i).packageName; 359 final PackageInfo disabledOverlayPackageInfo = mPackageManager.getPackageInfo( 360 disabledOverlayPackageName, userId); 361 if (disabledOverlayPackageInfo == null) { 362 modified |= mSettings.remove(disabledOverlayPackageName, userId); 363 continue; 364 } 365 366 if (disabledOverlayPackageInfo.isStaticOverlay) { 367 // Don't touch static overlays. 368 continue; 369 } 370 371 // Disable the overlay. 372 modified |= mSettings.setEnabled(disabledOverlayPackageName, userId, false); 373 modified |= updateState(targetPackage, disabledOverlayPackageInfo, userId); 374 } 375 376 // Enable the selected overlay. 377 modified |= mSettings.setEnabled(packageName, userId, true); 378 modified |= updateState(targetPackage, overlayPackage, userId); 379 380 if (modified) { 381 mListener.onOverlaysChanged(oi.targetPackageName, userId); 382 } 383 return true; 384 } catch (OverlayManagerSettings.BadKeyException e) { 385 return false; 386 } 387 } 388 389 private boolean isPackageUpdatableOverlay(@NonNull final String packageName, final int userId) { 390 final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); 391 if (overlayPackage == null || overlayPackage.isStaticOverlay) { 392 return false; 393 } 394 return true; 395 } 396 397 boolean setPriority(@NonNull final String packageName, 398 @NonNull final String newParentPackageName, final int userId) { 399 if (DEBUG) { 400 Slog.d(TAG, "setPriority packageName=" + packageName + " newParentPackageName=" 401 + newParentPackageName + " userId=" + userId); 402 } 403 404 if (!isPackageUpdatableOverlay(packageName, userId)) { 405 return false; 406 } 407 408 final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); 409 if (overlayPackage == null) { 410 return false; 411 } 412 413 if (mSettings.setPriority(packageName, newParentPackageName, userId)) { 414 mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId); 415 } 416 return true; 417 } 418 419 boolean setHighestPriority(@NonNull final String packageName, final int userId) { 420 if (DEBUG) { 421 Slog.d(TAG, "setHighestPriority packageName=" + packageName + " userId=" + userId); 422 } 423 424 if (!isPackageUpdatableOverlay(packageName, userId)) { 425 return false; 426 } 427 428 final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); 429 if (overlayPackage == null) { 430 return false; 431 } 432 433 if (mSettings.setHighestPriority(packageName, userId)) { 434 mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId); 435 } 436 return true; 437 } 438 439 boolean setLowestPriority(@NonNull final String packageName, final int userId) { 440 if (DEBUG) { 441 Slog.d(TAG, "setLowestPriority packageName=" + packageName + " userId=" + userId); 442 } 443 444 if (!isPackageUpdatableOverlay(packageName, userId)) { 445 return false; 446 } 447 448 final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); 449 if (overlayPackage == null) { 450 return false; 451 } 452 453 if (mSettings.setLowestPriority(packageName, userId)) { 454 mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId); 455 } 456 return true; 457 } 458 459 void onDump(@NonNull final PrintWriter pw) { 460 mSettings.dump(pw); 461 pw.println("Default overlays: " + TextUtils.join(";", mDefaultOverlays)); 462 } 463 464 List<String> getEnabledOverlayPackageNames(@NonNull final String targetPackageName, 465 final int userId) { 466 final List<OverlayInfo> overlays = mSettings.getOverlaysForTarget(targetPackageName, userId); 467 final List<String> paths = new ArrayList<>(overlays.size()); 468 final int N = overlays.size(); 469 for (int i = 0; i < N; i++) { 470 final OverlayInfo oi = overlays.get(i); 471 if (oi.isEnabled()) { 472 paths.add(oi.packageName); 473 } 474 } 475 return paths; 476 } 477 478 /** 479 * Returns true if the settings/state was modified, false otherwise. 480 */ 481 private boolean updateState(@Nullable final PackageInfo targetPackage, 482 @NonNull final PackageInfo overlayPackage, final int userId) 483 throws OverlayManagerSettings.BadKeyException { 484 // Static RROs targeting to "android", ie framework-res.apk, are handled by native layers. 485 if (targetPackage != null && 486 !("android".equals(targetPackage.packageName) && overlayPackage.isStaticOverlay)) { 487 mIdmapManager.createIdmap(targetPackage, overlayPackage, userId); 488 } 489 490 boolean modified = mSettings.setBaseCodePath(overlayPackage.packageName, userId, 491 overlayPackage.applicationInfo.getBaseCodePath()); 492 493 final int currentState = mSettings.getState(overlayPackage.packageName, userId); 494 final int newState = calculateNewState(targetPackage, overlayPackage, userId); 495 if (currentState != newState) { 496 if (DEBUG) { 497 Slog.d(TAG, String.format("%s:%d: %s -> %s", 498 overlayPackage.packageName, userId, 499 OverlayInfo.stateToString(currentState), 500 OverlayInfo.stateToString(newState))); 501 } 502 modified |= mSettings.setState(overlayPackage.packageName, userId, newState); 503 } 504 return modified; 505 } 506 507 private int calculateNewState(@Nullable final PackageInfo targetPackage, 508 @NonNull final PackageInfo overlayPackage, final int userId) 509 throws OverlayManagerSettings.BadKeyException { 510 if (targetPackage == null) { 511 return STATE_MISSING_TARGET; 512 } 513 514 if (!mIdmapManager.idmapExists(overlayPackage, userId)) { 515 return STATE_NO_IDMAP; 516 } 517 518 final boolean enabled = mSettings.getEnabled(overlayPackage.packageName, userId); 519 return enabled ? STATE_ENABLED : STATE_DISABLED; 520 } 521 522 private void removeIdmapIfPossible(@NonNull final OverlayInfo oi) { 523 // For a given package, all Android users share the same idmap file. 524 // This works because Android currently does not support users to 525 // install different versions of the same package. It also means we 526 // cannot remove an idmap file if any user still needs it. 527 // 528 // When/if the Android framework allows different versions of the same 529 // package to be installed for different users, idmap file handling 530 // should be revised: 531 // 532 // - an idmap file should be unique for each {user, package} pair 533 // 534 // - the path to the idmap file should be passed to the native Asset 535 // Manager layers, just like the path to the apk is passed today 536 // 537 // As part of that change, calls to this method should be replaced by 538 // direct calls to IdmapManager.removeIdmap, without looping over all 539 // users. 540 541 if (!mIdmapManager.idmapExists(oi)) { 542 return; 543 } 544 final int[] userIds = mSettings.getUsers(); 545 for (int userId : userIds) { 546 try { 547 final OverlayInfo tmp = mSettings.getOverlayInfo(oi.packageName, userId); 548 if (tmp != null && tmp.isEnabled()) { 549 // someone is still using the idmap file -> we cannot remove it 550 return; 551 } 552 } catch (OverlayManagerSettings.BadKeyException e) { 553 // intentionally left empty 554 } 555 } 556 mIdmapManager.removeIdmap(oi, oi.userId); 557 } 558 559 interface OverlayChangeListener { 560 void onOverlaysChanged(@NonNull String targetPackage, int userId); 561 } 562 563 interface PackageManagerHelper { 564 PackageInfo getPackageInfo(@NonNull String packageName, int userId); 565 boolean signaturesMatching(@NonNull String packageName1, @NonNull String packageName2, 566 int userId); 567 List<PackageInfo> getOverlayPackages(int userId); 568 } 569 } 570