blob: beda8777ef841942e60293529688a83f7f39907e [file] [log] [blame]
/*
* Copyright (C) 2022 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 android.healthconnect.tests.withmanagepermissions;
import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.healthconnect.HealthConnectManager;
import android.healthconnect.HealthPermissions;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.Arrays;
import java.util.List;
/**
* Integration tests for {@link android.healthconnect.HealthConnectManager} Permission-related APIs.
*
* <p><b>Note:</b> These tests operate while holding {@link
* HealthPermissions.MANAGE_HEALTH_PERMISSIONS}. For tests asserting that non-holders of the
* permission cannot call the APIs, please see {@link
* android.healthconnect.tests.withoutmanagepermissions.HealthConnectWithoutManagePermissionsTest}.
*
* <p><b>Note:</b> Since we need to hold the aforementioned permission, this test needs to be signed
* with the same certificate as the HealthConnect module. Therefore, <b>we skip this test when it
* cannot hold {@link HealthPermissions.MANAGE_HEALTH_PERMISSIONS}. The primary use of these tests
* is therefore during development, when we are building from source rather than using
* prebuilts</b>. Additionally, this test can run as a presubmit on the main (master) branch where
* modules are always built from source.
*
* <p><b>Build/Install/Run:</b> {@code atest HealthConnectWithManagePermissionsIntegrationTests}.
*/
@RunWith(AndroidJUnit4.class)
public class HealthConnectWithManagePermissionsTest {
private static final String DEFAULT_APP_PACKAGE = "android.healthconnect.test.app";
private static final String INEXISTENT_APP_PACKAGE = "my.invalid.package.name";
private static final String DEFAULT_PERM = HealthPermissions.READ_ACTIVE_CALORIES_BURNED;
private static final String DEFAULT_PERM_2 = HealthPermissions.WRITE_ACTIVE_CALORIES_BURNED;
private static final String UNDECLARED_PERM = HealthPermissions.READ_DISTANCE;
private static final String INVALID_PERM = "android.permission.health.MY_INVALID_PERM";
private static final String NON_HEALTH_PERM = Manifest.permission.ACCESS_COARSE_LOCATION;
private Context mContext;
private HealthConnectManager mHealthConnectManager;
@Before
public void setUp() throws Exception {
mContext = InstrumentationRegistry.getTargetContext();
assumeTrue(
"Skipping test - test cannot hold "
+ HealthPermissions.MANAGE_HEALTH_PERMISSIONS
+ " for the build-under-test. This is likely because the build uses "
+ "mainline prebuilts and therefore does not have a compatible signature "
+ "with this test app. See the test class Javadoc for more info.",
canHoldManageHealthPermsPermission());
mHealthConnectManager = mContext.getSystemService(HealthConnectManager.class);
revokePermissionViaPackageManager(DEFAULT_APP_PACKAGE, DEFAULT_PERM);
revokePermissionViaPackageManager(DEFAULT_APP_PACKAGE, DEFAULT_PERM_2);
assertPermNotGrantedForApp(DEFAULT_APP_PACKAGE, DEFAULT_PERM);
assertPermNotGrantedForApp(DEFAULT_APP_PACKAGE, DEFAULT_PERM_2);
}
@Test
public void testGrantHealthPermission_appHasPermissionDeclared_success() throws Exception {
mHealthConnectManager.grantHealthPermission(DEFAULT_APP_PACKAGE, DEFAULT_PERM);
assertPermGrantedForApp(DEFAULT_APP_PACKAGE, DEFAULT_PERM);
}
@Test
public void testGrantHealthPermission_permissionAlreadyGranted_success() throws Exception {
mHealthConnectManager.grantHealthPermission(DEFAULT_APP_PACKAGE, DEFAULT_PERM);
assertPermGrantedForApp(DEFAULT_APP_PACKAGE, DEFAULT_PERM);
// Let's regrant it
mHealthConnectManager.grantHealthPermission(DEFAULT_APP_PACKAGE, DEFAULT_PERM);
assertPermGrantedForApp(DEFAULT_APP_PACKAGE, DEFAULT_PERM);
}
@Test(expected = SecurityException.class)
public void testGrantHealthPermission_appHasPermissionNotDeclared_throwsSecurityException()
throws Exception {
mHealthConnectManager.grantHealthPermission(DEFAULT_APP_PACKAGE, UNDECLARED_PERM);
fail("Expected SecurityException due permission not being declared by target app.");
}
@Test(expected = IllegalArgumentException.class)
public void testGrantHealthPermission_invalidPermission_throwsIllegalArgumentException()
throws Exception {
mHealthConnectManager.grantHealthPermission(DEFAULT_APP_PACKAGE, INVALID_PERM);
fail("Expected IllegalArgumentException due to invalid permission.");
}
@Test(expected = IllegalArgumentException.class)
public void testGrantHealthPermission_nonHealthPermission_throwsIllegalArgumentException()
throws Exception {
mHealthConnectManager.grantHealthPermission(DEFAULT_APP_PACKAGE, NON_HEALTH_PERM);
fail("Expected IllegalArgumentException due to non-health permission.");
}
@Test(expected = IllegalArgumentException.class)
public void testGrantHealthPermission_invalidPackageName_throwsIllegalArgumentException()
throws Exception {
mHealthConnectManager.grantHealthPermission(INEXISTENT_APP_PACKAGE, DEFAULT_PERM);
fail("Expected IllegalArgumentException due to invalid package.");
}
@Test(expected = NullPointerException.class)
public void testGrantHealthPermission_nullPermission_throwsNPE() throws Exception {
mHealthConnectManager.grantHealthPermission(
DEFAULT_APP_PACKAGE, /* permissionName= */ null);
fail("Expected NullPointerException due to null permission.");
}
@Test(expected = NullPointerException.class)
public void testGrantHealthPermission_nullPackageName_throwsNPE() throws Exception {
mHealthConnectManager.grantHealthPermission(/* packageName= */ null, DEFAULT_PERM);
fail("Expected NullPointerException due to null package.");
}
@Test
public void testRevokeHealthPermission_appHasPermissionGranted_success() throws Exception {
grantPermissionViaPackageManager(DEFAULT_APP_PACKAGE, DEFAULT_PERM);
mHealthConnectManager.revokeHealthPermission(
DEFAULT_APP_PACKAGE, DEFAULT_PERM, /* reason= */ null);
assertPermNotGrantedForApp(DEFAULT_APP_PACKAGE, DEFAULT_PERM);
}
@Test
public void testRevokeHealthPermission_appHasPermissionNotGranted_success() throws Exception {
mHealthConnectManager.revokeHealthPermission(
DEFAULT_APP_PACKAGE, DEFAULT_PERM, /* reason= */ null);
assertPermNotGrantedForApp(DEFAULT_APP_PACKAGE, DEFAULT_PERM);
}
@Test(expected = SecurityException.class)
public void testRevokeHealthPermission_appHasPermissionNotDeclared_throwsSecurityException()
throws Exception {
mHealthConnectManager.revokeHealthPermission(
DEFAULT_APP_PACKAGE, UNDECLARED_PERM, /* reason= */ null);
fail("Expected SecurityException due permission not being declared by target app.");
}
@Test(expected = IllegalArgumentException.class)
public void testRevokeHealthPermission_invalidPermission_throwsIllegalArgumentException()
throws Exception {
mHealthConnectManager.revokeHealthPermission(
DEFAULT_APP_PACKAGE, INVALID_PERM, /* reason= */ null);
fail("Expected IllegalArgumentException due to invalid permission.");
}
@Test(expected = IllegalArgumentException.class)
public void testRevokeHealthPermission_nonHealthPermission_throwsIllegalArgumentException()
throws Exception {
mHealthConnectManager.revokeHealthPermission(
DEFAULT_APP_PACKAGE, NON_HEALTH_PERM, /* reason= */ null);
fail("Expected IllegalArgumentException due to non-health permission.");
}
@Test(expected = IllegalArgumentException.class)
public void testRevokeHealthPermission_invalidPackageName_throwsIllegalArgumentException()
throws Exception {
mHealthConnectManager.revokeHealthPermission(
INEXISTENT_APP_PACKAGE, DEFAULT_PERM, /* reason= */ null);
fail("Expected IllegalArgumentException due to invalid package.");
}
@Test(expected = NullPointerException.class)
public void testRevokeHealthPermission_nullPermission_throwsNPE() throws Exception {
mHealthConnectManager.revokeHealthPermission(
DEFAULT_APP_PACKAGE, /* permissionName= */ null, /* reason= */ null);
fail("Expected NullPointerException due to null permission.");
}
@Test(expected = NullPointerException.class)
public void testRevokeHealthPermission_nullPackageName_throwsNPE() throws Exception {
mHealthConnectManager.revokeHealthPermission(
/* packageName= */ null, DEFAULT_PERM, /* reason= */ null);
fail("Expected NullPointerException due to null package.");
}
@Test
public void testGetGrantedHealthPermissions_appHasNoPermissionGranted_emptyList()
throws Exception {
List<String> grantedPerms =
mHealthConnectManager.getGrantedHealthPermissions(DEFAULT_APP_PACKAGE);
assertThat(grantedPerms.size()).isEqualTo(0);
}
@Test
public void testGetGrantedHealthPermissions_appHasPermissionsGranted_success()
throws Exception {
grantPermissionViaPackageManager(DEFAULT_APP_PACKAGE, DEFAULT_PERM);
grantPermissionViaPackageManager(DEFAULT_APP_PACKAGE, DEFAULT_PERM_2);
List<String> grantedPerms =
mHealthConnectManager.getGrantedHealthPermissions(DEFAULT_APP_PACKAGE);
assertThat(grantedPerms)
.containsExactlyElementsIn(
Arrays.asList(new String[] {DEFAULT_PERM, DEFAULT_PERM_2}));
}
@Test(expected = IllegalArgumentException.class)
public void testGetGrantedHealthPermissions_invalidPackageName_throwsIllegalArgumentException()
throws Exception {
mHealthConnectManager.getGrantedHealthPermissions(INEXISTENT_APP_PACKAGE);
fail("Expected IllegalArgumentException due to invalid package.");
}
@Test(expected = NullPointerException.class)
public void testGetGrantedHealthPermissions_nullPackageName_throwsNPE() throws Exception {
mHealthConnectManager.getGrantedHealthPermissions(/* packageName= */ null);
fail("Expected NullPointerException due to null package.");
}
@Test
public void testRevokeAllHealthPermissions_appHasNoPermissionsGranted_success()
throws Exception {
mHealthConnectManager.revokeAllHealthPermissions(DEFAULT_APP_PACKAGE, /* reason= */ null);
assertThat(mHealthConnectManager.getGrantedHealthPermissions(DEFAULT_APP_PACKAGE).size())
.isEqualTo(0);
assertPermNotGrantedForApp(DEFAULT_APP_PACKAGE, DEFAULT_PERM);
assertPermNotGrantedForApp(DEFAULT_APP_PACKAGE, DEFAULT_PERM_2);
}
@Test
public void testRevokeAllHealthPermissions_appHasPermissionsGranted_success() throws Exception {
grantPermissionViaPackageManager(DEFAULT_APP_PACKAGE, DEFAULT_PERM);
grantPermissionViaPackageManager(DEFAULT_APP_PACKAGE, DEFAULT_PERM_2);
mHealthConnectManager.revokeAllHealthPermissions(DEFAULT_APP_PACKAGE, /* reason= */ null);
assertThat(mHealthConnectManager.getGrantedHealthPermissions(DEFAULT_APP_PACKAGE).size())
.isEqualTo(0);
assertPermNotGrantedForApp(DEFAULT_APP_PACKAGE, DEFAULT_PERM);
assertPermNotGrantedForApp(DEFAULT_APP_PACKAGE, DEFAULT_PERM_2);
}
@Test(expected = IllegalArgumentException.class)
public void testRevokeAllHealthPermissions_invalidPackageName_throwsIllegalArgumentException()
throws Exception {
mHealthConnectManager.revokeAllHealthPermissions(
INEXISTENT_APP_PACKAGE, /* reason= */ null);
fail("Expected IllegalArgumentException due to invalid package.");
}
@Test(expected = NullPointerException.class)
public void testRevokeAllHealthPermissions_nullPackageName_throwsNPE() throws Exception {
mHealthConnectManager.revokeAllHealthPermissions(
/* packageName= */ null, /* reason= */ null);
fail("Expected NullPointerException due to null package.");
}
private void grantPermissionViaPackageManager(String packageName, String permName) {
runWithShellPermissionIdentity(
() ->
mContext.getPackageManager()
.grantRuntimePermission(packageName, permName, mContext.getUser()),
Manifest.permission.GRANT_RUNTIME_PERMISSIONS);
}
private void revokePermissionViaPackageManager(String packageName, String permName) {
runWithShellPermissionIdentity(
() ->
mContext.getPackageManager()
.revokeRuntimePermission(
packageName,
permName,
mContext.getUser(),
/* reason= */ null),
Manifest.permission.REVOKE_RUNTIME_PERMISSIONS);
}
private void assertPermGrantedForApp(String packageName, String permName) throws Exception {
assertThat(mContext.getPackageManager().checkPermission(permName, packageName))
.isEqualTo(PackageManager.PERMISSION_GRANTED);
}
private void assertPermNotGrantedForApp(String packageName, String permName) throws Exception {
assertThat(mContext.getPackageManager().checkPermission(permName, packageName))
.isEqualTo(PackageManager.PERMISSION_DENIED);
}
/**
* Returns {@code true} if the test can hold {@link
* HealthPermissions.MANAGE_HEALTH_PERMISSIONS}.
*
* <p>The permission is protected at signature level, and should be granted automatically if
* it's possible to hold. So effectively this method can simply check if this test's package
* holds the permission.
*/
private static boolean canHoldManageHealthPermsPermission() {
return InstrumentationRegistry.getContext()
.checkSelfPermission(HealthPermissions.MANAGE_HEALTH_PERMISSIONS)
== PackageManager.PERMISSION_GRANTED;
}
}