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<T> 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
This commit is contained in:
Mårten Kongstad
2020-02-14 15:49:48 +01:00
committed by Ryan Mitchell
parent ecae429892
commit f4312ccb5e
6 changed files with 335 additions and 189 deletions

View File

@@ -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<PackageAndUser> 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.
}
});
}
/**

View File

@@ -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<PackageAndUser> 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<PackageAndUser> 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<PackageAndUser> 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<PackageAndUser> 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<PackageAndUser> 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<PackageAndUser> updateAndRefreshOverlaysForTarget(
@NonNull final String targetPackageName, final int userId, final int flags)
throws OperationFailedException {
final List<OverlayInfo> 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<PackageAndUser> 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<PackageAndUser> 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<PackageAndUser> 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<PackageAndUser> 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<PackageAndUser> 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<PackageAndUser> 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<PackageAndUser> 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<PackageAndUser> 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<PackageAndUser> 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<PackageAndUser> 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);
}
}
}

View File

@@ -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);
}
}

View File

@@ -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);

View File

@@ -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<PackageAndUser>, Optional<PackageAndUser>> 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();

View File

@@ -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<PackageAndUser> 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<PackageAndUser>, Optional<PackageAndUser>> 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<PackageAndUser> opt1 = Optional.empty();
if (replacedPackage.targetPackageName != null) {
mImpl.onOverlayPackageReplacing(pkg.packageName, userId);
opt1 = mImpl.onOverlayPackageReplacing(pkg.packageName, userId);
}
mState.add(pkg, userId);
Optional<PackageAndUser> 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++;
}
}
}