diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 5d4fa1e1c123d..587e625e053f6 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -5940,6 +5940,21 @@ public class PackageManagerService extends IPackageManager.Stub } } + // Link watchables to the class + private void registerObserver() { + mPackages.registerObserver(mWatcher); + mSharedLibraries.registerObserver(mWatcher); + mStaticLibsByDeclaringPackage.registerObserver(mWatcher); + mInstrumentation.registerObserver(mWatcher); + mWebInstantAppsDisabled.registerObserver(mWatcher); + mAppsFilter.registerObserver(mWatcher); + mInstantAppRegistry.registerObserver(mWatcher); + mSettings.registerObserver(mWatcher); + // If neither "build" attribute is true then this may be a mockito test, and verification + // can fail as a false positive. + Watchable.verifyWatchedAttributes(this, mWatcher, !(mIsEngBuild || mIsUserDebugBuild)); + } + /** * A extremely minimal constructor designed to start up a PackageManagerService instance for * testing. @@ -6023,15 +6038,7 @@ public class PackageManagerService extends IPackageManager.Stub sSnapshotCorked = true; mLiveComputer = createLiveComputer(); mSnapshotComputer = mLiveComputer; - - // Link up the watchers - mPackages.registerObserver(mWatcher); - mSharedLibraries.registerObserver(mWatcher); - mStaticLibsByDeclaringPackage.registerObserver(mWatcher); - mInstrumentation.registerObserver(mWatcher); - mWebInstantAppsDisabled.registerObserver(mWatcher); - mAppsFilter.registerObserver(mWatcher); - Watchable.verifyWatchedAttributes(this, mWatcher); + registerObserver(); mPackages.putAll(testParams.packages); mEnableFreeCacheV2 = testParams.enableFreeCacheV2; @@ -6185,15 +6192,6 @@ public class PackageManagerService extends IPackageManager.Stub mDomainVerificationManager = injector.getDomainVerificationManagerInternal(); mDomainVerificationManager.setConnection(mDomainVerificationConnection); - // Link up the watchers - mPackages.registerObserver(mWatcher); - mSharedLibraries.registerObserver(mWatcher); - mStaticLibsByDeclaringPackage.registerObserver(mWatcher); - mInstrumentation.registerObserver(mWatcher); - mWebInstantAppsDisabled.registerObserver(mWatcher); - mAppsFilter.registerObserver(mWatcher); - Watchable.verifyWatchedAttributes(this, mWatcher); - // Create the computer as soon as the state objects have been installed. The // cached computer is the same as the live computer until the end of the // constructor, at which time the invalidation method updates it. The cache is @@ -6202,6 +6200,7 @@ public class PackageManagerService extends IPackageManager.Stub sSnapshotCorked = true; mLiveComputer = createLiveComputer(); mSnapshotComputer = mLiveComputer; + registerObserver(); // CHECKSTYLE:OFF IndentationCheck synchronized (mInstallLock) { diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index fb033e6594b8d..b8040ec98fda8 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -105,9 +105,6 @@ import com.android.permission.persistence.RuntimePermissionsState; import com.android.server.LocalServices; import com.android.server.backup.PreferredActivityBackupHelper; import com.android.server.pm.Installer.InstallerException; -import com.android.server.pm.verify.domain.DomainVerificationLegacySettings; -import com.android.server.pm.verify.domain.DomainVerificationManagerInternal; -import com.android.server.pm.verify.domain.DomainVerificationPersistence; import com.android.server.pm.parsing.PackageInfoUtils; import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.pm.parsing.pkg.AndroidPackageUtils; @@ -115,6 +112,9 @@ import com.android.server.pm.permission.LegacyPermissionDataProvider; import com.android.server.pm.permission.LegacyPermissionSettings; import com.android.server.pm.permission.LegacyPermissionState; import com.android.server.pm.permission.LegacyPermissionState.PermissionState; +import com.android.server.pm.verify.domain.DomainVerificationLegacySettings; +import com.android.server.pm.verify.domain.DomainVerificationManagerInternal; +import com.android.server.pm.verify.domain.DomainVerificationPersistence; import com.android.server.utils.Snappable; import com.android.server.utils.TimingsTraceAndSlog; import com.android.server.utils.Watchable; @@ -487,7 +487,7 @@ public final class Settings implements Watchable, Snappable { // App-link priority tracking, per-user @NonNull @Watched - final WatchedSparseIntArray mNextAppLinkGeneration = new WatchedSparseIntArray(); + private final WatchedSparseIntArray mNextAppLinkGeneration = new WatchedSparseIntArray(); final StringBuilder mReadMessages = new StringBuilder(); @@ -552,6 +552,7 @@ public final class Settings implements Watchable, Snappable { mAppIds.registerObserver(mObserver); mOtherAppIds.registerObserver(mObserver); mRenamedPackages.registerObserver(mObserver); + mNextAppLinkGeneration.registerObserver(mObserver); mDefaultBrowserApp.registerObserver(mObserver); Watchable.verifyWatchedAttributes(this, mObserver); @@ -602,6 +603,7 @@ public final class Settings implements Watchable, Snappable { mAppIds.registerObserver(mObserver); mOtherAppIds.registerObserver(mObserver); mRenamedPackages.registerObserver(mObserver); + mNextAppLinkGeneration.registerObserver(mObserver); mDefaultBrowserApp.registerObserver(mObserver); Watchable.verifyWatchedAttributes(this, mObserver); @@ -649,6 +651,7 @@ public final class Settings implements Watchable, Snappable { mPastSignatures.addAll(r.mPastSignatures); mKeySetRefs.putAll(r.mKeySetRefs); mRenamedPackages.snapshot(r.mRenamedPackages); + mNextAppLinkGeneration.snapshot(r.mNextAppLinkGeneration); mDefaultBrowserApp.snapshot(r.mDefaultBrowserApp); // mReadMessages mPendingPackages.addAll(r.mPendingPackages); diff --git a/services/core/java/com/android/server/utils/Watchable.java b/services/core/java/com/android/server/utils/Watchable.java index f936693bd621f..44a74593cda2b 100644 --- a/services/core/java/com/android/server/utils/Watchable.java +++ b/services/core/java/com/android/server/utils/Watchable.java @@ -19,8 +19,8 @@ package com.android.server.utils; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Build; +import android.util.Log; -import java.lang.annotation.Annotation; import java.lang.reflect.Field; /** @@ -60,41 +60,55 @@ public interface Watchable { */ 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. + * @param base The object that contains watched attributes. + * @param observer The {@link Watcher} that should be watching these attributes. + * @param logOnly If true then log errors; if false then throw an RuntimeExecption on error. */ - static void verifyWatchedAttributes(Object base, Watcher observer) { - if (Build.IS_ENG || Build.IS_USERDEBUG) { - for (Field f : base.getClass().getDeclaredFields()) { + static void verifyWatchedAttributes(Object base, Watcher observer, boolean logOnly) { + if (!(Build.IS_ENG || Build.IS_USERDEBUG)) { + return; + } + for (Field f : base.getClass().getDeclaredFields()) { + if (f.getAnnotation(Watched.class) != null) { + final String fn = base.getClass().getName() + "." + f.getName(); try { - final boolean flagged = isWatched(f); + f.setAccessible(true); final Object o = f.get(base); - final boolean watchable = o instanceof Watchable; - if (flagged && watchable) { - Watchable attr = (Watchable) f.get(base); + if (o instanceof Watchable) { + Watchable attr = (Watchable) (o); if (attr != null && !attr.isRegisteredObserver(observer)) { - throw new RuntimeException(f.getName() + " missing an observer"); + if (logOnly) { + Log.e("Watchable", fn + " missing an observer"); + } else { + throw new RuntimeException("Watchable " + fn + + " 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. + if (logOnly) { + Log.e("Watchable", fn + " not visible"); + } else { + throw new RuntimeException("Watchable " + fn + " not visible"); + } } } } } + + /** + * Verify that all @Watched {@link Watchable} attributes are being watched by this + * class. This calls verifyWatchedAttributes() with logOnly set to false. + * @param base The object that contains watched attributes. + * @param observer The {@link Watcher} that should be watching these attributes. + */ + static void verifyWatchedAttributes(Object base, Watcher observer) { + verifyWatchedAttributes(base, observer, false); + } }