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:
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1 +1 @@
|
||||
Iff96eae150c7cdd281c9ecb5d93f4ef697e89f1a
|
||||
I03df55376689c1557c651d5b9671c40da1c35955
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user