Rebind to the remote service if AppPrediction is restarted or updated

Rebind to the remote AppPredictionService when the service package is
updated. Also adds new restart policy flags in
AbstractMasterSystemService to be able to keep per-user instances in the
cache when service restarts as a result of package data getting cleared,
so that we can revive the existing sessions. This would potentially fix
the issue for other services that try to keep the sessions over data
resets.

Bug: 139445447
Test: Reinstall the AiAi package and verify that suggested apps in launcher are getting updated
Test: Clear data for Device Personalization Services and verify that suggested apps in launcher are getting updated
Test: atest CtsAppPredictionServiceTestCases CtsContentCaptureServiceTestCases

Change-Id: Ie835cb159faa9c0718cd63a3045251444b89e505
diff --git a/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java b/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java
index d7e68f8..5844f98 100644
--- a/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java
+++ b/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java
@@ -23,6 +23,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.app.prediction.AppPredictionContext;
 import android.app.prediction.AppPredictionSessionId;
 import android.app.prediction.AppTargetEvent;
@@ -61,7 +62,8 @@
 
     public AppPredictionManagerService(Context context) {
         super(context, new FrameworkResourcesServiceNameResolver(context,
-                com.android.internal.R.string.config_defaultAppPredictionService), null);
+                com.android.internal.R.string.config_defaultAppPredictionService), null,
+                PACKAGE_UPDATE_POLICY_NO_REFRESH | PACKAGE_RESTART_POLICY_NO_REFRESH);
         mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
     }
 
@@ -80,6 +82,22 @@
         getContext().enforceCallingPermission(MANAGE_APP_PREDICTIONS, TAG);
     }
 
+    @Override // from AbstractMasterSystemService
+    protected void onServicePackageUpdatedLocked(@UserIdInt int userId) {
+        final AppPredictionPerUserService service = peekServiceForUserLocked(userId);
+        if (service != null) {
+            service.onPackageUpdatedLocked();
+        }
+    }
+
+    @Override // from AbstractMasterSystemService
+    protected void onServicePackageRestartedLocked(@UserIdInt int userId) {
+        final AppPredictionPerUserService service = peekServiceForUserLocked(userId);
+        if (service != null) {
+            service.onPackageRestartedLocked();
+        }
+    }
+
     @Override
     protected int getMaximumTemporaryServiceDurationMs() {
         return MAX_TEMP_SERVICE_DURATION_MS;
diff --git a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
index 03c4542..4f49fb75 100644
--- a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
+++ b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
@@ -251,6 +251,40 @@
         // Do nothing, eventually the system will bind to the remote service again...
     }
 
+    void onPackageUpdatedLocked() {
+        if (isDebug()) {
+            Slog.v(TAG, "onPackageUpdatedLocked()");
+        }
+        destroyAndRebindRemoteService();
+    }
+
+    void onPackageRestartedLocked() {
+        if (isDebug()) {
+            Slog.v(TAG, "onPackageRestartedLocked()");
+        }
+        destroyAndRebindRemoteService();
+    }
+
+    private void destroyAndRebindRemoteService() {
+        if (mRemoteService == null) {
+            return;
+        }
+
+        if (isDebug()) {
+            Slog.d(TAG, "Destroying the old remote service.");
+        }
+        mRemoteService.destroy();
+        mRemoteService = null;
+
+        mRemoteService = getRemoteServiceLocked();
+        if (mRemoteService != null) {
+            if (isDebug()) {
+                Slog.d(TAG, "Rebinding to the new remote service.");
+            }
+            mRemoteService.reconnect();
+        }
+    }
+
     /**
      * Called after the remote service connected, it's used to restore state from a 'zombie'
      * service (i.e., after it died).
diff --git a/services/appprediction/java/com/android/server/appprediction/RemoteAppPredictionService.java b/services/appprediction/java/com/android/server/appprediction/RemoteAppPredictionService.java
index c82e7a0..04e0e7f 100644
--- a/services/appprediction/java/com/android/server/appprediction/RemoteAppPredictionService.java
+++ b/services/appprediction/java/com/android/server/appprediction/RemoteAppPredictionService.java
@@ -135,6 +135,13 @@
     }
 
     /**
+     * Schedules a request to bind to the remote service.
+     */
+    public void reconnect() {
+        super.scheduleBind();
+    }
+
+    /**
      * Failure callback
      */
     public interface RemoteAppPredictionServiceCallbacks
diff --git a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
index 9782f30..259527a 100644
--- a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
@@ -79,7 +79,7 @@
         S extends AbstractPerUserSystemService<S, M>> extends SystemService {
 
     /** On a package update, does not refresh the per-user service in the cache. */
-    public static final int PACKAGE_UPDATE_POLICY_NO_REFRESH = 0;
+    public static final int PACKAGE_UPDATE_POLICY_NO_REFRESH = 0x00000001;
 
     /**
      * On a package update, removes any existing per-user services in the cache.
@@ -87,20 +87,40 @@
      * <p>This does not immediately recreate these services. It is assumed they will be recreated
      * for the next user request.
      */
-    public static final int PACKAGE_UPDATE_POLICY_REFRESH_LAZY = 1;
+    public static final int PACKAGE_UPDATE_POLICY_REFRESH_LAZY = 0x00000002;
 
     /**
      * On a package update, removes and recreates any existing per-user services in the cache.
      */
-    public static final int PACKAGE_UPDATE_POLICY_REFRESH_EAGER = 2;
+    public static final int PACKAGE_UPDATE_POLICY_REFRESH_EAGER = 0x00000004;
 
-    @IntDef(flag = true, prefix = { "PACKAGE_UPDATE_POLICY_" }, value = {
+    /** On a package restart, does not refresh the per-user service in the cache. */
+    public static final int PACKAGE_RESTART_POLICY_NO_REFRESH = 0x00000010;
+
+    /**
+     * On a package restart, removes any existing per-user services in the cache.
+     *
+     * <p>This does not immediately recreate these services. It is assumed they will be recreated
+     * for the next user request.
+     */
+    public static final int PACKAGE_RESTART_POLICY_REFRESH_LAZY = 0x00000020;
+
+    /**
+     * On a package restart, removes and recreates any existing per-user services in the cache.
+     */
+    public static final int PACKAGE_RESTART_POLICY_REFRESH_EAGER = 0x00000040;
+
+    @IntDef(flag = true, prefix = { "PACKAGE_" }, value = {
             PACKAGE_UPDATE_POLICY_NO_REFRESH,
             PACKAGE_UPDATE_POLICY_REFRESH_LAZY,
-            PACKAGE_UPDATE_POLICY_REFRESH_EAGER
+            PACKAGE_UPDATE_POLICY_REFRESH_EAGER,
+            PACKAGE_RESTART_POLICY_NO_REFRESH,
+            PACKAGE_RESTART_POLICY_REFRESH_LAZY,
+            PACKAGE_RESTART_POLICY_REFRESH_EAGER
     })
+
     @Retention(RetentionPolicy.SOURCE)
-    public @interface PackageUpdatePolicy {}
+    public @interface ServicePackagePolicyFlags {}
 
     /**
      * Log tag
@@ -153,12 +173,10 @@
     private final SparseArray<S> mServicesCache = new SparseArray<>();
 
     /**
-     * Whether the per-user service should be removed from the cache when its apk is updated.
-     *
-     * <p>One of {@link #PACKAGE_UPDATE_POLICY_NO_REFRESH},
-     * {@link #PACKAGE_UPDATE_POLICY_REFRESH_LAZY} or {@link #PACKAGE_UPDATE_POLICY_REFRESH_EAGER}.
+     * Value that determines whether the per-user service should be removed from the cache when its
+     * apk is updated or restarted.
      */
-    private final @PackageUpdatePolicy int mPackageUpdatePolicy;
+    private final @ServicePackagePolicyFlags int mServicePackagePolicyFlags;
 
     /**
      * Name of the service packages whose APK are being updated, keyed by user id.
@@ -184,11 +202,11 @@
             @Nullable ServiceNameResolver serviceNameResolver,
             @Nullable String disallowProperty) {
         this(context, serviceNameResolver, disallowProperty,
-                /*packageUpdatePolicy=*/ PACKAGE_UPDATE_POLICY_REFRESH_LAZY);
+                PACKAGE_UPDATE_POLICY_REFRESH_LAZY | PACKAGE_RESTART_POLICY_REFRESH_LAZY);
     }
 
     /**
-     * Full constructor.
+     * Full Constructor.
      *
      * @param context system context.
      * @param serviceNameResolver resolver for
@@ -197,19 +215,32 @@
      * @param disallowProperty when not {@code null}, defines a {@link UserManager} restriction that
      *        disables the service. <b>NOTE: </b> you'll also need to add it to
      *        {@code UserRestrictionsUtils.USER_RESTRICTIONS}.
-     * @param packageUpdatePolicy when {@link #PACKAGE_UPDATE_POLICY_REFRESH_LAZY}, the
-     *        {@link AbstractPerUserSystemService} is removed from the cache when the service
-     *        package is updated; when {@link #PACKAGE_UPDATE_POLICY_REFRESH_EAGER}, the
-     *        {@link AbstractPerUserSystemService} is removed from the cache and immediately
-     *        re-added when the service package is updated; when
-     *        {@link #PACKAGE_UPDATE_POLICY_NO_REFRESH}, the service is untouched during the update.
+     * @param servicePackagePolicyFlags a combination of
+     *        {@link #PACKAGE_UPDATE_POLICY_NO_REFRESH},
+     *        {@link #PACKAGE_UPDATE_POLICY_REFRESH_LAZY},
+     *        {@link #PACKAGE_UPDATE_POLICY_REFRESH_EAGER},
+     *        {@link #PACKAGE_RESTART_POLICY_NO_REFRESH},
+     *        {@link #PACKAGE_RESTART_POLICY_REFRESH_LAZY} or
+     *        {@link #PACKAGE_RESTART_POLICY_REFRESH_EAGER}
      */
     protected AbstractMasterSystemService(@NonNull Context context,
-            @Nullable ServiceNameResolver serviceNameResolver,
-            @Nullable String disallowProperty, @PackageUpdatePolicy int packageUpdatePolicy) {
+            @Nullable ServiceNameResolver serviceNameResolver, @Nullable String disallowProperty,
+            @ServicePackagePolicyFlags int servicePackagePolicyFlags) {
         super(context);
 
-        mPackageUpdatePolicy = packageUpdatePolicy;
+        final int updatePolicyMask = PACKAGE_UPDATE_POLICY_NO_REFRESH
+                | PACKAGE_UPDATE_POLICY_REFRESH_LAZY | PACKAGE_UPDATE_POLICY_REFRESH_EAGER;
+        if ((servicePackagePolicyFlags & updatePolicyMask) == 0) {
+            // If the package update policy is not set, add the default flag
+            servicePackagePolicyFlags |= PACKAGE_UPDATE_POLICY_REFRESH_LAZY;
+        }
+        final int restartPolicyMask = PACKAGE_RESTART_POLICY_NO_REFRESH
+                | PACKAGE_RESTART_POLICY_REFRESH_LAZY | PACKAGE_RESTART_POLICY_REFRESH_EAGER;
+        if ((servicePackagePolicyFlags & restartPolicyMask) == 0) {
+            // If the package restart policy is not set, add the default flag
+            servicePackagePolicyFlags |= PACKAGE_RESTART_POLICY_REFRESH_LAZY;
+        }
+        mServicePackagePolicyFlags = servicePackagePolicyFlags;
 
         mServiceNameResolver = serviceNameResolver;
         if (mServiceNameResolver != null) {
@@ -606,6 +637,20 @@
     }
 
     /**
+     * Called after the package data that provides the service for the given user is cleared.
+     */
+    protected void onServicePackageDataClearedLocked(@UserIdInt int userId) {
+        if (verbose) Slog.v(mTag, "onServicePackageDataCleared(" + userId + ")");
+    }
+
+    /**
+     * Called after the package that provides the service for the given user is restarted.
+     */
+    protected void onServicePackageRestartedLocked(@UserIdInt int userId) {
+        if (verbose) Slog.v(mTag, "onServicePackageRestarted(" + userId + ")");
+    }
+
+    /**
      * Called after the service is removed from the cache.
      */
     @SuppressWarnings("unused")
@@ -677,7 +722,7 @@
             final int size = mServicesCache.size();
             pw.print(prefix); pw.print("Debug: "); pw.print(realDebug);
             pw.print(" Verbose: "); pw.println(realVerbose);
-            pw.print("Refresh on package update: "); pw.println(mPackageUpdatePolicy);
+            pw.print("Package policy flags: "); pw.println(mServicePackagePolicyFlags);
             if (mUpdatingPackageNames != null) {
                 pw.print("Packages being updated: "); pw.println(mUpdatingPackageNames);
             }
@@ -733,7 +778,12 @@
                     }
                     mUpdatingPackageNames.put(userId, packageName);
                     onServicePackageUpdatingLocked(userId);
-                    if (mPackageUpdatePolicy != PACKAGE_UPDATE_POLICY_NO_REFRESH) {
+                    if ((mServicePackagePolicyFlags & PACKAGE_UPDATE_POLICY_NO_REFRESH) != 0) {
+                        if (debug) {
+                            Slog.d(mTag, "Holding service for user " + userId + " while package "
+                                    + activePackageName + " is being updated");
+                        }
+                    } else {
                         if (debug) {
                             Slog.d(mTag, "Removing service for user " + userId
                                     + " because package " + activePackageName
@@ -741,18 +791,14 @@
                         }
                         removeCachedServiceLocked(userId);
 
-                        if (mPackageUpdatePolicy == PACKAGE_UPDATE_POLICY_REFRESH_EAGER) {
+                        if ((mServicePackagePolicyFlags & PACKAGE_UPDATE_POLICY_REFRESH_EAGER)
+                                != 0) {
                             if (debug) {
                                 Slog.d(mTag, "Eagerly recreating service for user "
                                         + userId);
                             }
                             getServiceForUserLocked(userId);
                         }
-                    } else {
-                        if (debug) {
-                            Slog.d(mTag, "Holding service for user " + userId + " while package "
-                                    + activePackageName + " is being updated");
-                        }
                     }
                 }
             }
@@ -804,7 +850,13 @@
                             if (!doit) {
                                 return true;
                             }
-                            removeCachedServiceLocked(getChangingUserId());
+                            final String action = intent.getAction();
+                            final int userId = getChangingUserId();
+                            if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) {
+                                handleActiveServiceRestartedLocked(activePackageName, userId);
+                            } else {
+                                removeCachedServiceLocked(userId);
+                            }
                         } else {
                             handlePackageUpdateLocked(pkg);
                         }
@@ -813,6 +865,23 @@
                 return false;
             }
 
+            @Override
+            public void onPackageDataCleared(String packageName, int uid) {
+                if (verbose) Slog.v(mTag, "onPackageDataCleared(): " + packageName);
+                final int userId = getChangingUserId();
+                synchronized (mLock) {
+                    final S service = peekServiceForUserLocked(userId);
+                    if (service != null) {
+                        final ComponentName componentName = service.getServiceComponentName();
+                        if (componentName != null) {
+                            if (packageName.equals(componentName.getPackageName())) {
+                                onServicePackageDataClearedLocked(userId);
+                            }
+                        }
+                    }
+                }
+            }
+
             private void handleActiveServiceRemoved(@UserIdInt int userId) {
                 synchronized (mLock) {
                     removeCachedServiceLocked(userId);
@@ -824,6 +893,31 @@
                 }
             }
 
+            private void handleActiveServiceRestartedLocked(String activePackageName,
+                    @UserIdInt int userId) {
+                if ((mServicePackagePolicyFlags & PACKAGE_RESTART_POLICY_NO_REFRESH) != 0) {
+                    if (debug) {
+                        Slog.d(mTag, "Holding service for user " + userId + " while package "
+                                + activePackageName + " is being restarted");
+                    }
+                } else {
+                    if (debug) {
+                        Slog.d(mTag, "Removing service for user " + userId
+                                + " because package " + activePackageName
+                                + " is being restarted");
+                    }
+                    removeCachedServiceLocked(userId);
+
+                    if ((mServicePackagePolicyFlags & PACKAGE_RESTART_POLICY_REFRESH_EAGER) != 0) {
+                        if (debug) {
+                            Slog.d(mTag, "Eagerly recreating service for user " + userId);
+                        }
+                        getServiceForUserLocked(userId);
+                    }
+                }
+                onServicePackageRestartedLocked(userId);
+            }
+
             private String getActiveServicePackageNameLocked() {
                 final int userId = getChangingUserId();
                 final S service = peekServiceForUserLocked(userId);