diff options
8 files changed, 123 insertions, 2 deletions
diff --git a/services/core/java/com/android/server/WatchableIntentResolver.java b/services/core/java/com/android/server/WatchableIntentResolver.java index 3b5d168eba70..2ef94f17e5d9 100644 --- a/services/core/java/com/android/server/WatchableIntentResolver.java +++ b/services/core/java/com/android/server/WatchableIntentResolver.java @@ -39,28 +39,45 @@ public abstract class WatchableIntentResolver<F, R extends Object> * Watchable machinery */ private final Watchable mWatchable = new WatchableImpl(); + /** * Register an observer to receive change notifications. * @param observer The observer to register. */ + @Override public void registerObserver(@NonNull Watcher observer) { mWatchable.registerObserver(observer); } + /** * Unregister the observer, which will no longer receive change notifications. * @param observer The observer to unregister. */ + @Override public void unregisterObserver(@NonNull Watcher observer) { mWatchable.unregisterObserver(observer); } + + /** + * Return true if the {@link Watcher) is a registered observer. + * @param observer A {@link Watcher} that might be registered + * @return true if the observer is registered with this {@link Watchable}. + */ + @Override + public boolean isRegisteredObserver(@NonNull Watcher observer) { + return mWatchable.isRegisteredObserver(observer); + } + /** * Notify listeners that the object has changd. The argument is a hint as to the * source of the change. * @param what The attribute or sub-object that changed, if not null. */ + @Override public void dispatchChange(@Nullable Watchable what) { mWatchable.dispatchChange(what); } + /** * Notify listeners that this object has changed. */ diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java index b76fff579918..f8990c065341 100644 --- a/services/core/java/com/android/server/pm/AppsFilter.java +++ b/services/core/java/com/android/server/pm/AppsFilter.java @@ -167,6 +167,7 @@ public class AppsFilter implements Watchable, Snappable { * * @param observer The {@link Watcher} to be notified when the {@link Watchable} changes. */ + @Override public void registerObserver(@NonNull Watcher observer) { mWatchable.registerObserver(observer); } @@ -177,17 +178,29 @@ public class AppsFilter implements Watchable, Snappable { * * @param observer The {@link Watcher} that should not be in the notification list. */ + @Override public void unregisterObserver(@NonNull Watcher observer) { mWatchable.unregisterObserver(observer); } /** + * Return true if the {@link Watcher) is a registered observer. + * @param observer A {@link Watcher} that might be registered + * @return true if the observer is registered with this {@link Watchable}. + */ + @Override + public boolean isRegisteredObserver(@NonNull Watcher observer) { + return mWatchable.isRegisteredObserver(observer); + } + + /** * Invokes {@link Watcher#onChange} on each registered observer. The method can be called * with the {@link Watchable} that generated the event. In a tree of {@link Watchable}s, this * is generally the first (deepest) {@link Watchable} to detect a change. * * @param what The {@link Watchable} that generated the event. */ + @Override public void dispatchChange(@Nullable Watchable what) { mSnapshot = null; mWatchable.dispatchChange(what); diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java index 69d3e5c2f941..c3bca285dca3 100644 --- a/services/core/java/com/android/server/pm/InstantAppRegistry.java +++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java @@ -154,6 +154,9 @@ class InstantAppRegistry implements Watchable, Snappable { public void unregisterObserver(@NonNull Watcher observer) { mWatchable.unregisterObserver(observer); } + public boolean isRegisteredObserver(@NonNull Watcher observer) { + return mWatchable.isRegisteredObserver(observer); + } public void dispatchChange(@Nullable Watchable what) { mSnapshot = null; mWatchable.dispatchChange(what); diff --git a/services/core/java/com/android/server/pm/SettingBase.java b/services/core/java/com/android/server/pm/SettingBase.java index 7924d5d48876..0e8a278f3b6b 100644 --- a/services/core/java/com/android/server/pm/SettingBase.java +++ b/services/core/java/com/android/server/pm/SettingBase.java @@ -49,6 +49,7 @@ public abstract class SettingBase implements Watchable { * * @param observer The {@link Watcher} to be notified when the {@link Watchable} changes. */ + @Override public void registerObserver(@NonNull Watcher observer) { mWatchable.registerObserver(observer); } @@ -59,20 +60,33 @@ public abstract class SettingBase implements Watchable { * * @param observer The {@link Watcher} that should not be in the notification list. */ + @Override public void unregisterObserver(@NonNull Watcher observer) { mWatchable.unregisterObserver(observer); } /** + * Return true if the {@link Watcher) is a registered observer. + * @param observer A {@link Watcher} that might be registered + * @return true if the observer is registered with this {@link Watchable}. + */ + @Override + public boolean isRegisteredObserver(@NonNull Watcher observer) { + return mWatchable.isRegisteredObserver(observer); + } + + /** * Invokes {@link Watcher#onChange} on each registered observer. The method can be called * with the {@link Watchable} that generated the event. In a tree of {@link Watchable}s, this * is generally the first (deepest) {@link Watchable} to detect a change. * * @param what The {@link Watchable} that generated the event. */ + @Override public void dispatchChange(@Nullable Watchable what) { mWatchable.dispatchChange(what); } + /** * Notify listeners that this object has changed. */ diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 2d41a687833f..50c1065a6000 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -192,6 +192,16 @@ public final class Settings implements Watchable, Snappable { } /** + * Return true if the {@link Watcher) is a registered observer. + * @param observer A {@link Watcher} that might be registered + * @return true if the observer is registered with this {@link Watchable}. + */ + @Override + public boolean isRegisteredObserver(@NonNull Watcher observer) { + return mWatchable.isRegisteredObserver(observer); + } + + /** * Invokes {@link Watcher#onChange} on each registered observer. The method can be called * with the {@link Watchable} that generated the event. In a tree of {@link Watchable}s, this * is generally the first (deepest) {@link Watchable} to detect a change. @@ -544,6 +554,8 @@ public final class Settings implements Watchable, Snappable { mRenamedPackages.registerObserver(mObserver); mDefaultBrowserApp.registerObserver(mObserver); mNextAppLinkGeneration.registerObserver(mObserver); + + Watchable.verifyWatchedAttributes(this, mObserver); } Settings(File dataDir, RuntimePermissionsPersistence runtimePermissionsPersistence, @@ -574,7 +586,6 @@ public final class Settings implements Watchable, Snappable { mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml"); mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml"); - mPackages.registerObserver(mObserver); mInstallerPackages.registerObserver(mObserver); mKernelMapping.registerObserver(mObserver); @@ -591,6 +602,8 @@ public final class Settings implements Watchable, Snappable { mRenamedPackages.registerObserver(mObserver); mDefaultBrowserApp.registerObserver(mObserver); mNextAppLinkGeneration.registerObserver(mObserver); + + Watchable.verifyWatchedAttributes(this, mObserver); } /** diff --git a/services/core/java/com/android/server/utils/Watchable.java b/services/core/java/com/android/server/utils/Watchable.java index 7c99274f3df2..f936693bd621 100644 --- a/services/core/java/com/android/server/utils/Watchable.java +++ b/services/core/java/com/android/server/utils/Watchable.java @@ -18,6 +18,10 @@ package com.android.server.utils; import android.annotation.NonNull; import android.annotation.Nullable; +import android.os.Build; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; /** * Notify registered {@link Watcher}s when the content changes. @@ -41,6 +45,13 @@ public interface Watchable { public void unregisterObserver(@NonNull Watcher observer); /** + * Return true if the {@link Watcher) is a registered observer. + * @param observer A {@link Watcher} that might be registered + * @return true if the observer is registered with this {@link Watchable}. + */ + public boolean isRegisteredObserver(@NonNull Watcher observer); + + /** * Invokes {@link Watcher#onChange} on each registered observer. The method can be called * with the {@link Watchable} that generated the event. In a tree of {@link Watchable}s, this * is generally the first (deepest) {@link Watchable} to detect a change. @@ -48,4 +59,42 @@ public interface Watchable { * @param what The {@link Watchable} that generated the event. */ public void dispatchChange(@Nullable Watchable what); + + /** + * Return true if the field is tagged with @Watched + */ + private static boolean isWatched(Field f) { + for (Annotation a : f.getDeclaredAnnotations()) { + if (a.annotationType().equals(Watched.class)) { + return true; + } + } + return false; + } + + /** + * Verify that all @Watched {@link Watchable} attributes are being watched by this + * class. This requires reflection and only runs in engineering or user debug + * builds. + */ + static void verifyWatchedAttributes(Object base, Watcher observer) { + if (Build.IS_ENG || Build.IS_USERDEBUG) { + for (Field f : base.getClass().getDeclaredFields()) { + try { + final boolean flagged = isWatched(f); + final Object o = f.get(base); + final boolean watchable = o instanceof Watchable; + if (flagged && watchable) { + Watchable attr = (Watchable) f.get(base); + if (attr != null && !attr.isRegisteredObserver(observer)) { + throw new RuntimeException(f.getName() + " missing an observer"); + } + } + } catch (IllegalAccessException e) { + // The field is protected; ignore it. Other exceptions that may be thrown by + // Field.get() are allowed to roll up. + } + } + } + } } diff --git a/services/core/java/com/android/server/utils/WatchableImpl.java b/services/core/java/com/android/server/utils/WatchableImpl.java index d17fca1d7a54..527db5402e74 100644 --- a/services/core/java/com/android/server/utils/WatchableImpl.java +++ b/services/core/java/com/android/server/utils/WatchableImpl.java @@ -66,6 +66,18 @@ public class WatchableImpl implements Watchable { } /** + * Return true if the {@link Watcher) is a registered observer. + * @param observer A {@link Watcher} that might be registered + * @return true if the observer is registered with this {@link Watchable}. + */ + @Override + public boolean isRegisteredObserver(@NonNull Watcher observer) { + synchronized (mObservers) { + return mObservers.contains(observer); + } + } + + /** * Return the number of registered observers. * * @return The number of registered observers. diff --git a/services/core/java/com/android/server/utils/Watched.java b/services/core/java/com/android/server/utils/Watched.java index d4a68ee735fd..4340d015119a 100644 --- a/services/core/java/com/android/server/utils/Watched.java +++ b/services/core/java/com/android/server/utils/Watched.java @@ -27,6 +27,6 @@ import java.lang.annotation.Target; * TODO(b/176923052) Automate validation of @Watchable attributes. */ @Target({ ElementType.FIELD }) -@Retention(RetentionPolicy.CLASS) +@Retention(RetentionPolicy.RUNTIME) public @interface Watched { } |