diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java index 13a39de3e3653..0ada13a73ad23 100644 --- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java +++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java @@ -163,6 +163,12 @@ public final class SystemUiDeviceConfigFlags { public static final String PROPERTY_LOCATION_INDICATORS_SMALL_ENABLED = "location_indicators_small_enabled"; + /** + * Whether to show the location indicator for system apps. + */ + public static final String PROPERTY_LOCATION_INDICATORS_SHOW_SYSTEM = + "location_indicators_show_system"; + // Flags related to Assistant /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java index 3831857c5c8d2..59969c0447b37 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java @@ -22,10 +22,14 @@ import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION; import static com.android.settingslib.Utils.updateLocationEnabled; +import android.app.AppOpsManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.PermissionChecker; +import android.content.pm.PackageManager; +import android.content.pm.UserInfo; import android.location.LocationManager; import android.os.Handler; import android.os.Looper; @@ -68,31 +72,37 @@ public class LocationControllerImpl extends BroadcastReceiver implements Locatio private final BootCompleteCache mBootCompleteCache; private final UserTracker mUserTracker; private final H mHandler; - + private final Handler mBackgroundHandler; + private final PackageManager mPackageManager; private boolean mAreActiveLocationRequests; private boolean mShouldDisplayAllAccesses; + private boolean mShowSystemAccesses; @Inject public LocationControllerImpl(Context context, AppOpsController appOpsController, DeviceConfigProxy deviceConfigProxy, @Main Looper mainLooper, @Background Handler backgroundHandler, BroadcastDispatcher broadcastDispatcher, BootCompleteCache bootCompleteCache, - UserTracker userTracker) { + UserTracker userTracker, PackageManager packageManager) { mContext = context; mAppOpsController = appOpsController; mDeviceConfigProxy = deviceConfigProxy; mBootCompleteCache = bootCompleteCache; mHandler = new H(mainLooper); mUserTracker = userTracker; - mShouldDisplayAllAccesses = getDeviceConfigSetting(); + mBackgroundHandler = backgroundHandler; + mPackageManager = packageManager; + mShouldDisplayAllAccesses = getAllAccessesSetting(); + mShowSystemAccesses = getShowSystemSetting(); // Register to listen for changes in DeviceConfig settings. mDeviceConfigProxy.addOnPropertiesChangedListener( DeviceConfig.NAMESPACE_PRIVACY, backgroundHandler::post, properties -> { - mShouldDisplayAllAccesses = getDeviceConfigSetting(); + mShouldDisplayAllAccesses = getAllAccessesSetting(); + mShowSystemAccesses = getShowSystemSetting(); updateActiveLocationRequests(); }); @@ -176,11 +186,15 @@ public class LocationControllerImpl extends BroadcastReceiver implements Locatio UserHandle.of(userId)); } - private boolean getDeviceConfigSetting() { + private boolean getAllAccessesSetting() { return mDeviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_SMALL_ENABLED, false); } + private boolean getShowSystemSetting() { + return mDeviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, + SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_SHOW_SYSTEM, false); + } /** * Returns true if there currently exist active high power location requests. */ @@ -202,35 +216,74 @@ public class LocationControllerImpl extends BroadcastReceiver implements Locatio * Returns true if there currently exist active location requests. */ @VisibleForTesting - protected boolean areActiveLocationRequests() { + protected void areActiveLocationRequests() { if (!mShouldDisplayAllAccesses) { - return false; + return; } - List appOpsItems = mAppOpsController.getActiveAppOps(); + boolean hadActiveLocationRequests = mAreActiveLocationRequests; + boolean shouldDisplay = false; + List appOpsItems = mAppOpsController.getActiveAppOps(); + final List profiles = mUserTracker.getUserProfiles(); final int numItems = appOpsItems.size(); for (int i = 0; i < numItems; i++) { if (appOpsItems.get(i).getCode() == OP_FINE_LOCATION || appOpsItems.get(i).getCode() == OP_COARSE_LOCATION) { - return true; + if (mShowSystemAccesses) { + shouldDisplay = true; + } else { + shouldDisplay |= !isSystemApp(profiles, appOpsItems.get(i)); + } } } - return false; + mAreActiveLocationRequests = areActiveHighPowerLocationRequests() || shouldDisplay; + if (mAreActiveLocationRequests != hadActiveLocationRequests) { + mHandler.sendEmptyMessage(H.MSG_LOCATION_ACTIVE_CHANGED); + } + } + + private boolean isSystemApp(List profiles, AppOpItem item) { + final String permission = AppOpsManager.opToPermission(item.getCode()); + UserHandle user = UserHandle.getUserHandleForUid(item.getUid()); + + // Don't show apps belonging to background users except managed users. + boolean foundUser = false; + final int numProfiles = profiles.size(); + for (int i = 0; i < numProfiles; i++) { + if (profiles.get(i).getUserHandle().equals(user)) { + foundUser = true; + } + } + if (!foundUser) { + return true; + } + + final int permissionFlags = mPackageManager.getPermissionFlags( + permission, item.getPackageName(), user); + if (PermissionChecker.checkPermissionForPreflight(mContext, permission, + PermissionChecker.PID_UNKNOWN, item.getUid(), item.getPackageName()) + == PermissionChecker.PERMISSION_GRANTED) { + return (permissionFlags + & PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED) + == 0; + } else { + return (permissionFlags + & PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED) == 0; + } } // Reads the active location requests from either OP_MONITOR_HIGH_POWER_LOCATION, // OP_FINE_LOCATION, or OP_COARSE_LOCATION and updates the status view if necessary. private void updateActiveLocationRequests() { - boolean hadActiveLocationRequests = mAreActiveLocationRequests; if (mShouldDisplayAllAccesses) { - mAreActiveLocationRequests = - areActiveHighPowerLocationRequests() || areActiveLocationRequests(); + mBackgroundHandler.post(this::areActiveLocationRequests); } else { + boolean hadActiveLocationRequests = mAreActiveLocationRequests; mAreActiveLocationRequests = areActiveHighPowerLocationRequests(); - } - if (mAreActiveLocationRequests != hadActiveLocationRequests) { - mHandler.sendEmptyMessage(H.MSG_LOCATION_ACTIVE_CHANGED); + if (mAreActiveLocationRequests != hadActiveLocationRequests) { + mHandler.sendEmptyMessage(H.MSG_LOCATION_ACTIVE_CHANGED); + } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java index 087f2e6006cf3..2126dda7b3109 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java @@ -22,6 +22,7 @@ import static org.mockito.Mockito.when; import android.app.AppOpsManager; import android.content.Intent; +import android.content.pm.UserInfo; import android.location.LocationManager; import android.os.Handler; import android.os.UserHandle; @@ -68,6 +69,8 @@ public class LocationControllerImplTest extends SysuiTestCase { MockitoAnnotations.initMocks(this); when(mUserTracker.getUserId()).thenReturn(UserHandle.USER_SYSTEM); when(mUserTracker.getUserHandle()).thenReturn(UserHandle.SYSTEM); + when(mUserTracker.getUserProfiles()) + .thenReturn(ImmutableList.of(new UserInfo(0, "name", 0))); mDeviceConfigProxy = new DeviceConfigProxyFake(); mTestableLooper = TestableLooper.get(this); @@ -78,7 +81,8 @@ public class LocationControllerImplTest extends SysuiTestCase { new Handler(mTestableLooper.getLooper()), mock(BroadcastDispatcher.class), mock(BootCompleteCache.class), - mUserTracker); + mUserTracker, + mContext.getPackageManager()); mTestableLooper.processAllMessages(); } @@ -161,17 +165,7 @@ public class LocationControllerImplTest extends SysuiTestCase { @Test public void testCallbackNotified_additionalOps() { LocationChangeCallback callback = mock(LocationChangeCallback.class); - mLocationController.addCallback(callback); - - mTestableLooper.processAllMessages(); - - mLocationController.onReceive(mContext, new Intent(LocationManager.MODE_CHANGED_ACTION)); - - mTestableLooper.processAllMessages(); - - verify(callback, times(2)).onLocationSettingsChanged(anyBoolean()); - mDeviceConfigProxy.setProperty( DeviceConfig.NAMESPACE_PRIVACY, SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_SMALL_ENABLED, @@ -181,10 +175,11 @@ public class LocationControllerImplTest extends SysuiTestCase { when(mAppOpsController.getActiveAppOps()) .thenReturn(ImmutableList.of( - new AppOpItem(AppOpsManager.OP_FINE_LOCATION, 0, "", + new AppOpItem(AppOpsManager.OP_FINE_LOCATION, 0, + "com.google.android.googlequicksearchbox", System.currentTimeMillis()))); mLocationController.onActiveStateChanged(AppOpsManager.OP_FINE_LOCATION, 0, - "", true); + "com.google.android.googlequicksearchbox", true); mTestableLooper.processAllMessages(); @@ -192,7 +187,67 @@ public class LocationControllerImplTest extends SysuiTestCase { when(mAppOpsController.getActiveAppOps()).thenReturn(ImmutableList.of()); mLocationController.onActiveStateChanged(AppOpsManager.OP_FINE_LOCATION, 0, - "", false); + "com.google.android.googlequicksearchbox", false); + mTestableLooper.processAllMessages(); + + verify(callback, times(1)).onLocationActiveChanged(false); + } + + @Test + public void testCallbackNotified_additionalOps_shouldShowSystem() { + LocationChangeCallback callback = mock(LocationChangeCallback.class); + mLocationController.addCallback(callback); + mDeviceConfigProxy.setProperty( + DeviceConfig.NAMESPACE_PRIVACY, + SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_SMALL_ENABLED, + "true", + true); + mTestableLooper.processAllMessages(); + + when(mAppOpsController.getActiveAppOps()) + .thenReturn(ImmutableList.of( + new AppOpItem(AppOpsManager.OP_FINE_LOCATION, 0, + "com.google.android.gms", + System.currentTimeMillis()))); + mLocationController.onActiveStateChanged(AppOpsManager.OP_FINE_LOCATION, 0, + "com.google.android.gms", true); + + mTestableLooper.processAllMessages(); + + verify(callback, times(0)).onLocationActiveChanged(true); + } + + + @Test + public void testCallbackNotified_additionalOps_shouldNotShowSystem() { + LocationChangeCallback callback = mock(LocationChangeCallback.class); + mLocationController.addCallback(callback); + mDeviceConfigProxy.setProperty( + DeviceConfig.NAMESPACE_PRIVACY, + SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_SMALL_ENABLED, + "true", + true); + mDeviceConfigProxy.setProperty( + DeviceConfig.NAMESPACE_PRIVACY, + SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_SHOW_SYSTEM, + "true", + true); + mTestableLooper.processAllMessages(); + + when(mAppOpsController.getActiveAppOps()) + .thenReturn(ImmutableList.of( + new AppOpItem(AppOpsManager.OP_FINE_LOCATION, 0, "com.google.android.gms", + System.currentTimeMillis()))); + mLocationController.onActiveStateChanged(AppOpsManager.OP_FINE_LOCATION, 0, + "com.google.android.gms", true); + + mTestableLooper.processAllMessages(); + + verify(callback, times(1)).onLocationActiveChanged(true); + + when(mAppOpsController.getActiveAppOps()).thenReturn(ImmutableList.of()); + mLocationController.onActiveStateChanged(AppOpsManager.OP_FINE_LOCATION, 0, + "com.google.android.gms", false); mTestableLooper.processAllMessages(); verify(callback, times(1)).onLocationActiveChanged(false);