Merge "Deprecate location modes"
diff --git a/api/current.txt b/api/current.txt
index bef160c..bb066b3 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -21587,6 +21587,7 @@
method public android.location.LocationProvider getProvider(java.lang.String);
method public java.util.List<java.lang.String> getProviders(boolean);
method public java.util.List<java.lang.String> getProviders(android.location.Criteria, boolean);
+ method public boolean isLocationEnabled();
method public boolean isProviderEnabled(java.lang.String);
method public boolean registerGnssMeasurementsCallback(android.location.GnssMeasurementsEvent.Callback);
method public boolean registerGnssMeasurementsCallback(android.location.GnssMeasurementsEvent.Callback, android.os.Handler);
@@ -36034,11 +36035,11 @@
field public static final deprecated java.lang.String HTTP_PROXY = "http_proxy";
field public static final java.lang.String INPUT_METHOD_SELECTOR_VISIBILITY = "input_method_selector_visibility";
field public static final deprecated java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
- field public static final java.lang.String LOCATION_MODE = "location_mode";
- field public static final int LOCATION_MODE_BATTERY_SAVING = 2; // 0x2
- field public static final int LOCATION_MODE_HIGH_ACCURACY = 3; // 0x3
- field public static final int LOCATION_MODE_OFF = 0; // 0x0
- field public static final int LOCATION_MODE_SENSORS_ONLY = 1; // 0x1
+ field public static final deprecated java.lang.String LOCATION_MODE = "location_mode";
+ field public static final deprecated int LOCATION_MODE_BATTERY_SAVING = 2; // 0x2
+ field public static final deprecated int LOCATION_MODE_HIGH_ACCURACY = 3; // 0x3
+ field public static final deprecated int LOCATION_MODE_OFF = 0; // 0x0
+ field public static final deprecated int LOCATION_MODE_SENSORS_ONLY = 1; // 0x1
field public static final deprecated java.lang.String LOCATION_PROVIDERS_ALLOWED = "location_providers_allowed";
field public static final deprecated java.lang.String LOCK_PATTERN_ENABLED = "lock_pattern_autolock";
field public static final deprecated java.lang.String LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED = "lock_pattern_tactile_feedback_enabled";
diff --git a/api/system-current.txt b/api/system-current.txt
index f2c057e..b21b02a 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -2333,11 +2333,15 @@
method public deprecated boolean addGpsNavigationMessageListener(android.location.GpsNavigationMessageEvent.Listener);
method public void flushGnssBatch();
method public int getGnssBatchSize();
+ method public boolean isLocationEnabledForUser(android.os.UserHandle);
+ method public boolean isProviderEnabledForUser(java.lang.String, android.os.UserHandle);
method public boolean registerGnssBatchedLocationCallback(long, boolean, android.location.BatchedLocationCallback, android.os.Handler);
method public deprecated void removeGpsMeasurementListener(android.location.GpsMeasurementsEvent.Listener);
method public deprecated void removeGpsNavigationMessageListener(android.location.GpsNavigationMessageEvent.Listener);
method public void requestLocationUpdates(android.location.LocationRequest, android.location.LocationListener, android.os.Looper);
method public void requestLocationUpdates(android.location.LocationRequest, android.app.PendingIntent);
+ method public void setLocationEnabledForUser(boolean, android.os.UserHandle);
+ method public boolean setProviderEnabledForUser(java.lang.String, boolean, android.os.UserHandle);
method public boolean unregisterGnssBatchedLocationCallback(android.location.BatchedLocationCallback);
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 228a86d..1c41979 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5486,37 +5486,54 @@
* Note: do not rely on this value being present in settings.db or on ContentObserver
* notifications for the corresponding Uri. Use {@link LocationManager#MODE_CHANGED_ACTION}
* to receive changes in this value.
+ *
+ * @deprecated To check location status, use {@link LocationManager#isLocationEnabled()}. To
+ * get the status of a location provider, use
+ * {@link LocationManager#isProviderEnabled(String)}.
*/
+ @Deprecated
public static final String LOCATION_MODE = "location_mode";
- /**
- * Stores the previous location mode when {@link #LOCATION_MODE} is set to
- * {@link #LOCATION_MODE_OFF}
- * @hide
- */
- public static final String LOCATION_PREVIOUS_MODE = "location_previous_mode";
/**
- * Sets all location providers to the previous states before location was turned off.
- * @hide
- */
- public static final int LOCATION_MODE_PREVIOUS = -1;
- /**
* Location access disabled.
+ *
+ * @deprecated To check location status, use {@link LocationManager#isLocationEnabled()}. To
+ * get the status of a location provider, use
+ * {@link LocationManager#isProviderEnabled(String)}.
*/
+ @Deprecated
public static final int LOCATION_MODE_OFF = 0;
+
/**
* Network Location Provider disabled, but GPS and other sensors enabled.
+ *
+ * @deprecated To check location status, use {@link LocationManager#isLocationEnabled()}. To
+ * get the status of a location provider, use
+ * {@link LocationManager#isProviderEnabled(String)}.
*/
+ @Deprecated
public static final int LOCATION_MODE_SENSORS_ONLY = 1;
+
/**
* Reduced power usage, such as limiting the number of GPS updates per hour. Requests
* with {@link android.location.Criteria#POWER_HIGH} may be downgraded to
* {@link android.location.Criteria#POWER_MEDIUM}.
+ *
+ * @deprecated To check location status, use {@link LocationManager#isLocationEnabled()}. To
+ * get the status of a location provider, use
+ * {@link LocationManager#isProviderEnabled(String)}.
*/
+ @Deprecated
public static final int LOCATION_MODE_BATTERY_SAVING = 2;
+
/**
* Best-effort location computation allowed.
+ *
+ * @deprecated To check location status, use {@link LocationManager#isLocationEnabled()}. To
+ * get the status of a location provider, use
+ * {@link LocationManager#isProviderEnabled(String)}.
*/
+ @Deprecated
public static final int LOCATION_MODE_HIGH_ACCURACY = 3;
/**
@@ -7843,7 +7860,6 @@
CLONE_TO_MANAGED_PROFILE.add(ENABLED_ACCESSIBILITY_SERVICES);
CLONE_TO_MANAGED_PROFILE.add(ENABLED_INPUT_METHODS);
CLONE_TO_MANAGED_PROFILE.add(LOCATION_MODE);
- CLONE_TO_MANAGED_PROFILE.add(LOCATION_PREVIOUS_MODE);
CLONE_TO_MANAGED_PROFILE.add(LOCATION_PROVIDERS_ALLOWED);
CLONE_TO_MANAGED_PROFILE.add(SELECTED_INPUT_METHOD_SUBTYPE);
}
@@ -7894,8 +7910,7 @@
* @param provider the location provider to query
* @return true if the provider is enabled
*
- * @deprecated use {@link #LOCATION_MODE} or
- * {@link LocationManager#isProviderEnabled(String)}
+ * @deprecated use {@link LocationManager#isProviderEnabled(String)}
*/
@Deprecated
public static final boolean isLocationProviderEnabled(ContentResolver cr, String provider) {
@@ -7908,12 +7923,13 @@
* @param provider the location provider to query
* @param userId the userId to query
* @return true if the provider is enabled
- * @deprecated use {@link #LOCATION_MODE} or
- * {@link LocationManager#isProviderEnabled(String)}
+ *
+ * @deprecated use {@link LocationManager#isProviderEnabled(String)}
* @hide
*/
@Deprecated
- public static final boolean isLocationProviderEnabledForUser(ContentResolver cr, String provider, int userId) {
+ public static final boolean isLocationProviderEnabledForUser(
+ ContentResolver cr, String provider, int userId) {
String allowedProviders = Settings.Secure.getStringForUser(cr,
LOCATION_PROVIDERS_ALLOWED, userId);
return TextUtils.delimitedStringContains(allowedProviders, ',', provider);
@@ -7924,7 +7940,8 @@
* @param cr the content resolver to use
* @param provider the location provider to enable or disable
* @param enabled true if the provider should be enabled
- * @deprecated use {@link #putInt(ContentResolver, String, int)} and {@link #LOCATION_MODE}
+ * @deprecated This API is deprecated. It requires WRITE_SECURE_SETTINGS permission to
+ * change location settings.
*/
@Deprecated
public static final void setLocationProviderEnabled(ContentResolver cr,
@@ -7940,8 +7957,8 @@
* @param enabled true if the provider should be enabled
* @param userId the userId for which to enable/disable providers
* @return true if the value was set, false on database errors
- * @deprecated use {@link #putIntForUser(ContentResolver, String, int, int)} and
- * {@link #LOCATION_MODE}
+ *
+ * @deprecated use {@link LocationManager#setProviderEnabledForUser(String, boolean, int)}
* @hide
*/
@Deprecated
@@ -7962,28 +7979,6 @@
}
/**
- * Saves the current location mode into {@link #LOCATION_PREVIOUS_MODE}.
- */
- private static final boolean saveLocationModeForUser(ContentResolver cr, int userId) {
- final int mode = getLocationModeForUser(cr, userId);
- return putIntForUser(cr, Settings.Secure.LOCATION_PREVIOUS_MODE, mode, userId);
- }
-
- /**
- * Restores the current location mode from {@link #LOCATION_PREVIOUS_MODE}.
- */
- private static final boolean restoreLocationModeForUser(ContentResolver cr, int userId) {
- int mode = getIntForUser(cr, Settings.Secure.LOCATION_PREVIOUS_MODE,
- LOCATION_MODE_HIGH_ACCURACY, userId);
- // Make sure that the previous mode is never "off". Otherwise the user won't be able to
- // turn on location any longer.
- if (mode == LOCATION_MODE_OFF) {
- mode = LOCATION_MODE_HIGH_ACCURACY;
- }
- return setLocationModeForUser(cr, mode, userId);
- }
-
- /**
* Thread-safe method for setting the location mode to one of
* {@link #LOCATION_MODE_HIGH_ACCURACY}, {@link #LOCATION_MODE_SENSORS_ONLY},
* {@link #LOCATION_MODE_BATTERY_SAVING}, or {@link #LOCATION_MODE_OFF}.
@@ -7996,18 +7991,20 @@
* @return true if the value was set, false on database errors
*
* @throws IllegalArgumentException if mode is not one of the supported values
+ *
+ * @deprecated To enable/disable location, use
+ * {@link LocationManager#setLocationEnabledForUser(boolean, int)}.
+ * To enable/disable a specific location provider, use
+ * {@link LocationManager#setProviderEnabledForUser(String, boolean, int)}.
*/
- private static final boolean setLocationModeForUser(ContentResolver cr, int mode,
- int userId) {
+ @Deprecated
+ private static boolean setLocationModeForUser(
+ ContentResolver cr, int mode, int userId) {
synchronized (mLocationSettingsLock) {
boolean gps = false;
boolean network = false;
switch (mode) {
- case LOCATION_MODE_PREVIOUS:
- // Retrieve the actual mode and set to that mode.
- return restoreLocationModeForUser(cr, userId);
case LOCATION_MODE_OFF:
- saveLocationModeForUser(cr, userId);
break;
case LOCATION_MODE_SENSORS_ONLY:
gps = true;
@@ -8022,15 +8019,7 @@
default:
throw new IllegalArgumentException("Invalid location mode: " + mode);
}
- // Note it's important that we set the NLP mode first. The Google implementation
- // of NLP clears its NLP consent setting any time it receives a
- // LocationManager.PROVIDERS_CHANGED_ACTION broadcast and NLP is disabled. Also,
- // it shows an NLP consent dialog any time it receives the broadcast, NLP is
- // enabled, and the NLP consent is not set. If 1) we were to enable GPS first,
- // 2) a setup wizard has its own NLP consent UI that sets the NLP consent setting,
- // and 3) the receiver happened to complete before we enabled NLP, then the Google
- // NLP would detect the attempt to enable NLP and show a redundant NLP consent
- // dialog. Then the people who wrote the setup wizard would be sad.
+
boolean nlpSuccess = Settings.Secure.setLocationProviderEnabledForUser(
cr, LocationManager.NETWORK_PROVIDER, network, userId);
boolean gpsSuccess = Settings.Secure.setLocationProviderEnabledForUser(
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 7403c26..bc2d099 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -481,7 +481,6 @@
Settings.Secure.INSTALL_NON_MARKET_APPS,
Settings.Secure.LAST_SETUP_SHOWN,
Settings.Secure.LOCATION_MODE,
- Settings.Secure.LOCATION_PREVIOUS_MODE,
Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, // Candidate?
Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT, // Candidate?
Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT,
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 0990dcc..018db9a 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -89,6 +89,10 @@
ProviderProperties getProviderProperties(String provider);
String getNetworkProviderPackage();
boolean isProviderEnabled(String provider);
+ boolean isProviderEnabledForUser(String provider, int userId);
+ boolean setProviderEnabledForUser(String provider, boolean enabled, int userId);
+ boolean isLocationEnabledForUser(int userId);
+ void setLocationEnabledForUser(boolean enabled, int userId);
void addTestProvider(String name, in ProviderProperties properties, String opPackageName);
void removeTestProvider(String provider, String opPackageName);
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index f0b2774..9db9d33 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -16,7 +16,10 @@
package android.location;
-import com.android.internal.location.ProviderProperties;
+import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
+import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+import static android.Manifest.permission.LOCATION_HARDWARE;
+import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
import android.Manifest;
import android.annotation.NonNull;
@@ -24,7 +27,6 @@
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
-import android.annotation.TestApi;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
@@ -33,17 +35,15 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.Process;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Log;
-
+import com.android.internal.location.ProviderProperties;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
-import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
-import static android.Manifest.permission.ACCESS_FINE_LOCATION;
-import static android.Manifest.permission.LOCATION_HARDWARE;
-
/**
* This class provides access to the system location services. These
* services allow applications to obtain periodic updates of the
@@ -1171,13 +1171,57 @@
}
/**
+ * Returns the current enabled/disabled status of location
+ *
+ * @return true if location is enabled. false if location is disabled.
+ */
+ public boolean isLocationEnabled() {
+ return isLocationEnabledForUser(Process.myUserHandle());
+ }
+
+ /**
+ * Method for enabling or disabling location.
+ *
+ * @param enabled true to enable location. false to disable location
+ * @param userHandle the user to set
+ * @return true if the value was set, false on database errors
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(WRITE_SECURE_SETTINGS)
+ public void setLocationEnabledForUser(boolean enabled, UserHandle userHandle) {
+ try {
+ mService.setLocationEnabledForUser(enabled, userHandle.getIdentifier());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the current enabled/disabled status of location
+ *
+ * @param userHandle the user to query
+ * @return true location is enabled. false if location is disabled.
+ *
+ * @hide
+ */
+ @SystemApi
+ public boolean isLocationEnabledForUser(UserHandle userHandle) {
+ try {
+ return mService.isLocationEnabledForUser(userHandle.getIdentifier());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Returns the current enabled/disabled status of the given provider.
*
* <p>If the user has enabled this provider in the Settings menu, true
* is returned otherwise false is returned
*
- * <p>Callers should instead use
- * {@link android.provider.Settings.Secure#LOCATION_MODE}
+ * <p>Callers should instead use {@link #isLocationEnabled()}
* unless they depend on provider-specific APIs such as
* {@link #requestLocationUpdates(String, long, float, LocationListener)}.
*
@@ -1202,6 +1246,64 @@
}
/**
+ * Returns the current enabled/disabled status of the given provider and user.
+ *
+ * <p>If the user has enabled this provider in the Settings menu, true
+ * is returned otherwise false is returned
+ *
+ * <p>Callers should instead use {@link #isLocationEnabled()}
+ * unless they depend on provider-specific APIs such as
+ * {@link #requestLocationUpdates(String, long, float, LocationListener)}.
+ *
+ * <p>
+ * Before API version {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this
+ * method would throw {@link SecurityException} if the location permissions
+ * were not sufficient to use the specified provider.
+ *
+ * @param provider the name of the provider
+ * @param userHandle the user to query
+ * @return true if the provider exists and is enabled
+ *
+ * @throws IllegalArgumentException if provider is null
+ * @hide
+ */
+ @SystemApi
+ public boolean isProviderEnabledForUser(String provider, UserHandle userHandle) {
+ checkProvider(provider);
+
+ try {
+ return mService.isProviderEnabledForUser(provider, userHandle.getIdentifier());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Method for enabling or disabling a single location provider.
+ *
+ * @param provider the name of the provider
+ * @param enabled true to enable the provider. false to disable the provider
+ * @param userHandle the user to set
+ * @return true if the value was set, false on database errors
+ *
+ * @throws IllegalArgumentException if provider is null
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(WRITE_SECURE_SETTINGS)
+ public boolean setProviderEnabledForUser(
+ String provider, boolean enabled, UserHandle userHandle) {
+ checkProvider(provider);
+
+ try {
+ return mService.setProviderEnabledForUser(
+ provider, enabled, userHandle.getIdentifier());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Get the last known location.
*
* <p>This location could be very old so use
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index 3c46d99..d001e66 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -21,10 +21,9 @@
import android.os.UserManager;
import android.print.PrintManager;
import android.provider.Settings;
-
import com.android.internal.util.UserIcons;
import com.android.settingslib.drawable.UserIconDrawable;
-
+import com.android.settingslib.wrapper.LocationManagerWrapper;
import java.text.NumberFormat;
public class Utils {
@@ -45,6 +44,24 @@
com.android.internal.R.drawable.ic_wifi_signal_4
};
+ public static void updateLocationEnabled(Context context, boolean enabled, int userId) {
+ Intent intent = new Intent(LocationManager.MODE_CHANGING_ACTION);
+
+ final int oldMode = Settings.Secure.getIntForUser(context.getContentResolver(),
+ Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF, userId);
+ final int newMode = enabled
+ ? Settings.Secure.LOCATION_MODE_HIGH_ACCURACY
+ : Settings.Secure.LOCATION_MODE_OFF;
+ intent.putExtra(CURRENT_MODE_KEY, oldMode);
+ intent.putExtra(NEW_MODE_KEY, newMode);
+ context.sendBroadcastAsUser(
+ intent, UserHandle.of(userId), android.Manifest.permission.WRITE_SECURE_SETTINGS);
+ LocationManager locationManager =
+ (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
+ LocationManagerWrapper wrapper = new LocationManagerWrapper(locationManager);
+ wrapper.setLocationEnabledForUser(enabled, UserHandle.of(userId));
+ }
+
public static boolean updateLocationMode(Context context, int oldMode, int newMode, int userId) {
Intent intent = new Intent(LocationManager.MODE_CHANGING_ACTION);
intent.putExtra(CURRENT_MODE_KEY, oldMode);
diff --git a/packages/SettingsLib/src/com/android/settingslib/wrapper/LocationManagerWrapper.java b/packages/SettingsLib/src/com/android/settingslib/wrapper/LocationManagerWrapper.java
new file mode 100644
index 0000000..1a268a6
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/wrapper/LocationManagerWrapper.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 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.settingslib.wrapper;
+
+import android.location.LocationManager;
+import android.os.UserHandle;
+
+/**
+ * This class replicates some methods of android.location.LocationManager that are new and not
+ * yet available in our current version of Robolectric. It provides a thin wrapper to call the real
+ * methods in production and a mock in tests.
+ */
+public class LocationManagerWrapper {
+
+ private LocationManager mLocationManager;
+
+ public LocationManagerWrapper(LocationManager locationManager) {
+ mLocationManager = locationManager;
+ }
+
+ /** Returns the real {@code LocationManager} object */
+ public LocationManager getLocationManager() {
+ return mLocationManager;
+ }
+
+ /** Wraps {@code LocationManager.isProviderEnabled} method */
+ public boolean isProviderEnabled(String provider) {
+ return mLocationManager.isProviderEnabled(provider);
+ }
+
+ /** Wraps {@code LocationManager.setProviderEnabledForUser} method */
+ public void setProviderEnabledForUser(String provider, boolean enabled, UserHandle userHandle) {
+ mLocationManager.setProviderEnabledForUser(provider, enabled, userHandle);
+ }
+
+ /** Wraps {@code LocationManager.isLocationEnabled} method */
+ public boolean isLocationEnabled() {
+ return mLocationManager.isLocationEnabled();
+ }
+
+ /** Wraps {@code LocationManager.isLocationEnabledForUser} method */
+ public boolean isLocationEnabledForUser(UserHandle userHandle) {
+ return mLocationManager.isLocationEnabledForUser(userHandle);
+ }
+
+ /** Wraps {@code LocationManager.setLocationEnabledForUser} method */
+ public void setLocationEnabledForUser(boolean enabled, UserHandle userHandle) {
+ mLocationManager.setLocationEnabledForUser(enabled, userHandle);
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
index 976bbee..327c1c8 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
@@ -15,28 +15,8 @@
*/
package com.android.settingslib;
-import android.app.ActivityManager;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.location.LocationManager;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.provider.Settings.Secure;
-import android.text.TextUtils;
-import java.util.HashMap;
-import java.util.Map;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentMatcher;
-import org.mockito.ArgumentMatchers;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
-
import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
import static com.google.common.truth.Truth.assertThat;
-
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
@@ -44,7 +24,28 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.ActivityManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
import android.content.res.Resources;
+import android.location.LocationManager;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.provider.Settings.Secure;
+import android.text.TextUtils;
+import com.android.settingslib.wrapper.LocationManagerWrapper;
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatcher;
+import org.mockito.ArgumentMatchers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.shadows.ShadowSettings;
@@ -53,7 +54,9 @@
@Config(
manifest = TestConfig.MANIFEST_PATH,
sdk = TestConfig.SDK_VERSION,
- shadows = {UtilsTest.ShadowSecure.class})
+ shadows = {
+ UtilsTest.ShadowSecure.class,
+ UtilsTest.ShadowLocationManagerWrapper.class})
public class UtilsTest {
private static final double[] TEST_PERCENTAGES = {0, 0.4, 0.5, 0.6, 49, 49.3, 49.8, 50, 100};
private static final String PERCENTAGE_0 = "0%";
@@ -63,10 +66,14 @@
private static final String PERCENTAGE_100 = "100%";
private Context mContext;
+ @Mock
+ private LocationManager mLocationManager;
@Before
public void setUp() {
+ MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
+ when(mContext.getSystemService(Context.LOCATION_SERVICE)).thenReturn(mLocationManager);
ShadowSecure.reset();
}
@@ -86,6 +93,17 @@
}
@Test
+ public void testUpdateLocationEnabled_sendBroadcast() {
+ int currentUserId = ActivityManager.getCurrentUser();
+ Utils.updateLocationEnabled(mContext, true, currentUserId);
+
+ verify(mContext).sendBroadcastAsUser(
+ argThat(actionMatches(LocationManager.MODE_CHANGING_ACTION)),
+ ArgumentMatchers.eq(UserHandle.of(currentUserId)),
+ ArgumentMatchers.eq(WRITE_SECURE_SETTINGS));
+ }
+
+ @Test
public void testFormatPercentage_RoundTrue_RoundUpIfPossible() {
final String[] expectedPercentages = {PERCENTAGE_0, PERCENTAGE_0, PERCENTAGE_1,
PERCENTAGE_1, PERCENTAGE_49, PERCENTAGE_49, PERCENTAGE_50, PERCENTAGE_50,
@@ -137,8 +155,26 @@
return true;
}
+ @Implementation
+ public static int getIntForUser(ContentResolver cr, String name, int def, int userHandle) {
+ if (map.containsKey(name)) {
+ return map.get(name);
+ } else {
+ return def;
+ }
+ }
+
public static void reset() {
map.clear();
}
}
+
+ @Implements(value = LocationManagerWrapper.class)
+ public static class ShadowLocationManagerWrapper {
+
+ @Implementation
+ public void setLocationEnabledForUser(boolean enabled, UserHandle userHandle) {
+ // Do nothing
+ }
+ }
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index fc765f4..cfd33a1 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -16,6 +16,7 @@
package com.android.providers.settings;
+import android.os.Process;
import com.android.internal.R;
import com.android.internal.app.LocalePicker;
import com.android.internal.annotations.VisibleForTesting;
@@ -288,12 +289,12 @@
}
final String GPS = LocationManager.GPS_PROVIDER;
boolean enabled =
- GPS.equals(value) ||
+ GPS.equals(value) ||
value.startsWith(GPS + ",") ||
value.endsWith("," + GPS) ||
value.contains("," + GPS + ",");
- Settings.Secure.setLocationProviderEnabled(
- mContext.getContentResolver(), GPS, enabled);
+ LocationManager lm = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
+ lm.setProviderEnabledForUser(GPS, enabled, Process.myUserHandle());
}
private void setSoundEffects(boolean enable) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 5a75681..55f7a0a 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1223,9 +1223,6 @@
dumpSetting(s, p,
Settings.Secure.LOCATION_MODE,
SecureSettingsProto.LOCATION_MODE);
- dumpSetting(s, p,
- Settings.Secure.LOCATION_PREVIOUS_MODE,
- SecureSettingsProto.LOCATION_PREVIOUS_MODE);
// Settings.Secure.LOCK_BIOMETRIC_WEAK_FLAGS intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.Secure.LOCK_TO_APP_EXIT_LOCKED,
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 4ee4ef4..0b666a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
@@ -16,11 +16,12 @@
package com.android.systemui.statusbar.policy;
+import static com.android.settingslib.Utils.updateLocationEnabled;
+
import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.StatusBarManager;
import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -28,19 +29,14 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
-import android.provider.Settings;
import android.support.annotation.VisibleForTesting;
-
-import com.android.systemui.R;
import com.android.systemui.util.Utils;
-
import java.util.ArrayList;
import java.util.List;
-import static com.android.settingslib.Utils.updateLocationMode;
-
/**
* A controller to manage changes of location related states and update the views accordingly.
*/
@@ -101,32 +97,27 @@
* @return true if attempt to change setting was successful.
*/
public boolean setLocationEnabled(boolean enabled) {
+ // QuickSettings always runs as the owner, so specifically set the settings
+ // for the current foreground user.
int currentUserId = ActivityManager.getCurrentUser();
if (isUserLocationRestricted(currentUserId)) {
return false;
}
- final ContentResolver cr = mContext.getContentResolver();
// When enabling location, a user consent dialog will pop up, and the
// setting won't be fully enabled until the user accepts the agreement.
- int currentMode = Settings.Secure.getIntForUser(cr, Settings.Secure.LOCATION_MODE,
- Settings.Secure.LOCATION_MODE_OFF, currentUserId);
- int mode = enabled
- ? Settings.Secure.LOCATION_MODE_PREVIOUS : Settings.Secure.LOCATION_MODE_OFF;
- // QuickSettings always runs as the owner, so specifically set the settings
- // for the current foreground user.
- return updateLocationMode(mContext, currentMode, mode, currentUserId);
+ updateLocationEnabled(mContext, enabled, currentUserId);
+ return true;
}
/**
- * Returns true if location isn't disabled in settings.
+ * Returns true if location is enabled in settings.
*/
public boolean isLocationEnabled() {
- ContentResolver resolver = mContext.getContentResolver();
// QuickSettings always runs as the owner, so specifically retrieve the settings
// for the current foreground user.
- int mode = Settings.Secure.getIntForUser(resolver, Settings.Secure.LOCATION_MODE,
- Settings.Secure.LOCATION_MODE_OFF, ActivityManager.getCurrentUser());
- return mode != Settings.Secure.LOCATION_MODE_OFF;
+ LocationManager locationManager =
+ (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
+ return locationManager.isLocationEnabledForUser(Process.myUserHandle());
}
@Override
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 6c63f43..5a4f7ca 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -16,11 +16,64 @@
package com.android.server;
-import android.app.ActivityManager;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
import android.annotation.NonNull;
+import android.app.ActivityManager;
+import android.app.AppOpsManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.ResolveInfo;
+import android.content.pm.Signature;
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.hardware.location.ActivityRecognitionHardware;
+import android.location.Address;
+import android.location.Criteria;
+import android.location.GeocoderParams;
+import android.location.Geofence;
+import android.location.IBatchedLocationCallback;
+import android.location.IGnssMeasurementsListener;
+import android.location.IGnssNavigationMessageListener;
+import android.location.IGnssStatusListener;
+import android.location.IGnssStatusProvider;
+import android.location.IGpsGeofenceHardware;
+import android.location.ILocationListener;
+import android.location.ILocationManager;
+import android.location.INetInitiatedListener;
+import android.location.Location;
+import android.location.LocationManager;
+import android.location.LocationProvider;
+import android.location.LocationRequest;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.os.WorkSource;
+import android.provider.Settings;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
-
+import android.util.EventLog;
+import android.util.Log;
+import android.util.Slog;
import com.android.internal.content.PackageMonitor;
import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
@@ -45,60 +98,6 @@
import com.android.server.location.LocationRequestStatistics.PackageStatistics;
import com.android.server.location.MockProvider;
import com.android.server.location.PassiveProvider;
-
-import android.app.AppOpsManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ResolveInfo;
-import android.content.pm.Signature;
-import android.content.res.Resources;
-import android.database.ContentObserver;
-import android.hardware.location.ActivityRecognitionHardware;
-import android.location.Address;
-import android.location.Criteria;
-import android.location.GeocoderParams;
-import android.location.Geofence;
-import android.location.IBatchedLocationCallback;
-import android.location.IGnssMeasurementsListener;
-import android.location.IGnssStatusListener;
-import android.location.IGnssStatusProvider;
-import android.location.IGpsGeofenceHardware;
-import android.location.IGnssNavigationMessageListener;
-import android.location.ILocationListener;
-import android.location.ILocationManager;
-import android.location.INetInitiatedListener;
-import android.location.Location;
-import android.location.LocationManager;
-import android.location.LocationProvider;
-import android.location.LocationRequest;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.PowerManager;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.os.WorkSource;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.EventLog;
-import android.util.Log;
-import android.util.Slog;
-
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -1378,10 +1377,7 @@
if (mDisabledProviders.contains(provider)) {
return false;
}
- // Use system settings
- ContentResolver resolver = mContext.getContentResolver();
-
- return Settings.Secure.isLocationProviderEnabledForUser(resolver, provider, mCurrentUserId);
+ return isLocationProviderEnabledForUser(provider, mCurrentUserId);
}
/**
@@ -1400,6 +1396,23 @@
}
/**
+ * Returns "true" if access to the specified location provider is allowed by the specified
+ * user's settings. Access to all location providers is forbidden to non-location-provider
+ * processes belonging to background users.
+ *
+ * @param provider the name of the location provider
+ * @param uid the requestor's UID
+ * @param userId the user id to query
+ */
+ private boolean isAllowedByUserSettingsLockedForUser(
+ String provider, int uid, int userId) {
+ if (!isCurrentProfile(UserHandle.getUserId(uid)) && !isUidALocationProvider(uid)) {
+ return false;
+ }
+ return isLocationProviderEnabledForUser(provider, userId);
+ }
+
+ /**
* Returns the permission string associated with the specified resolution level.
*
* @param resolutionLevel the resolution level
@@ -1425,10 +1438,10 @@
*/
private int getAllowedResolutionLevel(int pid, int uid) {
if (mContext.checkPermission(android.Manifest.permission.ACCESS_FINE_LOCATION,
- pid, uid) == PackageManager.PERMISSION_GRANTED) {
+ pid, uid) == PERMISSION_GRANTED) {
return RESOLUTION_LEVEL_FINE;
} else if (mContext.checkPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION,
- pid, uid) == PackageManager.PERMISSION_GRANTED) {
+ pid, uid) == PERMISSION_GRANTED) {
return RESOLUTION_LEVEL_COARSE;
} else {
return RESOLUTION_LEVEL_NONE;
@@ -2053,7 +2066,7 @@
}
boolean callerHasLocationHardwarePermission =
mContext.checkCallingPermission(android.Manifest.permission.LOCATION_HARDWARE)
- == PackageManager.PERMISSION_GRANTED;
+ == PERMISSION_GRANTED;
LocationRequest sanitizedRequest = createSanitizedRequest(request, allowedResolutionLevel,
callerHasLocationHardwarePermission);
@@ -2326,7 +2339,7 @@
// Require that caller can manage given document
boolean callerHasLocationHardwarePermission =
mContext.checkCallingPermission(android.Manifest.permission.LOCATION_HARDWARE)
- == PackageManager.PERMISSION_GRANTED;
+ == PERMISSION_GRANTED;
LocationRequest sanitizedRequest = createSanitizedRequest(request, allowedResolutionLevel,
callerHasLocationHardwarePermission);
@@ -2476,7 +2489,7 @@
// and check for ACCESS_LOCATION_EXTRA_COMMANDS
if ((mContext.checkCallingOrSelfPermission(ACCESS_LOCATION_EXTRA_COMMANDS)
- != PackageManager.PERMISSION_GRANTED)) {
+ != PERMISSION_GRANTED)) {
throw new SecurityException("Requires ACCESS_LOCATION_EXTRA_COMMANDS permission");
}
@@ -2546,8 +2559,64 @@
return null;
}
+ /**
+ * Method for enabling or disabling location.
+ *
+ * @param enabled true to enable location. false to disable location
+ * @param userId the user id to set
+ */
+ @Override
+ public void setLocationEnabledForUser(boolean enabled, int userId) {
+ // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
+ checkInteractAcrossUsersPermission(userId);
+
+ // enable all location providers
+ synchronized (mLock) {
+ for(String provider : getAllProviders()) {
+ setProviderEnabledForUser(provider, enabled, userId);
+ }
+ }
+ }
+
+ /**
+ * Returns the current enabled/disabled status of location
+ *
+ * @param userId the user id to query
+ * @return true if location is enabled. false if location is disabled.
+ */
+ @Override
+ public boolean isLocationEnabledForUser(int userId) {
+
+ // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
+ checkInteractAcrossUsersPermission(userId);
+
+ synchronized (mLock) {
+ for (String provider : getAllProviders()) {
+ if (isProviderEnabledForUser(provider, userId)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
@Override
public boolean isProviderEnabled(String provider) {
+ return isProviderEnabledForUser(provider, UserHandle.myUserId());
+ }
+
+ /**
+ * Method for determining if a location provider is enabled.
+ *
+ * @param provider the location provider to query
+ * @param userId the user id to query
+ * @return true if the provider is enabled
+ */
+ @Override
+ public boolean isProviderEnabledForUser(String provider, int userId) {
+ // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
+ checkInteractAcrossUsersPermission(userId);
+
// Fused provider is accessed indirectly via criteria rather than the provider-based APIs,
// so we discourage its use
if (LocationManager.FUSED_PROVIDER.equals(provider)) return false;
@@ -2557,7 +2626,8 @@
try {
synchronized (mLock) {
LocationProviderInterface p = mProvidersByName.get(provider);
- return p != null && isAllowedByUserSettingsLocked(provider, uid);
+ return p != null
+ && isAllowedByUserSettingsLockedForUser(provider, uid, userId);
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -2565,6 +2635,83 @@
}
/**
+ * Method for enabling or disabling a single location provider.
+ *
+ * @param provider the name of the provider
+ * @param enabled true to enable the provider. false to disable the provider
+ * @param userId the user id to set
+ * @return true if the value was set successfully. false on failure.
+ */
+ @Override
+ public boolean setProviderEnabledForUser(
+ String provider, boolean enabled, int userId) {
+ mContext.enforceCallingPermission(
+ android.Manifest.permission.WRITE_SECURE_SETTINGS,
+ "Requires WRITE_SECURE_SETTINGS permission");
+
+ // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
+ checkInteractAcrossUsersPermission(userId);
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ // to ensure thread safety, we write the provider name with a '+' or '-'
+ // and let the SettingsProvider handle it rather than reading and modifying
+ // the list of enabled providers.
+ if (enabled) {
+ provider = "+" + provider;
+ } else {
+ provider = "-" + provider;
+ }
+ return Settings.Secure.putStringForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+ provider,
+ userId);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Read location provider status from Settings.Secure
+ *
+ * @param provider the location provider to query
+ * @param userId the user id to query
+ * @return true if the provider is enabled
+ */
+ private boolean isLocationProviderEnabledForUser(String provider, int userId) {
+ long identity = Binder.clearCallingIdentity();
+ try {
+ // Use system settings
+ ContentResolver cr = mContext.getContentResolver();
+ String allowedProviders = Settings.Secure.getStringForUser(
+ cr, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, userId);
+ return TextUtils.delimitedStringContains(allowedProviders, ',', provider);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Method for checking INTERACT_ACROSS_USERS permission if specified user id is not the same as
+ * current user id
+ *
+ * @param userId the user id to get or set value
+ */
+ private void checkInteractAcrossUsersPermission(int userId) {
+ int uid = Binder.getCallingUid();
+ if (UserHandle.getUserId(uid) != userId) {
+ if (ActivityManager.checkComponentPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS, uid, -1, true)
+ != PERMISSION_GRANTED) {
+ throw new SecurityException("Requires INTERACT_ACROSS_USERS permission");
+ }
+ }
+ }
+
+ /**
* Returns "true" if the UID belongs to a bound location provider.
*
* @param uid the uid
@@ -2585,7 +2732,7 @@
private void checkCallerIsProvider() {
if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER)
- == PackageManager.PERMISSION_GRANTED) {
+ == PERMISSION_GRANTED) {
return;
}