Sync framework from jetpack.

Included changes:
* 31866d: Migrate VisibilityStore to a no-op.

Bug: 169883602
Test: Presubmit
Change-Id: I9a4abbf64d29a248a0d04e883961f1a6da556802
This commit is contained in:
Alexander Dorokhine
2021-01-20 16:06:16 -08:00
parent 17e406e2fd
commit 8e5ad5d46e
7 changed files with 29 additions and 795 deletions

View File

@@ -69,7 +69,7 @@ public final class ImplInstanceManager {
private static AppSearchImpl createImpl(@NonNull Context context, @UserIdInt int userId)
throws AppSearchException {
File appSearchDir = getAppSearchDir(context, userId);
return AppSearchImpl.create(appSearchDir, context, /*globalQuerierPackage=*/"");
return AppSearchImpl.create(appSearchDir, context, userId, /*globalQuerierPackage=*/"");
}
private static File getAppSearchDir(@NonNull Context context, @UserIdInt int userId) {

View File

@@ -160,18 +160,25 @@ public final class AppSearchImpl {
*/
@NonNull
public static AppSearchImpl create(
@NonNull File icingDir, @NonNull Context context, @NonNull String globalQuerierPackage)
@NonNull File icingDir,
@NonNull Context context,
int userId,
@NonNull String globalQuerierPackage)
throws AppSearchException {
Preconditions.checkNotNull(icingDir);
Preconditions.checkNotNull(context);
Preconditions.checkNotNull(globalQuerierPackage);
AppSearchImpl appSearchImpl = new AppSearchImpl(icingDir, context, globalQuerierPackage);
AppSearchImpl appSearchImpl =
new AppSearchImpl(icingDir, context, userId, globalQuerierPackage);
appSearchImpl.initializeVisibilityStore();
return appSearchImpl;
}
private AppSearchImpl(
@NonNull File icingDir, @NonNull Context context, @NonNull String globalQuerierPackage)
@NonNull File icingDir,
@NonNull Context context,
int userId,
@NonNull String globalQuerierPackage)
throws AppSearchException {
mReadWriteLock.writeLock().lock();
@@ -184,7 +191,8 @@ public final class AppSearchImpl {
.build();
mIcingSearchEngineLocked = new IcingSearchEngine(options);
mVisibilityStoreLocked = new VisibilityStore(this, context, globalQuerierPackage);
mVisibilityStoreLocked =
new VisibilityStore(this, context, globalQuerierPackage);
InitializeResultProto initializeResultProto = mIcingSearchEngineLocked.initialize();
SchemaProto schemaProto;
@@ -1350,6 +1358,7 @@ public final class AppSearchImpl {
}
@GuardedBy("mReadWriteLock")
@NonNull
@VisibleForTesting
VisibilityStore getVisibilityStoreLocked() {
return mVisibilityStoreLocked;

View File

@@ -1 +1 @@
Iff96eae150c7cdd281c9ecb5d93f4ef697e89f1a
I03df55376689c1557c651d5b9671c40da1c35955

View File

@@ -22,14 +22,12 @@ import static org.testng.Assert.expectThrows;
import android.app.appsearch.AppSearchSchema;
import android.app.appsearch.GenericDocument;
import android.app.appsearch.PackageIdentifier;
import android.app.appsearch.SearchResult;
import android.app.appsearch.SearchResultPage;
import android.app.appsearch.SearchSpec;
import android.app.appsearch.exceptions.AppSearchException;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.PackageManager;
import android.util.ArraySet;
import androidx.test.core.app.ApplicationProvider;
@@ -46,7 +44,6 @@ import com.android.server.appsearch.proto.StringIndexingConfig;
import com.android.server.appsearch.proto.TermMatchType;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import org.junit.Before;
@@ -57,33 +54,23 @@ import org.junit.rules.TemporaryFolder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
public class AppSearchImplTest {
@Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
private MockPackageManager mMockPackageManager = new MockPackageManager();
private Context mContext;
private AppSearchImpl mAppSearchImpl;
private int mGlobalQuerierUid;
@Before
public void setUp() throws Exception {
Context context = ApplicationProvider.getApplicationContext();
mContext =
new ContextWrapper(context) {
@Override
public PackageManager getPackageManager() {
return mMockPackageManager.getMockPackageManager();
}
};
// Give ourselves global query permissions
mAppSearchImpl =
AppSearchImpl.create(
mTemporaryFolder.newFolder(),
mContext,
/*globalQuerierPackage=*/ mContext.getPackageName());
mGlobalQuerierUid =
mContext.getPackageManager().getPackageUid(mContext.getPackageName(), /*flags=*/ 0);
context,
/*userId=*/-1,
/*globalQuerierPackage
=*/ context.getPackageName());
}
// TODO(b/175430168) add test to verify reset is working properly.
@@ -702,125 +689,10 @@ public class AppSearchImplTest {
new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build();
SearchResultPage searchResultPage =
mAppSearchImpl.globalQuery(
"", searchSpec, mContext.getPackageName(), /*callerUid=*/ 0);
"", searchSpec, /*callerPackageName=*/ "", /*callerUid=*/ 0);
assertThat(searchResultPage.getResults()).isEmpty();
}
/**
* TODO(b/169883602): This should be an integration test at the cts-level. This is a short-term
* test until we have official support for multiple-apps indexing at once.
*/
@Test
public void testGlobalQueryWithMultiplePackages_noPackageFilters() throws Exception {
// Insert package1 schema
List<AppSearchSchema> schema1 =
ImmutableList.of(new AppSearchSchema.Builder("schema1").build());
mAppSearchImpl.setSchema(
"package1",
"database1",
schema1,
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
/*schemasPackageAccessible=*/ Collections.emptyMap(),
/*forceOverride=*/ false);
// Insert package2 schema
List<AppSearchSchema> schema2 =
ImmutableList.of(new AppSearchSchema.Builder("schema2").build());
mAppSearchImpl.setSchema(
"package2",
"database2",
schema2,
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
/*schemasPackageAccessible=*/ Collections.emptyMap(),
/*forceOverride=*/ false);
// Insert package1 document
GenericDocument document1 =
new GenericDocument.Builder<>("uri", "schema1").setNamespace("namespace").build();
mAppSearchImpl.putDocument("package1", "database1", document1);
// Insert package2 document
GenericDocument document2 =
new GenericDocument.Builder<>("uri", "schema2").setNamespace("namespace").build();
mAppSearchImpl.putDocument("package2", "database2", document2);
// No query filters specified, global query can retrieve all documents.
SearchSpec searchSpec =
new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build();
SearchResultPage searchResultPage =
mAppSearchImpl.globalQuery(
"", searchSpec, mContext.getPackageName(), mGlobalQuerierUid);
assertThat(searchResultPage.getResults()).hasSize(2);
// Document2 will be first since it got indexed later and has a "better", aka more recent
// score.
assertThat(searchResultPage.getResults().get(0).getDocument()).isEqualTo(document2);
assertThat(searchResultPage.getResults().get(1).getDocument()).isEqualTo(document1);
}
/**
* TODO(b/169883602): This should be an integration test at the cts-level. This is a short-term
* test until we have official support for multiple-apps indexing at once.
*/
@Test
public void testGlobalQueryWithMultiplePackages_withPackageFilters() throws Exception {
// Insert package1 schema
List<AppSearchSchema> schema1 =
ImmutableList.of(new AppSearchSchema.Builder("schema1").build());
mAppSearchImpl.setSchema(
"package1",
"database1",
schema1,
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
/*schemasPackageAccessible=*/ Collections.emptyMap(),
/*forceOverride=*/ false);
// Insert package2 schema
List<AppSearchSchema> schema2 =
ImmutableList.of(new AppSearchSchema.Builder("schema2").build());
mAppSearchImpl.setSchema(
"package2",
"database2",
schema2,
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
/*schemasPackageAccessible=*/ Collections.emptyMap(),
/*forceOverride=*/ false);
// Insert package1 document
GenericDocument document1 =
new GenericDocument.Builder<>("uri", "schema1").setNamespace("namespace").build();
mAppSearchImpl.putDocument("package1", "database1", document1);
// Insert package2 document
GenericDocument document2 =
new GenericDocument.Builder<>("uri", "schema2").setNamespace("namespace").build();
mAppSearchImpl.putDocument("package2", "database2", document2);
// "package1" filter specified
SearchSpec searchSpec =
new SearchSpec.Builder()
.setTermMatch(TermMatchType.Code.PREFIX_VALUE)
.addFilterPackageNames("package1")
.build();
SearchResultPage searchResultPage =
mAppSearchImpl.globalQuery(
"", searchSpec, mContext.getPackageName(), mGlobalQuerierUid);
assertThat(searchResultPage.getResults()).hasSize(1);
assertThat(searchResultPage.getResults().get(0).getDocument()).isEqualTo(document1);
// "package2" filter specified
searchSpec =
new SearchSpec.Builder()
.setTermMatch(TermMatchType.Code.PREFIX_VALUE)
.addFilterPackageNames("package2")
.build();
searchResultPage =
mAppSearchImpl.globalQuery(
"", searchSpec, mContext.getPackageName(), mGlobalQuerierUid);
assertThat(searchResultPage.getResults()).hasSize(1);
assertThat(searchResultPage.getResults().get(0).getDocument()).isEqualTo(document2);
}
@Test
public void testRemoveEmptyDatabase_noExceptionThrown() throws Exception {
SearchSpec searchSpec =
@@ -873,82 +745,6 @@ public class AppSearchImplTest {
.containsExactlyElementsIn(expectedTypes);
}
@Test
public void testSetSchema_existingSchemaRetainsVisibilitySetting() throws Exception {
// Values for a "foo" client
String packageNameFoo = "packageFoo";
byte[] sha256CertFoo = new byte[] {10};
int uidFoo = 1;
// Make sure foo package will pass package manager checks.
mMockPackageManager.mockGetPackageUid(packageNameFoo, uidFoo);
mMockPackageManager.mockAddSigningCertificate(packageNameFoo, sha256CertFoo);
// Set schema1
String prefix = AppSearchImpl.createPrefix("package", "database");
mAppSearchImpl.setSchema(
"package",
"database",
Collections.singletonList(new AppSearchSchema.Builder("schema1").build()),
/*schemasNotPlatformSurfaceable=*/ Collections.singletonList("schema1"),
/*schemasPackageAccessible=*/ ImmutableMap.of(
"schema1",
ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))),
/*forceOverride=*/ false);
// "schema1" is platform hidden now and package visible to package1
assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
.isSchemaSearchableByCaller(
prefix, prefix + "schema1", mGlobalQuerierUid))
.isFalse();
assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
.isSchemaSearchableByCaller(prefix, prefix + "schema1", uidFoo))
.isTrue();
// Add a new schema, and include the already-existing "schema1"
mAppSearchImpl.setSchema(
"package",
"database",
ImmutableList.of(
new AppSearchSchema.Builder("schema1").build(),
new AppSearchSchema.Builder("schema2").build()),
/*schemasNotPlatformSurfaceable=*/ Collections.singletonList("schema1"),
/*schemasPackageAccessible=*/ ImmutableMap.of(
"schema1",
ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))),
/*forceOverride=*/ false);
// Check that "schema1" still has the same visibility settings
assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
.isSchemaSearchableByCaller(
prefix, prefix + "schema1", mGlobalQuerierUid))
.isFalse();
assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
.isSchemaSearchableByCaller(prefix, prefix + "schema1", uidFoo))
.isTrue();
// "schema2" has default visibility settings
assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
.isSchemaSearchableByCaller(
prefix, prefix + "schema2", mGlobalQuerierUid))
.isTrue();
assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
.isSchemaSearchableByCaller(prefix, prefix + "schema2", uidFoo))
.isFalse();
}
@Test
public void testRemoveSchema() throws Exception {
List<SchemaTypeConfigProto> existingSchemas =
@@ -1120,167 +916,6 @@ public class AppSearchImplTest {
.containsExactlyElementsIn(expectedTypes);
}
@Test
public void testRemoveSchema_removedFromVisibilityStore() throws Exception {
// Values for a "foo" client
String packageNameFoo = "packageFoo";
byte[] sha256CertFoo = new byte[] {10};
int uidFoo = 1;
// Make sure foo package will pass package manager checks.
mMockPackageManager.mockGetPackageUid(packageNameFoo, uidFoo);
mMockPackageManager.mockAddSigningCertificate(packageNameFoo, sha256CertFoo);
String prefix = AppSearchImpl.createPrefix("package", "database");
mAppSearchImpl.setSchema(
"package",
"database",
Collections.singletonList(new AppSearchSchema.Builder("schema1").build()),
/*schemasNotPlatformSurfaceable=*/ Collections.singletonList("schema1"),
/*schemasPackageAccessible=*/ ImmutableMap.of(
"schema1",
ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))),
/*forceOverride=*/ false);
// "schema1" is platform hidden now and package accessible
assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
.isSchemaSearchableByCaller(
prefix, prefix + "schema1", mGlobalQuerierUid))
.isFalse();
assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
.isSchemaSearchableByCaller(prefix, prefix + "schema1", uidFoo))
.isTrue();
// Remove "schema1" by force overriding
mAppSearchImpl.setSchema(
"package",
"database",
/*schemas=*/ Collections.emptyList(),
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
/*schemasPackageAccessible=*/ Collections.emptyMap(),
/*forceOverride=*/ true);
// Check that "schema1" is no longer considered platform hidden or package accessible
assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
.isSchemaSearchableByCaller(
prefix, prefix + "schema1", mGlobalQuerierUid))
.isTrue();
assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
.isSchemaSearchableByCaller(prefix, prefix + "schema1", uidFoo))
.isFalse();
// Add "schema1" back, it gets default visibility settings which means it's not platform
// hidden and not package accessible
mAppSearchImpl.setSchema(
"package",
"database",
Collections.singletonList(new AppSearchSchema.Builder("schema1").build()),
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
/*schemasPackageAccessible=*/ Collections.emptyMap(),
/*forceOverride=*/ false);
assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
.isSchemaSearchableByCaller(
prefix, prefix + "schema1", mGlobalQuerierUid))
.isTrue();
assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
.isSchemaSearchableByCaller(prefix, prefix + "schema1", uidFoo))
.isFalse();
}
@Test
public void testSetSchema_defaultPlatformVisible() throws Exception {
String prefix = AppSearchImpl.createPrefix("package", "database");
mAppSearchImpl.setSchema(
"package",
"database",
Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
/*schemasPackageAccessible=*/ Collections.emptyMap(),
/*forceOverride=*/ false);
assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
.isSchemaSearchableByCaller(
prefix, prefix + "Schema", mGlobalQuerierUid))
.isTrue();
}
@Test
public void testSetSchema_platformHidden() throws Exception {
String prefix = AppSearchImpl.createPrefix("package", "database");
mAppSearchImpl.setSchema(
"package",
"database",
Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
/*schemasNotPlatformSurfaceable=*/ Collections.singletonList("Schema"),
/*schemasPackageAccessible=*/ Collections.emptyMap(),
/*forceOverride=*/ false);
assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
.isSchemaSearchableByCaller(
prefix, prefix + "Schema", mGlobalQuerierUid))
.isFalse();
}
@Test
public void testSetSchema_defaultNotPackageAccessible() throws Exception {
String prefix = AppSearchImpl.createPrefix("package", "database");
mAppSearchImpl.setSchema(
"package",
"database",
Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
/*schemasPackageAccessible=*/ Collections.emptyMap(),
/*forceOverride=*/ false);
assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
.isSchemaSearchableByCaller(
prefix, prefix + "Schema", /*callerUid=*/ 42))
.isFalse();
}
@Test
public void testSetSchema_packageAccessible() throws Exception {
// Values for a "foo" client
String packageNameFoo = "packageFoo";
byte[] sha256CertFoo = new byte[] {10};
int uidFoo = 1;
// Make sure foo package will pass package manager checks.
mMockPackageManager.mockGetPackageUid(packageNameFoo, uidFoo);
mMockPackageManager.mockAddSigningCertificate(packageNameFoo, sha256CertFoo);
String prefix = AppSearchImpl.createPrefix("package", "database");
mAppSearchImpl.setSchema(
"package",
"database",
Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
/*schemasPackageAccessible=*/ ImmutableMap.of(
"Schema",
ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))),
/*forceOverride=*/ false);
assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
.isSchemaSearchableByCaller(prefix, prefix + "Schema", uidFoo))
.isTrue();
}
@Test
public void testHasSchemaType() throws Exception {
// Nothing exists yet
@@ -1300,14 +935,12 @@ public class AppSearchImplTest {
}
@Test
public void testGetDatabases() throws Exception {
// No client databases exist yet, but the VisibilityStore's does
assertThat(mAppSearchImpl.getPrefixesLocked())
.containsExactly(
AppSearchImpl.createPrefix(
VisibilityStore.PACKAGE_NAME, VisibilityStore.DATABASE_NAME));
public void testGetPrefixes() throws Exception {
Set<String> existingPrefixes = mAppSearchImpl.getPrefixesLocked();
// Has database1
Set<String> expectedPrefixes = new ArraySet<>(existingPrefixes);
expectedPrefixes.add(AppSearchImpl.createPrefix("package", "database1"));
mAppSearchImpl.setSchema(
"package",
"database1",
@@ -1315,13 +948,10 @@ public class AppSearchImplTest {
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
/*schemasPackageAccessible=*/ Collections.emptyMap(),
/*forceOverride=*/ false);
assertThat(mAppSearchImpl.getPrefixesLocked())
.containsExactly(
AppSearchImpl.createPrefix(
VisibilityStore.PACKAGE_NAME, VisibilityStore.DATABASE_NAME),
AppSearchImpl.createPrefix("package", "database1"));
assertThat(mAppSearchImpl.getPrefixesLocked()).containsExactlyElementsIn(expectedPrefixes);
// Has both databases
expectedPrefixes.add(AppSearchImpl.createPrefix("package", "database2"));
mAppSearchImpl.setSchema(
"package",
"database2",
@@ -1329,12 +959,7 @@ public class AppSearchImplTest {
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
/*schemasPackageAccessible=*/ Collections.emptyMap(),
/*forceOverride=*/ false);
assertThat(mAppSearchImpl.getPrefixesLocked())
.containsExactly(
AppSearchImpl.createPrefix(
VisibilityStore.PACKAGE_NAME, VisibilityStore.DATABASE_NAME),
AppSearchImpl.createPrefix("package", "database1"),
AppSearchImpl.createPrefix("package", "database2"));
assertThat(mAppSearchImpl.getPrefixesLocked()).containsExactlyElementsIn(expectedPrefixes);
}
@Test

View File

@@ -1,82 +0,0 @@
/*
* Copyright 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.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.content.pm.PackageManager;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
/**
* Mock to help test package name, UID, and certificate verification
*
* @hide
*/
public class MockPackageManager {
@Mock private PackageManager mMockPackageManager;
public MockPackageManager() {
MockitoAnnotations.initMocks(this);
}
@NonNull
public PackageManager getMockPackageManager() {
return mMockPackageManager;
}
/** Mock a NameNotFoundException if the package name isn't installed. */
public void mockThrowsNameNotFoundException(String packageName) {
try {
when(mMockPackageManager.getPackageUid(eq(packageName), /*flags=*/ 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 mockGetPackageUid(String packageName, int uid) {
try {
when(mMockPackageManager.getPackageUid(eq(packageName), /*flags=*/ anyInt()))
.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

@@ -1,318 +0,0 @@
/*
* Copyright 2020 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.external.localstorage;
import static com.google.common.truth.Truth.assertThat;
import android.app.appsearch.PackageIdentifier;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.PackageManager;
import androidx.test.core.app.ApplicationProvider;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import java.util.Collections;
public class VisibilityStoreTest {
@Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
private MockPackageManager mMockPackageManager = new MockPackageManager();
private Context mContext;
private AppSearchImpl mAppSearchImpl;
private VisibilityStore mVisibilityStore;
private int mGlobalQuerierUid;
@Before
public void setUp() throws Exception {
Context context = ApplicationProvider.getApplicationContext();
mContext =
new ContextWrapper(context) {
@Override
public PackageManager getPackageManager() {
return mMockPackageManager.getMockPackageManager();
}
};
// Give ourselves global query permissions
mAppSearchImpl =
AppSearchImpl.create(
mTemporaryFolder.newFolder(),
mContext,
/*globalQuerierPackage=*/ mContext.getPackageName());
mGlobalQuerierUid =
mContext.getPackageManager().getPackageUid(mContext.getPackageName(), /*flags=*/ 0);
mVisibilityStore = mAppSearchImpl.getVisibilityStoreLocked();
}
/**
* Make sure that we don't conflict with any special characters that AppSearchImpl has reserved.
*/
@Test
public void testValidPackageName() {
assertThat(VisibilityStore.PACKAGE_NAME)
.doesNotContain(
"" + AppSearchImpl.PACKAGE_DELIMITER); // Convert the chars to CharSequences
assertThat(VisibilityStore.PACKAGE_NAME)
.doesNotContain(
""
+ AppSearchImpl
.DATABASE_DELIMITER); // Convert the chars to CharSequences
}
/**
* Make sure that we don't conflict with any special characters that AppSearchImpl has reserved.
*/
@Test
public void testValidDatabaseName() {
assertThat(VisibilityStore.DATABASE_NAME)
.doesNotContain(
"" + AppSearchImpl.PACKAGE_DELIMITER); // Convert the chars to CharSequences
assertThat(VisibilityStore.DATABASE_NAME)
.doesNotContain(
""
+ AppSearchImpl
.DATABASE_DELIMITER); // Convert the chars to CharSequences
}
@Test
public void testSetVisibility_platformSurfaceable() throws Exception {
mVisibilityStore.setVisibility(
"prefix",
/*schemasNotPlatformSurfaceable=*/ ImmutableSet.of(
"prefix/schema1", "prefix/schema2"),
/*schemasPackageAccessible=*/ Collections.emptyMap());
assertThat(
mVisibilityStore.isSchemaSearchableByCaller(
"prefix", "prefix/schema1", mGlobalQuerierUid))
.isFalse();
assertThat(
mVisibilityStore.isSchemaSearchableByCaller(
"prefix", "prefix/schema2", mGlobalQuerierUid))
.isFalse();
// New .setVisibility() call completely overrides previous visibility settings. So
// "schema2" isn't preserved.
mVisibilityStore.setVisibility(
"prefix",
/*schemasNotPlatformSurfaceable=*/ ImmutableSet.of(
"prefix/schema1", "prefix/schema3"),
/*schemasPackageAccessible=*/ Collections.emptyMap());
assertThat(
mVisibilityStore.isSchemaSearchableByCaller(
"prefix", "prefix/schema1", mGlobalQuerierUid))
.isFalse();
assertThat(
mVisibilityStore.isSchemaSearchableByCaller(
"prefix", "prefix/schema2", mGlobalQuerierUid))
.isTrue();
assertThat(
mVisibilityStore.isSchemaSearchableByCaller(
"prefix", "prefix/schema3", mGlobalQuerierUid))
.isFalse();
// Everything defaults to visible again.
mVisibilityStore.setVisibility(
"prefix",
/*schemasNotPlatformSurfaceable=*/ Collections.emptySet(),
/*schemasPackageAccessible=*/ Collections.emptyMap());
assertThat(
mVisibilityStore.isSchemaSearchableByCaller(
"prefix", "prefix/schema1", mGlobalQuerierUid))
.isTrue();
assertThat(
mVisibilityStore.isSchemaSearchableByCaller(
"prefix", "prefix/schema2", mGlobalQuerierUid))
.isTrue();
assertThat(
mVisibilityStore.isSchemaSearchableByCaller(
"prefix", "prefix/schema3", mGlobalQuerierUid))
.isTrue();
}
@Test
public void testIsSchemaSearchableByCaller_platformQuerierHandlesNameNotFoundException()
throws Exception {
// Initialized the VisibilityStore with this context's package name as the global querier.
mMockPackageManager.mockThrowsNameNotFoundException(mContext.getPackageName());
// Create a new VisibilityStore instance since we look up the UID on initialization
AppSearchImpl appSearchImpl =
AppSearchImpl.create(
mTemporaryFolder.newFolder(),
mContext,
/*globalQuerierPackage=*/ mContext.getPackageName());
VisibilityStore visibilityStore = appSearchImpl.getVisibilityStoreLocked();
// Use some arbitrary callerUid. If we can't find the global querier's uid though,
// nothing should be platform surfaceable.
assertThat(
visibilityStore.isSchemaSearchableByCaller(
"prefix", "prefix/schemaFoo", /*callerUid=*/ 0))
.isFalse();
}
@Test
public void testSetVisibility_packageAccessible() throws Exception {
// Values for a "foo" client
String packageNameFoo = "packageFoo";
byte[] sha256CertFoo = new byte[] {10};
int uidFoo = 1;
// Values for a "bar" client
String packageNameBar = "packageBar";
byte[] sha256CertBar = new byte[] {100};
int uidBar = 2;
// Can't be the same value as uidFoo nor uidBar
int uidNotFooOrBar = 3;
// By default, a schema isn't package accessible.
assertThat(
mVisibilityStore.isSchemaSearchableByCaller(
"prefix", "prefix/schemaFoo", uidFoo))
.isFalse();
assertThat(
mVisibilityStore.isSchemaSearchableByCaller(
"prefix", "prefix/schemaBar", uidBar))
.isFalse();
// Grant package access
mVisibilityStore.setVisibility(
"prefix",
/*schemasNotPlatformSurfaceable=*/ Collections.emptySet(),
/*schemasPackageAccessible=*/ ImmutableMap.of(
"prefix/schemaFoo",
ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo)),
"prefix/schemaBar",
ImmutableList.of(new PackageIdentifier(packageNameBar, sha256CertBar))));
// Should fail if PackageManager doesn't see that it has the proper certificate
mMockPackageManager.mockGetPackageUid(packageNameFoo, uidFoo);
mMockPackageManager.mockRemoveSigningCertificate(packageNameFoo, sha256CertFoo);
assertThat(
mVisibilityStore.isSchemaSearchableByCaller(
"prefix", "prefix/schemaFoo", uidFoo))
.isFalse();
// Should fail if PackageManager doesn't think the package belongs to the uid
mMockPackageManager.mockGetPackageUid(packageNameFoo, uidNotFooOrBar);
mMockPackageManager.mockAddSigningCertificate(packageNameFoo, sha256CertFoo);
assertThat(
mVisibilityStore.isSchemaSearchableByCaller(
"prefix", "prefix/schemaFoo", uidFoo))
.isFalse();
// But if uid and certificate match, then we should have access
mMockPackageManager.mockGetPackageUid(packageNameFoo, uidFoo);
mMockPackageManager.mockAddSigningCertificate(packageNameFoo, sha256CertFoo);
assertThat(
mVisibilityStore.isSchemaSearchableByCaller(
"prefix", "prefix/schemaFoo", uidFoo))
.isTrue();
mMockPackageManager.mockGetPackageUid(packageNameBar, uidBar);
mMockPackageManager.mockAddSigningCertificate(packageNameBar, sha256CertBar);
assertThat(
mVisibilityStore.isSchemaSearchableByCaller(
"prefix", "prefix/schemaBar", uidBar))
.isTrue();
// New .setVisibility() call completely overrides previous visibility settings. So
// "schemaBar" settings aren't preserved.
mVisibilityStore.setVisibility(
"prefix",
/*schemasNotPlatformSurfaceable=*/ Collections.emptySet(),
/*schemasPackageAccessible=*/ ImmutableMap.of(
"prefix/schemaFoo",
ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))));
mMockPackageManager.mockGetPackageUid(packageNameFoo, uidFoo);
mMockPackageManager.mockAddSigningCertificate(packageNameFoo, sha256CertFoo);
assertThat(
mVisibilityStore.isSchemaSearchableByCaller(
"prefix", "prefix/schemaFoo", uidFoo))
.isTrue();
mMockPackageManager.mockGetPackageUid(packageNameBar, uidBar);
mMockPackageManager.mockAddSigningCertificate(packageNameBar, sha256CertBar);
assertThat(
mVisibilityStore.isSchemaSearchableByCaller(
"prefix", "prefix/schemaBar", uidBar))
.isFalse();
}
@Test
public void testIsSchemaSearchableByCaller_packageAccessibilityHandlesNameNotFoundException()
throws Exception {
// Values for a "foo" client
String packageNameFoo = "packageFoo";
byte[] sha256CertFoo = new byte[] {10};
int uidFoo = 1;
// Pretend we can't find the Foo package.
mMockPackageManager.mockThrowsNameNotFoundException(packageNameFoo);
// Grant package access
mVisibilityStore.setVisibility(
"prefix",
/*schemasNotPlatformSurfaceable=*/ Collections.emptySet(),
/*schemasPackageAccessible=*/ ImmutableMap.of(
"prefix/schemaFoo",
ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))));
// If we can't verify the Foo package that has access, assume it doesn't have access.
assertThat(
mVisibilityStore.isSchemaSearchableByCaller(
"prefix", "prefix/schemaFoo", uidFoo))
.isFalse();
}
@Test
public void testEmptyPrefix() throws Exception {
// Values for a "foo" client
String packageNameFoo = "packageFoo";
byte[] sha256CertFoo = new byte[] {10};
int uidFoo = 1;
mVisibilityStore.setVisibility(
/*prefix=*/ "",
/*schemasNotPlatformSurfaceable=*/ Collections.emptySet(),
/*schemasPackageAccessible=*/ ImmutableMap.of(
"schema",
ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))));
assertThat(
mVisibilityStore.isSchemaSearchableByCaller(
/*prefix=*/ "", "schema", mGlobalQuerierUid))
.isTrue();
mMockPackageManager.mockGetPackageUid(packageNameFoo, uidFoo);
mMockPackageManager.mockAddSigningCertificate(packageNameFoo, sha256CertFoo);
assertThat(mVisibilityStore.isSchemaSearchableByCaller(/*prefix=*/ "", "schema", uidFoo))
.isTrue();
}
}