Add more checks for location access
Add checks as detailed in the frameworks/base change, and modify
testapps to be able to test the changes.
Bug: 116258458
Test: testapps
Change-Id: I7eec05d80b1dd0b458342f1b6a026f4c0a23e773
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index e251516..d21f369 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -35,6 +35,7 @@
import android.net.Uri;
import android.os.AsyncResult;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -156,6 +157,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -1841,9 +1843,21 @@
public Bundle getCellLocation(String callingPackage) {
mApp.getSystemService(AppOpsManager.class)
.checkPackage(Binder.getCallingUid(), callingPackage);
- if (!LocationAccessPolicy.canAccessCellLocation(mApp, callingPackage,
- Binder.getCallingUid(), Binder.getCallingPid(), true)) {
- return null;
+
+ LocationAccessPolicy.LocationPermissionResult locationResult =
+ LocationAccessPolicy.checkLocationPermission(mApp,
+ new LocationAccessPolicy.LocationPermissionQuery.Builder()
+ .setCallingPackage(callingPackage)
+ .setCallingPid(Binder.getCallingPid())
+ .setCallingUid(Binder.getCallingUid())
+ .setMethod("getCellLocation")
+ .setMinSdkVersionForFine(Build.VERSION_CODES.Q)
+ .build());
+ switch (locationResult) {
+ case DENIED_HARD:
+ throw new SecurityException("Not allowed to access cell location");
+ case DENIED_SOFT:
+ return new Bundle();
}
WorkSource workSource = getWorkSource(Binder.getCallingUid());
@@ -1993,9 +2007,21 @@
public List<CellInfo> getAllCellInfo(String callingPackage) {
mApp.getSystemService(AppOpsManager.class)
.checkPackage(Binder.getCallingUid(), callingPackage);
- if (!LocationAccessPolicy.canAccessCellLocation(mApp,
- callingPackage, Binder.getCallingUid(), Binder.getCallingPid(), true)) {
- return null;
+
+ LocationAccessPolicy.LocationPermissionResult locationResult =
+ LocationAccessPolicy.checkLocationPermission(mApp,
+ new LocationAccessPolicy.LocationPermissionQuery.Builder()
+ .setCallingPackage(callingPackage)
+ .setCallingPid(Binder.getCallingPid())
+ .setCallingUid(Binder.getCallingUid())
+ .setMethod("getAllCellInfo")
+ .setMinSdkVersionForFine(Build.VERSION_CODES.Q)
+ .build());
+ switch (locationResult) {
+ case DENIED_HARD:
+ throw new SecurityException("Not allowed to access cell info");
+ case DENIED_SOFT:
+ return new ArrayList<>();
}
final int targetSdk = getTargetSdk(callingPackage);
@@ -2036,9 +2062,21 @@
int subId, ICellInfoCallback cb, String callingPackage, WorkSource workSource) {
mApp.getSystemService(AppOpsManager.class)
.checkPackage(Binder.getCallingUid(), callingPackage);
- if (!LocationAccessPolicy.canAccessCellLocation(mApp, callingPackage,
- Binder.getCallingUid(), Binder.getCallingPid(), true)) {
- return;
+
+ LocationAccessPolicy.LocationPermissionResult locationResult =
+ LocationAccessPolicy.checkLocationPermission(mApp,
+ new LocationAccessPolicy.LocationPermissionQuery.Builder()
+ .setCallingPackage(callingPackage)
+ .setCallingPid(Binder.getCallingPid())
+ .setCallingUid(Binder.getCallingUid())
+ .setMethod("requestCellInfoUpdate")
+ .setMinSdkVersionForFine(Build.VERSION_CODES.Q)
+ .build());
+ switch (locationResult) {
+ case DENIED_HARD:
+ throw new SecurityException("Not allowed to access cell info");
+ case DENIED_SOFT:
+ return;
}
final Phone phone = getPhone(subId);
@@ -4187,9 +4225,24 @@
* Scans for available networks.
*/
@Override
- public CellNetworkScanResult getCellNetworkScanResults(int subId) {
+ public CellNetworkScanResult getCellNetworkScanResults(int subId, String callingPackage) {
TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
mApp, subId, "getCellNetworkScanResults");
+ LocationAccessPolicy.LocationPermissionResult locationResult =
+ LocationAccessPolicy.checkLocationPermission(mApp,
+ new LocationAccessPolicy.LocationPermissionQuery.Builder()
+ .setCallingPackage(callingPackage)
+ .setCallingPid(Binder.getCallingPid())
+ .setCallingUid(Binder.getCallingUid())
+ .setMethod("getCellNetworkScanResults")
+ .setMinSdkVersionForFine(Build.VERSION_CODES.Q)
+ .build());
+ switch (locationResult) {
+ case DENIED_HARD:
+ throw new SecurityException("Not allowed to access scan results -- location");
+ case DENIED_SOFT:
+ return null;
+ }
long identity = Binder.clearCallingIdentity();
try {
@@ -4212,17 +4265,29 @@
*/
@Override
public int requestNetworkScan(int subId, NetworkScanRequest request, Messenger messenger,
- IBinder binder) {
+ IBinder binder, String callingPackage) {
TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
mApp, subId, "requestNetworkScan");
- final long identity = Binder.clearCallingIdentity();
- try {
- return mNetworkScanRequestTracker.startNetworkScan(
- request, messenger, binder, getPhone(subId));
- } finally {
- Binder.restoreCallingIdentity(identity);
+ LocationAccessPolicy.LocationPermissionResult locationResult =
+ LocationAccessPolicy.checkLocationPermission(mApp,
+ new LocationAccessPolicy.LocationPermissionQuery.Builder()
+ .setCallingPackage(callingPackage)
+ .setCallingPid(Binder.getCallingPid())
+ .setCallingUid(Binder.getCallingUid())
+ .setMethod("requestNetworkScan")
+ .setMinSdkVersionForFine(Build.VERSION_CODES.Q)
+ .build());
+ switch (locationResult) {
+ case DENIED_HARD:
+ throw new SecurityException("Not allowed to request network scan -- location");
+ case DENIED_SOFT:
+ return -1;
}
+
+ return mNetworkScanRequestTracker.startNetworkScan(
+ request, messenger, binder, getPhone(subId),
+ callingPackage);
}
/**
@@ -5263,6 +5328,31 @@
return null;
}
+ LocationAccessPolicy.LocationPermissionResult fineLocationResult =
+ LocationAccessPolicy.checkLocationPermission(mApp,
+ new LocationAccessPolicy.LocationPermissionQuery.Builder()
+ .setCallingPackage(callingPackage)
+ .setCallingPid(Binder.getCallingPid())
+ .setCallingUid(Binder.getCallingUid())
+ .setMethod("getServiceStateForSubscriber")
+ .setMinSdkVersionForFine(Build.VERSION_CODES.Q)
+ .build());
+
+ LocationAccessPolicy.LocationPermissionResult coarseLocationResult =
+ LocationAccessPolicy.checkLocationPermission(mApp,
+ new LocationAccessPolicy.LocationPermissionQuery.Builder()
+ .setCallingPackage(callingPackage)
+ .setCallingPid(Binder.getCallingPid())
+ .setCallingUid(Binder.getCallingUid())
+ .setMethod("getServiceStateForSubscriber")
+ .setMinSdkVersionForCoarse(Build.VERSION_CODES.Q)
+ .build());
+ // We don't care about hard or soft here -- all we need to know is how much info to scrub.
+ boolean hasFinePermission =
+ fineLocationResult == LocationAccessPolicy.LocationPermissionResult.ALLOWED;
+ boolean hasCoarsePermission =
+ coarseLocationResult == LocationAccessPolicy.LocationPermissionResult.ALLOWED;
+
final long identity = Binder.clearCallingIdentity();
try {
final Phone phone = getPhone(subId);
@@ -5270,7 +5360,13 @@
return null;
}
- return phone.getServiceState();
+ ServiceState ss = phone.getServiceState();
+
+ // Scrub out the location info in ServiceState depending on what level of access
+ // the caller has.
+ if (hasFinePermission) return ss;
+ if (hasCoarsePermission) return ss.sanitizeLocationInfo(false);
+ return ss.sanitizeLocationInfo(true);
} finally {
Binder.restoreCallingIdentity(identity);
}
diff --git a/testapps/TelephonyManagerTestApp/res/layout/calling_method.xml b/testapps/TelephonyManagerTestApp/res/layout/calling_method.xml
index 5145a63..6dd8bc2 100644
--- a/testapps/TelephonyManagerTestApp/res/layout/calling_method.xml
+++ b/testapps/TelephonyManagerTestApp/res/layout/calling_method.xml
@@ -73,7 +73,6 @@
android:layout_height="50dip">
</Button>
-
<ScrollView
android:id="@+id/return_value_wrapper"
android:layout_width="fill_parent"
diff --git a/testapps/TelephonyRegistryTestApp/AndroidManifest.xml b/testapps/TelephonyRegistryTestApp/AndroidManifest.xml
index 1cfd3ba..550c9f0 100644
--- a/testapps/TelephonyRegistryTestApp/AndroidManifest.xml
+++ b/testapps/TelephonyRegistryTestApp/AndroidManifest.xml
@@ -16,6 +16,9 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.phone.testapps.telephonyregistry">
+ <uses-sdk android:minSdkVersion="25"
+ android:targetSdkVersion="25"/>
+
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
diff --git a/testapps/TelephonyRegistryTestApp/src/com/android/phone/testapps/telephonyregistry/TelephonyRegistryTestApp.java b/testapps/TelephonyRegistryTestApp/src/com/android/phone/testapps/telephonyregistry/TelephonyRegistryTestApp.java
index 74cafcd..96f8bf7 100644
--- a/testapps/TelephonyRegistryTestApp/src/com/android/phone/testapps/telephonyregistry/TelephonyRegistryTestApp.java
+++ b/testapps/TelephonyRegistryTestApp/src/com/android/phone/testapps/telephonyregistry/TelephonyRegistryTestApp.java
@@ -24,6 +24,7 @@
import android.telephony.CellInfo;
import android.telephony.CellLocation;
import android.telephony.PhoneStateListener;
+import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import android.util.SparseArray;
import android.widget.Button;
@@ -76,6 +77,11 @@
notify("onSrvccStateChanged", srvccState);
}
+ @Override
+ public void onServiceStateChanged(ServiceState state) {
+ notify("onServiceStateChanged", state);
+ }
+
private void notify(String method, Object data) {
Notification.Builder builder = new Notification.Builder(TelephonyRegistryTestApp.this,
NOTIFICATION_CHANNEL);
diff --git a/tests/src/com/android/phone/LocationAccessPolicyTest.java b/tests/src/com/android/phone/LocationAccessPolicyTest.java
new file mode 100644
index 0000000..9938bf2
--- /dev/null
+++ b/tests/src/com/android/phone/LocationAccessPolicyTest.java
@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) 2018 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.phone;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+
+import android.Manifest;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.location.LocationManager;
+import android.os.Build;
+import android.os.UserHandle;
+import android.telephony.LocationAccessPolicy;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+@RunWith(Parameterized.class)
+public class LocationAccessPolicyTest {
+ private static class Scenario {
+ static class Builder {
+ private int mAppSdkLevel;
+ private boolean mAppHasFineManifest = false;
+ private boolean mAppHasCoarseManifest = false;
+ private int mFineAppOp = AppOpsManager.MODE_IGNORED;
+ private int mCoarseAppOp = AppOpsManager.MODE_IGNORED;
+ private boolean mIsDynamicLocationEnabled;
+ private LocationAccessPolicy.LocationPermissionQuery mQuery;
+ private LocationAccessPolicy.LocationPermissionResult mExpectedResult;
+ private String mName;
+
+ public Builder setAppSdkLevel(int appSdkLevel) {
+ mAppSdkLevel = appSdkLevel;
+ return this;
+ }
+
+ public Builder setAppHasFineManifest(boolean appHasFineManifest) {
+ mAppHasFineManifest = appHasFineManifest;
+ return this;
+ }
+
+ public Builder setAppHasCoarseManifest(
+ boolean appHasCoarseManifest) {
+ mAppHasCoarseManifest = appHasCoarseManifest;
+ return this;
+ }
+
+ public Builder setFineAppOp(int fineAppOp) {
+ mFineAppOp = fineAppOp;
+ return this;
+ }
+
+ public Builder setCoarseAppOp(int coarseAppOp) {
+ mCoarseAppOp = coarseAppOp;
+ return this;
+ }
+
+ public Builder setIsDynamicLocationEnabled(
+ boolean isDynamicLocationEnabled) {
+ mIsDynamicLocationEnabled = isDynamicLocationEnabled;
+ return this;
+ }
+
+ public Builder setQuery(
+ LocationAccessPolicy.LocationPermissionQuery query) {
+ mQuery = query;
+ return this;
+ }
+
+ public Builder setExpectedResult(
+ LocationAccessPolicy.LocationPermissionResult expectedResult) {
+ mExpectedResult = expectedResult;
+ return this;
+ }
+
+ public Builder setName(String name) {
+ mName = name;
+ return this;
+ }
+
+ public Scenario build() {
+ return new Scenario(mAppSdkLevel, mAppHasFineManifest, mAppHasCoarseManifest,
+ mFineAppOp, mCoarseAppOp, mIsDynamicLocationEnabled, mQuery,
+ mExpectedResult, mName);
+ }
+ }
+ int appSdkLevel;
+ boolean appHasFineManifest;
+ boolean appHasCoarseManifest;
+ int fineAppOp;
+ int coarseAppOp;
+ boolean isDynamicLocationEnabled;
+ LocationAccessPolicy.LocationPermissionQuery query;
+ LocationAccessPolicy.LocationPermissionResult expectedResult;
+ String name;
+
+ private Scenario(int appSdkLevel, boolean appHasFineManifest, boolean appHasCoarseManifest,
+ int fineAppOp, int coarseAppOp,
+ boolean isDynamicLocationEnabled,
+ LocationAccessPolicy.LocationPermissionQuery query,
+ LocationAccessPolicy.LocationPermissionResult expectedResult,
+ String name) {
+ this.appSdkLevel = appSdkLevel;
+ this.appHasFineManifest = appHasFineManifest;
+ this.appHasCoarseManifest = appHasFineManifest || appHasCoarseManifest;
+ this.fineAppOp = fineAppOp;
+ this.coarseAppOp = coarseAppOp == AppOpsManager.MODE_ALLOWED ? coarseAppOp : fineAppOp;
+ this.isDynamicLocationEnabled = isDynamicLocationEnabled;
+ this.query = query;
+ this.expectedResult = expectedResult;
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+ }
+
+ @Mock Context mContext;
+ @Mock AppOpsManager mAppOpsManager;
+ @Mock LocationManager mLocationManager;
+ @Mock PackageManager mPackageManager;
+ Scenario mScenario;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mockContextSystemService(AppOpsManager.class, mAppOpsManager);
+ mockContextSystemService(LocationManager.class, mLocationManager);
+ mockContextSystemService(PackageManager.class, mPackageManager);
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
+ }
+
+ private <T> void mockContextSystemService(Class<T> clazz , T obj) {
+ when(mContext.getSystemServiceName(eq(clazz))).thenReturn(clazz.getSimpleName());
+ when(mContext.getSystemService(clazz.getSimpleName())).thenReturn(obj);
+ }
+
+ public LocationAccessPolicyTest(Scenario scenario) {
+ mScenario = scenario;
+ }
+
+
+ @Test
+ public void test() {
+ setupScenario(mScenario);
+ assertEquals(mScenario.expectedResult,
+ LocationAccessPolicy.checkLocationPermission(mContext, mScenario.query));
+ }
+
+ private void setupScenario(Scenario s) {
+ when(mContext.checkPermission(eq(Manifest.permission.ACCESS_FINE_LOCATION),
+ anyInt(), anyInt())).thenReturn(s.appHasFineManifest
+ ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED);
+
+ when(mContext.checkPermission(eq(Manifest.permission.ACCESS_COARSE_LOCATION),
+ anyInt(), anyInt())).thenReturn(s.appHasCoarseManifest
+ ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED);
+
+ when(mAppOpsManager.noteOpNoThrow(eq(AppOpsManager.OP_FINE_LOCATION),
+ anyInt(), anyString()))
+ .thenReturn(s.fineAppOp);
+ when(mAppOpsManager.noteOpNoThrow(eq(AppOpsManager.OP_COARSE_LOCATION),
+ anyInt(), anyString()))
+ .thenReturn(s.coarseAppOp);
+
+ if (s.isDynamicLocationEnabled) {
+ when(mLocationManager.isLocationEnabledForUser(any(UserHandle.class))).thenReturn(true);
+ when(mContext.checkPermission(eq(Manifest.permission.INTERACT_ACROSS_USERS_FULL),
+ anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED);
+ } else {
+ when(mLocationManager.isLocationEnabledForUser(any(UserHandle.class)))
+ .thenReturn(false);
+ when(mContext.checkPermission(eq(Manifest.permission.INTERACT_ACROSS_USERS_FULL),
+ anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_DENIED);
+ }
+
+ ApplicationInfo fakeAppInfo = new ApplicationInfo();
+ fakeAppInfo.targetSdkVersion = s.appSdkLevel;
+
+ try {
+ when(mPackageManager.getApplicationInfo(anyString(), anyInt()))
+ .thenReturn(fakeAppInfo);
+ } catch (Exception e) {
+ // this is a formality
+ }
+ }
+
+ private static LocationAccessPolicy.LocationPermissionQuery.Builder getDefaultQueryBuilder() {
+ return new LocationAccessPolicy.LocationPermissionQuery.Builder()
+ .setMethod("test")
+ .setCallingPackage("com.android.test")
+ .setCallingPid(10001)
+ .setCallingUid(10001);
+ }
+
+ @Parameterized.Parameters(name = "{0}")
+ public static Collection<Scenario> getScenarios() {
+ List<Scenario> scenarios = new ArrayList<>();
+ scenarios.add(new Scenario.Builder()
+ .setName("System location is off")
+ .setAppHasFineManifest(true)
+ .setFineAppOp(AppOpsManager.MODE_ALLOWED)
+ .setAppSdkLevel(Build.VERSION_CODES.P)
+ .setIsDynamicLocationEnabled(false)
+ .setQuery(getDefaultQueryBuilder()
+ .setMinSdkVersionForFine(Build.VERSION_CODES.N)
+ .setMinSdkVersionForCoarse(Build.VERSION_CODES.N).build())
+ .setExpectedResult(LocationAccessPolicy.LocationPermissionResult.DENIED_SOFT)
+ .build());
+
+ scenarios.add(new Scenario.Builder()
+ .setName("App on latest SDK level has all proper permissions for fine")
+ .setAppHasFineManifest(true)
+ .setFineAppOp(AppOpsManager.MODE_ALLOWED)
+ .setAppSdkLevel(Build.VERSION_CODES.P)
+ .setIsDynamicLocationEnabled(true)
+ .setQuery(getDefaultQueryBuilder()
+ .setMinSdkVersionForFine(Build.VERSION_CODES.N)
+ .setMinSdkVersionForCoarse(Build.VERSION_CODES.N).build())
+ .setExpectedResult(LocationAccessPolicy.LocationPermissionResult.ALLOWED)
+ .build());
+
+ scenarios.add(new Scenario.Builder()
+ .setName("App on older SDK level missing permissions for fine but has coarse")
+ .setAppHasCoarseManifest(true)
+ .setCoarseAppOp(AppOpsManager.MODE_ALLOWED)
+ .setAppSdkLevel(Build.VERSION_CODES.JELLY_BEAN)
+ .setIsDynamicLocationEnabled(true)
+ .setQuery(getDefaultQueryBuilder()
+ .setMinSdkVersionForFine(Build.VERSION_CODES.M)
+ .setMinSdkVersionForCoarse(Build.VERSION_CODES.JELLY_BEAN).build())
+ .setExpectedResult(LocationAccessPolicy.LocationPermissionResult.ALLOWED)
+ .build());
+
+ scenarios.add(new Scenario.Builder()
+ .setName("App on latest SDK level missing fine app ops permission")
+ .setAppHasFineManifest(true)
+ .setFineAppOp(AppOpsManager.MODE_ERRORED)
+ .setAppSdkLevel(Build.VERSION_CODES.P)
+ .setIsDynamicLocationEnabled(true)
+ .setQuery(getDefaultQueryBuilder()
+ .setMinSdkVersionForFine(Build.VERSION_CODES.N)
+ .setMinSdkVersionForCoarse(Build.VERSION_CODES.N).build())
+ .setExpectedResult(LocationAccessPolicy.LocationPermissionResult.DENIED_HARD)
+ .build());
+
+ scenarios.add(new Scenario.Builder()
+ .setName("App has coarse permission but fine permission isn't being enforced yet")
+ .setAppHasCoarseManifest(true)
+ .setCoarseAppOp(AppOpsManager.MODE_ALLOWED)
+ .setAppSdkLevel(LocationAccessPolicy.MAX_SDK_FOR_ANY_ENFORCEMENT + 1)
+ .setIsDynamicLocationEnabled(true)
+ .setQuery(getDefaultQueryBuilder()
+ .setMinSdkVersionForFine(
+ LocationAccessPolicy.MAX_SDK_FOR_ANY_ENFORCEMENT + 1)
+ .setMinSdkVersionForCoarse(Build.VERSION_CODES.N).build())
+ .setExpectedResult(LocationAccessPolicy.LocationPermissionResult.ALLOWED)
+ .build());
+
+ scenarios.add(new Scenario.Builder()
+ .setName("App on latest SDK level has coarse but missing fine when fine is req.")
+ .setAppHasCoarseManifest(true)
+ .setCoarseAppOp(AppOpsManager.MODE_ALLOWED)
+ .setAppSdkLevel(Build.VERSION_CODES.P)
+ .setIsDynamicLocationEnabled(true)
+ .setQuery(getDefaultQueryBuilder()
+ .setMinSdkVersionForFine(Build.VERSION_CODES.P)
+ .setMinSdkVersionForCoarse(Build.VERSION_CODES.N).build())
+ .setExpectedResult(LocationAccessPolicy.LocationPermissionResult.DENIED_HARD)
+ .build());
+
+ scenarios.add(new Scenario.Builder()
+ .setName("App on latest SDK level has MODE_IGNORED for app ops on fine")
+ .setAppHasCoarseManifest(true)
+ .setCoarseAppOp(AppOpsManager.MODE_ALLOWED)
+ .setFineAppOp(AppOpsManager.MODE_IGNORED)
+ .setAppSdkLevel(Build.VERSION_CODES.P)
+ .setIsDynamicLocationEnabled(true)
+ .setQuery(getDefaultQueryBuilder()
+ .setMinSdkVersionForFine(Build.VERSION_CODES.P)
+ .setMinSdkVersionForCoarse(Build.VERSION_CODES.O).build())
+ .setExpectedResult(LocationAccessPolicy.LocationPermissionResult.DENIED_HARD)
+ .build());
+
+ scenarios.add(new Scenario.Builder()
+ .setName("App has no permissions but it's sdk level grandfathers it in")
+ .setAppSdkLevel(Build.VERSION_CODES.N)
+ .setIsDynamicLocationEnabled(true)
+ .setQuery(getDefaultQueryBuilder()
+ .setMinSdkVersionForFine(Build.VERSION_CODES.Q)
+ .setMinSdkVersionForCoarse(Build.VERSION_CODES.O).build())
+ .setExpectedResult(LocationAccessPolicy.LocationPermissionResult.ALLOWED)
+ .build());
+
+ scenarios.add(new Scenario.Builder()
+ .setName("App on latest SDK level has proper permissions for coarse")
+ .setAppHasCoarseManifest(true)
+ .setCoarseAppOp(AppOpsManager.MODE_ALLOWED)
+ .setAppSdkLevel(Build.VERSION_CODES.P)
+ .setIsDynamicLocationEnabled(true)
+ .setQuery(getDefaultQueryBuilder()
+ .setMinSdkVersionForCoarse(Build.VERSION_CODES.P).build())
+ .setExpectedResult(LocationAccessPolicy.LocationPermissionResult.ALLOWED)
+ .build());
+ return scenarios;
+ }
+}