Merge "Register PackageChangedReceiver to AppSearchManagerService." into sc-dev am: 7ce25ed80e

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/14210825

Change-Id: If33ff86b8e8a70b1ee304114574fc7d5ec7d1299
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
index 7ca77d4..3f6e8a5 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -16,6 +16,7 @@
 package com.android.server.appsearch;
 
 import static android.app.appsearch.AppSearchResult.throwableToFailedResult;
+import static android.os.Process.INVALID_UID;
 import static android.os.UserHandle.USER_NULL;
 
 import android.annotation.ElapsedRealtimeLong;
@@ -53,7 +54,6 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
-import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.server.LocalServices;
@@ -128,6 +128,17 @@
         mContext.registerReceiverAsUser(new UserActionReceiver(), UserHandle.ALL,
                 new IntentFilter(Intent.ACTION_USER_REMOVED), /*broadcastPermission=*/ null,
                 /*scheduler=*/ null);
+
+        //TODO(b/145759910) Add a direct callback when user clears the data instead of relying on
+        // broadcasts
+        IntentFilter packageChangedFilter = new IntentFilter();
+        packageChangedFilter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
+        packageChangedFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
+        packageChangedFilter.addDataScheme("package");
+        packageChangedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+        mContext.registerReceiverAsUser(new PackageChangedReceiver(), UserHandle.ALL,
+                packageChangedFilter, /*broadcastPermission=*/ null,
+                /*scheduler=*/ null);
     }
 
     private class UserActionReceiver extends BroadcastReceiver {
@@ -135,15 +146,15 @@
         public void onReceive(@NonNull Context context, @NonNull Intent intent) {
             switch (intent.getAction()) {
                 case Intent.ACTION_USER_REMOVED:
-                    final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
+                    int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
                     if (userId == USER_NULL) {
-                        Slog.e(TAG, "userId is missing in the intent: " + intent);
+                        Log.e(TAG, "userId is missing in the intent: " + intent);
                         return;
                     }
                     handleUserRemoved(userId);
                     break;
                 default:
-                    Slog.e(TAG, "Received unknown intent: " + intent);
+                    Log.e(TAG, "Received unknown intent: " + intent);
             }
         }
     }
@@ -163,9 +174,52 @@
         try {
             mImplInstanceManager.removeAppSearchImplForUser(userId);
             mLoggerInstanceManager.removePlatformLoggerForUser(userId);
-            Slog.i(TAG, "Removed AppSearchImpl instance for user: " + userId);
+            Log.i(TAG, "Removed AppSearchImpl instance for user: " + userId);
         } catch (Throwable t) {
-            Slog.e(TAG, "Unable to remove data for user: " + userId, t);
+            Log.e(TAG, "Unable to remove data for user: " + userId, t);
+        }
+    }
+
+    private class PackageChangedReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(@NonNull Context context, @NonNull Intent intent) {
+            switch (intent.getAction()) {
+                case Intent.ACTION_PACKAGE_FULLY_REMOVED:
+                case Intent.ACTION_PACKAGE_DATA_CLEARED:
+                    String packageName = intent.getData().getSchemeSpecificPart();
+                    if (packageName == null) {
+                        Log.e(TAG, "Package name is missing in the intent: " + intent);
+                        return;
+                    }
+                    int uid = intent.getIntExtra(Intent.EXTRA_UID, INVALID_UID);
+                    if (uid == INVALID_UID) {
+                        Log.e(TAG, "uid is missing in the intent: " + intent);
+                        return;
+                    }
+                    handlePackageRemoved(packageName, uid);
+                    break;
+                default:
+                    Log.e(TAG, "Received unknown intent: " + intent);
+            }
+        }
+    }
+
+    private void handlePackageRemoved(String packageName, int uid) {
+        int userId = UserHandle.getUserId(uid);
+        try {
+            if (isUserLocked(userId)) {
+                //TODO(b/186151459) clear the uninstalled package data when user is unlocked.
+                return;
+            }
+            if (ImplInstanceManager.getAppSearchDir(userId).exists()) {
+                // Only clear the package's data if AppSearch exists for this user.
+                AppSearchImpl impl = mImplInstanceManager.getOrCreateAppSearchImpl(mContext,
+                        userId);
+                //TODO(b/145759910) clear visibility setting for package.
+                impl.clearPackageData(packageName);
+            }
+        } catch (Throwable t) {
+            Log.e(TAG, "Unable to remove data for package: " + packageName, t);
         }
     }
 
@@ -177,17 +231,20 @@
     }
 
     private void verifyUserUnlocked(int callingUserId) {
+        if (isUserLocked(callingUserId)) {
+            throw new IllegalStateException("User " + callingUserId + " is locked or not running.");
+        }
+    }
+
+    private boolean isUserLocked(int callingUserId) {
         synchronized (mUnlockedUserIdsLocked) {
             // First, check the local copy.
             if (mUnlockedUserIdsLocked.contains(callingUserId)) {
-                return;
+                return false;
             }
             // If the local copy says the user is locked, check with UM for the actual state,
             // since the user might just have been unlocked.
-            if (!mUserManager.isUserUnlockingOrUnlocked(UserHandle.of(callingUserId))) {
-                throw new IllegalStateException(
-                        "User " + callingUserId + " is locked or not running.");
-            }
+            return !mUserManager.isUserUnlockingOrUnlocked(UserHandle.of(callingUserId));
         }
     }
 
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
index af39b79..45023f9 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
@@ -73,6 +73,15 @@
     }
 
     /**
+     * Returns AppSearch directory in the credential encrypted system directory for the given user.
+     *
+     * <p>This folder should only be accessed after unlock.
+     */
+    public static File getAppSearchDir(@UserIdInt int userId) {
+        return new File(Environment.getDataSystemCeDirectory(userId), APP_SEARCH_DIR);
+    }
+
+    /**
      * Gets an instance of AppSearchImpl for the given user, or creates one if none exists.
      *
      * <p>If no AppSearchImpl instance exists for the unlocked user, Icing will be initialized and
@@ -139,15 +148,10 @@
 
     private AppSearchImpl createImpl(@NonNull Context context, @UserIdInt int userId)
             throws AppSearchException {
-        File appSearchDir = getAppSearchDir(context, userId);
+        File appSearchDir = getAppSearchDir(userId);
         return AppSearchImpl.create(appSearchDir, context, userId, mGlobalQuerierPackage);
     }
 
-    private static File getAppSearchDir(@NonNull Context context, @UserIdInt int userId) {
-        // See com.android.internal.app.ChooserActivity::getPinnedSharedPrefs
-        return new File(Environment.getDataSystemCeDirectory(userId), APP_SEARCH_DIR);
-    }
-
     /**
      * Returns the global querier package if it's a system package. Otherwise, empty string.
      *