Merge "Move to non-hidden APIs for handleUser and broadcast registration." into sc-dev

This commit is contained in:
Alexander Dorokhine
2021-06-08 17:07:52 +00:00
committed by Android (Google) Code Review
12 changed files with 355 additions and 319 deletions

View File

@@ -17,11 +17,10 @@ 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.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.AppSearchImpl;
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 @@ public class AppSearchManagerService extends SystemService {
}
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 @@ public class AppSearchManagerService extends SystemService {
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 @@ public class AppSearchManagerService extends SystemService {
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 @@ public class AppSearchManagerService extends SystemService {
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 @@ public class AppSearchManagerService extends SystemService {
*
* <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.

View File

@@ -172,11 +172,6 @@ public final class ImplInstanceManager {
@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);
}
}

View File

@@ -201,7 +201,6 @@ public final class AppSearchImpl implements Closeable {
public static AppSearchImpl create(
@NonNull File icingDir,
@NonNull Context userContext,
int userId,
@Nullable AppSearchLogger logger)
throws AppSearchException {
Objects.requireNonNull(icingDir);
@@ -213,8 +212,7 @@ public final class AppSearchImpl implements Closeable {
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 @@ public final class AppSearchImpl implements Closeable {
private AppSearchImpl(
@NonNull File icingDir,
@NonNull Context userContext,
int userId,
@Nullable InitializeStats.Builder initStatsBuilder)
throws AppSearchException {
mReadWriteLock.writeLock().lock();
@@ -256,7 +253,7 @@ public final class AppSearchImpl implements Closeable {
"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).

View File

@@ -20,7 +20,6 @@ import android.annotation.NonNull;
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.InitializeStats;
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 @@ public final class PlatformLogger implements AppSearchLogger {
@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;
}
//

View File

@@ -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;
}
}
}

View File

@@ -18,7 +18,6 @@ package com.android.server.appsearch.visibilitystore;
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.PackageIdentifier;
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 android.util.ArraySet;
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 @@ import java.util.Set;
* @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 @@ public class VisibilityStore {
// 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 @@ public class VisibilityStore {
* @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 @@ public class VisibilityStore {
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 @@ public class VisibilityStore {
// 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 @@ public class VisibilityStore {
@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;
}
}

View File

@@ -25,11 +25,18 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
import android.annotation.NonNull;
import android.app.appsearch.AppSearchSchema;
import android.app.appsearch.PackageIdentifier;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.PackageManager;
import android.os.UserHandle;
import android.util.ArrayMap;
import androidx.test.core.app.ApplicationProvider;
@@ -43,13 +50,15 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.mockito.Mockito;
import java.util.Collections;
import java.util.Map;
/** This tests AppSearchImpl when it's running with a platform-backed VisibilityStore. */
public class AppSearchImplPlatformTest {
@Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
private MockPackageManager mMockPackageManager = new MockPackageManager();
private final Map<UserHandle, PackageManager> mMockPackageManagers = new ArrayMap<>();
private Context mContext;
private AppSearchImpl mAppSearchImpl;
private int mGlobalQuerierUid;
@@ -57,20 +66,28 @@ public class AppSearchImplPlatformTest {
@Before
public void setUp() throws Exception {
Context context = ApplicationProvider.getApplicationContext();
mContext =
new ContextWrapper(context) {
mContext = new ContextWrapper(context) {
@Override
public Context createContextAsUser(UserHandle user, int flags) {
return new ContextWrapper(super.createContextAsUser(user, flags)) {
@Override
public PackageManager getPackageManager() {
return mMockPackageManager.getMockPackageManager();
return getMockPackageManager(user);
}
};
}
@Override
public PackageManager getPackageManager() {
return createContextAsUser(getUser(), /*flags=*/ 0).getPackageManager();
}
};
// Give ourselves global query permissions
mAppSearchImpl =
AppSearchImpl.create(
mTemporaryFolder.newFolder(),
mContext,
mContext.getUserId(),
/*logger=*/ null);
mGlobalQuerierUid =
@@ -85,14 +102,19 @@ public class AppSearchImplPlatformTest {
int uidFoo = 1;
// Make sure foo package will pass package manager checks.
mMockPackageManager.mockGetPackageUidAsUser(packageNameFoo, mContext.getUserId(), uidFoo);
mMockPackageManager.mockAddSigningCertificate(packageNameFoo, sha256CertFoo);
PackageManager mockPackageManager = getMockPackageManager(mContext.getUser());
when(mockPackageManager.getPackageUid(eq(packageNameFoo), /*flags=*/ anyInt()))
.thenReturn(uidFoo);
when(mockPackageManager.hasSigningCertificate(
packageNameFoo, sha256CertFoo, PackageManager.CERT_INPUT_SHA256))
.thenReturn(true);
// Make sure we have global query privileges and "foo" doesn't
mMockPackageManager.mockCheckPermission(
READ_GLOBAL_APP_SEARCH_DATA, mContext.getPackageName(), PERMISSION_GRANTED);
mMockPackageManager.mockCheckPermission(
READ_GLOBAL_APP_SEARCH_DATA, packageNameFoo, PERMISSION_DENIED);
when(mockPackageManager.checkPermission(
READ_GLOBAL_APP_SEARCH_DATA, mContext.getPackageName()))
.thenReturn(PERMISSION_GRANTED);
when(mockPackageManager.checkPermission(READ_GLOBAL_APP_SEARCH_DATA, packageNameFoo))
.thenReturn(PERMISSION_DENIED);
// Set schema1
String prefix = PrefixUtil.createPrefix("package", "database");
@@ -145,19 +167,16 @@ public class AppSearchImplPlatformTest {
/*schemaVersion=*/ 0);
// Check that "schema1" still has the same visibility settings
SystemUtil.runWithShellPermissionIdentity(
() -> {
assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
.isSchemaSearchableByCaller(
"package",
"database",
prefix + "schema1",
mContext.getPackageName(),
mGlobalQuerierUid))
.isFalse();
},
SystemUtil.runWithShellPermissionIdentity(() -> assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
.isSchemaSearchableByCaller(
"package",
"database",
prefix + "schema1",
mContext.getPackageName(),
mGlobalQuerierUid))
.isFalse(),
READ_GLOBAL_APP_SEARCH_DATA);
assertThat(
@@ -172,19 +191,16 @@ public class AppSearchImplPlatformTest {
.isTrue();
// "schema2" has default visibility settings
SystemUtil.runWithShellPermissionIdentity(
() -> {
assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
.isSchemaSearchableByCaller(
"package",
"database",
prefix + "schema2",
mContext.getPackageName(),
mGlobalQuerierUid))
.isTrue();
},
SystemUtil.runWithShellPermissionIdentity(() -> assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
.isSchemaSearchableByCaller(
"package",
"database",
prefix + "schema2",
mContext.getPackageName(),
mGlobalQuerierUid))
.isTrue(),
READ_GLOBAL_APP_SEARCH_DATA);
assertThat(
@@ -207,14 +223,19 @@ public class AppSearchImplPlatformTest {
int uidFoo = 1;
// Make sure foo package will pass package manager checks.
mMockPackageManager.mockGetPackageUidAsUser(packageNameFoo, mContext.getUserId(), uidFoo);
mMockPackageManager.mockAddSigningCertificate(packageNameFoo, sha256CertFoo);
PackageManager mockPackageManager = getMockPackageManager(mContext.getUser());
when(mockPackageManager.getPackageUid(eq(packageNameFoo), /*flags=*/ anyInt()))
.thenReturn(uidFoo);
when(mockPackageManager.hasSigningCertificate(
packageNameFoo, sha256CertFoo, PackageManager.CERT_INPUT_SHA256))
.thenReturn(true);
// Make sure we have global query privileges and "foo" doesn't
mMockPackageManager.mockCheckPermission(
READ_GLOBAL_APP_SEARCH_DATA, mContext.getPackageName(), PERMISSION_GRANTED);
mMockPackageManager.mockCheckPermission(
READ_GLOBAL_APP_SEARCH_DATA, packageNameFoo, PERMISSION_DENIED);
when(mockPackageManager.checkPermission(
READ_GLOBAL_APP_SEARCH_DATA, mContext.getPackageName()))
.thenReturn(PERMISSION_GRANTED);
when(mockPackageManager.checkPermission(READ_GLOBAL_APP_SEARCH_DATA, packageNameFoo))
.thenReturn(PERMISSION_DENIED);
String prefix = PrefixUtil.createPrefix("package", "database");
mAppSearchImpl.setSchema(
@@ -320,8 +341,10 @@ public class AppSearchImplPlatformTest {
@Test
public void testSetSchema_defaultPlatformVisible() throws Exception {
// Make sure we have global query privileges
mMockPackageManager.mockCheckPermission(
READ_GLOBAL_APP_SEARCH_DATA, mContext.getPackageName(), PERMISSION_GRANTED);
PackageManager mockPackageManager = getMockPackageManager(mContext.getUser());
when(mockPackageManager.checkPermission(
READ_GLOBAL_APP_SEARCH_DATA, mContext.getPackageName()))
.thenReturn(PERMISSION_GRANTED);
String prefix = PrefixUtil.createPrefix("package", "database");
mAppSearchImpl.setSchema(
@@ -348,8 +371,10 @@ public class AppSearchImplPlatformTest {
@Test
public void testSetSchema_platformHidden() throws Exception {
// Make sure we have global query privileges
mMockPackageManager.mockCheckPermission(
READ_GLOBAL_APP_SEARCH_DATA, mContext.getPackageName(), PERMISSION_GRANTED);
PackageManager mockPackageManager = getMockPackageManager(mContext.getUser());
when(mockPackageManager.checkPermission(
READ_GLOBAL_APP_SEARCH_DATA, mContext.getPackageName()))
.thenReturn(PERMISSION_GRANTED);
String prefix = PrefixUtil.createPrefix("package", "database");
mAppSearchImpl.setSchema(
@@ -378,8 +403,9 @@ public class AppSearchImplPlatformTest {
String packageName = "com.package";
// Make sure package doesn't global query privileges
mMockPackageManager.mockCheckPermission(
READ_GLOBAL_APP_SEARCH_DATA, packageName, PERMISSION_DENIED);
PackageManager mockPackageManager = getMockPackageManager(mContext.getUser());
when(mockPackageManager.checkPermission(
READ_GLOBAL_APP_SEARCH_DATA, packageName)).thenReturn(PERMISSION_DENIED);
String prefix = PrefixUtil.createPrefix("package", "database");
mAppSearchImpl.setSchema(
@@ -410,12 +436,16 @@ public class AppSearchImplPlatformTest {
int uidFoo = 1;
// Make sure foo package will pass package manager checks.
mMockPackageManager.mockGetPackageUidAsUser(packageNameFoo, mContext.getUserId(), uidFoo);
mMockPackageManager.mockAddSigningCertificate(packageNameFoo, sha256CertFoo);
PackageManager mockPackageManager = getMockPackageManager(mContext.getUser());
when(mockPackageManager.getPackageUid(eq(packageNameFoo), /*flags=*/ anyInt()))
.thenReturn(uidFoo);
when(mockPackageManager.hasSigningCertificate(
packageNameFoo, sha256CertFoo, PackageManager.CERT_INPUT_SHA256))
.thenReturn(true);
// Make sure foo doesn't have global query privileges
mMockPackageManager.mockCheckPermission(
READ_GLOBAL_APP_SEARCH_DATA, packageNameFoo, PERMISSION_DENIED);
when(mockPackageManager.checkPermission(READ_GLOBAL_APP_SEARCH_DATA, packageNameFoo))
.thenReturn(PERMISSION_DENIED);
String prefix = PrefixUtil.createPrefix("package", "database");
mAppSearchImpl.setSchema(
@@ -439,4 +469,14 @@ public class AppSearchImplPlatformTest {
uidFoo))
.isTrue();
}
@NonNull
private PackageManager getMockPackageManager(@NonNull UserHandle user) {
PackageManager pm = mMockPackageManagers.get(user);
if (pm == null) {
pm = Mockito.mock(PackageManager.class);
mMockPackageManagers.put(user, pm);
}
return pm;
}
}

View File

@@ -1,94 +0,0 @@
/*
* 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.
*/
// TODO(b/169883602): This is purposely a different package from the path so that AppSearchImplTest
// can use it without an extra import. This should be moved into a proper package once
// AppSearchImpl-VisibilityStore's dependencies are refactored.
package com.android.server.appsearch.external.localstorage;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.content.pm.PackageManager;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
/** Mock to help test package name, UID, and certificate verification. */
public class MockPackageManager {
@Mock private PackageManager mMockPackageManager;
public MockPackageManager() {
MockitoAnnotations.initMocks(this);
}
@NonNull
public PackageManager getMockPackageManager() {
return mMockPackageManager;
}
/** Mock a checkPermission call. */
public void mockCheckPermission(String permission, String packageName, int permissionResult) {
when(mMockPackageManager.checkPermission(permission, packageName))
.thenReturn(permissionResult);
}
/** Mock a NameNotFoundException if the package name isn't installed. */
public void mockThrowsNameNotFoundException(String packageName) {
try {
when(mMockPackageManager.getPackageUidAsUser(eq(packageName), /*userId=*/ anyInt()))
.thenThrow(new PackageManager.NameNotFoundException());
when(mMockPackageManager.getPackageUidAsUser(
eq(packageName), /*flags=*/ anyInt(), /*userId=*/ anyInt()))
.thenThrow(new PackageManager.NameNotFoundException());
} catch (PackageManager.NameNotFoundException e) {
// Shouldn't ever happen since we're mocking the exception
e.printStackTrace();
}
}
/** Mocks that {@code uid} contains the {@code packageName} */
public void mockGetPackageUidAsUser(String packageName, @UserIdInt int callerUserId, int uid) {
try {
when(mMockPackageManager.getPackageUidAsUser(eq(packageName), eq(callerUserId)))
.thenReturn(uid);
when(mMockPackageManager.getPackageUidAsUser(
eq(packageName), /*flags=*/ anyInt(), eq(callerUserId)))
.thenReturn(uid);
} catch (PackageManager.NameNotFoundException e) {
// Shouldn't ever happen since we're mocking the method.
e.printStackTrace();
}
}
/** Mocks that {@code packageName} has been signed with {@code sha256Cert}. */
public void mockAddSigningCertificate(String packageName, byte[] sha256Cert) {
when(mMockPackageManager.hasSigningCertificate(
packageName, sha256Cert, PackageManager.CERT_INPUT_SHA256))
.thenReturn(true);
}
/** Mocks that {@code packageName} has NOT been signed with {@code sha256Cert}. */
public void mockRemoveSigningCertificate(String packageName, byte[] sha256Cert) {
when(mMockPackageManager.hasSigningCertificate(
packageName, sha256Cert, PackageManager.CERT_INPUT_SHA256))
.thenReturn(false);
}
}

View File

@@ -25,10 +25,17 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
import android.annotation.NonNull;
import android.app.appsearch.PackageIdentifier;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.PackageManager;
import android.os.UserHandle;
import android.util.ArrayMap;
import androidx.test.core.app.ApplicationProvider;
@@ -43,39 +50,47 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.mockito.Mockito;
import java.util.Collections;
import java.util.Map;
public class VisibilityStoreTest {
@Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
private MockPackageManager mMockPackageManager = new MockPackageManager();
private final Map<UserHandle, PackageManager> mMockPackageManagers = new ArrayMap<>();
private Context mContext;
private AppSearchImpl mAppSearchImpl;
private VisibilityStore mVisibilityStore;
private int mUid;
@Before
public void setUp() throws Exception {
Context context = ApplicationProvider.getApplicationContext();
mContext =
new ContextWrapper(context) {
mContext = new ContextWrapper(context) {
@Override
public Context createContextAsUser(UserHandle user, int flags) {
return new ContextWrapper(super.createContextAsUser(user, flags)) {
@Override
public PackageManager getPackageManager() {
return mMockPackageManager.getMockPackageManager();
return getMockPackageManager(user);
}
};
}
@Override
public PackageManager getPackageManager() {
return createContextAsUser(getUser(), /*flags=*/ 0).getPackageManager();
}
};
// Give ourselves global query permissions
mAppSearchImpl =
AppSearchImpl appSearchImpl =
AppSearchImpl.create(
mTemporaryFolder.newFolder(),
mContext,
mContext.getUserId(),
/*logger=*/ null);
mUid = mContext.getPackageManager().getPackageUid(mContext.getPackageName(), /*flags=*/ 0);
mVisibilityStore = mAppSearchImpl.getVisibilityStoreLocked();
mVisibilityStore = appSearchImpl.getVisibilityStoreLocked();
}
/**
@@ -103,8 +118,10 @@ public class VisibilityStoreTest {
@Test
public void testSetVisibility_platformSurfaceable() throws Exception {
// Make sure we have global query privileges
mMockPackageManager.mockCheckPermission(
READ_GLOBAL_APP_SEARCH_DATA, mContext.getPackageName(), PERMISSION_GRANTED);
PackageManager mockPackageManager = getMockPackageManager(mContext.getUser());
when(mockPackageManager
.checkPermission(READ_GLOBAL_APP_SEARCH_DATA, mContext.getPackageName()))
.thenReturn(PERMISSION_GRANTED);
mVisibilityStore.setVisibility(
"package",
@@ -210,10 +227,13 @@ public class VisibilityStoreTest {
int uidNotFooOrBar = 3;
// Make sure none of them have global query privileges
mMockPackageManager.mockCheckPermission(
READ_GLOBAL_APP_SEARCH_DATA, packageNameFoo, PERMISSION_DENIED);
mMockPackageManager.mockCheckPermission(
READ_GLOBAL_APP_SEARCH_DATA, packageNameBar, PERMISSION_DENIED);
PackageManager mockPackageManager = getMockPackageManager(mContext.getUser());
when(mockPackageManager
.checkPermission(READ_GLOBAL_APP_SEARCH_DATA, packageNameFoo))
.thenReturn(PERMISSION_DENIED);
when(mockPackageManager
.checkPermission(READ_GLOBAL_APP_SEARCH_DATA, packageNameBar))
.thenReturn(PERMISSION_DENIED);
// By default, a schema isn't package accessible.
assertThat(
@@ -237,32 +257,43 @@ public class VisibilityStoreTest {
ImmutableList.of(new PackageIdentifier(packageNameBar, sha256CertBar))));
// Should fail if PackageManager doesn't see that it has the proper certificate
mMockPackageManager.mockGetPackageUidAsUser(packageNameFoo, mContext.getUserId(), uidFoo);
mMockPackageManager.mockRemoveSigningCertificate(packageNameFoo, sha256CertFoo);
when(mockPackageManager.getPackageUid(eq(packageNameFoo), /*flags=*/ anyInt()))
.thenReturn(uidFoo);
when(mockPackageManager.hasSigningCertificate(
packageNameFoo, sha256CertFoo, PackageManager.CERT_INPUT_SHA256))
.thenReturn(false);
assertThat(
mVisibilityStore.isSchemaSearchableByCaller(
"package", "database", "prefix/schemaFoo", packageNameFoo, uidFoo))
.isFalse();
// Should fail if PackageManager doesn't think the package belongs to the uid
mMockPackageManager.mockGetPackageUidAsUser(
packageNameFoo, mContext.getUserId(), uidNotFooOrBar);
mMockPackageManager.mockAddSigningCertificate(packageNameFoo, sha256CertFoo);
when(mockPackageManager.getPackageUid(eq(packageNameFoo), /*flags=*/ anyInt()))
.thenReturn(uidNotFooOrBar);
when(mockPackageManager.hasSigningCertificate(
packageNameFoo, sha256CertFoo, PackageManager.CERT_INPUT_SHA256))
.thenReturn(true);
assertThat(
mVisibilityStore.isSchemaSearchableByCaller(
"package", "database", "prefix/schemaFoo", packageNameFoo, uidFoo))
.isFalse();
// But if uid and certificate match, then we should have access
mMockPackageManager.mockGetPackageUidAsUser(packageNameFoo, mContext.getUserId(), uidFoo);
mMockPackageManager.mockAddSigningCertificate(packageNameFoo, sha256CertFoo);
when(mockPackageManager.getPackageUid(eq(packageNameFoo), /*flags=*/ anyInt()))
.thenReturn(uidFoo);
when(mockPackageManager.hasSigningCertificate(
packageNameFoo, sha256CertFoo, PackageManager.CERT_INPUT_SHA256))
.thenReturn(true);
assertThat(
mVisibilityStore.isSchemaSearchableByCaller(
"package", "database", "prefix/schemaFoo", packageNameFoo, uidFoo))
.isTrue();
mMockPackageManager.mockGetPackageUidAsUser(packageNameBar, mContext.getUserId(), uidBar);
mMockPackageManager.mockAddSigningCertificate(packageNameBar, sha256CertBar);
when(mockPackageManager.getPackageUid(eq(packageNameBar), /*flags=*/ anyInt()))
.thenReturn(uidBar);
when(mockPackageManager.hasSigningCertificate(
packageNameBar, sha256CertBar, PackageManager.CERT_INPUT_SHA256))
.thenReturn(true);
assertThat(
mVisibilityStore.isSchemaSearchableByCaller(
"package", "database", "prefix/schemaBar", packageNameBar, uidBar))
@@ -278,15 +309,21 @@ public class VisibilityStoreTest {
"prefix/schemaFoo",
ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))));
mMockPackageManager.mockGetPackageUidAsUser(packageNameFoo, mContext.getUserId(), uidFoo);
mMockPackageManager.mockAddSigningCertificate(packageNameFoo, sha256CertFoo);
when(mockPackageManager.getPackageUid(eq(packageNameFoo), /*flags=*/ anyInt()))
.thenReturn(uidFoo);
when(mockPackageManager.hasSigningCertificate(
packageNameFoo, sha256CertFoo, PackageManager.CERT_INPUT_SHA256))
.thenReturn(true);
assertThat(
mVisibilityStore.isSchemaSearchableByCaller(
"package", "database", "prefix/schemaFoo", packageNameFoo, uidFoo))
.isTrue();
mMockPackageManager.mockGetPackageUidAsUser(packageNameBar, mContext.getUserId(), uidBar);
mMockPackageManager.mockAddSigningCertificate(packageNameBar, sha256CertBar);
when(mockPackageManager.getPackageUid(eq(packageNameBar), /*flags=*/ anyInt()))
.thenReturn(uidBar);
when(mockPackageManager.hasSigningCertificate(
packageNameBar, sha256CertBar, PackageManager.CERT_INPUT_SHA256))
.thenReturn(true);
assertThat(
mVisibilityStore.isSchemaSearchableByCaller(
"package", "database", "prefix/schemaBar", packageNameBar, uidBar))
@@ -302,11 +339,13 @@ public class VisibilityStoreTest {
int uidFoo = 1;
// Pretend we can't find the Foo package.
mMockPackageManager.mockThrowsNameNotFoundException(packageNameFoo);
PackageManager mockPackageManager = getMockPackageManager(mContext.getUser());
when(mockPackageManager.getPackageUid(eq(packageNameFoo), /*flags=*/ anyInt()))
.thenThrow(new PackageManager.NameNotFoundException());
// Make sure "foo" doesn't have global query privileges
mMockPackageManager.mockCheckPermission(
READ_GLOBAL_APP_SEARCH_DATA, packageNameFoo, PERMISSION_DENIED);
when(mockPackageManager.checkPermission(READ_GLOBAL_APP_SEARCH_DATA, packageNameFoo))
.thenReturn(PERMISSION_DENIED);
// Grant package access
mVisibilityStore.setVisibility(
@@ -332,10 +371,12 @@ public class VisibilityStoreTest {
int uidFoo = 1;
// Set it up such that the test package has global query privileges, but "foo" doesn't.
mMockPackageManager.mockCheckPermission(
READ_GLOBAL_APP_SEARCH_DATA, mContext.getPackageName(), PERMISSION_GRANTED);
mMockPackageManager.mockCheckPermission(
READ_GLOBAL_APP_SEARCH_DATA, packageNameFoo, PERMISSION_DENIED);
PackageManager mockPackageManager = getMockPackageManager(mContext.getUser());
when(mockPackageManager.checkPermission(
READ_GLOBAL_APP_SEARCH_DATA, mContext.getPackageName()))
.thenReturn(PERMISSION_GRANTED);
when(mockPackageManager.checkPermission(READ_GLOBAL_APP_SEARCH_DATA, packageNameFoo))
.thenReturn(PERMISSION_DENIED);
mVisibilityStore.setVisibility(
/*packageName=*/ "",
@@ -354,8 +395,11 @@ public class VisibilityStoreTest {
mUid))
.isTrue();
mMockPackageManager.mockGetPackageUidAsUser(packageNameFoo, mContext.getUserId(), uidFoo);
mMockPackageManager.mockAddSigningCertificate(packageNameFoo, sha256CertFoo);
when(mockPackageManager.getPackageUid(eq(packageNameFoo), /*flags=*/ anyInt()))
.thenReturn(uidFoo);
when(mockPackageManager.hasSigningCertificate(
packageNameFoo, sha256CertFoo, PackageManager.CERT_INPUT_SHA256))
.thenReturn(true);
assertThat(
mVisibilityStore.isSchemaSearchableByCaller(
/*packageName=*/ "",
@@ -365,4 +409,14 @@ public class VisibilityStoreTest {
uidFoo))
.isTrue();
}
@NonNull
private PackageManager getMockPackageManager(@NonNull UserHandle user) {
PackageManager pm = mMockPackageManagers.get(user);
if (pm == null) {
pm = Mockito.mock(PackageManager.class);
mMockPackageManagers.put(user, pm);
}
return pm;
}
}

View File

@@ -86,7 +86,6 @@ public class AppSearchImplTest {
AppSearchImpl.create(
mTemporaryFolder.newFolder(),
context,
VisibilityStore.NO_OP_USER_ID,
/*logger=*/ null);
}
@@ -494,9 +493,7 @@ public class AppSearchImplTest {
// Setup the index
Context context = ApplicationProvider.getApplicationContext();
File appsearchDir = mTemporaryFolder.newFolder();
AppSearchImpl appSearchImpl =
AppSearchImpl.create(
appsearchDir, context, VisibilityStore.NO_OP_USER_ID, /*logger=*/ null);
AppSearchImpl appSearchImpl = AppSearchImpl.create(appsearchDir, context, /*logger=*/ null);
// Insert schema
List<AppSearchSchema> schemas =
@@ -557,9 +554,7 @@ public class AppSearchImplTest {
// Initialize AppSearchImpl. This should cause a reset.
appSearchImpl.close();
appSearchImpl =
AppSearchImpl.create(
appsearchDir, context, VisibilityStore.NO_OP_USER_ID, testLogger);
appSearchImpl = AppSearchImpl.create(appsearchDir, context, testLogger);
// Check recovery state
InitializeStats initStats = testLogger.mInitializeStats;
@@ -1688,7 +1683,6 @@ public class AppSearchImplTest {
AppSearchImpl.create(
mTemporaryFolder.newFolder(),
context,
VisibilityStore.NO_OP_USER_ID,
/*logger=*/ null);
// Initial check that we could do something at first.
@@ -1836,9 +1830,7 @@ public class AppSearchImplTest {
// Setup the index
Context context = ApplicationProvider.getApplicationContext();
File appsearchDir = mTemporaryFolder.newFolder();
AppSearchImpl appSearchImpl =
AppSearchImpl.create(
appsearchDir, context, VisibilityStore.NO_OP_USER_ID, /*logger=*/ null);
AppSearchImpl appSearchImpl = AppSearchImpl.create(appsearchDir, context, /*logger=*/ null);
List<AppSearchSchema> schemas =
Collections.singletonList(new AppSearchSchema.Builder("type").build());
@@ -1864,8 +1856,7 @@ public class AppSearchImplTest {
// That document should be visible even from another instance.
AppSearchImpl appSearchImpl2 =
AppSearchImpl.create(
appsearchDir, context, VisibilityStore.NO_OP_USER_ID, /*logger=*/ null);
AppSearchImpl.create(appsearchDir, context, /*logger=*/ null);
getResult =
appSearchImpl2.getDocument(
"package", "database", "namespace1", "id1", Collections.emptyMap());
@@ -1877,9 +1868,7 @@ public class AppSearchImplTest {
// Setup the index
Context context = ApplicationProvider.getApplicationContext();
File appsearchDir = mTemporaryFolder.newFolder();
AppSearchImpl appSearchImpl =
AppSearchImpl.create(
appsearchDir, context, VisibilityStore.NO_OP_USER_ID, /*logger=*/ null);
AppSearchImpl appSearchImpl = AppSearchImpl.create(appsearchDir, context, /*logger=*/ null);
List<AppSearchSchema> schemas =
Collections.singletonList(new AppSearchSchema.Builder("type").build());
@@ -1929,8 +1918,7 @@ public class AppSearchImplTest {
// Only the second document should be retrievable from another instance.
AppSearchImpl appSearchImpl2 =
AppSearchImpl.create(
appsearchDir, context, VisibilityStore.NO_OP_USER_ID, /*logger=*/ null);
AppSearchImpl.create(appsearchDir, context, /*logger=*/ null);
expectThrows(
AppSearchException.class,
() ->
@@ -1951,9 +1939,7 @@ public class AppSearchImplTest {
// Setup the index
Context context = ApplicationProvider.getApplicationContext();
File appsearchDir = mTemporaryFolder.newFolder();
AppSearchImpl appSearchImpl =
AppSearchImpl.create(
appsearchDir, context, VisibilityStore.NO_OP_USER_ID, /*logger=*/ null);
AppSearchImpl appSearchImpl = AppSearchImpl.create(appsearchDir, context, /*logger=*/ null);
List<AppSearchSchema> schemas =
Collections.singletonList(new AppSearchSchema.Builder("type").build());
@@ -2011,8 +1997,7 @@ public class AppSearchImplTest {
// Only the second document should be retrievable from another instance.
AppSearchImpl appSearchImpl2 =
AppSearchImpl.create(
appsearchDir, context, VisibilityStore.NO_OP_USER_ID, /*logger=*/ null);
AppSearchImpl.create(appsearchDir, context, /*logger=*/ null);
expectThrows(
AppSearchException.class,
() ->

View File

@@ -40,7 +40,6 @@ import com.android.server.appsearch.proto.PutDocumentStatsProto;
import com.android.server.appsearch.proto.QueryStatsProto;
import com.android.server.appsearch.proto.ScoringSpecProto;
import com.android.server.appsearch.proto.TermMatchType;
import com.android.server.appsearch.visibilitystore.VisibilityStore;
import org.junit.Before;
import org.junit.Rule;
@@ -64,7 +63,6 @@ public class AppSearchLoggerTest {
AppSearchImpl.create(
mTemporaryFolder.newFolder(),
context,
VisibilityStore.NO_OP_USER_ID,
/*logger=*/ null);
mLogger = new TestLogger();
}
@@ -292,7 +290,6 @@ public class AppSearchLoggerTest {
AppSearchImpl.create(
mTemporaryFolder.newFolder(),
context,
VisibilityStore.NO_OP_USER_ID,
mLogger);
InitializeStats iStats = mLogger.mInitializeStats;

View File

@@ -22,6 +22,7 @@ import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.annotation.NonNull;
import android.content.Context;
@@ -29,46 +30,46 @@ import android.content.ContextWrapper;
import android.content.pm.PackageManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.SparseIntArray;
import androidx.test.core.app.ApplicationProvider;
import com.android.server.appsearch.external.localstorage.MockPackageManager;
import com.android.server.appsearch.external.localstorage.stats.CallStats;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Map;
public class PlatformLoggerTest {
private static final int TEST_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS = 100;
private static final int TEST_DEFAULT_SAMPLING_INTERVAL = 10;
private static final String TEST_PACKAGE_NAME = "packageName";
private MockPackageManager mMockPackageManager = new MockPackageManager();
private final Map<UserHandle, PackageManager> mMockPackageManagers = new ArrayMap<>();
private Context mContext;
@Before
public void setUp() throws Exception {
Context context = ApplicationProvider.getApplicationContext();
mContext =
new ContextWrapper(context) {
mContext = new ContextWrapper(context) {
@Override
public Context createContextAsUser(UserHandle user, int flags) {
return new ContextWrapper(super.createContextAsUser(user, flags)) {
@Override
public PackageManager getPackageManager() {
return mMockPackageManager.getMockPackageManager();
return getMockPackageManager(user);
}
};
}
static int calculateHashCodeMd5withBigInteger(@NonNull String str) throws
NoSuchAlgorithmException, UnsupportedEncodingException {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(str.getBytes(/*charsetName=*/ "UTF-8"));
byte[] digest = md.digest();
return new BigInteger(digest).intValue();
}
};
}
@Test
@@ -294,39 +295,51 @@ public class PlatformLoggerTest {
TEST_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS,
TEST_DEFAULT_SAMPLING_INTERVAL,
/*samplingIntervals=*/ new SparseIntArray()));
mMockPackageManager.mockGetPackageUidAsUser(testPackageName, mContext.getUserId(), testUid);
PackageManager mockPackageManager = getMockPackageManager(mContext.getUser());
when(mockPackageManager.getPackageUid(testPackageName, /*flags=*/0)).thenReturn(testUid);
//
// First time, no cache
//
PlatformLogger.ExtraStats extraStats = logger.createExtraStatsLocked(testPackageName,
CallStats.CALL_TYPE_PUT_DOCUMENT);
verify(mMockPackageManager.getMockPackageManager(), times(1)).getPackageUidAsUser(
eq(testPackageName), /*userId=*/ anyInt());
verify(mockPackageManager, times(1))
.getPackageUid(eq(testPackageName), /*flags=*/ anyInt());
assertThat(extraStats.mPackageUid).isEqualTo(testUid);
//
// Second time, we have cache
//
extraStats = logger.createExtraStatsLocked(testPackageName,
CallStats.CALL_TYPE_PUT_DOCUMENT);
// Count is still one since we will use the cache
verify(mMockPackageManager.getMockPackageManager(), times(1)).getPackageUidAsUser(
eq(testPackageName), /*userId=*/ anyInt());
verify(mockPackageManager, times(1))
.getPackageUid(eq(testPackageName), /*flags=*/ anyInt());
assertThat(extraStats.mPackageUid).isEqualTo(testUid);
//
// Remove the cache and try again
//
assertThat(logger.removeCachedUidForPackage(testPackageName)).isEqualTo(testUid);
extraStats = logger.createExtraStatsLocked(testPackageName,
CallStats.CALL_TYPE_PUT_DOCUMENT);
// count increased by 1 since cache is cleared
verify(mMockPackageManager.getMockPackageManager(), times(2)).getPackageUidAsUser(
eq(testPackageName), /*userId=*/ anyInt());
verify(mockPackageManager, times(2))
.getPackageUid(eq(testPackageName), /*flags=*/ anyInt());
assertThat(extraStats.mPackageUid).isEqualTo(testUid);
}
private static int calculateHashCodeMd5withBigInteger(@NonNull String str)
throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(str.getBytes(StandardCharsets.UTF_8));
byte[] digest = md.digest();
return new BigInteger(digest).intValue();
}
@NonNull
private PackageManager getMockPackageManager(@NonNull UserHandle user) {
PackageManager pm = mMockPackageManagers.get(user);
if (pm == null) {
pm = Mockito.mock(PackageManager.class);
mMockPackageManagers.put(user, pm);
}
return pm;
}
}