• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.location;
18 
19 import static android.location.LocationManager.FUSED_PROVIDER;
20 import static android.location.LocationManager.PASSIVE_PROVIDER;
21 import static android.provider.Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS;
22 import static android.provider.Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST;
23 import static android.provider.Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS;
24 import static android.provider.Settings.Global.LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST;
25 import static android.provider.Settings.Secure.LOCATION_COARSE_ACCURACY_M;
26 import static android.provider.Settings.Secure.LOCATION_MODE;
27 import static android.provider.Settings.Secure.LOCATION_MODE_OFF;
28 
29 import static com.android.server.location.LocationManagerService.D;
30 import static com.android.server.location.LocationManagerService.TAG;
31 
32 import android.app.ActivityManager;
33 import android.content.Context;
34 import android.database.ContentObserver;
35 import android.net.Uri;
36 import android.os.Binder;
37 import android.os.Handler;
38 import android.os.UserHandle;
39 import android.provider.Settings;
40 import android.text.TextUtils;
41 import android.util.ArraySet;
42 import android.util.Log;
43 
44 import com.android.internal.annotations.GuardedBy;
45 import com.android.internal.util.IndentingPrintWriter;
46 import com.android.internal.util.Preconditions;
47 import com.android.server.SystemConfig;
48 
49 import java.io.FileDescriptor;
50 import java.io.PrintWriter;
51 import java.util.Arrays;
52 import java.util.Collections;
53 import java.util.List;
54 import java.util.Set;
55 import java.util.concurrent.CopyOnWriteArrayList;
56 import java.util.function.Supplier;
57 
58 /**
59  * Provides accessors and listeners for all location related settings.
60  */
61 public class SettingsHelper {
62 
63     /**
64      * Listener for user-specific settings changes.
65      */
66     public interface UserSettingChangedListener {
67         /**
68          * Called when setting changes.
69          */
onSettingChanged(int userId)70         void onSettingChanged(int userId);
71     }
72 
73     /**
74      * Listener for global settings changes.
75      */
76     public interface GlobalSettingChangedListener extends UserSettingChangedListener {
77         /**
78          * Called when setting changes.
79          */
onSettingChanged()80         void onSettingChanged();
81 
82         @Override
onSettingChanged(int userId)83         default void onSettingChanged(int userId) {
84             onSettingChanged();
85         }
86     }
87 
88     private static final String LOCATION_PACKAGE_BLACKLIST = "locationPackagePrefixBlacklist";
89     private static final String LOCATION_PACKAGE_WHITELIST = "locationPackagePrefixWhitelist";
90 
91     private static final long DEFAULT_BACKGROUND_THROTTLE_INTERVAL_MS = 30 * 60 * 1000;
92     private static final long DEFAULT_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS =
93             30 * 60 * 1000;
94     private static final float DEFAULT_COARSE_LOCATION_ACCURACY_M = 2000.0f;
95 
96     private final Context mContext;
97 
98     private final IntegerSecureSetting mLocationMode;
99     private final LongGlobalSetting mBackgroundThrottleIntervalMs;
100     private final StringListCachedSecureSetting mLocationPackageBlacklist;
101     private final StringListCachedSecureSetting mLocationPackageWhitelist;
102     private final StringSetCachedGlobalSetting mBackgroundThrottlePackageWhitelist;
103     private final StringSetCachedGlobalSetting mIgnoreSettingsPackageWhitelist;
104 
105     // TODO: get rid of handler
SettingsHelper(Context context, Handler handler)106     public SettingsHelper(Context context, Handler handler) {
107         mContext = context;
108 
109         mLocationMode = new IntegerSecureSetting(context, LOCATION_MODE, handler);
110         mBackgroundThrottleIntervalMs = new LongGlobalSetting(context,
111                 LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS, handler);
112         mLocationPackageBlacklist = new StringListCachedSecureSetting(context,
113                 LOCATION_PACKAGE_BLACKLIST, handler);
114         mLocationPackageWhitelist = new StringListCachedSecureSetting(context,
115                 LOCATION_PACKAGE_WHITELIST, handler);
116         mBackgroundThrottlePackageWhitelist = new StringSetCachedGlobalSetting(context,
117                 LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST,
118                 () -> SystemConfig.getInstance().getAllowUnthrottledLocation(), handler);
119         mIgnoreSettingsPackageWhitelist = new StringSetCachedGlobalSetting(context,
120                 LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST,
121                 () -> SystemConfig.getInstance().getAllowIgnoreLocationSettings(), handler);
122     }
123 
124     /** Called when system is ready. */
onSystemReady()125     public void onSystemReady() {
126         mLocationMode.register();
127         mBackgroundThrottleIntervalMs.register();
128         mLocationPackageBlacklist.register();
129         mLocationPackageWhitelist.register();
130         mBackgroundThrottlePackageWhitelist.register();
131         mIgnoreSettingsPackageWhitelist.register();
132     }
133 
134     /**
135      * Retrieve if location is enabled or not.
136      */
isLocationEnabled(int userId)137     public boolean isLocationEnabled(int userId) {
138         return mLocationMode.getValueForUser(LOCATION_MODE_OFF, userId) != LOCATION_MODE_OFF;
139     }
140 
141     /**
142      * Set location enabled for a user.
143      */
setLocationEnabled(boolean enabled, int userId)144     public void setLocationEnabled(boolean enabled, int userId) {
145         long identity = Binder.clearCallingIdentity();
146         try {
147             Settings.Secure.putIntForUser(
148                     mContext.getContentResolver(),
149                     Settings.Secure.LOCATION_MODE,
150                     enabled
151                         ? Settings.Secure.LOCATION_MODE_ON
152                         : Settings.Secure.LOCATION_MODE_OFF,
153                     userId);
154         } finally {
155             Binder.restoreCallingIdentity(identity);
156         }
157     }
158 
159     /**
160      * Add a listener for changes to the location enabled setting. Callbacks occur on an unspecified
161      * thread.
162      */
addOnLocationEnabledChangedListener(UserSettingChangedListener listener)163     public void addOnLocationEnabledChangedListener(UserSettingChangedListener listener) {
164         mLocationMode.addListener(listener);
165     }
166 
167     /**
168      * Remove a listener for changes to the location enabled setting.
169      */
removeOnLocationEnabledChangedListener(UserSettingChangedListener listener)170     public void removeOnLocationEnabledChangedListener(UserSettingChangedListener listener) {
171         mLocationMode.removeListener(listener);
172     }
173 
174     /**
175      * Retrieve the background throttle interval.
176      */
getBackgroundThrottleIntervalMs()177     public long getBackgroundThrottleIntervalMs() {
178         return mBackgroundThrottleIntervalMs.getValue(DEFAULT_BACKGROUND_THROTTLE_INTERVAL_MS);
179     }
180 
181     /**
182      * Add a listener for changes to the background throttle interval. Callbacks occur on an
183      * unspecified thread.
184      */
addOnBackgroundThrottleIntervalChangedListener( GlobalSettingChangedListener listener)185     public void addOnBackgroundThrottleIntervalChangedListener(
186             GlobalSettingChangedListener listener) {
187         mBackgroundThrottleIntervalMs.addListener(listener);
188     }
189 
190     /**
191      * Remove a listener for changes to the background throttle interval.
192      */
removeOnBackgroundThrottleIntervalChangedListener( GlobalSettingChangedListener listener)193     public void removeOnBackgroundThrottleIntervalChangedListener(
194             GlobalSettingChangedListener listener) {
195         mBackgroundThrottleIntervalMs.removeListener(listener);
196     }
197 
198     /**
199      * Check if the given package is blacklisted for location access.
200      */
isLocationPackageBlacklisted(int userId, String packageName)201     public boolean isLocationPackageBlacklisted(int userId, String packageName) {
202         List<String> locationPackageBlacklist = mLocationPackageBlacklist.getValueForUser(userId);
203         if (locationPackageBlacklist.isEmpty()) {
204             return false;
205         }
206 
207         List<String> locationPackageWhitelist = mLocationPackageWhitelist.getValueForUser(userId);
208         for (String locationWhitelistPackage : locationPackageWhitelist) {
209             if (packageName.startsWith(locationWhitelistPackage)) {
210                 return false;
211             }
212         }
213 
214         for (String locationBlacklistPackage : locationPackageBlacklist) {
215             if (packageName.startsWith(locationBlacklistPackage)) {
216                 return true;
217             }
218         }
219 
220         return false;
221     }
222 
223     /**
224      * Retrieve the background throttle package whitelist.
225      */
getBackgroundThrottlePackageWhitelist()226     public Set<String> getBackgroundThrottlePackageWhitelist() {
227         return mBackgroundThrottlePackageWhitelist.getValue();
228     }
229 
230     /**
231      * Add a listener for changes to the background throttle package whitelist. Callbacks occur on
232      * an unspecified thread.
233      */
addOnBackgroundThrottlePackageWhitelistChangedListener( GlobalSettingChangedListener listener)234     public void addOnBackgroundThrottlePackageWhitelistChangedListener(
235             GlobalSettingChangedListener listener) {
236         mBackgroundThrottlePackageWhitelist.addListener(listener);
237     }
238 
239     /**
240      * Remove a listener for changes to the background throttle package whitelist.
241      */
removeOnBackgroundThrottlePackageWhitelistChangedListener( GlobalSettingChangedListener listener)242     public void removeOnBackgroundThrottlePackageWhitelistChangedListener(
243             GlobalSettingChangedListener listener) {
244         mBackgroundThrottlePackageWhitelist.removeListener(listener);
245     }
246 
247     /**
248      * Retrieve the ignore settings package whitelist.
249      */
getIgnoreSettingsPackageWhitelist()250     public Set<String> getIgnoreSettingsPackageWhitelist() {
251         return mIgnoreSettingsPackageWhitelist.getValue();
252     }
253 
254     /**
255      * Add a listener for changes to the ignore settings package whitelist. Callbacks occur on an
256      * unspecified thread.
257      */
addOnIgnoreSettingsPackageWhitelistChangedListener( GlobalSettingChangedListener listener)258     public void addOnIgnoreSettingsPackageWhitelistChangedListener(
259             GlobalSettingChangedListener listener) {
260         mIgnoreSettingsPackageWhitelist.addListener(listener);
261     }
262 
263     /**
264      * Remove a listener for changes to the ignore settings package whitelist.
265      */
removeOnIgnoreSettingsPackageWhitelistChangedListener( GlobalSettingChangedListener listener)266     public void removeOnIgnoreSettingsPackageWhitelistChangedListener(
267             GlobalSettingChangedListener listener) {
268         mIgnoreSettingsPackageWhitelist.removeListener(listener);
269     }
270 
271     /**
272      * Retrieve the background throttling proximity alert interval.
273      */
getBackgroundThrottleProximityAlertIntervalMs()274     public long getBackgroundThrottleProximityAlertIntervalMs() {
275         long identity = Binder.clearCallingIdentity();
276         try {
277             return Settings.Global.getLong(mContext.getContentResolver(),
278                     LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS,
279                     DEFAULT_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS);
280         } finally {
281             Binder.restoreCallingIdentity(identity);
282         }
283     }
284 
285     /**
286      * Retrieve the accuracy for coarsening location, ie, the grid size used for snap-to-grid
287      * coarsening.
288      */
getCoarseLocationAccuracyM()289     public float getCoarseLocationAccuracyM() {
290         long identity = Binder.clearCallingIdentity();
291         try {
292             return Settings.Secure.getFloat(
293                     mContext.getContentResolver(),
294                     LOCATION_COARSE_ACCURACY_M,
295                     DEFAULT_COARSE_LOCATION_ACCURACY_M);
296         } finally {
297             Binder.restoreCallingIdentity(identity);
298         }
299     }
300 
301     /**
302      * Set a value for the deprecated LOCATION_PROVIDERS_ALLOWED setting. This is used purely for
303      * backwards compatibility for old clients, and may be removed in the future.
304      */
setLocationProviderAllowed(String provider, boolean enabled, int userId)305     public void setLocationProviderAllowed(String provider, boolean enabled, int userId) {
306         // fused and passive provider never get public updates for legacy reasons
307         if (FUSED_PROVIDER.equals(provider) || PASSIVE_PROVIDER.equals(provider)) {
308             return;
309         }
310 
311         long identity = Binder.clearCallingIdentity();
312         try {
313             // update LOCATION_PROVIDERS_ALLOWED for best effort backwards compatibility
314             Settings.Secure.putStringForUser(
315                     mContext.getContentResolver(),
316                     Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
317                     (enabled ? "+" : "-") + provider,
318                     userId);
319         } finally {
320             Binder.restoreCallingIdentity(identity);
321         }
322     }
323 
324     /**
325      * Dump info for debugging.
326      */
dump(FileDescriptor fd, PrintWriter pw, String[] args)327     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
328         IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
329         int userId = ActivityManager.getCurrentUser();
330 
331         ipw.print("Location Enabled: ");
332         ipw.println(isLocationEnabled(userId));
333 
334         List<String> locationPackageBlacklist = mLocationPackageBlacklist.getValueForUser(userId);
335         if (!locationPackageBlacklist.isEmpty()) {
336             ipw.println("Location Blacklisted Packages:");
337             ipw.increaseIndent();
338             for (String packageName : locationPackageBlacklist) {
339                 ipw.println(packageName);
340             }
341             ipw.decreaseIndent();
342 
343             List<String> locationPackageWhitelist = mLocationPackageWhitelist.getValueForUser(
344                     userId);
345             if (!locationPackageWhitelist.isEmpty()) {
346                 ipw.println("Location Whitelisted Packages:");
347                 ipw.increaseIndent();
348                 for (String packageName : locationPackageWhitelist) {
349                     ipw.println(packageName);
350                 }
351                 ipw.decreaseIndent();
352             }
353         }
354 
355         Set<String> backgroundThrottlePackageWhitelist =
356                 mBackgroundThrottlePackageWhitelist.getValue();
357         if (!backgroundThrottlePackageWhitelist.isEmpty()) {
358             ipw.println("Throttling Whitelisted Packages:");
359             ipw.increaseIndent();
360             for (String packageName : backgroundThrottlePackageWhitelist) {
361                 ipw.println(packageName);
362             }
363             ipw.decreaseIndent();
364         }
365 
366         Set<String> ignoreSettingsPackageWhitelist = mIgnoreSettingsPackageWhitelist.getValue();
367         if (!ignoreSettingsPackageWhitelist.isEmpty()) {
368             ipw.println("Bypass Whitelisted Packages:");
369             ipw.increaseIndent();
370             for (String packageName : ignoreSettingsPackageWhitelist) {
371                 ipw.println(packageName);
372             }
373             ipw.decreaseIndent();
374         }
375     }
376 
377     private abstract static class ObservingSetting extends ContentObserver {
378 
379         private final CopyOnWriteArrayList<UserSettingChangedListener> mListeners;
380 
381         @GuardedBy("this")
382         private boolean mRegistered;
383 
ObservingSetting(Handler handler)384         private ObservingSetting(Handler handler) {
385             super(handler);
386             mListeners = new CopyOnWriteArrayList<>();
387         }
388 
isRegistered()389         protected synchronized boolean isRegistered() {
390             return mRegistered;
391         }
392 
register(Context context, Uri uri)393         protected synchronized void register(Context context, Uri uri) {
394             if (mRegistered) {
395                 return;
396             }
397 
398             context.getContentResolver().registerContentObserver(
399                     uri, false, this, UserHandle.USER_ALL);
400             mRegistered = true;
401         }
402 
addListener(UserSettingChangedListener listener)403         public void addListener(UserSettingChangedListener listener) {
404             mListeners.add(listener);
405         }
406 
removeListener(UserSettingChangedListener listener)407         public void removeListener(UserSettingChangedListener listener) {
408             mListeners.remove(listener);
409         }
410 
411         @Override
onChange(boolean selfChange, Uri uri, int userId)412         public void onChange(boolean selfChange, Uri uri, int userId) {
413             if (D) {
414                 Log.d(TAG, "location setting changed [u" + userId + "]: " + uri);
415             }
416 
417             for (UserSettingChangedListener listener : mListeners) {
418                 listener.onSettingChanged(userId);
419             }
420         }
421     }
422 
423     private static class IntegerSecureSetting extends ObservingSetting {
424 
425         private final Context mContext;
426         private final String mSettingName;
427 
IntegerSecureSetting(Context context, String settingName, Handler handler)428         private IntegerSecureSetting(Context context, String settingName, Handler handler) {
429             super(handler);
430             mContext = context;
431             mSettingName = settingName;
432         }
433 
register()434         private void register() {
435             register(mContext, Settings.Secure.getUriFor(mSettingName));
436         }
437 
getValueForUser(int defaultValue, int userId)438         public int getValueForUser(int defaultValue, int userId) {
439             long identity = Binder.clearCallingIdentity();
440             try {
441                 return Settings.Secure.getIntForUser(mContext.getContentResolver(), mSettingName,
442                         defaultValue, userId);
443             } finally {
444                 Binder.restoreCallingIdentity(identity);
445             }
446         }
447     }
448 
449     private static class StringListCachedSecureSetting extends ObservingSetting {
450 
451         private final Context mContext;
452         private final String mSettingName;
453 
454         @GuardedBy("this")
455         private int mCachedUserId;
456         @GuardedBy("this")
457         private List<String> mCachedValue;
458 
StringListCachedSecureSetting(Context context, String settingName, Handler handler)459         private StringListCachedSecureSetting(Context context, String settingName,
460                 Handler handler) {
461             super(handler);
462             mContext = context;
463             mSettingName = settingName;
464 
465             mCachedUserId = UserHandle.USER_NULL;
466         }
467 
register()468         public void register() {
469             register(mContext, Settings.Secure.getUriFor(mSettingName));
470         }
471 
getValueForUser(int userId)472         public synchronized List<String> getValueForUser(int userId) {
473             Preconditions.checkArgument(userId != UserHandle.USER_NULL);
474 
475             List<String> value = mCachedValue;
476             if (userId != mCachedUserId) {
477                 long identity = Binder.clearCallingIdentity();
478                 try {
479                     String setting = Settings.Secure.getStringForUser(mContext.getContentResolver(),
480                             mSettingName, userId);
481                     if (TextUtils.isEmpty(setting)) {
482                         value = Collections.emptyList();
483                     } else {
484                         value = Arrays.asList(setting.split(","));
485                     }
486                 } finally {
487                     Binder.restoreCallingIdentity(identity);
488                 }
489 
490                 if (isRegistered()) {
491                     mCachedUserId = userId;
492                     mCachedValue = value;
493                 }
494             }
495 
496             return value;
497         }
498 
invalidateForUser(int userId)499         public synchronized void invalidateForUser(int userId) {
500             if (mCachedUserId == userId) {
501                 mCachedUserId = UserHandle.USER_NULL;
502                 mCachedValue = null;
503             }
504         }
505 
506         @Override
onChange(boolean selfChange, Uri uri, int userId)507         public void onChange(boolean selfChange, Uri uri, int userId) {
508             invalidateForUser(userId);
509             super.onChange(selfChange, uri, userId);
510         }
511     }
512 
513     private static class LongGlobalSetting extends ObservingSetting {
514 
515         private final Context mContext;
516         private final String mSettingName;
517 
LongGlobalSetting(Context context, String settingName, Handler handler)518         private LongGlobalSetting(Context context, String settingName, Handler handler) {
519             super(handler);
520             mContext = context;
521             mSettingName = settingName;
522         }
523 
register()524         public void register() {
525             register(mContext, Settings.Global.getUriFor(mSettingName));
526         }
527 
getValue(long defaultValue)528         public long getValue(long defaultValue) {
529             long identity = Binder.clearCallingIdentity();
530             try {
531                 return Settings.Global.getLong(mContext.getContentResolver(), mSettingName,
532                         defaultValue);
533             } finally {
534                 Binder.restoreCallingIdentity(identity);
535             }
536         }
537     }
538 
539     private static class StringSetCachedGlobalSetting extends ObservingSetting {
540 
541         private final Context mContext;
542         private final String mSettingName;
543         private final Supplier<ArraySet<String>> mBaseValuesSupplier;
544 
545         @GuardedBy("this")
546         private boolean mValid;
547         @GuardedBy("this")
548         private ArraySet<String> mCachedValue;
549 
StringSetCachedGlobalSetting(Context context, String settingName, Supplier<ArraySet<String>> baseValuesSupplier, Handler handler)550         private StringSetCachedGlobalSetting(Context context, String settingName,
551                 Supplier<ArraySet<String>> baseValuesSupplier, Handler handler) {
552             super(handler);
553             mContext = context;
554             mSettingName = settingName;
555             mBaseValuesSupplier = baseValuesSupplier;
556 
557             mValid = false;
558         }
559 
register()560         public void register() {
561             register(mContext, Settings.Global.getUriFor(mSettingName));
562         }
563 
getValue()564         public synchronized Set<String> getValue() {
565             ArraySet<String> value = mCachedValue;
566             if (!mValid) {
567                 long identity = Binder.clearCallingIdentity();
568                 try {
569                     value = new ArraySet<>(mBaseValuesSupplier.get());
570                     String setting = Settings.Global.getString(mContext.getContentResolver(),
571                             mSettingName);
572                     if (!TextUtils.isEmpty(setting)) {
573                         value.addAll(Arrays.asList(setting.split(",")));
574                     }
575                 } finally {
576                     Binder.restoreCallingIdentity(identity);
577                 }
578 
579                 if (isRegistered()) {
580                     mValid = true;
581                     mCachedValue = value;
582                 }
583             }
584 
585             return value;
586         }
587 
invalidate()588         public synchronized void invalidate() {
589             mValid = false;
590             mCachedValue = null;
591         }
592 
593         @Override
onChange(boolean selfChange, Uri uri, int userId)594         public void onChange(boolean selfChange, Uri uri, int userId) {
595             invalidate();
596             super.onChange(selfChange, uri, userId);
597         }
598     }
599 }
600