blob: eb2a37b38f1cb20e4aa5355126cd8a2a598f331a [file] [log] [blame]
Soonil Nagarkarb8466b72019-10-25 14:10:30 -07001/*
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
17package com.android.server.location;
18
19import static android.provider.Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS;
20import static android.provider.Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST;
21import static android.provider.Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS;
22import static android.provider.Settings.Global.LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST;
23import static android.provider.Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS;
24import static android.provider.Settings.Secure.LOCATION_MODE;
25import static android.provider.Settings.Secure.LOCATION_MODE_OFF;
26import static android.provider.Settings.Secure.LOCATION_PROVIDERS_ALLOWED;
27
28import android.app.ActivityManager;
29import android.content.Context;
30import android.database.ContentObserver;
31import android.net.Uri;
32import android.os.Handler;
33import android.os.UserHandle;
34import android.provider.Settings;
35import android.text.TextUtils;
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -080036import android.util.ArraySet;
Soonil Nagarkarb8466b72019-10-25 14:10:30 -070037
38import com.android.internal.util.IndentingPrintWriter;
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -080039import com.android.internal.util.Preconditions;
40import com.android.server.SystemConfig;
Soonil Nagarkarb8466b72019-10-25 14:10:30 -070041
42import java.io.FileDescriptor;
43import java.io.PrintWriter;
44import java.util.Arrays;
45import java.util.Collections;
46import java.util.List;
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -080047import java.util.Set;
Soonil Nagarkarb8466b72019-10-25 14:10:30 -070048import java.util.concurrent.CopyOnWriteArrayList;
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -080049import java.util.function.Supplier;
Soonil Nagarkarb8466b72019-10-25 14:10:30 -070050
51/**
52 * Provides accessors and listeners for all location related settings.
53 */
54public class LocationSettingsStore {
55
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -080056 /**
57 * Listener for user-specific settings changes.
58 */
59 public interface UserSettingChangedListener {
60 /**
61 * Called when setting changes.
62 */
63 void onSettingChanged(int userId);
64 }
65
66 /**
67 * Listener for global settings changes.
68 */
69 public interface GlobalSettingChangedListener extends UserSettingChangedListener {
70 /**
71 * Called when setting changes.
72 */
73 void onSettingChanged();
74
75 @Override
76 default void onSettingChanged(int userId) {
77 onSettingChanged();
78 }
79 }
80
Soonil Nagarkarb8466b72019-10-25 14:10:30 -070081 private static final String LOCATION_PACKAGE_BLACKLIST = "locationPackagePrefixBlacklist";
82 private static final String LOCATION_PACKAGE_WHITELIST = "locationPackagePrefixWhitelist";
83
84 private static final long DEFAULT_BACKGROUND_THROTTLE_INTERVAL_MS = 30 * 60 * 1000;
85 private static final long DEFAULT_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS =
86 30 * 60 * 1000;
87 private static final long DEFAULT_MAX_LAST_LOCATION_AGE_MS = 20 * 60 * 1000;
88
89 private final Context mContext;
90
91 private final IntegerSecureSetting mLocationMode;
92 private final StringListCachedSecureSetting mLocationProvidersAllowed;
93 private final LongGlobalSetting mBackgroundThrottleIntervalMs;
94 private final StringListCachedSecureSetting mLocationPackageBlacklist;
95 private final StringListCachedSecureSetting mLocationPackageWhitelist;
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -080096 private final StringSetCachedGlobalSetting mBackgroundThrottlePackageWhitelist;
97 private final StringSetCachedGlobalSetting mIgnoreSettingsPackageWhitelist;
Soonil Nagarkarb8466b72019-10-25 14:10:30 -070098
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -080099 // TODO: get rid of handler
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700100 public LocationSettingsStore(Context context, Handler handler) {
101 mContext = context;
102
103 mLocationMode = new IntegerSecureSetting(context, LOCATION_MODE, handler);
104 mLocationProvidersAllowed = new StringListCachedSecureSetting(context,
105 LOCATION_PROVIDERS_ALLOWED, handler);
106 mBackgroundThrottleIntervalMs = new LongGlobalSetting(context,
107 LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS, handler);
108 mLocationPackageBlacklist = new StringListCachedSecureSetting(context,
109 LOCATION_PACKAGE_BLACKLIST, handler);
110 mLocationPackageWhitelist = new StringListCachedSecureSetting(context,
111 LOCATION_PACKAGE_WHITELIST, handler);
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800112 mBackgroundThrottlePackageWhitelist = new StringSetCachedGlobalSetting(context,
113 LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST,
114 () -> SystemConfig.getInstance().getAllowUnthrottledLocation(), handler);
115 mIgnoreSettingsPackageWhitelist = new StringSetCachedGlobalSetting(context,
116 LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST,
117 () -> SystemConfig.getInstance().getAllowIgnoreLocationSettings(), handler);
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700118 }
119
120 /**
121 * Retrieve if location is enabled or not.
122 */
123 public boolean isLocationEnabled(int userId) {
124 return mLocationMode.getValueForUser(LOCATION_MODE_OFF, userId) != LOCATION_MODE_OFF;
125 }
126
127 /**
128 * Add a listener for changes to the location enabled setting.
129 */
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800130 public void addOnLocationEnabledChangedListener(UserSettingChangedListener listener) {
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700131 mLocationMode.addListener(listener);
132 }
133
134 /**
135 * Remove a listener for changes to the location enabled setting.
136 */
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800137 public void removeOnLocationEnabledChangedListener(UserSettingChangedListener listener) {
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700138 mLocationMode.addListener(listener);
139 }
140
141 /**
142 * Retrieve the currently allowed location providers.
143 */
144 public List<String> getLocationProvidersAllowed(int userId) {
145 return mLocationProvidersAllowed.getValueForUser(userId);
146 }
147
148 /**
149 * Add a listener for changes to the currently allowed location providers.
150 */
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800151 public void addOnLocationProvidersAllowedChangedListener(UserSettingChangedListener listener) {
152 mLocationProvidersAllowed.addListener(listener);
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700153 }
154
155 /**
156 * Remove a listener for changes to the currently allowed location providers.
157 */
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800158 public void removeOnLocationProvidersAllowedChangedListener(
159 UserSettingChangedListener listener) {
160 mLocationProvidersAllowed.removeListener(listener);
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700161 }
162
163 /**
164 * Retrieve the background throttle interval.
165 */
166 public long getBackgroundThrottleIntervalMs() {
167 return mBackgroundThrottleIntervalMs.getValue(DEFAULT_BACKGROUND_THROTTLE_INTERVAL_MS);
168 }
169
170 /**
171 * Add a listener for changes to the background throttle interval.
172 */
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800173 public void addOnBackgroundThrottleIntervalChangedListener(
174 GlobalSettingChangedListener listener) {
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700175 mBackgroundThrottleIntervalMs.addListener(listener);
176 }
177
178 /**
179 * Remove a listener for changes to the background throttle interval.
180 */
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800181 public void removeOnBackgroundThrottleIntervalChangedListener(
182 GlobalSettingChangedListener listener) {
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700183 mBackgroundThrottleIntervalMs.removeListener(listener);
184 }
185
186 /**
187 * Check if the given package is blacklisted for location access.
188 */
189 public boolean isLocationPackageBlacklisted(int userId, String packageName) {
190 List<String> locationPackageBlacklist = mLocationPackageBlacklist.getValueForUser(userId);
191 if (locationPackageBlacklist.isEmpty()) {
192 return false;
193 }
194
195 List<String> locationPackageWhitelist = mLocationPackageWhitelist.getValueForUser(userId);
196 for (String locationWhitelistPackage : locationPackageWhitelist) {
197 if (packageName.startsWith(locationWhitelistPackage)) {
198 return false;
199 }
200 }
201
202 for (String locationBlacklistPackage : locationPackageBlacklist) {
203 if (packageName.startsWith(locationBlacklistPackage)) {
204 return true;
205 }
206 }
207
208 return false;
209 }
210
211 /**
212 * Retrieve the background throttle package whitelist.
213 */
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800214 public Set<String> getBackgroundThrottlePackageWhitelist() {
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700215 return mBackgroundThrottlePackageWhitelist.getValue();
216 }
217
218 /**
219 * Add a listener for changes to the background throttle package whitelist.
220 */
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800221 public void addOnBackgroundThrottlePackageWhitelistChangedListener(
222 GlobalSettingChangedListener listener) {
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700223 mBackgroundThrottlePackageWhitelist.addListener(listener);
224 }
225
226 /**
227 * Remove a listener for changes to the background throttle package whitelist.
228 */
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800229 public void removeOnBackgroundThrottlePackageWhitelistChangedListener(
230 GlobalSettingChangedListener listener) {
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700231 mBackgroundThrottlePackageWhitelist.removeListener(listener);
232 }
233
234 /**
235 * Retrieve the ignore settings package whitelist.
236 */
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800237 public Set<String> getIgnoreSettingsPackageWhitelist() {
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700238 return mIgnoreSettingsPackageWhitelist.getValue();
239 }
240
241 /**
242 * Add a listener for changes to the ignore settings package whitelist.
243 */
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800244 public void addOnIgnoreSettingsPackageWhitelistChangedListener(
245 GlobalSettingChangedListener listener) {
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700246 mIgnoreSettingsPackageWhitelist.addListener(listener);
247 }
248
249 /**
250 * Remove a listener for changes to the ignore settings package whitelist.
251 */
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800252 public void removeOnIgnoreSettingsPackageWhitelistChangedListener(
253 GlobalSettingChangedListener listener) {
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700254 mIgnoreSettingsPackageWhitelist.removeListener(listener);
255 }
256
257 /**
258 * Retrieve the background throttling proximity alert interval.
259 */
260 public long getBackgroundThrottleProximityAlertIntervalMs() {
261 return Settings.Global.getLong(mContext.getContentResolver(),
262 LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS,
263 DEFAULT_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS);
264 }
265
266 public long getMaxLastLocationAgeMs() {
267 return Settings.Global.getLong(
268 mContext.getContentResolver(),
269 LOCATION_LAST_LOCATION_MAX_AGE_MILLIS,
270 DEFAULT_MAX_LAST_LOCATION_AGE_MS);
271 }
272
273 /**
274 * Dump info for debugging.
275 */
276 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
277 IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
278 int userId = ActivityManager.getCurrentUser();
279
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700280 ipw.print("Location Enabled: ");
281 ipw.println(isLocationEnabled(userId));
282
283 ipw.print("Location Providers Allowed: ");
284 ipw.println(getLocationProvidersAllowed(userId));
285
286 List<String> locationPackageBlacklist = mLocationPackageBlacklist.getValueForUser(userId);
287 if (!locationPackageBlacklist.isEmpty()) {
288 ipw.println("Location Blacklisted Packages:");
289 ipw.increaseIndent();
290 for (String packageName : locationPackageBlacklist) {
291 ipw.println(packageName);
292 }
293 ipw.decreaseIndent();
294
295 List<String> locationPackageWhitelist = mLocationPackageWhitelist.getValueForUser(
296 userId);
297 if (!locationPackageWhitelist.isEmpty()) {
298 ipw.println("Location Whitelisted Packages:");
299 ipw.increaseIndent();
300 for (String packageName : locationPackageWhitelist) {
301 ipw.println(packageName);
302 }
303 ipw.decreaseIndent();
304 }
305 }
306
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800307 Set<String> backgroundThrottlePackageWhitelist =
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700308 mBackgroundThrottlePackageWhitelist.getValue();
309 if (!backgroundThrottlePackageWhitelist.isEmpty()) {
310 ipw.println("Throttling Whitelisted Packages:");
311 ipw.increaseIndent();
312 for (String packageName : backgroundThrottlePackageWhitelist) {
313 ipw.println(packageName);
314 }
315 ipw.decreaseIndent();
316 }
317
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800318 Set<String> ignoreSettingsPackageWhitelist = mIgnoreSettingsPackageWhitelist.getValue();
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700319 if (!ignoreSettingsPackageWhitelist.isEmpty()) {
320 ipw.println("Bypass Whitelisted Packages:");
321 ipw.increaseIndent();
322 for (String packageName : ignoreSettingsPackageWhitelist) {
323 ipw.println(packageName);
324 }
325 ipw.decreaseIndent();
326 }
327 }
328
329 private abstract static class ObservingSetting extends ContentObserver {
330
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800331 private final CopyOnWriteArrayList<UserSettingChangedListener> mListeners;
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700332
333 private ObservingSetting(Context context, String settingName, Handler handler) {
334 super(handler);
335 mListeners = new CopyOnWriteArrayList<>();
336
337 context.getContentResolver().registerContentObserver(
338 getUriFor(settingName), false, this, UserHandle.USER_ALL);
339 }
340
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800341 public void addListener(UserSettingChangedListener listener) {
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700342 mListeners.add(listener);
343 }
344
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800345 public void removeListener(UserSettingChangedListener listener) {
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700346 mListeners.remove(listener);
347 }
348
349 protected abstract Uri getUriFor(String settingName);
350
351 @Override
352 public void onChange(boolean selfChange, Uri uri, int userId) {
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800353 for (UserSettingChangedListener listener : mListeners) {
354 listener.onSettingChanged(userId);
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700355 }
356 }
357 }
358
359 private static class IntegerSecureSetting extends ObservingSetting {
360
361 private final Context mContext;
362 private final String mSettingName;
363
364 private IntegerSecureSetting(Context context, String settingName, Handler handler) {
365 super(context, settingName, handler);
366 mContext = context;
367 mSettingName = settingName;
368 }
369
370 public int getValueForUser(int defaultValue, int userId) {
371 return Settings.Secure.getIntForUser(mContext.getContentResolver(), mSettingName,
372 defaultValue, userId);
373 }
374
375 @Override
376 protected Uri getUriFor(String settingName) {
377 return Settings.Secure.getUriFor(settingName);
378 }
379 }
380
381 private static class StringListCachedSecureSetting extends ObservingSetting {
382
383 private final Context mContext;
384 private final String mSettingName;
385
386 private int mCachedUserId = UserHandle.USER_NULL;
387 private List<String> mCachedValue;
388
389 private StringListCachedSecureSetting(Context context, String settingName,
390 Handler handler) {
391 super(context, settingName, handler);
392 mContext = context;
393 mSettingName = settingName;
394 }
395
396 public synchronized List<String> getValueForUser(int userId) {
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800397 Preconditions.checkArgument(userId != UserHandle.USER_NULL);
398
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700399 if (userId != mCachedUserId) {
400 String setting = Settings.Secure.getStringForUser(mContext.getContentResolver(),
401 mSettingName, userId);
402 if (TextUtils.isEmpty(setting)) {
403 mCachedValue = Collections.emptyList();
404 } else {
405 mCachedValue = Arrays.asList(setting.split(","));
406 }
407 mCachedUserId = userId;
408 }
409
410 return mCachedValue;
411 }
412
413 public synchronized void invalidateForUser(int userId) {
414 if (mCachedUserId == userId) {
415 mCachedUserId = UserHandle.USER_NULL;
416 mCachedValue = null;
417 }
418 }
419
420 @Override
421 public void onChange(boolean selfChange, Uri uri, int userId) {
422 invalidateForUser(userId);
423 super.onChange(selfChange, uri, userId);
424 }
425
426 @Override
427 protected Uri getUriFor(String settingName) {
428 return Settings.Secure.getUriFor(settingName);
429 }
430 }
431
432 private static class LongGlobalSetting extends ObservingSetting {
433
434 private final Context mContext;
435 private final String mSettingName;
436
437 private LongGlobalSetting(Context context, String settingName, Handler handler) {
438 super(context, settingName, handler);
439 mContext = context;
440 mSettingName = settingName;
441 }
442
443 public long getValue(long defaultValue) {
444 return Settings.Global.getLong(mContext.getContentResolver(), mSettingName,
445 defaultValue);
446 }
447
448 @Override
449 protected Uri getUriFor(String settingName) {
450 return Settings.Global.getUriFor(settingName);
451 }
452 }
453
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800454 private static class StringSetCachedGlobalSetting extends ObservingSetting {
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700455
456 private final Context mContext;
457 private final String mSettingName;
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800458 private final Supplier<ArraySet<String>> mBaseValuesSupplier;
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700459
460 private boolean mValid;
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800461 private ArraySet<String> mCachedValue;
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700462
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800463 private StringSetCachedGlobalSetting(Context context, String settingName,
464 Supplier<ArraySet<String>> baseValuesSupplier, Handler handler) {
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700465 super(context, settingName, handler);
466 mContext = context;
467 mSettingName = settingName;
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800468 mBaseValuesSupplier = baseValuesSupplier;
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700469 }
470
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800471 public synchronized Set<String> getValue() {
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700472 if (!mValid) {
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800473 mCachedValue = new ArraySet<>(mBaseValuesSupplier.get());
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700474 String setting = Settings.Global.getString(mContext.getContentResolver(),
475 mSettingName);
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800476 if (!TextUtils.isEmpty(setting)) {
477 mCachedValue.addAll(Arrays.asList(setting.split(",")));
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700478 }
479 mValid = true;
480 }
481
482 return mCachedValue;
483 }
484
485 public synchronized void invalidate() {
486 mValid = false;
487 mCachedValue = null;
488 }
489
490 @Override
491 public void onChange(boolean selfChange, Uri uri, int userId) {
492 invalidate();
493 super.onChange(selfChange, uri, userId);
494 }
495
496 @Override
497 protected Uri getUriFor(String settingName) {
498 return Settings.Global.getUriFor(settingName);
499 }
500 }
501}