From f4312ccb5e0f008b8df0f353fb9583638df418ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Kongstad?= Date: Fri, 14 Feb 2020 15:49:48 +0100 Subject: [PATCH] OMS: replace OMSImpl.OverlayChangeListener with return values Remove the listener callback triggered when the OMS settings have changed. Teach the OMSImpl methods to return the equivalent information instead: an Optional of the target package and Android user who's overlays need to be refreshed, or an exception in case of error. This is the first step towards an OMS transactional API, where multiple requests can be batched together and the outcome is either that all operations succeed, or some operation fails and (to the outside system) nothing happens. Replacing callbacks with explicit return values allow the caller more control of what data is exposed to the rest of Android. Bug: 119916381 Test: atest FrameworksServicesTests:com.android.server.om OverlayDeviceTests OverlayHostTests CtsAppSecurityHostTestCases:OverlayHostTest Change-Id: If58d5e9ddeec649e708224bb29efcc38ebe51bf7 --- .../server/om/OverlayManagerService.java | 168 ++++++++++++----- .../server/om/OverlayManagerServiceImpl.java | 172 ++++++++++-------- .../com/android/server/om/PackageAndUser.java | 57 ++++++ .../OverlayManagerServiceImplRebootTests.java | 10 +- .../om/OverlayManagerServiceImplTests.java | 76 ++++---- .../OverlayManagerServiceImplTestsBase.java | 41 ++--- 6 files changed, 335 insertions(+), 189 deletions(-) create mode 100644 services/core/java/com/android/server/om/PackageAndUser.java diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java index 0f8c9c789a3f5..40ac204e440cf 100644 --- a/services/core/java/com/android/server/om/OverlayManagerService.java +++ b/services/core/java/com/android/server/om/OverlayManagerService.java @@ -29,6 +29,8 @@ import static android.os.Trace.TRACE_TAG_RRO; import static android.os.Trace.traceBegin; import static android.os.Trace.traceEnd; +import static com.android.server.om.OverlayManagerServiceImpl.OperationFailedException; + import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; @@ -88,6 +90,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Consumer; /** * Service to manage asset overlays. @@ -238,6 +241,10 @@ public final class OverlayManagerService extends SystemService { private final AtomicBoolean mPersistSettingsScheduled = new AtomicBoolean(false); + private final Consumer mOnOverlaysChanged = (pair) -> { + onOverlaysChanged(pair.packageName, pair.userId); + }; + public OverlayManagerService(@NonNull final Context context) { super(context); try { @@ -249,8 +256,7 @@ public final class OverlayManagerService extends SystemService { IdmapManager im = new IdmapManager(IdmapDaemon.getInstance(), mPackageManager); mSettings = new OverlayManagerSettings(); mImpl = new OverlayManagerServiceImpl(mPackageManager, im, mSettings, - OverlayConfig.getSystemInstance(), getDefaultOverlayPackages(), - new OverlayChangeListener()); + OverlayConfig.getSystemInstance(), getDefaultOverlayPackages()); mActorEnforcer = new OverlayActorEnforcer(mPackageManager); final IntentFilter packageFilter = new IntentFilter(); @@ -396,10 +402,17 @@ public final class OverlayManagerService extends SystemService { false); if (pi != null && !pi.applicationInfo.isInstantApp()) { mPackageManager.cachePackageInfo(packageName, userId, pi); - if (pi.isOverlayPackage()) { - mImpl.onOverlayPackageAdded(packageName, userId); - } else { - mImpl.onTargetPackageAdded(packageName, userId); + + try { + if (pi.isOverlayPackage()) { + mImpl.onOverlayPackageAdded(packageName, userId) + .ifPresent(mOnOverlaysChanged); + } else { + mImpl.onTargetPackageAdded(packageName, userId) + .ifPresent(mOnOverlaysChanged); + } + } catch (OperationFailedException e) { + Slog.e(TAG, "onPackageAdded internal error", e); } } } @@ -419,10 +432,17 @@ public final class OverlayManagerService extends SystemService { false); if (pi != null && pi.applicationInfo.isInstantApp()) { mPackageManager.cachePackageInfo(packageName, userId, pi); - if (pi.isOverlayPackage()) { - mImpl.onOverlayPackageChanged(packageName, userId); - } else { - mImpl.onTargetPackageChanged(packageName, userId); + + try { + if (pi.isOverlayPackage()) { + mImpl.onOverlayPackageChanged(packageName, userId) + .ifPresent(mOnOverlaysChanged); + } else { + mImpl.onTargetPackageChanged(packageName, userId) + .ifPresent(mOnOverlaysChanged); + } + } catch (OperationFailedException e) { + Slog.e(TAG, "onPackageChanged internal error", e); } } } @@ -441,7 +461,12 @@ public final class OverlayManagerService extends SystemService { mPackageManager.forgetPackageInfo(packageName, userId); final OverlayInfo oi = mImpl.getOverlayInfo(packageName, userId); if (oi != null) { - mImpl.onOverlayPackageReplacing(packageName, userId); + try { + mImpl.onOverlayPackageReplacing(packageName, userId) + .ifPresent(mOnOverlaysChanged); + } catch (OperationFailedException e) { + Slog.e(TAG, "onPackageReplacing internal error", e); + } } } } @@ -460,10 +485,16 @@ public final class OverlayManagerService extends SystemService { false); if (pi != null && !pi.applicationInfo.isInstantApp()) { mPackageManager.cachePackageInfo(packageName, userId, pi); - if (pi.isOverlayPackage()) { - mImpl.onOverlayPackageReplaced(packageName, userId); - } else { - mImpl.onTargetPackageReplaced(packageName, userId); + try { + if (pi.isOverlayPackage()) { + mImpl.onOverlayPackageReplaced(packageName, userId) + .ifPresent(mOnOverlaysChanged); + } else { + mImpl.onTargetPackageReplaced(packageName, userId) + .ifPresent(mOnOverlaysChanged); + } + } catch (OperationFailedException e) { + Slog.e(TAG, "onPackageReplaced internal error", e); } } } @@ -481,10 +512,17 @@ public final class OverlayManagerService extends SystemService { synchronized (mLock) { mPackageManager.forgetPackageInfo(packageName, userId); final OverlayInfo oi = mImpl.getOverlayInfo(packageName, userId); - if (oi != null) { - mImpl.onOverlayPackageRemoved(packageName, userId); - } else { - mImpl.onTargetPackageRemoved(packageName, userId); + + try { + if (oi != null) { + mImpl.onOverlayPackageRemoved(packageName, userId) + .ifPresent(mOnOverlaysChanged); + } else { + mImpl.onTargetPackageRemoved(packageName, userId) + .ifPresent(mOnOverlaysChanged); + } + } catch (OperationFailedException e) { + Slog.e(TAG, "onPackageRemoved internal error", e); } } } @@ -602,7 +640,13 @@ public final class OverlayManagerService extends SystemService { final long ident = Binder.clearCallingIdentity(); try { synchronized (mLock) { - return mImpl.setEnabled(packageName, enable, realUserId); + try { + mImpl.setEnabled(packageName, enable, realUserId) + .ifPresent(mOnOverlaysChanged); + return true; + } catch (OperationFailedException e) { + return false; + } } } finally { Binder.restoreCallingIdentity(ident); @@ -627,8 +671,14 @@ public final class OverlayManagerService extends SystemService { final long ident = Binder.clearCallingIdentity(); try { synchronized (mLock) { - return mImpl.setEnabledExclusive(packageName, false /* withinCategory */, - realUserId); + try { + mImpl.setEnabledExclusive(packageName, + false /* withinCategory */, realUserId) + .ifPresent(mOnOverlaysChanged); + return true; + } catch (OperationFailedException e) { + return false; + } } } finally { Binder.restoreCallingIdentity(ident); @@ -654,8 +704,14 @@ public final class OverlayManagerService extends SystemService { final long ident = Binder.clearCallingIdentity(); try { synchronized (mLock) { - return mImpl.setEnabledExclusive(packageName, true /* withinCategory */, - realUserId); + try { + mImpl.setEnabledExclusive(packageName, + true /* withinCategory */, realUserId) + .ifPresent(mOnOverlaysChanged); + return true; + } catch (OperationFailedException e) { + return false; + } } } finally { Binder.restoreCallingIdentity(ident); @@ -681,7 +737,13 @@ public final class OverlayManagerService extends SystemService { final long ident = Binder.clearCallingIdentity(); try { synchronized (mLock) { - return mImpl.setPriority(packageName, parentPackageName, realUserId); + try { + mImpl.setPriority(packageName, parentPackageName, realUserId) + .ifPresent(mOnOverlaysChanged); + return true; + } catch (OperationFailedException e) { + return false; + } } } finally { Binder.restoreCallingIdentity(ident); @@ -705,7 +767,13 @@ public final class OverlayManagerService extends SystemService { final long ident = Binder.clearCallingIdentity(); try { synchronized (mLock) { - return mImpl.setHighestPriority(packageName, realUserId); + try { + mImpl.setHighestPriority(packageName, realUserId) + .ifPresent(mOnOverlaysChanged); + return true; + } catch (OperationFailedException e) { + return false; + } } } finally { Binder.restoreCallingIdentity(ident); @@ -729,7 +797,13 @@ public final class OverlayManagerService extends SystemService { final long ident = Binder.clearCallingIdentity(); try { synchronized (mLock) { - return mImpl.setLowestPriority(packageName, realUserId); + try { + mImpl.setLowestPriority(packageName, realUserId) + .ifPresent(mOnOverlaysChanged); + return true; + } catch (OperationFailedException e) { + return false; + } } } finally { Binder.restoreCallingIdentity(ident); @@ -898,31 +972,27 @@ public final class OverlayManagerService extends SystemService { } }; - private final class OverlayChangeListener - implements OverlayManagerServiceImpl.OverlayChangeListener { - @Override - public void onOverlaysChanged(@NonNull final String targetPackageName, final int userId) { - schedulePersistSettings(); - FgThread.getHandler().post(() -> { - updateAssets(userId, targetPackageName); + private void onOverlaysChanged(@NonNull final String targetPackageName, final int userId) { + schedulePersistSettings(); + FgThread.getHandler().post(() -> { + updateAssets(userId, targetPackageName); - final Intent intent = new Intent(ACTION_OVERLAY_CHANGED, - Uri.fromParts("package", targetPackageName, null)); - intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + final Intent intent = new Intent(ACTION_OVERLAY_CHANGED, + Uri.fromParts("package", targetPackageName, null)); + intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - if (DEBUG) { - Slog.d(TAG, "send broadcast " + intent); - } + if (DEBUG) { + Slog.d(TAG, "send broadcast " + intent); + } - try { - ActivityManager.getService().broadcastIntentWithFeature(null, null, intent, - null, null, 0, null, null, null, android.app.AppOpsManager.OP_NONE, - null, false, false, userId); - } catch (RemoteException e) { - // Intentionally left empty. - } - }); - } + try { + ActivityManager.getService().broadcastIntent(null, intent, null, null, 0, + null, null, null, android.app.AppOpsManager.OP_NONE, null, false, false, + userId); + } catch (RemoteException e) { + // Intentionally left empty. + } + }); } /** diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java index 05a4a38feef12..e60411bb78c5a 100644 --- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java +++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java @@ -45,6 +45,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.Set; /** @@ -71,7 +72,6 @@ final class OverlayManagerServiceImpl { private final OverlayManagerSettings mSettings; private final OverlayConfig mOverlayConfig; private final String[] mDefaultOverlays; - private final OverlayChangeListener mListener; /** * Helper method to merge the overlay manager's (as read from overlays.xml) @@ -114,14 +114,12 @@ final class OverlayManagerServiceImpl { @NonNull final IdmapManager idmapManager, @NonNull final OverlayManagerSettings settings, @NonNull final OverlayConfig overlayConfig, - @NonNull final String[] defaultOverlays, - @NonNull final OverlayChangeListener listener) { + @NonNull final String[] defaultOverlays) { mPackageManager = packageManager; mIdmapManager = idmapManager; mSettings = settings; mOverlayConfig = overlayConfig; mDefaultOverlays = defaultOverlays; - mListener = listener; } /** @@ -259,52 +257,58 @@ final class OverlayManagerServiceImpl { mSettings.removeUser(userId); } - void onTargetPackageAdded(@NonNull final String packageName, final int userId) { + Optional onTargetPackageAdded(@NonNull final String packageName, + final int userId) throws OperationFailedException { if (DEBUG) { Slog.d(TAG, "onTargetPackageAdded packageName=" + packageName + " userId=" + userId); } - updateAndRefreshOverlaysForTarget(packageName, userId, 0); + return updateAndRefreshOverlaysForTarget(packageName, userId, 0); } - void onTargetPackageChanged(@NonNull final String packageName, final int userId) { + Optional onTargetPackageChanged(@NonNull final String packageName, + final int userId) throws OperationFailedException { if (DEBUG) { Slog.d(TAG, "onTargetPackageChanged packageName=" + packageName + " userId=" + userId); } - updateAndRefreshOverlaysForTarget(packageName, userId, 0); + return updateAndRefreshOverlaysForTarget(packageName, userId, 0); } - void onTargetPackageReplacing(@NonNull final String packageName, final int userId) { + Optional onTargetPackageReplacing(@NonNull final String packageName, + final int userId) throws OperationFailedException { if (DEBUG) { Slog.d(TAG, "onTargetPackageReplacing packageName=" + packageName + " userId=" + userId); } - updateAndRefreshOverlaysForTarget(packageName, userId, 0); + return updateAndRefreshOverlaysForTarget(packageName, userId, 0); } - void onTargetPackageReplaced(@NonNull final String packageName, final int userId) { + Optional onTargetPackageReplaced(@NonNull final String packageName, + final int userId) throws OperationFailedException { if (DEBUG) { Slog.d(TAG, "onTargetPackageReplaced packageName=" + packageName + " userId=" + userId); } - updateAndRefreshOverlaysForTarget(packageName, userId, 0); + return updateAndRefreshOverlaysForTarget(packageName, userId, 0); } - void onTargetPackageRemoved(@NonNull final String packageName, final int userId) { + Optional onTargetPackageRemoved(@NonNull final String packageName, + final int userId) throws OperationFailedException { if (DEBUG) { Slog.d(TAG, "onTargetPackageRemoved packageName=" + packageName + " userId=" + userId); } - updateAndRefreshOverlaysForTarget(packageName, userId, 0); + return updateAndRefreshOverlaysForTarget(packageName, userId, 0); } /** * Update the state of any overlays for this target. */ - private void updateAndRefreshOverlaysForTarget(@NonNull final String targetPackageName, - final int userId, final int flags) { + private Optional updateAndRefreshOverlaysForTarget( + @NonNull final String targetPackageName, final int userId, final int flags) + throws OperationFailedException { final List targetOverlays = mSettings.getOverlaysForTarget(targetPackageName, userId); @@ -364,11 +368,13 @@ final class OverlayManagerServiceImpl { } if (modified) { - mListener.onOverlaysChanged(targetPackageName, userId); + return Optional.of(new PackageAndUser(targetPackageName, userId)); } + return Optional.empty(); } - void onOverlayPackageAdded(@NonNull final String packageName, final int userId) { + Optional onOverlayPackageAdded(@NonNull final String packageName, + final int userId) throws OperationFailedException { if (DEBUG) { Slog.d(TAG, "onOverlayPackageAdded packageName=" + packageName + " userId=" + userId); } @@ -376,8 +382,7 @@ final class OverlayManagerServiceImpl { final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); if (overlayPackage == null) { Slog.w(TAG, "overlay package " + packageName + " was added, but couldn't be found"); - onOverlayPackageRemoved(packageName, userId); - return; + return onOverlayPackageRemoved(packageName, userId); } mSettings.init(packageName, userId, overlayPackage.overlayTarget, @@ -389,15 +394,17 @@ final class OverlayManagerServiceImpl { overlayPackage.overlayCategory); try { if (updateState(overlayPackage.overlayTarget, packageName, userId, 0)) { - mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId); + return Optional.of(new PackageAndUser(overlayPackage.overlayTarget, userId)); } + return Optional.empty(); } catch (OverlayManagerSettings.BadKeyException e) { - Slog.e(TAG, "failed to update settings", e); mSettings.remove(packageName, userId); + throw new OperationFailedException("failed to update settings", e); } } - void onOverlayPackageChanged(@NonNull final String packageName, final int userId) { + Optional onOverlayPackageChanged(@NonNull final String packageName, + final int userId) throws OperationFailedException { if (DEBUG) { Slog.d(TAG, "onOverlayPackageChanged packageName=" + packageName + " userId=" + userId); } @@ -405,14 +412,16 @@ final class OverlayManagerServiceImpl { try { final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId); if (updateState(oi.targetPackageName, packageName, userId, 0)) { - mListener.onOverlaysChanged(oi.targetPackageName, userId); + return Optional.of(new PackageAndUser(oi.targetPackageName, userId)); } + return Optional.empty(); } catch (OverlayManagerSettings.BadKeyException e) { - Slog.e(TAG, "failed to update settings", e); + throw new OperationFailedException("failed to update settings", e); } } - void onOverlayPackageReplacing(@NonNull final String packageName, final int userId) { + Optional onOverlayPackageReplacing(@NonNull final String packageName, + final int userId) throws OperationFailedException { if (DEBUG) { Slog.d(TAG, "onOverlayPackageReplacing packageName=" + packageName + " userId=" + userId); @@ -423,14 +432,16 @@ final class OverlayManagerServiceImpl { if (updateState(oi.targetPackageName, packageName, userId, FLAG_OVERLAY_IS_BEING_REPLACED)) { removeIdmapIfPossible(oi); - mListener.onOverlaysChanged(oi.targetPackageName, userId); + return Optional.of(new PackageAndUser(oi.targetPackageName, userId)); } + return Optional.empty(); } catch (OverlayManagerSettings.BadKeyException e) { - Slog.e(TAG, "failed to update settings", e); + throw new OperationFailedException("failed to update settings", e); } } - void onOverlayPackageReplaced(@NonNull final String packageName, final int userId) { + Optional onOverlayPackageReplaced(@NonNull final String packageName, + final int userId) throws OperationFailedException { if (DEBUG) { Slog.d(TAG, "onOverlayPackageReplaced packageName=" + packageName + " userId=" + userId); @@ -439,16 +450,12 @@ final class OverlayManagerServiceImpl { final PackageInfo pkg = mPackageManager.getPackageInfo(packageName, userId); if (pkg == null) { Slog.w(TAG, "overlay package " + packageName + " was replaced, but couldn't be found"); - onOverlayPackageRemoved(packageName, userId); - return; + return onOverlayPackageRemoved(packageName, userId); } try { final OverlayInfo oldOi = mSettings.getOverlayInfo(packageName, userId); if (mustReinitializeOverlay(pkg, oldOi)) { - if (oldOi != null && !oldOi.targetPackageName.equals(pkg.overlayTarget)) { - mListener.onOverlaysChanged(pkg.overlayTarget, userId); - } mSettings.init(packageName, userId, pkg.overlayTarget, pkg.targetOverlayableName, pkg.applicationInfo.getBaseCodePath(), isPackageConfiguredMutable(pkg.packageName), @@ -457,22 +464,25 @@ final class OverlayManagerServiceImpl { } if (updateState(pkg.overlayTarget, packageName, userId, 0)) { - mListener.onOverlaysChanged(pkg.overlayTarget, userId); + return Optional.of(new PackageAndUser(pkg.overlayTarget, userId)); } + return Optional.empty(); } catch (OverlayManagerSettings.BadKeyException e) { - Slog.e(TAG, "failed to update settings", e); + throw new OperationFailedException("failed to update settings", e); } } - void onOverlayPackageRemoved(@NonNull final String packageName, final int userId) { + Optional onOverlayPackageRemoved(@NonNull final String packageName, + final int userId) throws OperationFailedException { try { final OverlayInfo overlayInfo = mSettings.getOverlayInfo(packageName, userId); if (mSettings.remove(packageName, userId)) { removeIdmapIfPossible(overlayInfo); - mListener.onOverlaysChanged(overlayInfo.targetPackageName, userId); + return Optional.of(new PackageAndUser(overlayInfo.targetPackageName, userId)); } + return Optional.empty(); } catch (OverlayManagerSettings.BadKeyException e) { - Slog.e(TAG, "failed to remove overlay", e); + throw new OperationFailedException("failed to remove overlay", e); } } @@ -493,8 +503,8 @@ final class OverlayManagerServiceImpl { return mSettings.getOverlaysForUser(userId); } - boolean setEnabled(@NonNull final String packageName, final boolean enable, - final int userId) { + Optional setEnabled(@NonNull final String packageName, final boolean enable, + final int userId) throws OperationFailedException { if (DEBUG) { Slog.d(TAG, String.format("setEnabled packageName=%s enable=%s userId=%d", packageName, enable, userId)); @@ -502,30 +512,33 @@ final class OverlayManagerServiceImpl { final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); if (overlayPackage == null) { - return false; + throw new OperationFailedException( + String.format("failed to find overlay package %s for user %d", + packageName, userId)); } try { final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId); if (!oi.isMutable) { // Ignore immutable overlays. - return false; + throw new OperationFailedException( + "cannot enable immutable overlay packages in runtime"); } boolean modified = mSettings.setEnabled(packageName, userId, enable); modified |= updateState(oi.targetPackageName, oi.packageName, userId, 0); if (modified) { - mListener.onOverlaysChanged(oi.targetPackageName, userId); + return Optional.of(new PackageAndUser(oi.targetPackageName, userId)); } - return true; + return Optional.empty(); } catch (OverlayManagerSettings.BadKeyException e) { - return false; + throw new OperationFailedException("failed to update settings", e); } } - boolean setEnabledExclusive(@NonNull final String packageName, boolean withinCategory, - final int userId) { + Optional setEnabledExclusive(@NonNull final String packageName, + boolean withinCategory, final int userId) throws OperationFailedException { if (DEBUG) { Slog.d(TAG, String.format("setEnabledExclusive packageName=%s" + " withinCategory=%s userId=%d", packageName, withinCategory, userId)); @@ -533,7 +546,8 @@ final class OverlayManagerServiceImpl { final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); if (overlayPackage == null) { - return false; + throw new OperationFailedException(String.format( + "failed to find overlay package %s for user %d", packageName, userId)); } try { @@ -576,11 +590,11 @@ final class OverlayManagerServiceImpl { modified |= updateState(targetPackageName, packageName, userId, 0); if (modified) { - mListener.onOverlaysChanged(targetPackageName, userId); + return Optional.of(new PackageAndUser(targetPackageName, userId)); } - return true; + return Optional.empty(); } catch (OverlayManagerSettings.BadKeyException e) { - return false; + throw new OperationFailedException("failed to update settings", e); } } @@ -596,66 +610,75 @@ final class OverlayManagerServiceImpl { return mOverlayConfig.isEnabled(packageName); } - boolean setPriority(@NonNull final String packageName, - @NonNull final String newParentPackageName, final int userId) { + Optional setPriority(@NonNull final String packageName, + @NonNull final String newParentPackageName, final int userId) + throws OperationFailedException { if (DEBUG) { Slog.d(TAG, "setPriority packageName=" + packageName + " newParentPackageName=" + newParentPackageName + " userId=" + userId); } if (!isPackageConfiguredMutable(packageName)) { - return false; + throw new OperationFailedException(String.format( + "overlay package %s user %d is not updatable", packageName, userId)); } final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); if (overlayPackage == null) { - return false; + throw new OperationFailedException(String.format( + "failed to find overlay package %s for user %d", packageName, userId)); } if (mSettings.setPriority(packageName, newParentPackageName, userId)) { - mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId); + return Optional.of(new PackageAndUser(overlayPackage.overlayTarget, userId)); } - return true; + return Optional.empty(); } - boolean setHighestPriority(@NonNull final String packageName, final int userId) { + Optional setHighestPriority(@NonNull final String packageName, + final int userId) throws OperationFailedException { if (DEBUG) { Slog.d(TAG, "setHighestPriority packageName=" + packageName + " userId=" + userId); } if (!isPackageConfiguredMutable(packageName)) { - return false; + throw new OperationFailedException(String.format( + "overlay package %s user %d is not updatable", packageName, userId)); } final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); if (overlayPackage == null) { - return false; + throw new OperationFailedException(String.format( + "failed to find overlay package %s for user %d", packageName, userId)); } if (mSettings.setHighestPriority(packageName, userId)) { - mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId); + return Optional.of(new PackageAndUser(overlayPackage.overlayTarget, userId)); } - return true; + return Optional.empty(); } - boolean setLowestPriority(@NonNull final String packageName, final int userId) { + Optional setLowestPriority(@NonNull final String packageName, final int userId) + throws OperationFailedException { if (DEBUG) { Slog.d(TAG, "setLowestPriority packageName=" + packageName + " userId=" + userId); } if (!isPackageConfiguredMutable(packageName)) { - return false; + throw new OperationFailedException(String.format( + "overlay package %s user %d is not updatable", packageName, userId)); } final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); if (overlayPackage == null) { - return false; + throw new OperationFailedException(String.format( + "failed to find overlay package %s for user %d", packageName, userId)); } if (mSettings.setLowestPriority(packageName, userId)) { - mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId); + return Optional.of(new PackageAndUser(overlayPackage.overlayTarget, userId)); } - return true; + return Optional.empty(); } void dump(@NonNull final PrintWriter pw, @NonNull DumpState dumpState) { @@ -797,12 +820,13 @@ final class OverlayManagerServiceImpl { mIdmapManager.removeIdmap(oi, oi.userId); } - interface OverlayChangeListener { + static final class OperationFailedException extends Exception { + OperationFailedException(@NonNull final String message) { + super(message); + } - /** - * An event triggered by changes made to overlay state or settings as well as changes that - * add or remove target packages of overlays. - **/ - void onOverlaysChanged(@NonNull String targetPackage, int userId); + OperationFailedException(@NonNull final String message, @NonNull Throwable cause) { + super(message, cause); + } } } diff --git a/services/core/java/com/android/server/om/PackageAndUser.java b/services/core/java/com/android/server/om/PackageAndUser.java new file mode 100644 index 0000000000000..5c38ba7ce97bc --- /dev/null +++ b/services/core/java/com/android/server/om/PackageAndUser.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.om; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; + +final class PackageAndUser { + public final @NonNull String packageName; + public final @UserIdInt int userId; + + PackageAndUser(@NonNull String packageName, @UserIdInt int userId) { + this.packageName = packageName; + this.userId = userId; + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof PackageAndUser)) { + return false; + } + PackageAndUser other = (PackageAndUser) obj; + return packageName.equals(other.packageName) && userId == other.userId; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + packageName.hashCode(); + result = prime * result + userId; + return result; + } + + @Override + public String toString() { + return String.format("PackageAndUser{packageName=%s, userId=%d}", packageName, userId); + } +} diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java index 391611b72dab8..5468fba59c109 100644 --- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java +++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java @@ -78,7 +78,7 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI } @Test - public void testImmutableEnabledChange() { + public void testImmutableEnabledChange() throws Exception { final OverlayManagerServiceImpl impl = getImpl(); installNewPackage(target(TARGET), USER); installNewPackage(overlay(OVERLAY, TARGET), USER); @@ -106,7 +106,7 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI } @Test - public void testMutableEnabledChangeHasNoEffect() { + public void testMutableEnabledChangeHasNoEffect() throws Exception { final OverlayManagerServiceImpl impl = getImpl(); installNewPackage(target(TARGET), USER); installNewPackage(overlay(OVERLAY, TARGET), USER); @@ -134,7 +134,7 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI } @Test - public void testMutableEnabledToImmutableEnabled() { + public void testMutableEnabledToImmutableEnabled() throws Exception { final OverlayManagerServiceImpl impl = getImpl(); installNewPackage(target(TARGET), USER); installNewPackage(overlay(OVERLAY, TARGET), USER); @@ -178,7 +178,7 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI } @Test - public void testMutablePriorityChange() { + public void testMutablePriorityChange() throws Exception { final OverlayManagerServiceImpl impl = getImpl(); installNewPackage(target(TARGET), USER); installNewPackage(overlay(OVERLAY, TARGET), USER); @@ -218,7 +218,7 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI } @Test - public void testImmutablePriorityChange() { + public void testImmutablePriorityChange() throws Exception { final OverlayManagerServiceImpl impl = getImpl(); installNewPackage(target(TARGET), USER); installNewPackage(overlay(OVERLAY, TARGET), USER); diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java index 4f882ce13dd4f..33dbcc0855be8 100644 --- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java +++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java @@ -22,11 +22,14 @@ import static android.content.om.OverlayInfo.STATE_MISSING_TARGET; import static android.os.OverlayablePolicy.CONFIG_SIGNATURE; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.testng.Assert.assertThrows; import android.content.om.OverlayInfo; +import android.util.Pair; import androidx.test.runner.AndroidJUnit4; @@ -35,6 +38,7 @@ import org.junit.runner.RunWith; import java.util.List; import java.util.Map; +import java.util.Optional; @RunWith(AndroidJUnit4.class) public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTestsBase { @@ -55,7 +59,7 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes private static final String CERT_CONFIG_NOK = "config_certificate_nok"; @Test - public void testGetOverlayInfo() { + public void testGetOverlayInfo() throws Exception { installNewPackage(overlay(OVERLAY, TARGET), USER); final OverlayManagerServiceImpl impl = getImpl(); @@ -67,7 +71,7 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes } @Test - public void testGetOverlayInfosForTarget() { + public void testGetOverlayInfosForTarget() throws Exception { installNewPackage(overlay(OVERLAY, TARGET), USER); installNewPackage(overlay(OVERLAY2, TARGET), USER); installNewPackage(overlay(OVERLAY3, TARGET), USER2); @@ -92,7 +96,7 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes } @Test - public void testGetOverlayInfosForUser() { + public void testGetOverlayInfosForUser() throws Exception { installNewPackage(target(TARGET), USER); installNewPackage(overlay(OVERLAY, TARGET), USER); installNewPackage(overlay(OVERLAY2, TARGET), USER); @@ -119,7 +123,7 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes } @Test - public void testPriority() { + public void testPriority() throws Exception { installNewPackage(overlay(OVERLAY, TARGET), USER); installNewPackage(overlay(OVERLAY2, TARGET), USER); installNewPackage(overlay(OVERLAY3, TARGET), USER); @@ -131,18 +135,21 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes assertOverlayInfoForTarget(TARGET, USER, o1, o2, o3); - assertTrue(impl.setLowestPriority(OVERLAY3, USER)); + assertEquals(impl.setLowestPriority(OVERLAY3, USER), + Optional.of(new PackageAndUser(TARGET, USER))); assertOverlayInfoForTarget(TARGET, USER, o3, o1, o2); - assertTrue(impl.setHighestPriority(OVERLAY3, USER)); + assertEquals(impl.setHighestPriority(OVERLAY3, USER), + Optional.of(new PackageAndUser(TARGET, USER))); assertOverlayInfoForTarget(TARGET, USER, o1, o2, o3); - assertTrue(impl.setPriority(OVERLAY, OVERLAY2, USER)); + assertEquals(impl.setPriority(OVERLAY, OVERLAY2, USER), + Optional.of(new PackageAndUser(TARGET, USER))); assertOverlayInfoForTarget(TARGET, USER, o2, o1, o3); } @Test - public void testOverlayInfoStateTransitions() { + public void testOverlayInfoStateTransitions() throws Exception { final OverlayManagerServiceImpl impl = getImpl(); assertNull(impl.getOverlayInfo(OVERLAY, USER)); @@ -153,7 +160,8 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes installNewPackage(target, USER); assertState(STATE_DISABLED, OVERLAY, USER); - impl.setEnabled(OVERLAY, true, USER); + assertEquals(impl.setEnabled(OVERLAY, true, USER), + Optional.of(new PackageAndUser(TARGET, USER))); assertState(STATE_ENABLED, OVERLAY, USER); // target upgrades do not change the state of the overlay @@ -168,50 +176,40 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes } @Test - public void testOnOverlayPackageUpgraded() { - final FakeListener listener = getListener(); + public void testOnOverlayPackageUpgraded() throws Exception { final FakeDeviceState.PackageBuilder target = target(TARGET); final FakeDeviceState.PackageBuilder overlay = overlay(OVERLAY, TARGET); installNewPackage(target, USER); installNewPackage(overlay, USER); - listener.count = 0; upgradePackage(overlay, USER); - assertEquals(2, listener.count); // upgrade to a version where the overlay has changed its target - // expect once for the old target package, once for the new target package - listener.count = 0; final FakeDeviceState.PackageBuilder overlay2 = overlay(OVERLAY, "some.other.target"); - upgradePackage(overlay2, USER); - assertEquals(3, listener.count); - - listener.count = 0; - upgradePackage(overlay2, USER); - assertEquals(2, listener.count); + final Pair, Optional> pair = + upgradePackage(overlay2, USER); + assertEquals(pair.first, Optional.of(new PackageAndUser(TARGET, USER))); + assertEquals(pair.second, Optional.of(new PackageAndUser("some.other.target", USER))); } @Test - public void testListener() { + public void testSetEnabledAtVariousConditions() throws Exception { final OverlayManagerServiceImpl impl = getImpl(); - final FakeListener listener = getListener(); - installNewPackage(overlay(OVERLAY, TARGET), USER); - assertEquals(1, listener.count); - listener.count = 0; + assertThrows(OverlayManagerServiceImpl.OperationFailedException.class, + () -> impl.setEnabled(OVERLAY, true, USER)); + // request succeeded, and there was a change that needs to be + // propagated to the rest of the system installNewPackage(target(TARGET), USER); - assertEquals(1, listener.count); - listener.count = 0; + installNewPackage(overlay(OVERLAY, TARGET), USER); + assertEquals(impl.setEnabled(OVERLAY, true, USER), + Optional.of(new PackageAndUser(TARGET, USER))); - impl.setEnabled(OVERLAY, true, USER); - assertEquals(1, listener.count); - listener.count = 0; - - impl.setEnabled(OVERLAY, true, USER); - assertEquals(0, listener.count); + // request succeeded, but nothing changed + assertFalse(impl.setEnabled(OVERLAY, true, USER).isPresent()); } @Test - public void testConfigSignaturePolicyOk() { + public void testConfigSignaturePolicyOk() throws Exception { setConfigSignaturePackageName(CONFIG_SIGNATURE_REFERENCE_PKG); reinitializeImpl(); @@ -229,7 +227,7 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes } @Test - public void testConfigSignaturePolicyCertNok() { + public void testConfigSignaturePolicyCertNok() throws Exception { setConfigSignaturePackageName(CONFIG_SIGNATURE_REFERENCE_PKG); reinitializeImpl(); @@ -247,7 +245,7 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes } @Test - public void testConfigSignaturePolicyNoConfig() { + public void testConfigSignaturePolicyNoConfig() throws Exception { addPackage(target(CONFIG_SIGNATURE_REFERENCE_PKG).setCertificate(CERT_CONFIG_OK), USER); installNewPackage(target(TARGET), USER); installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER); @@ -262,7 +260,7 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes } @Test - public void testConfigSignaturePolicyNoRefPkg() { + public void testConfigSignaturePolicyNoRefPkg() throws Exception { installNewPackage(target(TARGET), USER); installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER); @@ -276,7 +274,7 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes } @Test - public void testConfigSignaturePolicyRefPkgNotSystem() { + public void testConfigSignaturePolicyRefPkgNotSystem() throws Exception { setConfigSignaturePackageName(CONFIG_SIGNATURE_REFERENCE_PKG); reinitializeImpl(); diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java index 006dda0f80e35..2c477c897b301 100644 --- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java +++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java @@ -16,6 +16,8 @@ package com.android.server.om; +import static com.android.server.om.OverlayManagerServiceImpl.OperationFailedException; + import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; @@ -30,6 +32,7 @@ import android.content.pm.PackageInfo; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.Pair; import androidx.annotation.Nullable; @@ -43,13 +46,13 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.stream.Collectors; /** Base class for creating {@link OverlayManagerServiceImplTests} tests. */ class OverlayManagerServiceImplTestsBase { private OverlayManagerServiceImpl mImpl; private FakeDeviceState mState; - private FakeListener mListener; private FakePackageManagerHelper mPackageManager; private FakeIdmapDaemon mIdmapDaemon; private OverlayConfig mOverlayConfig; @@ -58,7 +61,6 @@ class OverlayManagerServiceImplTestsBase { @Before public void setUp() { mState = new FakeDeviceState(); - mListener = new FakeListener(); mPackageManager = new FakePackageManagerHelper(mState); mIdmapDaemon = new FakeIdmapDaemon(mState); mOverlayConfig = mock(OverlayConfig.class); @@ -73,18 +75,13 @@ class OverlayManagerServiceImplTestsBase { new IdmapManager(mIdmapDaemon, mPackageManager), new OverlayManagerSettings(), mOverlayConfig, - new String[0], - mListener); + new String[0]); } OverlayManagerServiceImpl getImpl() { return mImpl; } - FakeListener getListener() { - return mListener; - } - FakeIdmapDaemon getIdmapd() { return mIdmapDaemon; } @@ -155,7 +152,8 @@ class OverlayManagerServiceImplTestsBase { * * @throws IllegalStateException if the package is currently installed */ - void installNewPackage(FakeDeviceState.PackageBuilder pkg, int userId) { + void installNewPackage(FakeDeviceState.PackageBuilder pkg, int userId) + throws OperationFailedException { if (mState.select(pkg.packageName, userId) != null) { throw new IllegalStateException("package " + pkg.packageName + " already installed"); } @@ -176,23 +174,30 @@ class OverlayManagerServiceImplTestsBase { * {@link android.content.Intent#ACTION_PACKAGE_ADDED} broadcast with the * {@link android.content.Intent#EXTRA_REPLACING} extra. * + * @return the two Optional objects from starting and finishing the upgrade + * * @throws IllegalStateException if the package is not currently installed */ - void upgradePackage(FakeDeviceState.PackageBuilder pkg, int userId) { + Pair, Optional> upgradePackage( + FakeDeviceState.PackageBuilder pkg, int userId) throws OperationFailedException { final FakeDeviceState.Package replacedPackage = mState.select(pkg.packageName, userId); if (replacedPackage == null) { throw new IllegalStateException("package " + pkg.packageName + " not installed"); } + Optional opt1 = Optional.empty(); if (replacedPackage.targetPackageName != null) { - mImpl.onOverlayPackageReplacing(pkg.packageName, userId); + opt1 = mImpl.onOverlayPackageReplacing(pkg.packageName, userId); } mState.add(pkg, userId); + Optional opt2; if (pkg.targetPackage == null) { - mImpl.onTargetPackageReplaced(pkg.packageName, userId); + opt2 = mImpl.onTargetPackageReplaced(pkg.packageName, userId); } else { - mImpl.onOverlayPackageReplaced(pkg.packageName, userId); + opt2 = mImpl.onOverlayPackageReplaced(pkg.packageName, userId); } + + return Pair.create(opt1, opt2); } /** @@ -203,7 +208,7 @@ class OverlayManagerServiceImplTestsBase { * * @throws IllegalStateException if the package is not currently installed */ - void uninstallPackage(String packageName, int userId) { + void uninstallPackage(String packageName, int userId) throws OperationFailedException { final FakeDeviceState.Package pkg = mState.select(packageName, userId); if (pkg == null) { throw new IllegalStateException("package " + packageName+ " not installed"); @@ -485,12 +490,4 @@ class OverlayManagerServiceImplTestsBase { } } } - - static class FakeListener implements OverlayManagerServiceImpl.OverlayChangeListener { - public int count; - - public void onOverlaysChanged(@NonNull String targetPackage, int userId) { - count++; - } - } }