1 /* 2 * Copyright (C) 2020 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 com.android.server.om.OverlayManagerServiceImpl.OperationFailedException; 20 21 import static org.junit.Assert.assertEquals; 22 import static org.mockito.ArgumentMatchers.any; 23 import static org.mockito.Mockito.mock; 24 import static org.mockito.Mockito.when; 25 26 import android.annotation.NonNull; 27 import android.content.om.OverlayIdentifier; 28 import android.content.om.OverlayInfo; 29 import android.content.om.OverlayInfo.State; 30 import android.content.om.OverlayableInfo; 31 import android.os.FabricatedOverlayInfo; 32 import android.os.FabricatedOverlayInternal; 33 import android.text.TextUtils; 34 import android.util.ArrayMap; 35 import android.util.ArraySet; 36 import android.util.Pair; 37 38 import androidx.annotation.Nullable; 39 40 import com.android.internal.content.om.OverlayConfig; 41 import com.android.internal.util.CollectionUtils; 42 import com.android.server.pm.parsing.pkg.AndroidPackage; 43 44 import org.junit.Assert; 45 import org.junit.Before; 46 import org.mockito.Mockito; 47 48 import java.util.ArrayList; 49 import java.util.Arrays; 50 import java.util.Collections; 51 import java.util.List; 52 import java.util.Map; 53 import java.util.Set; 54 55 /** Base class for creating {@link OverlayManagerServiceImplTests} tests. */ 56 class OverlayManagerServiceImplTestsBase { 57 private OverlayManagerServiceImpl mImpl; 58 private FakeDeviceState mState; 59 private FakePackageManagerHelper mPackageManager; 60 private FakeIdmapDaemon mIdmapDaemon; 61 private OverlayConfig mOverlayConfig; 62 private String mConfigSignaturePackageName; 63 64 @Before setUp()65 public void setUp() { 66 mState = new FakeDeviceState(); 67 mPackageManager = new FakePackageManagerHelper(mState); 68 mIdmapDaemon = new FakeIdmapDaemon(mState); 69 mOverlayConfig = mock(OverlayConfig.class); 70 when(mOverlayConfig.getPriority(any())).thenReturn(OverlayConfig.DEFAULT_PRIORITY); 71 when(mOverlayConfig.isEnabled(any())).thenReturn(false); 72 when(mOverlayConfig.isMutable(any())).thenReturn(true); 73 reinitializeImpl(); 74 } 75 reinitializeImpl()76 void reinitializeImpl() { 77 mImpl = new OverlayManagerServiceImpl(mPackageManager, 78 new IdmapManager(mIdmapDaemon, mPackageManager), 79 new OverlayManagerSettings(), 80 mOverlayConfig, 81 new String[0]); 82 } 83 getImpl()84 OverlayManagerServiceImpl getImpl() { 85 return mImpl; 86 } 87 getIdmapd()88 FakeIdmapDaemon getIdmapd() { 89 return mIdmapDaemon; 90 } 91 getState()92 FakeDeviceState getState() { 93 return mState; 94 } 95 setConfigSignaturePackageName(String packageName)96 void setConfigSignaturePackageName(String packageName) { 97 mConfigSignaturePackageName = packageName; 98 } 99 assertState(@tate int expected, final OverlayIdentifier overlay, int userId)100 void assertState(@State int expected, final OverlayIdentifier overlay, int userId) { 101 final OverlayInfo info = mImpl.getOverlayInfo(overlay, userId); 102 if (info == null) { 103 throw new IllegalStateException("overlay '" + overlay + "' not installed"); 104 } 105 final String msg = String.format("expected %s but was %s:", 106 OverlayInfo.stateToString(expected), OverlayInfo.stateToString(info.state)); 107 assertEquals(msg, expected, info.state); 108 } 109 assertOverlayInfoForTarget(final String targetPackageName, int userId, OverlayInfo... overlayInfos)110 void assertOverlayInfoForTarget(final String targetPackageName, int userId, 111 OverlayInfo... overlayInfos) { 112 final List<OverlayInfo> expected = 113 mImpl.getOverlayInfosForTarget(targetPackageName, userId); 114 final List<OverlayInfo> actual = Arrays.asList(overlayInfos); 115 assertEquals(expected, actual); 116 } 117 app(String packageName)118 FakeDeviceState.PackageBuilder app(String packageName) { 119 return new FakeDeviceState.PackageBuilder(packageName, null /* targetPackageName */, 120 null /* targetOverlayableName */, "data"); 121 } 122 target(String packageName)123 FakeDeviceState.PackageBuilder target(String packageName) { 124 return new FakeDeviceState.PackageBuilder(packageName, null /* targetPackageName */, 125 null /* targetOverlayableName */, ""); 126 } 127 overlay(String packageName, String targetPackageName)128 FakeDeviceState.PackageBuilder overlay(String packageName, String targetPackageName) { 129 return overlay(packageName, targetPackageName, null /* targetOverlayableName */); 130 } 131 overlay(String packageName, String targetPackageName, String targetOverlayableName)132 FakeDeviceState.PackageBuilder overlay(String packageName, String targetPackageName, 133 String targetOverlayableName) { 134 return new FakeDeviceState.PackageBuilder(packageName, targetPackageName, 135 targetOverlayableName, ""); 136 } 137 addPackage(FakeDeviceState.PackageBuilder pkg, int userId)138 void addPackage(FakeDeviceState.PackageBuilder pkg, int userId) { 139 mState.add(pkg, userId); 140 } 141 142 enum ConfigState { 143 IMMUTABLE_DISABLED, 144 IMMUTABLE_ENABLED, 145 MUTABLE_DISABLED, 146 MUTABLE_ENABLED 147 } 148 configureSystemOverlay(@onNull String packageName, @NonNull ConfigState state, int priority)149 void configureSystemOverlay(@NonNull String packageName, @NonNull ConfigState state, 150 int priority) { 151 final boolean mutable = state == ConfigState.MUTABLE_DISABLED 152 || state == ConfigState.MUTABLE_ENABLED; 153 final boolean enabled = state == ConfigState.IMMUTABLE_ENABLED 154 || state == ConfigState.MUTABLE_ENABLED; 155 when(mOverlayConfig.getPriority(packageName)).thenReturn(priority); 156 when(mOverlayConfig.isEnabled(packageName)).thenReturn(enabled); 157 when(mOverlayConfig.isMutable(packageName)).thenReturn(mutable); 158 } 159 160 /** 161 * Adds the package to the device. 162 * 163 * This corresponds to when the OMS receives the 164 * {@link android.content.Intent#ACTION_PACKAGE_ADDED} broadcast. 165 * 166 * @throws IllegalStateException if the package is currently installed 167 */ installAndAssert(@onNull FakeDeviceState.PackageBuilder pkg, int userId, @NonNull Set<PackageAndUser> onAddedUpdatedPackages)168 void installAndAssert(@NonNull FakeDeviceState.PackageBuilder pkg, int userId, 169 @NonNull Set<PackageAndUser> onAddedUpdatedPackages) 170 throws OperationFailedException { 171 if (mState.select(pkg.packageName, userId) != null) { 172 throw new IllegalStateException("package " + pkg.packageName + " already installed"); 173 } 174 mState.add(pkg, userId); 175 assertEquals(onAddedUpdatedPackages, mImpl.onPackageAdded(pkg.packageName, userId)); 176 } 177 178 /** 179 * Begins upgrading the package. 180 * 181 * This corresponds to when the OMS receives the 182 * {@link android.content.Intent#ACTION_PACKAGE_REMOVED} broadcast with the 183 * {@link android.content.Intent#EXTRA_REPLACING} extra and then receives the 184 * {@link android.content.Intent#ACTION_PACKAGE_ADDED} broadcast with the 185 * {@link android.content.Intent#EXTRA_REPLACING} extra. 186 * 187 * @throws IllegalStateException if the package is not currently installed 188 */ upgradeAndAssert(FakeDeviceState.PackageBuilder pkg, int userId, @NonNull Set<PackageAndUser> onReplacingUpdatedPackages, @NonNull Set<PackageAndUser> onReplacedUpdatedPackages)189 void upgradeAndAssert(FakeDeviceState.PackageBuilder pkg, int userId, 190 @NonNull Set<PackageAndUser> onReplacingUpdatedPackages, 191 @NonNull Set<PackageAndUser> onReplacedUpdatedPackages) 192 throws OperationFailedException { 193 final FakeDeviceState.Package replacedPackage = mState.select(pkg.packageName, userId); 194 if (replacedPackage == null) { 195 throw new IllegalStateException("package " + pkg.packageName + " not installed"); 196 } 197 198 assertEquals(onReplacingUpdatedPackages, mImpl.onPackageReplacing(pkg.packageName, userId)); 199 mState.add(pkg, userId); 200 assertEquals(onReplacedUpdatedPackages, mImpl.onPackageReplaced(pkg.packageName, userId)); 201 } 202 203 /** 204 * Removes the package from the device. 205 * 206 * This corresponds to when the OMS receives the 207 * {@link android.content.Intent#ACTION_PACKAGE_REMOVED} broadcast. 208 * 209 * @throws IllegalStateException if the package is not currently installed 210 */ uninstallAndAssert(@onNull String packageName, int userId, @NonNull Set<PackageAndUser> onRemovedUpdatedPackages)211 void uninstallAndAssert(@NonNull String packageName, int userId, 212 @NonNull Set<PackageAndUser> onRemovedUpdatedPackages) { 213 final FakeDeviceState.Package pkg = mState.select(packageName, userId); 214 if (pkg == null) { 215 throw new IllegalStateException("package " + packageName + " not installed"); 216 } 217 mState.remove(pkg.packageName); 218 assertEquals(onRemovedUpdatedPackages, mImpl.onPackageRemoved(pkg.packageName, userId)); 219 } 220 221 /** Represents the state of packages installed on a fake device. */ 222 static class FakeDeviceState { 223 private ArrayMap<String, Package> mPackages = new ArrayMap<>(); 224 add(PackageBuilder pkgBuilder, int userId)225 void add(PackageBuilder pkgBuilder, int userId) { 226 final Package pkg = pkgBuilder.build(); 227 final Package previousPkg = select(pkg.packageName, userId); 228 mPackages.put(pkg.packageName, pkg); 229 230 pkg.installedUserIds.add(userId); 231 if (previousPkg != null) { 232 pkg.installedUserIds.addAll(previousPkg.installedUserIds); 233 } 234 } 235 remove(String packageName)236 void remove(String packageName) { 237 mPackages.remove(packageName); 238 } 239 uninstall(String packageName, int userId)240 void uninstall(String packageName, int userId) { 241 final Package pkg = mPackages.get(packageName); 242 if (pkg != null) { 243 pkg.installedUserIds.remove(userId); 244 } 245 } 246 select(String packageName, int userId)247 Package select(String packageName, int userId) { 248 final Package pkg = mPackages.get(packageName); 249 return pkg != null && pkg.installedUserIds.contains(userId) ? pkg : null; 250 } 251 selectFromPath(String path)252 private Package selectFromPath(String path) { 253 return mPackages.values().stream() 254 .filter(p -> p.apkPath.equals(path)).findFirst().orElse(null); 255 } 256 257 static final class PackageBuilder { 258 private String packageName; 259 private String targetPackage; 260 private String certificate = "[default]"; 261 private String partition; 262 private int version = 0; 263 private ArrayList<String> overlayableNames = new ArrayList<>(); 264 private String targetOverlayableName; 265 PackageBuilder(String packageName, String targetPackage, String targetOverlayableName, String partition)266 private PackageBuilder(String packageName, String targetPackage, 267 String targetOverlayableName, String partition) { 268 this.packageName = packageName; 269 this.targetPackage = targetPackage; 270 this.targetOverlayableName = targetOverlayableName; 271 this.partition = partition; 272 } 273 setCertificate(String certificate)274 PackageBuilder setCertificate(String certificate) { 275 this.certificate = certificate; 276 return this; 277 } 278 addOverlayable(String overlayableName)279 PackageBuilder addOverlayable(String overlayableName) { 280 overlayableNames.add(overlayableName); 281 return this; 282 } 283 setVersion(int version)284 PackageBuilder setVersion(int version) { 285 this.version = version; 286 return this; 287 } 288 build()289 Package build() { 290 String path = ""; 291 if (TextUtils.isEmpty(partition)) { 292 if (targetPackage == null) { 293 path = "/system/app"; 294 } else { 295 path = "/vendor/overlay"; 296 } 297 } else { 298 String type = targetPackage == null ? "app" : "overlay"; 299 path = String.format("%s/%s", partition, type); 300 } 301 302 final String apkPath = String.format("%s/%s/base.apk", path, packageName); 303 final Package newPackage = new Package(packageName, targetPackage, 304 targetOverlayableName, version, apkPath, certificate); 305 newPackage.overlayableNames.addAll(overlayableNames); 306 return newPackage; 307 } 308 } 309 310 static final class Package { 311 final String packageName; 312 final String targetPackageName; 313 final String targetOverlayableName; 314 final int versionCode; 315 final String apkPath; 316 final String certificate; 317 final ArrayList<String> overlayableNames = new ArrayList<>(); 318 private final ArraySet<Integer> installedUserIds = new ArraySet<>(); 319 Package(String packageName, String targetPackageName, String targetOverlayableName, int versionCode, String apkPath, String certificate)320 private Package(String packageName, String targetPackageName, 321 String targetOverlayableName, int versionCode, String apkPath, 322 String certificate) { 323 this.packageName = packageName; 324 this.targetPackageName = targetPackageName; 325 this.targetOverlayableName = targetOverlayableName; 326 this.versionCode = versionCode; 327 this.apkPath = apkPath; 328 this.certificate = certificate; 329 } 330 331 @Nullable getPackageForUser(int user)332 private AndroidPackage getPackageForUser(int user) { 333 if (!installedUserIds.contains(user)) { 334 return null; 335 } 336 final AndroidPackage pkg = Mockito.mock(AndroidPackage.class); 337 when(pkg.getPackageName()).thenReturn(packageName); 338 when(pkg.getBaseApkPath()).thenReturn(apkPath); 339 when(pkg.getLongVersionCode()).thenReturn((long) versionCode); 340 when(pkg.getOverlayTarget()).thenReturn(targetPackageName); 341 when(pkg.getOverlayTargetName()).thenReturn(targetOverlayableName); 342 when(pkg.getOverlayCategory()).thenReturn("Fake-category-" + targetPackageName); 343 return pkg; 344 } 345 } 346 } 347 348 final class FakePackageManagerHelper implements PackageManagerHelper { 349 private final FakeDeviceState mState; 350 FakePackageManagerHelper(FakeDeviceState state)351 private FakePackageManagerHelper(FakeDeviceState state) { 352 mState = state; 353 } 354 355 @NonNull 356 @Override initializeForUser(int userId)357 public ArrayMap<String, AndroidPackage> initializeForUser(int userId) { 358 final ArrayMap<String, AndroidPackage> packages = new ArrayMap<>(); 359 mState.mPackages.forEach((key, value) -> { 360 final AndroidPackage pkg = value.getPackageForUser(userId); 361 if (pkg != null) { 362 packages.put(key, pkg); 363 } 364 }); 365 return packages; 366 } 367 368 @Nullable 369 @Override getPackageForUser(@onNull String packageName, int userId)370 public AndroidPackage getPackageForUser(@NonNull String packageName, int userId) { 371 final FakeDeviceState.Package pkgState = mState.select(packageName, userId); 372 return pkgState == null ? null : pkgState.getPackageForUser(userId); 373 } 374 375 @Override isInstantApp(@onNull String packageName, int userId)376 public boolean isInstantApp(@NonNull String packageName, int userId) { 377 return false; 378 } 379 380 @Override signaturesMatching(@onNull String packageName1, @NonNull String packageName2, int userId)381 public boolean signaturesMatching(@NonNull String packageName1, 382 @NonNull String packageName2, int userId) { 383 final FakeDeviceState.Package pkg1 = mState.select(packageName1, userId); 384 final FakeDeviceState.Package pkg2 = mState.select(packageName2, userId); 385 return pkg1 != null && pkg2 != null && pkg1.certificate.equals(pkg2.certificate); 386 } 387 388 @Override getConfigSignaturePackage()389 public @NonNull String getConfigSignaturePackage() { 390 return mConfigSignaturePackageName; 391 } 392 393 @Nullable 394 @Override getOverlayableForTarget(@onNull String packageName, @NonNull String targetOverlayableName, int userId)395 public OverlayableInfo getOverlayableForTarget(@NonNull String packageName, 396 @NonNull String targetOverlayableName, int userId) { 397 final FakeDeviceState.Package pkg = mState.select(packageName, userId); 398 if (pkg == null || !pkg.overlayableNames.contains(targetOverlayableName)) { 399 return null; 400 } 401 return new OverlayableInfo(targetOverlayableName, null /* actor */); 402 } 403 404 @Nullable 405 @Override getPackagesForUid(int uid)406 public String[] getPackagesForUid(int uid) { 407 throw new UnsupportedOperationException(); 408 } 409 410 @NonNull 411 @Override getNamedActors()412 public Map<String, Map<String, String>> getNamedActors() { 413 return Collections.emptyMap(); 414 } 415 416 @Override doesTargetDefineOverlayable(String targetPackageName, int userId)417 public boolean doesTargetDefineOverlayable(String targetPackageName, int userId) { 418 final FakeDeviceState.Package pkg = mState.select(targetPackageName, userId); 419 return pkg != null && pkg.overlayableNames.contains(targetPackageName); 420 } 421 422 @Override enforcePermission(String permission, String message)423 public void enforcePermission(String permission, String message) throws SecurityException { 424 throw new UnsupportedOperationException(); 425 } 426 } 427 428 static class FakeIdmapDaemon extends IdmapDaemon { 429 private final FakeDeviceState mState; 430 private final ArrayMap<String, IdmapHeader> mIdmapFiles = new ArrayMap<>(); 431 private final ArrayMap<String, FabricatedOverlayInfo> mFabricatedOverlays = 432 new ArrayMap<>(); 433 private int mFabricatedAssetSeq = 0; 434 FakeIdmapDaemon(FakeDeviceState state)435 FakeIdmapDaemon(FakeDeviceState state) { 436 this.mState = state; 437 } 438 getCrc(@onNull final String path)439 private int getCrc(@NonNull final String path) { 440 final FakeDeviceState.Package pkg = mState.selectFromPath(path); 441 Assert.assertNotNull(pkg); 442 return pkg.versionCode; 443 } 444 445 @Override createIdmap(String targetPath, String overlayPath, String overlayName, int policies, boolean enforce, int userId)446 String createIdmap(String targetPath, String overlayPath, String overlayName, 447 int policies, boolean enforce, int userId) { 448 mIdmapFiles.put(overlayPath, new IdmapHeader(getCrc(targetPath), 449 getCrc(overlayPath), targetPath, overlayName, policies, enforce)); 450 return overlayPath; 451 } 452 453 @Override removeIdmap(String overlayPath, int userId)454 boolean removeIdmap(String overlayPath, int userId) { 455 return mIdmapFiles.remove(overlayPath) != null; 456 } 457 458 @Override verifyIdmap(String targetPath, String overlayPath, String overlayName, int policies, boolean enforce, int userId)459 boolean verifyIdmap(String targetPath, String overlayPath, String overlayName, int policies, 460 boolean enforce, int userId) { 461 final IdmapHeader idmap = mIdmapFiles.get(overlayPath); 462 if (idmap == null) { 463 return false; 464 } 465 return idmap.isUpToDate(getCrc(targetPath), getCrc(overlayPath), targetPath, policies, 466 enforce); 467 } 468 469 @Override idmapExists(String overlayPath, int userId)470 boolean idmapExists(String overlayPath, int userId) { 471 return mIdmapFiles.containsKey(overlayPath); 472 } 473 474 @Override createFabricatedOverlay(@onNull FabricatedOverlayInternal overlay)475 FabricatedOverlayInfo createFabricatedOverlay(@NonNull FabricatedOverlayInternal overlay) { 476 final String path = Integer.toString(mFabricatedAssetSeq++); 477 final FabricatedOverlayInfo info = new FabricatedOverlayInfo(); 478 info.path = path; 479 info.overlayName = overlay.overlayName; 480 info.packageName = overlay.packageName; 481 info.targetPackageName = overlay.targetPackageName; 482 info.targetOverlayable = overlay.targetOverlayable; 483 mFabricatedOverlays.put(path, info); 484 return info; 485 } 486 487 @Override deleteFabricatedOverlay(@onNull String path)488 boolean deleteFabricatedOverlay(@NonNull String path) { 489 return mFabricatedOverlays.remove(path) != null; 490 } 491 492 @Override getFabricatedOverlayInfos()493 List<FabricatedOverlayInfo> getFabricatedOverlayInfos() { 494 return new ArrayList<>(mFabricatedOverlays.values()); 495 } 496 getIdmap(String overlayPath)497 IdmapHeader getIdmap(String overlayPath) { 498 return mIdmapFiles.get(overlayPath); 499 } 500 501 static class IdmapHeader { 502 private final int targetCrc; 503 private final int overlayCrc; 504 final String targetPath; 505 final String overlayName; 506 final int policies; 507 final boolean enforceOverlayable; 508 IdmapHeader(int targetCrc, int overlayCrc, String targetPath, String overlayName, int policies, boolean enforceOverlayable)509 private IdmapHeader(int targetCrc, int overlayCrc, String targetPath, 510 String overlayName, int policies, boolean enforceOverlayable) { 511 this.targetCrc = targetCrc; 512 this.overlayCrc = overlayCrc; 513 this.targetPath = targetPath; 514 this.overlayName = overlayName; 515 this.policies = policies; 516 this.enforceOverlayable = enforceOverlayable; 517 } 518 isUpToDate(int expectedTargetCrc, int expectedOverlayCrc, String expectedTargetPath, int expectedPolicies, boolean expectedEnforceOverlayable)519 private boolean isUpToDate(int expectedTargetCrc, int expectedOverlayCrc, 520 String expectedTargetPath, int expectedPolicies, 521 boolean expectedEnforceOverlayable) { 522 return expectedTargetCrc == targetCrc && expectedOverlayCrc == overlayCrc 523 && expectedTargetPath.equals(targetPath) && expectedPolicies == policies 524 && expectedEnforceOverlayable == enforceOverlayable; 525 } 526 } 527 } 528 } 529