Add location notification and target sdk checks
Check target SDKs to disallow using setSecureSetting with LOCATION_MODE
and point clients to setLocationEnabled() instead. Show notifications to
users when location settings are changed by the admin.
Bug: 136219903
Test: manual - triggered setLocationEnabled and observed notification
Change-Id: I07c150e62230b06f76ec7bd5197a23e703ffb918
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 7599791..73a40cf 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -8951,7 +8951,8 @@
*
* <strong>Note: Starting from Android R, apps should no longer call this method with the
* setting {@link android.provider.Settings.Secure#LOCATION_MODE}, which is deprecated. Instead,
- * device owners should call {@link #setLocationEnabled(ComponentName, boolean)}.
+ * device owners should call {@link #setLocationEnabled(ComponentName, boolean)}. This will be
+ * enforced for all apps targeting Android R or above.
* </strong>
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
@@ -8961,6 +8962,7 @@
*/
public void setSecureSetting(@NonNull ComponentName admin, String setting, String value) {
throwIfParentInstance("setSecureSetting");
+
if (mService != null) {
try {
mService.setSecureSetting(admin, setting, value);
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index a54566c..08c8403 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -414,6 +414,14 @@
logging. [CHAR LIMIT=NONE]-->
<string name="network_logging_notification_text">Your organization manages this device and may monitor network traffic. Tap for details.</string>
+ <!-- Content title for a notification. This notification indicates that the device owner has
+ changed the location settings. [CHAR LIMIT=NONE] -->
+ <string name="location_changed_notification_title">Location settings changed by your admin</string>
+ <!-- Content text for a notification. Tapping opens device location settings.
+ [CHAR LIMIT=NONE] -->
+ <string name="location_changed_notification_text">Tap to see your location settings.</string>
+
+
<!-- Factory reset warning dialog strings--> <skip />
<!-- Shows up in the dialog's title to warn about an impeding factory reset. [CHAR LIMIT=NONE] -->
<string name="factory_reset_warning">Your device will be erased</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 2453bb1..dc52133 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1213,6 +1213,8 @@
<java-symbol type="string" name="device_ownership_relinquished" />
<java-symbol type="string" name="network_logging_notification_title" />
<java-symbol type="string" name="network_logging_notification_text" />
+ <java-symbol type="string" name="location_changed_notification_title" />
+ <java-symbol type="string" name="location_changed_notification_text" />
<java-symbol type="string" name="personal_apps_suspended_notification_title" />
<java-symbol type="string" name="personal_apps_suspended_notification_text" />
<java-symbol type="string" name="factory_reset_warning" />
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index 54b4201..63dd99e 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -244,6 +244,10 @@
// Package: android
NOTE_SOFTAP_AUTO_DISABLED = 58;
+ // Notify the user that their admin has changed location settings.
+ // Package: android
+ NOTE_LOCATION_CHANGED = 59;
+
// ADD_NEW_IDS_ABOVE_THIS_LINE
// Legacy IDs with arbitrary values appear below
// Legacy IDs existed as stable non-conflicting constants prior to the O release
diff --git a/services/core/java/com/android/server/location/TEST_MAPPING b/services/core/java/com/android/server/location/TEST_MAPPING
index 2e21fa6..214d2f3 100644
--- a/services/core/java/com/android/server/location/TEST_MAPPING
+++ b/services/core/java/com/android/server/location/TEST_MAPPING
@@ -1,10 +1,19 @@
{
"presubmit": [
{
+ "name": "CtsLocationFineTestCases"
+ },
+ {
"name": "CtsLocationCoarseTestCases"
},
{
"name": "CtsLocationNoneTestCases"
+ },
+ {
+ "name": "FrameworksMockingServicesTests",
+ "options": [{
+ "include-filter": "com.android.server.location"
+ }]
}
]
}
\ No newline at end of file
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 553ec42..9bc1f57 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -560,6 +560,16 @@
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
private static final long ADMIN_APP_PASSWORD_COMPLEXITY = 123562444L;
+ /**
+ * Admin apps targeting Android R+ may not use
+ * {@link android.app.admin.DevicePolicyManager#setSecureSetting} to change the deprecated
+ * {@link android.provider.Settings.Secure#LOCATION_MODE} setting. Instead they should use
+ * {@link android.app.admin.DevicePolicyManager#setLocationEnabled}.
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
+ private static final long USE_SET_LOCATION_ENABLED = 117835097L;
+
final Context mContext;
final Injector mInjector;
final IPackageManager mIPackageManager;
@@ -11558,13 +11568,22 @@
@Override
public void setLocationEnabled(ComponentName who, boolean locationEnabled) {
- Objects.requireNonNull(who, "ComponentName is null");
- enforceDeviceOwner(who);
+ enforceDeviceOwner(Objects.requireNonNull(who));
- UserHandle userHandle = mInjector.binderGetCallingUserHandle();
- mInjector.binderWithCleanCallingIdentity(
- () -> mInjector.getLocationManager().setLocationEnabledForUser(locationEnabled,
- userHandle));
+ UserHandle user = mInjector.binderGetCallingUserHandle();
+
+ mInjector.binderWithCleanCallingIdentity(() -> {
+ boolean wasLocationEnabled = mInjector.getLocationManager().isLocationEnabledForUser(
+ user);
+ mInjector.getLocationManager().setLocationEnabledForUser(locationEnabled, user);
+
+ // make a best effort to only show the notification if the admin is actually changing
+ // something. this is subject to race conditions with settings changes, but those are
+ // unlikely to realistically interfere
+ if (wasLocationEnabled != locationEnabled) {
+ showLocationSettingsChangedNotification(user);
+ }
+ });
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_SECURE_SETTING)
@@ -11575,6 +11594,25 @@
.write();
}
+ private void showLocationSettingsChangedNotification(UserHandle user) {
+ PendingIntent locationSettingsIntent = mInjector.pendingIntentGetActivityAsUser(mContext, 0,
+ new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK), PendingIntent.FLAG_UPDATE_CURRENT,
+ null, user);
+ Notification notification = new Notification.Builder(mContext,
+ SystemNotificationChannels.DEVICE_ADMIN)
+ .setSmallIcon(R.drawable.ic_info_outline)
+ .setContentTitle(mContext.getString(R.string.location_changed_notification_title))
+ .setContentText(mContext.getString(R.string.location_changed_notification_text))
+ .setColor(mContext.getColor(R.color.system_notification_accent_color))
+ .setShowWhen(true)
+ .setContentIntent(locationSettingsIntent)
+ .setAutoCancel(true)
+ .build();
+ mInjector.getNotificationManager().notify(SystemMessage.NOTE_LOCATION_CHANGED,
+ notification);
+ }
+
@Override
public void requestSetLocationProviderAllowed(ComponentName who, String provider,
boolean providerAllowed) {
@@ -11645,6 +11683,12 @@
throw new SecurityException(String.format(
"Permission denial: Profile owners cannot update %1$s", setting));
}
+ if (setting.equals(Settings.Secure.LOCATION_MODE)
+ && isSetSecureSettingLocationModeCheckEnabled(who.getPackageName(),
+ callingUserId)) {
+ throw new UnsupportedOperationException(Settings.Secure.LOCATION_MODE + " is "
+ + "deprecated. Please use setLocationEnabled() instead.");
+ }
if (setting.equals(Settings.Secure.INSTALL_NON_MARKET_APPS)) {
if (getTargetSdk(who.getPackageName(), callingUserId) >= Build.VERSION_CODES.O) {
throw new UnsupportedOperationException(Settings.Secure.INSTALL_NON_MARKET_APPS
@@ -11689,6 +11733,9 @@
saveSettingsLocked(callingUserId);
}
mInjector.settingsSecurePutStringForUser(setting, value, callingUserId);
+ if (setting.equals(Settings.Secure.LOCATION_MODE)) {
+ showLocationSettingsChangedNotification(UserHandle.of(callingUserId));
+ }
});
}
DevicePolicyEventLogger
@@ -11698,6 +11745,16 @@
.write();
}
+ private boolean isSetSecureSettingLocationModeCheckEnabled(String packageName, int userId) {
+ try {
+ return mIPlatformCompat.isChangeEnabledByPackageName(USE_SET_LOCATION_ENABLED,
+ packageName, userId);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Failed to get a response from PLATFORM_COMPAT_SERVICE", e);
+ return getTargetSdk(packageName, userId) > Build.VERSION_CODES.Q;
+ }
+ }
+
@Override
public void setMasterVolumeMuted(ComponentName who, boolean on) {
Objects.requireNonNull(who, "ComponentName is null");