blob: 755795db184b9ea993707ec00271c19591370bc1 [file] [log] [blame]
/*
* 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 it can access
// AppSearchImpl's methods without having to make them public. This should be replaced by proper
// global query integration tests that can test AppSearchImpl-VisibilityStore integration logic.
package com.android.server.appsearch.external.localstorage;
import static android.Manifest.permission.READ_GLOBAL_APP_SEARCH_DATA;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static com.google.common.truth.Truth.assertThat;
import android.app.appsearch.AppSearchSchema;
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.android.compatibility.common.util.SystemUtil;
import com.android.server.appsearch.external.localstorage.util.PrefixUtil;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import java.util.Collections;
/** 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 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,
mContext.getUserId(),
/*logger=*/ null);
mGlobalQuerierUid =
mContext.getPackageManager().getPackageUid(mContext.getPackageName(), /*flags=*/ 0);
}
@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.mockGetPackageUidAsUser(packageNameFoo, mContext.getUserId(), uidFoo);
mMockPackageManager.mockAddSigningCertificate(packageNameFoo, sha256CertFoo);
// 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);
// Set schema1
String prefix = PrefixUtil.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,
/*schemaVersion=*/ 0);
// "schema1" is platform hidden now and package visible to package1
assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
.isSchemaSearchableByCaller(
"package",
"database",
prefix + "schema1",
mContext.getPackageName(),
mGlobalQuerierUid))
.isFalse();
assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
.isSchemaSearchableByCaller(
"package",
"database",
prefix + "schema1",
packageNameFoo,
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,
/*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();
},
READ_GLOBAL_APP_SEARCH_DATA);
assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
.isSchemaSearchableByCaller(
"package",
"database",
prefix + "schema1",
packageNameFoo,
uidFoo))
.isTrue();
// "schema2" has default visibility settings
SystemUtil.runWithShellPermissionIdentity(
() -> {
assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
.isSchemaSearchableByCaller(
"package",
"database",
prefix + "schema2",
mContext.getPackageName(),
mGlobalQuerierUid))
.isTrue();
},
READ_GLOBAL_APP_SEARCH_DATA);
assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
.isSchemaSearchableByCaller(
"package",
"database",
prefix + "schema2",
packageNameFoo,
uidFoo))
.isFalse();
}
@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.mockGetPackageUidAsUser(packageNameFoo, mContext.getUserId(), uidFoo);
mMockPackageManager.mockAddSigningCertificate(packageNameFoo, sha256CertFoo);
// 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);
String prefix = PrefixUtil.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,
/*schemaVersion=*/ 0);
// "schema1" is platform hidden now and package accessible
assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
.isSchemaSearchableByCaller(
"package",
"database",
prefix + "schema1",
mContext.getPackageName(),
mGlobalQuerierUid))
.isFalse();
assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
.isSchemaSearchableByCaller(
"package",
"database",
prefix + "schema1",
packageNameFoo,
uidFoo))
.isTrue();
// Remove "schema1" by force overriding
mAppSearchImpl.setSchema(
"package",
"database",
/*schemas=*/ Collections.emptyList(),
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
/*schemasPackageAccessible=*/ Collections.emptyMap(),
/*forceOverride=*/ true,
/*schemaVersion=*/ 0);
// Check that "schema1" is no longer considered platform hidden or package accessible
assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
.isSchemaSearchableByCaller(
"package",
"database",
prefix + "schema1",
mContext.getPackageName(),
mGlobalQuerierUid))
.isTrue();
assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
.isSchemaSearchableByCaller(
"package",
"database",
prefix + "schema1",
packageNameFoo,
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,
/*schemaVersion=*/ 0);
assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
.isSchemaSearchableByCaller(
"package",
"database",
prefix + "schema1",
mContext.getPackageName(),
mGlobalQuerierUid))
.isTrue();
assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
.isSchemaSearchableByCaller(
"package",
"database",
prefix + "schema1",
packageNameFoo,
uidFoo))
.isFalse();
}
@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);
String prefix = PrefixUtil.createPrefix("package", "database");
mAppSearchImpl.setSchema(
"package",
"database",
Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
/*schemasPackageAccessible=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*schemaVersion=*/ 0);
assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
.isSchemaSearchableByCaller(
"package",
"database",
prefix + "Schema",
mContext.getPackageName(),
mGlobalQuerierUid))
.isTrue();
}
@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);
String prefix = PrefixUtil.createPrefix("package", "database");
mAppSearchImpl.setSchema(
"package",
"database",
Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
/*schemasNotPlatformSurfaceable=*/ Collections.singletonList("Schema"),
/*schemasPackageAccessible=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*schemaVersion=*/ 0);
assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
.isSchemaSearchableByCaller(
"package",
"database",
prefix + "Schema",
mContext.getPackageName(),
mGlobalQuerierUid))
.isFalse();
}
@Test
public void testSetSchema_defaultNotPackageAccessible() throws Exception {
String packageName = "com.package";
// Make sure package doesn't global query privileges
mMockPackageManager.mockCheckPermission(
READ_GLOBAL_APP_SEARCH_DATA, packageName, PERMISSION_DENIED);
String prefix = PrefixUtil.createPrefix("package", "database");
mAppSearchImpl.setSchema(
"package",
"database",
Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
/*schemasPackageAccessible=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*schemaVersion=*/ 0);
assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
.isSchemaSearchableByCaller(
"package",
"database",
prefix + "Schema",
packageName,
/*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.mockGetPackageUidAsUser(packageNameFoo, mContext.getUserId(), uidFoo);
mMockPackageManager.mockAddSigningCertificate(packageNameFoo, sha256CertFoo);
// Make sure foo doesn't have global query privileges
mMockPackageManager.mockCheckPermission(
READ_GLOBAL_APP_SEARCH_DATA, packageNameFoo, PERMISSION_DENIED);
String prefix = PrefixUtil.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,
/*schemaVersion=*/ 0);
assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
.isSchemaSearchableByCaller(
"package",
"database",
prefix + "Schema",
packageNameFoo,
uidFoo))
.isTrue();
}
}