Move to non-hidden APIs for handleUser and broadcast registration.

The implementation of handleIncomingUser is copied from
ag/13200660 (1d1547b).

Bug: 181787682
Test: Presubmit
Change-Id: I946199a8844e7fd6ea5574b873da56d8b8630f62
diff --git a/service/java/com/android/server/appsearch/AppSearchManagerService.java b/service/java/com/android/server/appsearch/AppSearchManagerService.java
index 0c07fa0..0709ff5 100644
--- a/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -17,11 +17,10 @@
 
 import static android.app.appsearch.AppSearchResult.throwableToFailedResult;
 import static android.os.Process.INVALID_UID;
-import static android.os.UserHandle.USER_NULL;
 
+import android.Manifest;
 import android.annotation.ElapsedRealtimeLong;
 import android.annotation.NonNull;
-import android.app.ActivityManager;
 import android.app.appsearch.AppSearchBatchResult;
 import android.app.appsearch.AppSearchMigrationHelper;
 import android.app.appsearch.AppSearchResult;
@@ -63,6 +62,7 @@
 import com.android.server.appsearch.external.localstorage.stats.CallStats;
 import com.android.server.appsearch.stats.LoggerInstanceManager;
 import com.android.server.appsearch.stats.PlatformLogger;
+import com.android.server.appsearch.util.PackageUtil;
 import com.android.server.usage.StorageStatsManagerLocal;
 import com.android.server.usage.StorageStatsManagerLocal.StorageStatsAugmenter;
 
@@ -124,8 +124,10 @@
     }
 
     private void registerReceivers() {
-        mContext.registerReceiverAsUser(new UserActionReceiver(), UserHandle.ALL,
-                new IntentFilter(Intent.ACTION_USER_REMOVED), /*broadcastPermission=*/ null,
+        mContext.registerReceiverForAllUsers(
+                new UserActionReceiver(),
+                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
@@ -135,8 +137,10 @@
         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,
+        mContext.registerReceiverForAllUsers(
+                new PackageChangedReceiver(),
+                packageChangedFilter,
+                /*broadcastPermission=*/ null,
                 /*scheduler=*/ null);
     }
 
@@ -148,12 +152,13 @@
 
             switch (intent.getAction()) {
                 case Intent.ACTION_USER_REMOVED:
-                    int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
-                    if (userId == USER_NULL) {
-                        Log.e(TAG, "userId is missing in the intent: " + intent);
+                    UserHandle userHandle = intent.getParcelableExtra(Intent.EXTRA_USER);
+                    if (userHandle == null) {
+                        Log.e(TAG, "Extra "
+                                + Intent.EXTRA_USER + " is missing in the intent: " + intent);
                         return;
                     }
-                    handleUserRemoved(UserHandle.of(userId));
+                    handleUserRemoved(userHandle);
                     break;
                 default:
                     Log.e(TAG, "Received unknown intent: " + intent);
@@ -1183,13 +1188,9 @@
             Objects.requireNonNull(actualCallingUser);
             Objects.requireNonNull(claimedCallingPackage);
 
-            int claimedCallingUid;
-            try {
-                Context claimedCallingContext =
-                        mContext.createContextAsUser(actualCallingUser, /*flags=*/ 0);
-                claimedCallingUid = claimedCallingContext.getPackageManager().getPackageUid(
-                        claimedCallingPackage, /*flags=*/ 0);
-            } catch (PackageManager.NameNotFoundException e) {
+            int claimedCallingUid = PackageUtil.getPackageUidAsUser(
+                    mContext, claimedCallingPackage, actualCallingUser);
+            if (claimedCallingUid == INVALID_UID) {
                 throw new SecurityException(
                         "Specified calling package [" + claimedCallingPackage + "] not found");
             }
@@ -1257,23 +1258,44 @@
      *
      * <p>Takes care of checking permissions and converting USER_CURRENT to the actual current user.
      *
+     * @param requestedUser The user which the caller is requesting to execute as.
+     * @param callingUid The actual uid of the caller as determined by Binder.
      * @return the user handle that the call should run as. Will always be a concrete user.
      */
     // TODO(b/173553485) verifying that the caller has permission to access target user's data
     // TODO(b/173553485) Handle ACTION_USER_REMOVED broadcast
     // TODO(b/173553485) Implement SystemService.onUserStopping()
     @NonNull
-    private static UserHandle handleIncomingUser(@NonNull UserHandle userHandle, int callingUid) {
+    private UserHandle handleIncomingUser(@NonNull UserHandle requestedUser, int callingUid) {
         int callingPid = Binder.getCallingPid();
-        int finalUserId = ActivityManager.handleIncomingUser(
+        UserHandle callingUser = UserHandle.getUserHandleForUid(callingUid);
+        if (callingUser.equals(requestedUser)) {
+            return requestedUser;
+        }
+        // Duplicates UserController#ensureNotSpecialUser
+        if (requestedUser.getIdentifier() < 0) {
+            throw new IllegalArgumentException(
+                    "Call does not support special user " + requestedUser);
+        }
+        boolean canInteractAcrossUsers = mContext.checkPermission(
+                Manifest.permission.INTERACT_ACROSS_USERS,
                 callingPid,
-                callingUid,
-                userHandle.getIdentifier(),
-                /*allowAll=*/ false,
-                /*requireFull=*/ false,
-                /*name=*/ null,
-                /*callerPackage=*/ null);
-        return UserHandle.of(finalUserId);
+                callingUid) == PackageManager.PERMISSION_GRANTED;
+        if (!canInteractAcrossUsers) {
+            canInteractAcrossUsers = mContext.checkPermission(
+                    Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+                    callingPid,
+                    callingUid) == PackageManager.PERMISSION_GRANTED;
+        }
+        if (canInteractAcrossUsers) {
+            return requestedUser;
+        }
+        throw new SecurityException(
+                "Permission denied while calling from uid " + callingUid
+                        + " with " + requestedUser + "; Need to run as either the calling user ("
+                        + callingUser + "), or with one of the following permissions: "
+                        + Manifest.permission.INTERACT_ACROSS_USERS + " or "
+                        + Manifest.permission.INTERACT_ACROSS_USERS_FULL);
     }
 
     // TODO(b/179160886): Cache the previous storage stats.
diff --git a/service/java/com/android/server/appsearch/ImplInstanceManager.java b/service/java/com/android/server/appsearch/ImplInstanceManager.java
index beb4d24..0775272 100644
--- a/service/java/com/android/server/appsearch/ImplInstanceManager.java
+++ b/service/java/com/android/server/appsearch/ImplInstanceManager.java
@@ -172,11 +172,6 @@
             @Nullable AppSearchLogger logger)
             throws AppSearchException {
         File appSearchDir = getAppSearchDir(userHandle);
-        // TODO(b/181787682): Swap AppSearchImpl and VisibilityStore to accept a UserHandle too
-        return AppSearchImpl.create(
-                appSearchDir,
-                userContext,
-                userHandle.getIdentifier(),
-                /*logger=*/ null);
+        return AppSearchImpl.create(appSearchDir, userContext, /*logger=*/ null);
     }
 }
diff --git a/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java b/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
index a940dde..29cb57c 100644
--- a/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
+++ b/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
@@ -201,7 +201,6 @@
     public static AppSearchImpl create(
             @NonNull File icingDir,
             @NonNull Context userContext,
-            int userId,
             @Nullable AppSearchLogger logger)
             throws AppSearchException {
         Objects.requireNonNull(icingDir);
@@ -213,8 +212,7 @@
             initStatsBuilder = new InitializeStats.Builder();
         }
 
-        AppSearchImpl appSearchImpl =
-                new AppSearchImpl(icingDir, userContext, userId, initStatsBuilder);
+        AppSearchImpl appSearchImpl = new AppSearchImpl(icingDir, userContext, initStatsBuilder);
 
         long prepareVisibilityStoreLatencyStartMillis = SystemClock.elapsedRealtime();
         appSearchImpl.initializeVisibilityStore();
@@ -238,7 +236,6 @@
     private AppSearchImpl(
             @NonNull File icingDir,
             @NonNull Context userContext,
-            int userId,
             @Nullable InitializeStats.Builder initStatsBuilder)
             throws AppSearchException {
         mReadWriteLock.writeLock().lock();
@@ -256,7 +253,7 @@
                     "Constructing IcingSearchEngine, response",
                     Objects.hashCode(mIcingSearchEngineLocked));
 
-            mVisibilityStoreLocked = new VisibilityStore(this, userContext, userId);
+            mVisibilityStoreLocked = new VisibilityStore(this, userContext);
 
             // The core initialization procedure. If any part of this fails, we bail into
             // resetLocked(), deleting all data (but hopefully allowing AppSearchImpl to come up).
diff --git a/service/java/com/android/server/appsearch/stats/PlatformLogger.java b/service/java/com/android/server/appsearch/stats/PlatformLogger.java
index 7d0ce41..c857fb6 100644
--- a/service/java/com/android/server/appsearch/stats/PlatformLogger.java
+++ b/service/java/com/android/server/appsearch/stats/PlatformLogger.java
@@ -20,7 +20,6 @@
 import android.annotation.Nullable;
 import android.app.appsearch.exceptions.AppSearchException;
 import android.content.Context;
-import android.content.pm.PackageManager;
 import android.os.Process;
 import android.os.SystemClock;
 import android.os.UserHandle;
@@ -36,6 +35,7 @@
 import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats;
 import com.android.server.appsearch.external.localstorage.stats.RemoveStats;
 import com.android.server.appsearch.external.localstorage.stats.SearchStats;
+import com.android.server.appsearch.util.PackageUtil;
 
 import java.io.UnsupportedEncodingException;
 import java.security.MessageDigest;
@@ -493,21 +493,13 @@
     @GuardedBy("mLock")
     private int getPackageUidAsUserLocked(@NonNull String packageName) {
         Integer packageUid = mPackageUidCacheLocked.get(packageName);
-        if (packageUid != null) {
-            return packageUid;
+        if (packageUid == null) {
+            packageUid = PackageUtil.getPackageUidAsUser(mContext, packageName, mUserHandle);
+            if (packageUid != Process.INVALID_UID) {
+                mPackageUidCacheLocked.put(packageName, packageUid);
+            }
         }
-
-        // TODO(b/173532925) since VisibilityStore has the same method, we can make this a
-        //  utility function
-        try {
-            packageUid = mContext.getPackageManager().getPackageUidAsUser(
-                    packageName, mUserHandle.getIdentifier());
-            mPackageUidCacheLocked.put(packageName, packageUid);
-            return packageUid;
-        } catch (PackageManager.NameNotFoundException e) {
-            // Package doesn't exist, continue
-        }
-        return Process.INVALID_UID;
+        return packageUid;
     }
 
     //
diff --git a/service/java/com/android/server/appsearch/util/PackageUtil.java b/service/java/com/android/server/appsearch/util/PackageUtil.java
new file mode 100644
index 0000000..53a1bed
--- /dev/null
+++ b/service/java/com/android/server/appsearch/util/PackageUtil.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.appsearch.util;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Process;
+import android.os.UserHandle;
+
+/**
+ * Utilities for interacting with {@link android.content.pm.PackageManager},
+ * {@link android.os.UserHandle}, and other parts of dealing with apps and binder.
+ *
+ * @hide
+ */
+public class PackageUtil {
+    private PackageUtil() {}
+
+    /**
+     * Finds the UID of the {@code packageName}. Returns {@link Process#INVALID_UID} if unable to
+     * find the UID.
+     */
+    public static int getPackageUidAsUser(
+            @NonNull Context context, @NonNull String packageName, @NonNull UserHandle user) {
+        Context userContext = context.createContextAsUser(user, /*flags=*/ 0);
+        return getPackageUid(userContext, packageName);
+    }
+
+    /**
+     * Finds the UID of the {@code packageName} in the given {@code context}. Returns
+     * {@link Process#INVALID_UID} if unable to find the UID.
+     */
+    public static int getPackageUid(@NonNull Context context, @NonNull String packageName) {
+        try {
+            return context.getPackageManager().getPackageUid(packageName, /*flags=*/ 0);
+        } catch (PackageManager.NameNotFoundException e) {
+            return Process.INVALID_UID;
+        }
+    }
+}
diff --git a/service/java/com/android/server/appsearch/visibilitystore/VisibilityStore.java b/service/java/com/android/server/appsearch/visibilitystore/VisibilityStore.java
index acff792..95ed368 100644
--- a/service/java/com/android/server/appsearch/visibilitystore/VisibilityStore.java
+++ b/service/java/com/android/server/appsearch/visibilitystore/VisibilityStore.java
@@ -18,7 +18,6 @@
 import static android.Manifest.permission.READ_GLOBAL_APP_SEARCH_DATA;
 
 import android.annotation.NonNull;
-import android.annotation.UserIdInt;
 import android.app.appsearch.AppSearchResult;
 import android.app.appsearch.AppSearchSchema;
 import android.app.appsearch.GenericDocument;
@@ -27,7 +26,6 @@
 import android.app.appsearch.exceptions.AppSearchException;
 import android.content.Context;
 import android.content.pm.PackageManager;
-import android.os.Process;
 import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -35,6 +33,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.appsearch.external.localstorage.AppSearchImpl;
 import com.android.server.appsearch.external.localstorage.util.PrefixUtil;
+import com.android.server.appsearch.util.PackageUtil;
 
 import com.google.android.icing.proto.PersistType;
 
@@ -67,9 +66,6 @@
  * @hide
  */
 public class VisibilityStore {
-
-    private static final String TAG = "AppSearchVisibilityStore";
-
     /** No-op user id that won't have any visibility settings. */
     public static final int NO_OP_USER_ID = -1;
 
@@ -95,9 +91,6 @@
     // Context of the user that the call is being made as.
     private final Context mUserContext;
 
-    // User ID of the caller who we're checking visibility settings for.
-    private final int mUserId;
-
     /** Stores the schemas that are platform-hidden. All values are prefixed. */
     private final NotPlatformSurfaceableMap mNotPlatformSurfaceableMap =
             new NotPlatformSurfaceableMap();
@@ -112,13 +105,9 @@
      * @param appSearchImpl AppSearchImpl instance
      * @param userContext Context of the user that the call is being made as
      */
-    public VisibilityStore(
-            @NonNull AppSearchImpl appSearchImpl,
-            @NonNull Context userContext,
-            @UserIdInt int userId) {
+    public VisibilityStore(@NonNull AppSearchImpl appSearchImpl, @NonNull Context userContext) {
         mAppSearchImpl = appSearchImpl;
-        mUserContext = userContext;
-        mUserId = userId;
+        mUserContext = Objects.requireNonNull(userContext);
     }
 
     /**
@@ -348,6 +337,9 @@
         Set<PackageIdentifier> packageIdentifiers =
                 mPackageAccessibleMap.getAccessiblePackages(
                         packageName, databaseName, prefixedSchema);
+        if (packageIdentifiers.isEmpty()) {
+            return false;
+        }
         for (PackageIdentifier packageIdentifier : packageIdentifiers) {
             // TODO(b/169883602): Consider caching the UIDs of packages. Looking this up in the
             // package manager could be costly. We would also need to update the cache on
@@ -357,9 +349,10 @@
             // the callerUid since clients can createContextAsUser with some other user, and then
             // make calls to us. So just check if the appId portion of the uid is the same. This is
             // essentially UserHandle.isSameApp, but that's not a system API for us to use.
-            int callerAppId = UserHandle.getAppId((callerUid));
-            int userAppId =
-                    UserHandle.getAppId(getPackageUidAsUser(packageIdentifier.getPackageName()));
+            int callerAppId = UserHandle.getAppId(callerUid);
+            int packageUid =
+                    PackageUtil.getPackageUid(mUserContext, packageIdentifier.getPackageName());
+            int userAppId = UserHandle.getAppId(packageUid);
             if (callerAppId != userAppId) {
                 continue;
             }
@@ -401,17 +394,4 @@
             @NonNull String packageName, @NonNull String databaseName) {
         return ID_PREFIX + PrefixUtil.createPrefix(packageName, databaseName);
     }
-
-    /**
-     * Finds the UID of the {@code packageName}. Returns {@link Process#INVALID_UID} if unable to
-     * find the UID.
-     */
-    private int getPackageUidAsUser(@NonNull String packageName) {
-        try {
-            return mUserContext.getPackageManager().getPackageUidAsUser(packageName, mUserId);
-        } catch (PackageManager.NameNotFoundException e) {
-            // Package doesn't exist, continue
-        }
-        return Process.INVALID_UID;
-    }
 }