Merge "OverlayManagerService: Make broadcasts/updates explicit" into oc-dev
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 5415eb5..b382cef 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3216,13 +3216,6 @@
"android.intent.action.MEDIA_RESOURCE_GRANTED";
/**
- * Broadcast Action: An overlay package has been installed. The data
- * contains the name of the added overlay package.
- * @hide
- */
- public static final String ACTION_OVERLAY_ADDED = "android.intent.action.OVERLAY_ADDED";
-
- /**
* Broadcast Action: An overlay package has changed. The data contains the
* name of the overlay package which has changed. This is broadcast on all
* changes to the OverlayInfo returned by {@link
@@ -3234,22 +3227,6 @@
public static final String ACTION_OVERLAY_CHANGED = "android.intent.action.OVERLAY_CHANGED";
/**
- * Broadcast Action: An overlay package has been removed. The data contains
- * the name of the overlay package which has been removed.
- * @hide
- */
- public static final String ACTION_OVERLAY_REMOVED = "android.intent.action.OVERLAY_REMOVED";
-
- /**
- * Broadcast Action: The order of a package's list of overlay packages has
- * changed. The data contains the package name of the overlay package that
- * had its position in the list adjusted.
- * @hide
- */
- public static final String
- ACTION_OVERLAY_PRIORITY_CHANGED = "android.intent.action.OVERLAY_PRIORITY_CHANGED";
-
- /**
* Activity Action: Allow the user to select and return one or more existing
* documents. When invoked, the system will display the various
* {@link DocumentsProvider} instances installed on the device, letting the
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 2026c1b..818c3ad 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -193,13 +193,10 @@
* </ul>
*/
public final class OverlayManagerService extends SystemService {
-
static final String TAG = "OverlayManager";
static final boolean DEBUG = false;
- static final String PERMISSION_DENIED = "Operation not permitted for user shell";
-
/**
* The system property that specifies the default overlays to apply.
* This is a semicolon separated list of package names.
@@ -234,7 +231,7 @@
IdmapManager im = new IdmapManager(installer);
mSettings = new OverlayManagerSettings();
mImpl = new OverlayManagerServiceImpl(mPackageManager, im, mSettings,
- getDefaultOverlayPackages());
+ getDefaultOverlayPackages(), new OverlayChangeListener());
mInitCompleteSignal = SystemServerInitThreadPool.get().submit(() -> {
final IntentFilter packageFilter = new IntentFilter();
packageFilter.addAction(ACTION_PACKAGE_ADDED);
@@ -251,9 +248,6 @@
restoreSettings();
onSwitchUser(UserHandle.USER_SYSTEM);
- schedulePersistSettings();
-
- mSettings.addChangeListener(new OverlayChangeListener());
publishBinderService(Context.OVERLAY_SERVICE, mService);
publishLocalService(OverlayManagerService.class, this);
@@ -281,8 +275,9 @@
final List<String> targets;
synchronized (mLock) {
targets = mImpl.onSwitchUser(newUserId);
+ updateAssetsLocked(newUserId, targets);
}
- updateAssets(newUserId, targets);
+ schedulePersistSettings();
}
private static Set<String> getDefaultOverlayPackages() {
@@ -348,7 +343,8 @@
@NonNull final int[] userIds) {
for (final int userId : userIds) {
synchronized (mLock) {
- final PackageInfo pi = mPackageManager.getPackageInfo(packageName, userId, false);
+ final PackageInfo pi = mPackageManager.getPackageInfo(packageName, userId,
+ false);
if (pi != null) {
mPackageManager.cachePackageInfo(packageName, userId, pi);
if (!isOverlayPackage(pi)) {
@@ -365,7 +361,8 @@
@NonNull final int[] userIds) {
for (int userId : userIds) {
synchronized (mLock) {
- final PackageInfo pi = mPackageManager.getPackageInfo(packageName, userId, false);
+ final PackageInfo pi = mPackageManager.getPackageInfo(packageName, userId,
+ false);
if (pi != null) {
mPackageManager.cachePackageInfo(packageName, userId, pi);
if (!isOverlayPackage(pi)) {
@@ -397,7 +394,8 @@
@NonNull final int[] userIds) {
for (int userId : userIds) {
synchronized (mLock) {
- final PackageInfo pi = mPackageManager.getPackageInfo(packageName, userId, false);
+ final PackageInfo pi = mPackageManager.getPackageInfo(packageName, userId,
+ false);
if (pi != null) {
mPackageManager.cachePackageInfo(packageName, userId, pi);
if (!isOverlayPackage(pi)) {
@@ -449,8 +447,7 @@
private final IBinder mService = new IOverlayManager.Stub() {
@Override
- public Map<String, List<OverlayInfo>> getAllOverlays(int userId)
- throws RemoteException {
+ public Map<String, List<OverlayInfo>> getAllOverlays(int userId) throws RemoteException {
userId = handleIncomingUser(userId, "getAllOverlays");
synchronized (mLock) {
@@ -508,14 +505,14 @@
int userId) throws RemoteException {
enforceChangeOverlayPackagesPermission("setEnabled");
userId = handleIncomingUser(userId, "setEnabled");
- if (packageName == null) {
+ if (packageName == null || !enable) {
return false;
}
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- return mImpl.setEnabledExclusive(packageName, enable, userId);
+ return mImpl.setEnabledExclusive(packageName, userId);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -643,68 +640,24 @@
return pi != null && pi.overlayTarget != null;
}
- private final class OverlayChangeListener implements OverlayManagerSettings.ChangeListener {
+ private final class OverlayChangeListener
+ implements OverlayManagerServiceImpl.OverlayChangeListener {
@Override
- public void onSettingsChanged() {
+ public void onOverlaysChanged(@NonNull final String targetPackageName, final int userId) {
schedulePersistSettings();
- }
-
- @Override
- public void onOverlayAdded(@NonNull final OverlayInfo oi) {
- scheduleBroadcast(Intent.ACTION_OVERLAY_ADDED, oi, oi.isEnabled());
- }
-
- @Override
- public void onOverlayRemoved(@NonNull final OverlayInfo oi) {
- scheduleBroadcast(Intent.ACTION_OVERLAY_REMOVED, oi, oi.isEnabled());
- }
-
- @Override
- public void onOverlayChanged(@NonNull final OverlayInfo oi,
- @NonNull final OverlayInfo oldOi) {
- scheduleBroadcast(Intent.ACTION_OVERLAY_CHANGED, oi, oi.isEnabled() != oldOi.isEnabled());
- }
-
- @Override
- public void onOverlayPriorityChanged(@NonNull final OverlayInfo oi) {
- scheduleBroadcast(Intent.ACTION_OVERLAY_PRIORITY_CHANGED, oi, oi.isEnabled());
- }
-
- private void scheduleBroadcast(@NonNull final String action, @NonNull final OverlayInfo oi,
- final boolean doUpdate) {
- FgThread.getHandler().post(new BroadcastRunnable(action, oi, doUpdate));
- }
-
- private final class BroadcastRunnable implements Runnable {
- private final String mAction;
- private final OverlayInfo mOverlayInfo;
- private final boolean mDoUpdate;
-
- BroadcastRunnable(@NonNull final String action, @NonNull final OverlayInfo oi,
- final boolean doUpdate) {
- mAction = action;
- mOverlayInfo = oi;
- mDoUpdate = doUpdate;
- }
-
- @Override
- public void run() {
- if (mDoUpdate) {
- updateAssets(mOverlayInfo.userId, mOverlayInfo.targetPackageName);
+ FgThread.getHandler().post(() -> {
+ synchronized (mLock) {
+ updateAssetsLocked(userId, targetPackageName);
}
- sendBroadcast(mAction, mOverlayInfo.targetPackageName, mOverlayInfo.packageName,
- mOverlayInfo.userId);
- }
- private void sendBroadcast(@NonNull final String action,
- @NonNull final String targetPackageName, @NonNull final String packageName,
- final int userId) {
- final Intent intent = new Intent(action, Uri.fromParts("package",
- String.format("%s/%s", targetPackageName, packageName), null));
+ final Intent intent = new Intent(Intent.ACTION_OVERLAY_CHANGED,
+ Uri.fromParts("package", targetPackageName, null));
intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+
if (DEBUG) {
- Slog.d(TAG, String.format("send broadcast %s", intent));
+ Slog.d(TAG, "send broadcast " + intent);
}
+
try {
ActivityManager.getService().broadcastIntent(null, intent, null, null, 0,
null, null, null, android.app.AppOpsManager.OP_NONE, null, false, false,
@@ -712,18 +665,20 @@
} catch (RemoteException e) {
// Intentionally left empty.
}
- }
-
+ });
}
}
- private void updateAssets(final int userId, final String targetPackageName) {
+ private void updateAssetsLocked(final int userId, final String targetPackageName) {
final List<String> list = new ArrayList<>();
list.add(targetPackageName);
- updateAssets(userId, list);
+ updateAssetsLocked(userId, list);
}
- private void updateAssets(final int userId, List<String> targetPackageNames) {
+ private void updateAssetsLocked(final int userId, List<String> targetPackageNames) {
+ if (DEBUG) {
+ Slog.d(TAG, "Updating overlay assets");
+ }
final PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
final boolean updateFrameworkRes = targetPackageNames.contains("android");
if (updateFrameworkRes) {
@@ -743,6 +698,12 @@
final int N = targetPackageNames.size();
for (int i = 0; i < N; i++) {
final String targetPackageName = targetPackageNames.get(i);
+ if (DEBUG) {
+ Slog.d(TAG, "-> Updating overlay: target=" + targetPackageName + " overlays=["
+ + TextUtils.join(",", pendingChanges.get(targetPackageName))
+ + "] userId=" + userId);
+ }
+
if (!pm.setEnabledOverlayPackages(
userId, targetPackageName, pendingChanges.get(targetPackageName))) {
Slog.e(TAG, String.format("Failed to change enabled overlays for %s user %d",
@@ -762,20 +723,20 @@
if (mPersistSettingsScheduled.getAndSet(true)) {
return;
}
- IoThread.getHandler().post(new Runnable() {
- @Override
- public void run() {
- mPersistSettingsScheduled.set(false);
- synchronized (mLock) {
- FileOutputStream stream = null;
- try {
- stream = mSettingsFile.startWrite();
- mSettings.persist(stream);
- mSettingsFile.finishWrite(stream);
- } catch (IOException | XmlPullParserException e) {
- mSettingsFile.failWrite(stream);
- Slog.e(TAG, "failed to persist overlay state", e);
- }
+ IoThread.getHandler().post(() -> {
+ mPersistSettingsScheduled.set(false);
+ if (DEBUG) {
+ Slog.d(TAG, "Writing overlay settings");
+ }
+ synchronized (mLock) {
+ FileOutputStream stream = null;
+ try {
+ stream = mSettingsFile.startWrite();
+ mSettings.persist(stream);
+ mSettingsFile.finishWrite(stream);
+ } catch (IOException | XmlPullParserException e) {
+ mSettingsFile.failWrite(stream);
+ Slog.e(TAG, "failed to persist overlay state", e);
}
}
});
@@ -862,7 +823,8 @@
// The package manager does not support different versions of packages
// to be installed for different users: ignore userId for now.
try {
- return mPackageManager.checkSignatures(packageName1, packageName2) == SIGNATURE_MATCH;
+ return mPackageManager.checkSignatures(
+ packageName1, packageName2) == SIGNATURE_MATCH;
} catch (RemoteException e) {
// Intentionally left blank
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index 3705946..c536278 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -54,15 +54,18 @@
private final IdmapManager mIdmapManager;
private final OverlayManagerSettings mSettings;
private final Set<String> mDefaultOverlays;
+ private final OverlayChangeListener mListener;
OverlayManagerServiceImpl(@NonNull final PackageManagerHelper packageManager,
@NonNull final IdmapManager idmapManager,
@NonNull final OverlayManagerSettings settings,
- @NonNull final Set<String> defaultOverlays) {
+ @NonNull final Set<String> defaultOverlays,
+ @NonNull final OverlayChangeListener listener) {
mPackageManager = packageManager;
mIdmapManager = idmapManager;
mSettings = settings;
mDefaultOverlays = defaultOverlays;
+ mListener = listener;
}
/*
@@ -145,7 +148,6 @@
iter.remove();
}
}
-
return new ArrayList<>(packagesToUpdateAssets);
}
@@ -199,25 +201,30 @@
updateAllOverlaysForTarget(packageName, userId, null);
}
- private void updateAllOverlaysForTarget(@NonNull final String packageName, final int userId,
+ /**
+ * Returns true if the settings were modified for this target.
+ */
+ private boolean updateAllOverlaysForTarget(@NonNull final String packageName, final int userId,
@Nullable final PackageInfo targetPackage) {
+ boolean modified = false;
final List<OverlayInfo> ois = mSettings.getOverlaysForTarget(packageName, userId);
final int N = ois.size();
for (int i = 0; i < N; i++) {
final OverlayInfo oi = ois.get(i);
final PackageInfo overlayPackage = mPackageManager.getPackageInfo(oi.packageName, userId);
if (overlayPackage == null) {
- mSettings.remove(oi.packageName, oi.userId);
+ modified |= mSettings.remove(oi.packageName, oi.userId);
removeIdmapIfPossible(oi);
} else {
try {
- updateState(targetPackage, overlayPackage, userId);
+ modified |= updateState(targetPackage, overlayPackage, userId);
} catch (OverlayManagerSettings.BadKeyException e) {
Slog.e(TAG, "failed to update settings", e);
- mSettings.remove(oi.packageName, userId);
+ modified |= mSettings.remove(oi.packageName, userId);
}
}
}
+ return modified;
}
void onOverlayPackageAdded(@NonNull final String packageName, final int userId) {
@@ -238,7 +245,9 @@
mSettings.init(packageName, userId, overlayPackage.overlayTarget,
overlayPackage.applicationInfo.getBaseCodePath());
try {
- updateState(targetPackage, overlayPackage, userId);
+ if (updateState(targetPackage, overlayPackage, userId)) {
+ mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId);
+ }
} catch (OverlayManagerSettings.BadKeyException e) {
Slog.e(TAG, "failed to update settings", e);
mSettings.remove(packageName, userId);
@@ -289,8 +298,9 @@
if (overlayPackage == null) {
return false;
}
- // Static overlay is always being enabled.
- if (!enable && overlayPackage.isStaticOverlay) {
+
+ // Ignore static overlays.
+ if (overlayPackage.isStaticOverlay) {
return false;
}
@@ -298,19 +308,21 @@
final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
final PackageInfo targetPackage =
mPackageManager.getPackageInfo(oi.targetPackageName, userId);
- mSettings.setEnabled(packageName, userId, enable);
- updateState(targetPackage, overlayPackage, userId);
+ boolean modified = mSettings.setEnabled(packageName, userId, enable);
+ modified |= updateState(targetPackage, overlayPackage, userId);
+
+ if (modified) {
+ mListener.onOverlaysChanged(oi.targetPackageName, userId);
+ }
return true;
} catch (OverlayManagerSettings.BadKeyException e) {
return false;
}
}
- boolean setEnabledExclusive(@NonNull final String packageName, final boolean enable,
- final int userId) {
+ boolean setEnabledExclusive(@NonNull final String packageName, final int userId) {
if (DEBUG) {
- Slog.d(TAG, String.format("setEnabled packageName=%s enable=%s userId=%d",
- packageName, enable, userId));
+ Slog.d(TAG, String.format("setEnabledExclusive packageName=%s userId=%d", packageName, userId));
}
final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
@@ -320,23 +332,48 @@
try {
final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
+ final PackageInfo targetPackage =
+ mPackageManager.getPackageInfo(oi.targetPackageName, userId);
+
List<OverlayInfo> allOverlays = getOverlayInfosForTarget(oi.targetPackageName, userId);
+ boolean modified = false;
+
// Disable all other overlays.
allOverlays.remove(oi);
for (int i = 0; i < allOverlays.size(); i++) {
- // TODO: Optimize this to only send updates after all changes.
- setEnabled(allOverlays.get(i).packageName, false, userId);
+ final String disabledOverlayPackageName = allOverlays.get(i).packageName;
+ final PackageInfo disabledOverlayPackageInfo = mPackageManager.getPackageInfo(
+ disabledOverlayPackageName, userId);
+ if (disabledOverlayPackageInfo == null) {
+ modified |= mSettings.remove(disabledOverlayPackageName, userId);
+ continue;
+ }
+
+ if (disabledOverlayPackageInfo.isStaticOverlay) {
+ // Don't touch static overlays.
+ continue;
+ }
+
+ // Disable the overlay.
+ modified |= mSettings.setEnabled(disabledOverlayPackageName, userId, false);
+ modified |= updateState(targetPackage, disabledOverlayPackageInfo, userId);
}
- setEnabled(packageName, enable, userId);
+ // Enable the selected overlay.
+ modified |= mSettings.setEnabled(packageName, userId, true);
+ modified |= updateState(targetPackage, overlayPackage, userId);
+
+ if (modified) {
+ mListener.onOverlaysChanged(oi.targetPackageName, userId);
+ }
return true;
} catch (OverlayManagerSettings.BadKeyException e) {
return false;
}
}
- boolean isPackageUpdatableOverlay(@NonNull final String packageName, final int userId) {
+ private boolean isPackageUpdatableOverlay(@NonNull final String packageName, final int userId) {
final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
if (overlayPackage == null || overlayPackage.isStaticOverlay) {
return false;
@@ -346,18 +383,64 @@
boolean setPriority(@NonNull final String packageName,
@NonNull final String newParentPackageName, final int userId) {
- return isPackageUpdatableOverlay(packageName, userId) &&
- mSettings.setPriority(packageName, newParentPackageName, userId);
+ if (DEBUG) {
+ Slog.d(TAG, "setPriority packageName=" + packageName + " newParentPackageName="
+ + newParentPackageName + " userId=" + userId);
+ }
+
+ if (!isPackageUpdatableOverlay(packageName, userId)) {
+ return false;
+ }
+
+ final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
+ if (overlayPackage == null) {
+ return false;
+ }
+
+ if (mSettings.setPriority(packageName, newParentPackageName, userId)) {
+ mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId);
+ }
+ return true;
}
boolean setHighestPriority(@NonNull final String packageName, final int userId) {
- return isPackageUpdatableOverlay(packageName, userId) &&
- mSettings.setHighestPriority(packageName, userId);
+ if (DEBUG) {
+ Slog.d(TAG, "setHighestPriority packageName=" + packageName + " userId=" + userId);
+ }
+
+ if (!isPackageUpdatableOverlay(packageName, userId)) {
+ return false;
+ }
+
+ final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
+ if (overlayPackage == null) {
+ return false;
+ }
+
+ if (mSettings.setHighestPriority(packageName, userId)) {
+ mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId);
+ }
+ return true;
}
boolean setLowestPriority(@NonNull final String packageName, final int userId) {
- return isPackageUpdatableOverlay(packageName, userId) &&
- mSettings.setLowestPriority(packageName, userId);
+ if (DEBUG) {
+ Slog.d(TAG, "setLowestPriority packageName=" + packageName + " userId=" + userId);
+ }
+
+ if (!isPackageUpdatableOverlay(packageName, userId)) {
+ return false;
+ }
+
+ final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
+ if (overlayPackage == null) {
+ return false;
+ }
+
+ if (mSettings.setLowestPriority(packageName, userId)) {
+ mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId);
+ }
+ return true;
}
void onDump(@NonNull final PrintWriter pw) {
@@ -379,16 +462,19 @@
return paths;
}
- private void updateState(@Nullable final PackageInfo targetPackage,
+ /**
+ * Returns true if the settings/state was modified, false otherwise.
+ */
+ private boolean updateState(@Nullable final PackageInfo targetPackage,
@NonNull final PackageInfo overlayPackage, final int userId)
- throws OverlayManagerSettings.BadKeyException {
+ throws OverlayManagerSettings.BadKeyException {
// Static RROs targeting to "android", ie framework-res.apk, are handled by native layers.
if (targetPackage != null &&
!("android".equals(targetPackage.packageName) && overlayPackage.isStaticOverlay)) {
mIdmapManager.createIdmap(targetPackage, overlayPackage, userId);
}
- mSettings.setBaseCodePath(overlayPackage.packageName, userId,
+ boolean modified = mSettings.setBaseCodePath(overlayPackage.packageName, userId,
overlayPackage.applicationInfo.getBaseCodePath());
final int currentState = mSettings.getState(overlayPackage.packageName, userId);
@@ -400,8 +486,9 @@
OverlayInfo.stateToString(currentState),
OverlayInfo.stateToString(newState)));
}
- mSettings.setState(overlayPackage.packageName, userId, newState);
+ modified |= mSettings.setState(overlayPackage.packageName, userId, newState);
}
+ return modified;
}
private int calculateNewState(@Nullable final PackageInfo targetPackage,
@@ -441,10 +528,8 @@
if (!mIdmapManager.idmapExists(oi)) {
return;
}
- final List<Integer> userIds = mSettings.getUsers();
- final int N = userIds.size();
- for (int i = 0; i < N; i++) {
- final int userId = userIds.get(i);
+ final int[] userIds = mSettings.getUsers();
+ for (int userId : userIds) {
try {
final OverlayInfo tmp = mSettings.getOverlayInfo(oi.packageName, userId);
if (tmp != null && tmp.isEnabled()) {
@@ -458,6 +543,10 @@
mIdmapManager.removeIdmap(oi, oi.userId);
}
+ interface OverlayChangeListener {
+ void onOverlaysChanged(@NonNull String targetPackage, int userId);
+ }
+
interface PackageManagerHelper {
PackageInfo getPackageInfo(@NonNull String packageName, int userId);
boolean signaturesMatching(@NonNull String packageName1, @NonNull String packageName2,
diff --git a/services/core/java/com/android/server/om/OverlayManagerSettings.java b/services/core/java/com/android/server/om/OverlayManagerSettings.java
index 2262a2e..2cafa39 100644
--- a/services/core/java/com/android/server/om/OverlayManagerSettings.java
+++ b/services/core/java/com/android/server/om/OverlayManagerSettings.java
@@ -16,17 +16,11 @@
package com.android.server.om;
-import static android.content.om.OverlayInfo.STATE_UNKNOWN;
-
-import static com.android.server.om.OverlayManagerService.DEBUG;
-import static com.android.server.om.OverlayManagerService.TAG;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.om.OverlayInfo;
import android.util.AndroidRuntimeException;
import android.util.ArrayMap;
-import android.util.Slog;
import android.util.Xml;
import com.android.internal.util.FastXmlSerializer;
@@ -41,23 +35,28 @@
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Iterator;
import java.util.List;
-import java.util.ListIterator;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
/**
* Data structure representing the current state of all overlay packages in the
* system.
*
- * Modifications to the data are exposed through the ChangeListener interface.
+ * Modifications to the data are signaled by returning true from any state mutating method.
*
- * @see ChangeListener
* @see OverlayManagerService
*/
final class OverlayManagerSettings {
- private final List<ChangeListener> mListeners = new ArrayList<>();
-
+ /**
+ * All overlay data for all users and target packages is stored in this list.
+ * This keeps memory down, while increasing the cost of running queries or mutating the
+ * data. This is ok, since changing of overlays is very rare and has larger costs associated
+ * with it.
+ *
+ * The order of the items in the list is important, those with a lower index having a lower
+ * priority.
+ */
private final ArrayList<SettingsItem> mItems = new ArrayList<>();
void init(@NonNull final String packageName, final int userId,
@@ -68,225 +67,176 @@
mItems.add(item);
}
- void remove(@NonNull final String packageName, final int userId) {
- final SettingsItem item = select(packageName, userId);
- if (item == null) {
- return;
+ /**
+ * Returns true if the settings were modified, false if they remain the same.
+ */
+ boolean remove(@NonNull final String packageName, final int userId) {
+ final int idx = select(packageName, userId);
+ if (idx < 0) {
+ return false;
}
- final OverlayInfo oi = item.getOverlayInfo();
- mItems.remove(item);
- if (oi != null) {
- notifyOverlayRemoved(oi);
- }
- }
- boolean contains(@NonNull final String packageName, final int userId) {
- return select(packageName, userId) != null;
+ mItems.remove(idx);
+ return true;
}
OverlayInfo getOverlayInfo(@NonNull final String packageName, final int userId)
throws BadKeyException {
- final SettingsItem item = select(packageName, userId);
- if (item == null) {
+ final int idx = select(packageName, userId);
+ if (idx < 0) {
throw new BadKeyException(packageName, userId);
}
- return item.getOverlayInfo();
+ return mItems.get(idx).getOverlayInfo();
}
- String getTargetPackageName(@NonNull final String packageName, final int userId)
- throws BadKeyException {
- final SettingsItem item = select(packageName, userId);
- if (item == null) {
- throw new BadKeyException(packageName, userId);
- }
- return item.getTargetPackageName();
- }
-
- void setBaseCodePath(@NonNull final String packageName, final int userId,
+ /**
+ * Returns true if the settings were modified, false if they remain the same.
+ */
+ boolean setBaseCodePath(@NonNull final String packageName, final int userId,
@NonNull final String path) throws BadKeyException {
- final SettingsItem item = select(packageName, userId);
- if (item == null) {
+ final int idx = select(packageName, userId);
+ if (idx < 0) {
throw new BadKeyException(packageName, userId);
}
- item.setBaseCodePath(path);
- notifySettingsChanged();
+ return mItems.get(idx).setBaseCodePath(path);
}
boolean getEnabled(@NonNull final String packageName, final int userId) throws BadKeyException {
- final SettingsItem item = select(packageName, userId);
- if (item == null) {
+ final int idx = select(packageName, userId);
+ if (idx < 0) {
throw new BadKeyException(packageName, userId);
}
- return item.isEnabled();
+ return mItems.get(idx).isEnabled();
}
- void setEnabled(@NonNull final String packageName, final int userId, final boolean enable)
+ /**
+ * Returns true if the settings were modified, false if they remain the same.
+ */
+ boolean setEnabled(@NonNull final String packageName, final int userId, final boolean enable)
throws BadKeyException {
- final SettingsItem item = select(packageName, userId);
- if (item == null) {
+ final int idx = select(packageName, userId);
+ if (idx < 0) {
throw new BadKeyException(packageName, userId);
}
- if (enable == item.isEnabled()) {
- return; // nothing to do
- }
-
- item.setEnabled(enable);
- notifySettingsChanged();
+ return mItems.get(idx).setEnabled(enable);
}
int getState(@NonNull final String packageName, final int userId) throws BadKeyException {
- final SettingsItem item = select(packageName, userId);
- if (item == null) {
+ final int idx = select(packageName, userId);
+ if (idx < 0) {
throw new BadKeyException(packageName, userId);
}
- return item.getState();
+ return mItems.get(idx).getState();
}
- void setState(@NonNull final String packageName, final int userId, final int state)
+ /**
+ * Returns true if the settings were modified, false if they remain the same.
+ */
+ boolean setState(@NonNull final String packageName, final int userId, final int state)
throws BadKeyException {
- final SettingsItem item = select(packageName, userId);
- if (item == null) {
+ final int idx = select(packageName, userId);
+ if (idx < 0) {
throw new BadKeyException(packageName, userId);
}
- final OverlayInfo previous = item.getOverlayInfo();
- item.setState(state);
- final OverlayInfo current = item.getOverlayInfo();
- if (previous.state == STATE_UNKNOWN) {
- notifyOverlayAdded(current);
- notifySettingsChanged();
- } else if (current.state != previous.state) {
- notifyOverlayChanged(current, previous);
- notifySettingsChanged();
- }
+ return mItems.get(idx).setState(state);
}
List<OverlayInfo> getOverlaysForTarget(@NonNull final String targetPackageName,
final int userId) {
- final List<SettingsItem> items = selectWhereTarget(targetPackageName, userId);
- if (items.isEmpty()) {
- return Collections.emptyList();
- }
- final List<OverlayInfo> out = new ArrayList<>(items.size());
- final int N = items.size();
- for (int i = 0; i < N; i++) {
- final SettingsItem item = items.get(i);
- out.add(item.getOverlayInfo());
- }
- return out;
+ return selectWhereTarget(targetPackageName, userId)
+ .map(SettingsItem::getOverlayInfo)
+ .collect(Collectors.toList());
}
ArrayMap<String, List<OverlayInfo>> getOverlaysForUser(final int userId) {
- final List<SettingsItem> items = selectWhereUser(userId);
- if (items.isEmpty()) {
- return ArrayMap.EMPTY;
- }
- final ArrayMap<String, List<OverlayInfo>> out = new ArrayMap<>(items.size());
- final int N = items.size();
- for (int i = 0; i < N; i++) {
- final SettingsItem item = items.get(i);
- final String targetPackageName = item.getTargetPackageName();
- if (!out.containsKey(targetPackageName)) {
- out.put(targetPackageName, new ArrayList<OverlayInfo>());
- }
- final List<OverlayInfo> overlays = out.get(targetPackageName);
- overlays.add(item.getOverlayInfo());
- }
- return out;
+ return selectWhereUser(userId)
+ .map(SettingsItem::getOverlayInfo)
+ .collect(Collectors.groupingBy(info -> info.targetPackageName, ArrayMap::new,
+ Collectors.toList()));
}
- List<String> getTargetPackageNamesForUser(final int userId) {
- final List<SettingsItem> items = selectWhereUser(userId);
- if (items.isEmpty()) {
- return Collections.emptyList();
- }
- final List<String> out = new ArrayList<>();
- final int N = items.size();
- for (int i = 0; i < N; i++) {
- final SettingsItem item = items.get(i);
- final String targetPackageName = item.getTargetPackageName();
- if (!out.contains(targetPackageName)) {
- out.add(targetPackageName);
- }
- }
- return out;
+ int[] getUsers() {
+ return mItems.stream().mapToInt(SettingsItem::getUserId).distinct().toArray();
}
- List<Integer> getUsers() {
- final ArrayList<Integer> users = new ArrayList<>();
- final int N = mItems.size();
- for (int i = 0; i < N; i++) {
+ /**
+ * Returns true if the settings were modified, false if they remain the same.
+ */
+ boolean removeUser(final int userId) {
+ boolean removed = false;
+ for (int i = 0; i < mItems.size(); i++) {
final SettingsItem item = mItems.get(i);
- if (!users.contains(item.userId)) {
- users.add(item.userId);
+ if (item.getUserId() == userId) {
+ mItems.remove(i);
+ removed = true;
+ i--;
}
}
- return users;
+ return removed;
}
- void removeUser(final int userId) {
- final Iterator<SettingsItem> iter = mItems.iterator();
- while (iter.hasNext()) {
- final SettingsItem item = iter.next();
- if (item.userId == userId) {
- iter.remove();
- }
- }
- }
-
+ /**
+ * Returns true if the settings were modified, false if they remain the same.
+ */
boolean setPriority(@NonNull final String packageName,
@NonNull final String newParentPackageName, final int userId) {
if (packageName.equals(newParentPackageName)) {
return false;
}
- final SettingsItem rowToMove = select(packageName, userId);
- if (rowToMove == null) {
- return false;
- }
- final SettingsItem newParentRow = select(newParentPackageName, userId);
- if (newParentRow == null) {
- return false;
- }
- if (!rowToMove.getTargetPackageName().equals(newParentRow.getTargetPackageName())) {
+ final int moveIdx = select(packageName, userId);
+ if (moveIdx < 0) {
return false;
}
- mItems.remove(rowToMove);
- final ListIterator<SettingsItem> iter = mItems.listIterator();
- while (iter.hasNext()) {
- final SettingsItem item = iter.next();
- if (item.userId == userId && item.packageName.equals(newParentPackageName)) {
- iter.add(rowToMove);
- notifyOverlayPriorityChanged(rowToMove.getOverlayInfo());
- notifySettingsChanged();
- return true;
- }
+ final int parentIdx = select(newParentPackageName, userId);
+ if (parentIdx < 0) {
+ return false;
}
- Slog.wtf(TAG, "failed to find the parent item a second time");
- return false;
+ final SettingsItem itemToMove = mItems.get(moveIdx);
+
+ // Make sure both packages are targeting the same package.
+ if (!itemToMove.getTargetPackageName().equals(
+ mItems.get(parentIdx).getTargetPackageName())) {
+ return false;
+ }
+
+ mItems.remove(moveIdx);
+ final int newParentIdx = select(newParentPackageName, userId);
+ mItems.add(newParentIdx, itemToMove);
+ return moveIdx != newParentIdx;
}
+ /**
+ * Returns true if the settings were modified, false if they remain the same.
+ */
boolean setLowestPriority(@NonNull final String packageName, final int userId) {
- final SettingsItem item = select(packageName, userId);
- if (item == null) {
+ final int idx = select(packageName, userId);
+ if (idx <= 0) {
+ // If the item doesn't exist or is already the lowest, don't change anything.
return false;
}
+
+ final SettingsItem item = mItems.get(idx);
mItems.remove(item);
mItems.add(0, item);
- notifyOverlayPriorityChanged(item.getOverlayInfo());
- notifySettingsChanged();
return true;
}
+ /**
+ * Returns true if the settings were modified, false if they remain the same.
+ */
boolean setHighestPriority(@NonNull final String packageName, final int userId) {
- final SettingsItem item = select(packageName, userId);
- if (item == null) {
+ final int idx = select(packageName, userId);
+
+ // If the item doesn't exist or is already the highest, don't change anything.
+ if (idx < 0 || idx == mItems.size() - 1) {
return false;
}
- mItems.remove(item);
+
+ final SettingsItem item = mItems.get(idx);
+ mItems.remove(idx);
mItems.add(item);
- notifyOverlayPriorityChanged(item.getOverlayInfo());
- notifySettingsChanged();
return true;
}
@@ -296,11 +246,6 @@
void dump(@NonNull final PrintWriter pw) {
pw.println("Settings");
- dumpItems(pw);
- dumpListeners(pw);
- }
-
- private void dumpItems(@NonNull final PrintWriter pw) {
pw.println(TAB1 + "Items");
if (mItems.isEmpty()) {
@@ -312,34 +257,18 @@
for (int i = 0; i < N; i++) {
final SettingsItem item = mItems.get(i);
final StringBuilder sb = new StringBuilder();
- sb.append(TAB2 + item.packageName + ":" + item.userId + " {\n");
- sb.append(TAB3 + "packageName.......: " + item.packageName + "\n");
- sb.append(TAB3 + "userId............: " + item.userId + "\n");
- sb.append(TAB3 + "targetPackageName.: " + item.getTargetPackageName() + "\n");
- sb.append(TAB3 + "baseCodePath......: " + item.getBaseCodePath() + "\n");
- sb.append(TAB3 + "state.............: " + OverlayInfo.stateToString(item.getState()) + "\n");
- sb.append(TAB3 + "isEnabled.........: " + item.isEnabled() + "\n");
+ sb.append(TAB2 + item.mPackageName + ":" + item.getUserId() + " {\n");
+ sb.append(TAB3 + "mPackageName.......: " + item.mPackageName + "\n");
+ sb.append(TAB3 + "mUserId............: " + item.getUserId() + "\n");
+ sb.append(TAB3 + "mTargetPackageName.: " + item.getTargetPackageName() + "\n");
+ sb.append(TAB3 + "mBaseCodePath......: " + item.getBaseCodePath() + "\n");
+ sb.append(TAB3 + "mState.............: " + OverlayInfo.stateToString(item.getState()) + "\n");
+ sb.append(TAB3 + "mIsEnabled.........: " + item.isEnabled() + "\n");
sb.append(TAB2 + "}");
pw.println(sb.toString());
}
}
- private void dumpListeners(@NonNull final PrintWriter pw) {
- pw.println(TAB1 + "Change listeners");
-
- if (mListeners.isEmpty()) {
- pw.println(TAB2 + "<none>");
- return;
- }
-
- final int N = mListeners.size();
- for (int i = 0; i < N; i++) {
- final ChangeListener ch = mListeners.get(i);
- pw.println(TAB2 + ch);
- }
-
- }
-
void restore(@NonNull final InputStream is) throws IOException, XmlPullParserException {
Serializer.restore(mItems, is);
}
@@ -434,127 +363,122 @@
private static void persistRow(@NonNull final FastXmlSerializer xml,
@NonNull final SettingsItem item) throws IOException {
xml.startTag(null, TAG_ITEM);
- XmlUtils.writeStringAttribute(xml, ATTR_PACKAGE_NAME, item.packageName);
- XmlUtils.writeIntAttribute(xml, ATTR_USER_ID, item.userId);
- XmlUtils.writeStringAttribute(xml, ATTR_TARGET_PACKAGE_NAME, item.targetPackageName);
- XmlUtils.writeStringAttribute(xml, ATTR_BASE_CODE_PATH, item.baseCodePath);
- XmlUtils.writeIntAttribute(xml, ATTR_STATE, item.state);
- XmlUtils.writeBooleanAttribute(xml, ATTR_IS_ENABLED, item.isEnabled);
+ XmlUtils.writeStringAttribute(xml, ATTR_PACKAGE_NAME, item.mPackageName);
+ XmlUtils.writeIntAttribute(xml, ATTR_USER_ID, item.mUserId);
+ XmlUtils.writeStringAttribute(xml, ATTR_TARGET_PACKAGE_NAME, item.mTargetPackageName);
+ XmlUtils.writeStringAttribute(xml, ATTR_BASE_CODE_PATH, item.mBaseCodePath);
+ XmlUtils.writeIntAttribute(xml, ATTR_STATE, item.mState);
+ XmlUtils.writeBooleanAttribute(xml, ATTR_IS_ENABLED, item.mIsEnabled);
xml.endTag(null, TAG_ITEM);
}
}
private static final class SettingsItem {
- private final int userId;
- private final String packageName;
- private final String targetPackageName;
- private String baseCodePath;
- private int state;
- private boolean isEnabled;
- private OverlayInfo cache;
+ private final int mUserId;
+ private final String mPackageName;
+ private final String mTargetPackageName;
+ private String mBaseCodePath;
+ private int mState;
+ private boolean mIsEnabled;
+ private OverlayInfo mCache;
SettingsItem(@NonNull final String packageName, final int userId,
@NonNull final String targetPackageName, @NonNull final String baseCodePath,
final int state, final boolean isEnabled) {
- this.packageName = packageName;
- this.userId = userId;
- this.targetPackageName = targetPackageName;
- this.baseCodePath = baseCodePath;
- this.state = state;
- this.isEnabled = isEnabled;
- cache = null;
+ mPackageName = packageName;
+ mUserId = userId;
+ mTargetPackageName = targetPackageName;
+ mBaseCodePath = baseCodePath;
+ mState = state;
+ mIsEnabled = isEnabled;
+ mCache = null;
}
SettingsItem(@NonNull final String packageName, final int userId,
@NonNull final String targetPackageName, @NonNull final String baseCodePath) {
- this(packageName, userId, targetPackageName, baseCodePath, STATE_UNKNOWN,
+ this(packageName, userId, targetPackageName, baseCodePath, OverlayInfo.STATE_UNKNOWN,
false);
}
private String getTargetPackageName() {
- return targetPackageName;
+ return mTargetPackageName;
+ }
+
+ private int getUserId() {
+ return mUserId;
}
private String getBaseCodePath() {
- return baseCodePath;
+ return mBaseCodePath;
}
- private void setBaseCodePath(@NonNull final String path) {
- if (!baseCodePath.equals(path)) {
- baseCodePath = path;
+ private boolean setBaseCodePath(@NonNull final String path) {
+ if (!mBaseCodePath.equals(path)) {
+ mBaseCodePath = path;
invalidateCache();
+ return true;
}
+ return false;
}
private int getState() {
- return state;
+ return mState;
}
- private void setState(final int state) {
- if (this.state != state) {
- this.state = state;
+ private boolean setState(final int state) {
+ if (mState != state) {
+ mState = state;
invalidateCache();
+ return true;
}
+ return false;
}
private boolean isEnabled() {
- return isEnabled;
+ return mIsEnabled;
}
- private void setEnabled(final boolean enable) {
- if (isEnabled != enable) {
- isEnabled = enable;
+ private boolean setEnabled(final boolean enable) {
+ if (mIsEnabled != enable) {
+ mIsEnabled = enable;
invalidateCache();
+ return true;
}
+ return false;
}
private OverlayInfo getOverlayInfo() {
- if (cache == null) {
- cache = new OverlayInfo(packageName, targetPackageName, baseCodePath,
- state, userId);
+ if (mCache == null) {
+ mCache = new OverlayInfo(mPackageName, mTargetPackageName, mBaseCodePath, mState,
+ mUserId);
}
- return cache;
+ return mCache;
}
private void invalidateCache() {
- cache = null;
+ mCache = null;
}
}
- private SettingsItem select(@NonNull final String packageName, final int userId) {
+ private int select(@NonNull final String packageName, final int userId) {
final int N = mItems.size();
for (int i = 0; i < N; i++) {
final SettingsItem item = mItems.get(i);
- if (item.userId == userId && item.packageName.equals(packageName)) {
- return item;
+ if (item.mUserId == userId && item.mPackageName.equals(packageName)) {
+ return i;
}
}
- return null;
+ return -1;
}
- private List<SettingsItem> selectWhereUser(final int userId) {
- final ArrayList<SettingsItem> items = new ArrayList<>();
- final int N = mItems.size();
- for (int i = 0; i < N; i++) {
- final SettingsItem item = mItems.get(i);
- if (item.userId == userId) {
- items.add(item);
- }
- }
- return items;
+ private Stream<SettingsItem> selectWhereUser(final int userId) {
+ return mItems.stream().filter(item -> item.mUserId == userId);
}
- private List<SettingsItem> selectWhereTarget(@NonNull final String targetPackageName,
+ private Stream<SettingsItem> selectWhereTarget(@NonNull final String targetPackageName,
final int userId) {
- final ArrayList<SettingsItem> items = new ArrayList<>();
- final int N = mItems.size();
- for (int i = 0; i < N; i++) {
- final SettingsItem item = mItems.get(i);
- if (item.userId == userId && item.getTargetPackageName().equals(targetPackageName)) {
- items.add(item);
- }
- }
- return items;
+ return selectWhereUser(userId)
+ .filter(item -> item.getTargetPackageName().equals(targetPackageName));
}
private void assertNotNull(@Nullable final Object o) {
@@ -563,79 +487,9 @@
}
}
- void addChangeListener(@NonNull final ChangeListener listener) {
- mListeners.add(listener);
- }
-
- void removeChangeListener(@NonNull final ChangeListener listener) {
- mListeners.remove(listener);
- }
-
- private void notifySettingsChanged() {
- final int N = mListeners.size();
- for (int i = 0; i < N; i++) {
- final ChangeListener listener = mListeners.get(i);
- listener.onSettingsChanged();
- }
- }
-
- private void notifyOverlayAdded(@NonNull final OverlayInfo oi) {
- if (DEBUG) {
- assertNotNull(oi);
- }
- final int N = mListeners.size();
- for (int i = 0; i < N; i++) {
- final ChangeListener listener = mListeners.get(i);
- listener.onOverlayAdded(oi);
- }
- }
-
- private void notifyOverlayRemoved(@NonNull final OverlayInfo oi) {
- if (DEBUG) {
- assertNotNull(oi);
- }
- final int N = mListeners.size();
- for (int i = 0; i < N; i++) {
- final ChangeListener listener = mListeners.get(i);
- listener.onOverlayRemoved(oi);
- }
- }
-
- private void notifyOverlayChanged(@NonNull final OverlayInfo oi,
- @NonNull final OverlayInfo oldOi) {
- if (DEBUG) {
- assertNotNull(oi);
- assertNotNull(oldOi);
- }
- final int N = mListeners.size();
- for (int i = 0; i < N; i++) {
- final ChangeListener listener = mListeners.get(i);
- listener.onOverlayChanged(oi, oldOi);
- }
- }
-
- private void notifyOverlayPriorityChanged(@NonNull final OverlayInfo oi) {
- if (DEBUG) {
- assertNotNull(oi);
- }
- final int N = mListeners.size();
- for (int i = 0; i < N; i++) {
- final ChangeListener listener = mListeners.get(i);
- listener.onOverlayPriorityChanged(oi);
- }
- }
-
- interface ChangeListener {
- void onSettingsChanged();
- void onOverlayAdded(@NonNull OverlayInfo oi);
- void onOverlayRemoved(@NonNull OverlayInfo oi);
- void onOverlayChanged(@NonNull OverlayInfo oi, @NonNull OverlayInfo oldOi);
- void onOverlayPriorityChanged(@NonNull OverlayInfo oi);
- }
-
static final class BadKeyException extends RuntimeException {
BadKeyException(@NonNull final String packageName, final int userId) {
- super("Bad key packageName=" + packageName + " userId=" + userId);
+ super("Bad key mPackageName=" + packageName + " mUserId=" + userId);
}
}
}