blob: 3d18d4a12e44cbf276777f8d792c41820c4b18de [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;
Soonil Nagarkarb8466b72019-10-25 14:10:30 -070026
27import android.app.ActivityManager;
28import android.content.Context;
29import android.database.ContentObserver;
30import android.net.Uri;
31import android.os.Handler;
32import android.os.UserHandle;
33import android.provider.Settings;
34import android.text.TextUtils;
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -080035import android.util.ArraySet;
Soonil Nagarkarb8466b72019-10-25 14:10:30 -070036
37import com.android.internal.util.IndentingPrintWriter;
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -080038import com.android.internal.util.Preconditions;
39import com.android.server.SystemConfig;
Soonil Nagarkarb8466b72019-10-25 14:10:30 -070040
41import java.io.FileDescriptor;
42import java.io.PrintWriter;
43import java.util.Arrays;
44import java.util.Collections;
45import java.util.List;
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -080046import java.util.Set;
Soonil Nagarkarb8466b72019-10-25 14:10:30 -070047import java.util.concurrent.CopyOnWriteArrayList;
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -080048import java.util.function.Supplier;
Soonil Nagarkarb8466b72019-10-25 14:10:30 -070049
50/**
51 * Provides accessors and listeners for all location related settings.
52 */
53public class LocationSettingsStore {
54
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -080055 /**
56 * Listener for user-specific settings changes.
57 */
58 public interface UserSettingChangedListener {
59 /**
60 * Called when setting changes.
61 */
62 void onSettingChanged(int userId);
63 }
64
65 /**
66 * Listener for global settings changes.
67 */
68 public interface GlobalSettingChangedListener extends UserSettingChangedListener {
69 /**
70 * Called when setting changes.
71 */
72 void onSettingChanged();
73
74 @Override
75 default void onSettingChanged(int userId) {
76 onSettingChanged();
77 }
78 }
79
Soonil Nagarkarb8466b72019-10-25 14:10:30 -070080 private static final String LOCATION_PACKAGE_BLACKLIST = "locationPackagePrefixBlacklist";
81 private static final String LOCATION_PACKAGE_WHITELIST = "locationPackagePrefixWhitelist";
82
83 private static final long DEFAULT_BACKGROUND_THROTTLE_INTERVAL_MS = 30 * 60 * 1000;
84 private static final long DEFAULT_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS =
85 30 * 60 * 1000;
86 private static final long DEFAULT_MAX_LAST_LOCATION_AGE_MS = 20 * 60 * 1000;
87
88 private final Context mContext;
89
90 private final IntegerSecureSetting mLocationMode;
Soonil Nagarkarb8466b72019-10-25 14:10:30 -070091 private final LongGlobalSetting mBackgroundThrottleIntervalMs;
92 private final StringListCachedSecureSetting mLocationPackageBlacklist;
93 private final StringListCachedSecureSetting mLocationPackageWhitelist;
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -080094 private final StringSetCachedGlobalSetting mBackgroundThrottlePackageWhitelist;
95 private final StringSetCachedGlobalSetting mIgnoreSettingsPackageWhitelist;
Soonil Nagarkarb8466b72019-10-25 14:10:30 -070096
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -080097 // TODO: get rid of handler
Soonil Nagarkarb8466b72019-10-25 14:10:30 -070098 public LocationSettingsStore(Context context, Handler handler) {
99 mContext = context;
100
101 mLocationMode = new IntegerSecureSetting(context, LOCATION_MODE, handler);
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700102 mBackgroundThrottleIntervalMs = new LongGlobalSetting(context,
103 LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS, handler);
104 mLocationPackageBlacklist = new StringListCachedSecureSetting(context,
105 LOCATION_PACKAGE_BLACKLIST, handler);
106 mLocationPackageWhitelist = new StringListCachedSecureSetting(context,
107 LOCATION_PACKAGE_WHITELIST, handler);
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800108 mBackgroundThrottlePackageWhitelist = new StringSetCachedGlobalSetting(context,
109 LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST,
110 () -> SystemConfig.getInstance().getAllowUnthrottledLocation(), handler);
111 mIgnoreSettingsPackageWhitelist = new StringSetCachedGlobalSetting(context,
112 LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST,
113 () -> SystemConfig.getInstance().getAllowIgnoreLocationSettings(), handler);
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700114 }
115
116 /**
117 * Retrieve if location is enabled or not.
118 */
119 public boolean isLocationEnabled(int userId) {
120 return mLocationMode.getValueForUser(LOCATION_MODE_OFF, userId) != LOCATION_MODE_OFF;
121 }
122
123 /**
124 * Add a listener for changes to the location enabled setting.
125 */
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800126 public void addOnLocationEnabledChangedListener(UserSettingChangedListener listener) {
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700127 mLocationMode.addListener(listener);
128 }
129
130 /**
131 * Remove a listener for changes to the location enabled setting.
132 */
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800133 public void removeOnLocationEnabledChangedListener(UserSettingChangedListener listener) {
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700134 mLocationMode.addListener(listener);
135 }
136
137 /**
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700138 * Retrieve the background throttle interval.
139 */
140 public long getBackgroundThrottleIntervalMs() {
141 return mBackgroundThrottleIntervalMs.getValue(DEFAULT_BACKGROUND_THROTTLE_INTERVAL_MS);
142 }
143
144 /**
145 * Add a listener for changes to the background throttle interval.
146 */
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800147 public void addOnBackgroundThrottleIntervalChangedListener(
148 GlobalSettingChangedListener listener) {
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700149 mBackgroundThrottleIntervalMs.addListener(listener);
150 }
151
152 /**
153 * Remove a listener for changes to the background throttle interval.
154 */
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800155 public void removeOnBackgroundThrottleIntervalChangedListener(
156 GlobalSettingChangedListener listener) {
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700157 mBackgroundThrottleIntervalMs.removeListener(listener);
158 }
159
160 /**
161 * Check if the given package is blacklisted for location access.
162 */
163 public boolean isLocationPackageBlacklisted(int userId, String packageName) {
164 List<String> locationPackageBlacklist = mLocationPackageBlacklist.getValueForUser(userId);
165 if (locationPackageBlacklist.isEmpty()) {
166 return false;
167 }
168
169 List<String> locationPackageWhitelist = mLocationPackageWhitelist.getValueForUser(userId);
170 for (String locationWhitelistPackage : locationPackageWhitelist) {
171 if (packageName.startsWith(locationWhitelistPackage)) {
172 return false;
173 }
174 }
175
176 for (String locationBlacklistPackage : locationPackageBlacklist) {
177 if (packageName.startsWith(locationBlacklistPackage)) {
178 return true;
179 }
180 }
181
182 return false;
183 }
184
185 /**
186 * Retrieve the background throttle package whitelist.
187 */
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800188 public Set<String> getBackgroundThrottlePackageWhitelist() {
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700189 return mBackgroundThrottlePackageWhitelist.getValue();
190 }
191
192 /**
193 * Add a listener for changes to the background throttle package whitelist.
194 */
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800195 public void addOnBackgroundThrottlePackageWhitelistChangedListener(
196 GlobalSettingChangedListener listener) {
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700197 mBackgroundThrottlePackageWhitelist.addListener(listener);
198 }
199
200 /**
201 * Remove a listener for changes to the background throttle package whitelist.
202 */
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800203 public void removeOnBackgroundThrottlePackageWhitelistChangedListener(
204 GlobalSettingChangedListener listener) {
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700205 mBackgroundThrottlePackageWhitelist.removeListener(listener);
206 }
207
208 /**
209 * Retrieve the ignore settings package whitelist.
210 */
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800211 public Set<String> getIgnoreSettingsPackageWhitelist() {
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700212 return mIgnoreSettingsPackageWhitelist.getValue();
213 }
214
215 /**
216 * Add a listener for changes to the ignore settings package whitelist.
217 */
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800218 public void addOnIgnoreSettingsPackageWhitelistChangedListener(
219 GlobalSettingChangedListener listener) {
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700220 mIgnoreSettingsPackageWhitelist.addListener(listener);
221 }
222
223 /**
224 * Remove a listener for changes to the ignore settings package whitelist.
225 */
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800226 public void removeOnIgnoreSettingsPackageWhitelistChangedListener(
227 GlobalSettingChangedListener listener) {
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700228 mIgnoreSettingsPackageWhitelist.removeListener(listener);
229 }
230
231 /**
232 * Retrieve the background throttling proximity alert interval.
233 */
234 public long getBackgroundThrottleProximityAlertIntervalMs() {
235 return Settings.Global.getLong(mContext.getContentResolver(),
236 LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS,
237 DEFAULT_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS);
238 }
239
240 public long getMaxLastLocationAgeMs() {
241 return Settings.Global.getLong(
242 mContext.getContentResolver(),
243 LOCATION_LAST_LOCATION_MAX_AGE_MILLIS,
244 DEFAULT_MAX_LAST_LOCATION_AGE_MS);
245 }
246
247 /**
248 * Dump info for debugging.
249 */
250 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
251 IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
252 int userId = ActivityManager.getCurrentUser();
253
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700254 ipw.print("Location Enabled: ");
255 ipw.println(isLocationEnabled(userId));
256
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700257 List<String> locationPackageBlacklist = mLocationPackageBlacklist.getValueForUser(userId);
258 if (!locationPackageBlacklist.isEmpty()) {
259 ipw.println("Location Blacklisted Packages:");
260 ipw.increaseIndent();
261 for (String packageName : locationPackageBlacklist) {
262 ipw.println(packageName);
263 }
264 ipw.decreaseIndent();
265
266 List<String> locationPackageWhitelist = mLocationPackageWhitelist.getValueForUser(
267 userId);
268 if (!locationPackageWhitelist.isEmpty()) {
269 ipw.println("Location Whitelisted Packages:");
270 ipw.increaseIndent();
271 for (String packageName : locationPackageWhitelist) {
272 ipw.println(packageName);
273 }
274 ipw.decreaseIndent();
275 }
276 }
277
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800278 Set<String> backgroundThrottlePackageWhitelist =
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700279 mBackgroundThrottlePackageWhitelist.getValue();
280 if (!backgroundThrottlePackageWhitelist.isEmpty()) {
281 ipw.println("Throttling Whitelisted Packages:");
282 ipw.increaseIndent();
283 for (String packageName : backgroundThrottlePackageWhitelist) {
284 ipw.println(packageName);
285 }
286 ipw.decreaseIndent();
287 }
288
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800289 Set<String> ignoreSettingsPackageWhitelist = mIgnoreSettingsPackageWhitelist.getValue();
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700290 if (!ignoreSettingsPackageWhitelist.isEmpty()) {
291 ipw.println("Bypass Whitelisted Packages:");
292 ipw.increaseIndent();
293 for (String packageName : ignoreSettingsPackageWhitelist) {
294 ipw.println(packageName);
295 }
296 ipw.decreaseIndent();
297 }
298 }
299
300 private abstract static class ObservingSetting extends ContentObserver {
301
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800302 private final CopyOnWriteArrayList<UserSettingChangedListener> mListeners;
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700303
304 private ObservingSetting(Context context, String settingName, Handler handler) {
305 super(handler);
306 mListeners = new CopyOnWriteArrayList<>();
307
308 context.getContentResolver().registerContentObserver(
309 getUriFor(settingName), false, this, UserHandle.USER_ALL);
310 }
311
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800312 public void addListener(UserSettingChangedListener listener) {
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700313 mListeners.add(listener);
314 }
315
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800316 public void removeListener(UserSettingChangedListener listener) {
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700317 mListeners.remove(listener);
318 }
319
320 protected abstract Uri getUriFor(String settingName);
321
322 @Override
323 public void onChange(boolean selfChange, Uri uri, int userId) {
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800324 for (UserSettingChangedListener listener : mListeners) {
325 listener.onSettingChanged(userId);
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700326 }
327 }
328 }
329
330 private static class IntegerSecureSetting extends ObservingSetting {
331
332 private final Context mContext;
333 private final String mSettingName;
334
335 private IntegerSecureSetting(Context context, String settingName, Handler handler) {
336 super(context, settingName, handler);
337 mContext = context;
338 mSettingName = settingName;
339 }
340
341 public int getValueForUser(int defaultValue, int userId) {
342 return Settings.Secure.getIntForUser(mContext.getContentResolver(), mSettingName,
343 defaultValue, userId);
344 }
345
346 @Override
347 protected Uri getUriFor(String settingName) {
348 return Settings.Secure.getUriFor(settingName);
349 }
350 }
351
352 private static class StringListCachedSecureSetting extends ObservingSetting {
353
354 private final Context mContext;
355 private final String mSettingName;
356
357 private int mCachedUserId = UserHandle.USER_NULL;
358 private List<String> mCachedValue;
359
360 private StringListCachedSecureSetting(Context context, String settingName,
361 Handler handler) {
362 super(context, settingName, handler);
363 mContext = context;
364 mSettingName = settingName;
365 }
366
367 public synchronized List<String> getValueForUser(int userId) {
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800368 Preconditions.checkArgument(userId != UserHandle.USER_NULL);
369
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700370 if (userId != mCachedUserId) {
371 String setting = Settings.Secure.getStringForUser(mContext.getContentResolver(),
372 mSettingName, userId);
373 if (TextUtils.isEmpty(setting)) {
374 mCachedValue = Collections.emptyList();
375 } else {
376 mCachedValue = Arrays.asList(setting.split(","));
377 }
378 mCachedUserId = userId;
379 }
380
381 return mCachedValue;
382 }
383
384 public synchronized void invalidateForUser(int userId) {
385 if (mCachedUserId == userId) {
386 mCachedUserId = UserHandle.USER_NULL;
387 mCachedValue = null;
388 }
389 }
390
391 @Override
392 public void onChange(boolean selfChange, Uri uri, int userId) {
393 invalidateForUser(userId);
394 super.onChange(selfChange, uri, userId);
395 }
396
397 @Override
398 protected Uri getUriFor(String settingName) {
399 return Settings.Secure.getUriFor(settingName);
400 }
401 }
402
403 private static class LongGlobalSetting extends ObservingSetting {
404
405 private final Context mContext;
406 private final String mSettingName;
407
408 private LongGlobalSetting(Context context, String settingName, Handler handler) {
409 super(context, settingName, handler);
410 mContext = context;
411 mSettingName = settingName;
412 }
413
414 public long getValue(long defaultValue) {
415 return Settings.Global.getLong(mContext.getContentResolver(), mSettingName,
416 defaultValue);
417 }
418
419 @Override
420 protected Uri getUriFor(String settingName) {
421 return Settings.Global.getUriFor(settingName);
422 }
423 }
424
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800425 private static class StringSetCachedGlobalSetting extends ObservingSetting {
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700426
427 private final Context mContext;
428 private final String mSettingName;
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800429 private final Supplier<ArraySet<String>> mBaseValuesSupplier;
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700430
431 private boolean mValid;
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800432 private ArraySet<String> mCachedValue;
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700433
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800434 private StringSetCachedGlobalSetting(Context context, String settingName,
435 Supplier<ArraySet<String>> baseValuesSupplier, Handler handler) {
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700436 super(context, settingName, handler);
437 mContext = context;
438 mSettingName = settingName;
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800439 mBaseValuesSupplier = baseValuesSupplier;
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700440 }
441
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800442 public synchronized Set<String> getValue() {
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700443 if (!mValid) {
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800444 mCachedValue = new ArraySet<>(mBaseValuesSupplier.get());
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700445 String setting = Settings.Global.getString(mContext.getContentResolver(),
446 mSettingName);
Soonil Nagarkar6bf751f2019-11-27 13:41:28 -0800447 if (!TextUtils.isEmpty(setting)) {
448 mCachedValue.addAll(Arrays.asList(setting.split(",")));
Soonil Nagarkarb8466b72019-10-25 14:10:30 -0700449 }
450 mValid = true;
451 }
452
453 return mCachedValue;
454 }
455
456 public synchronized void invalidate() {
457 mValid = false;
458 mCachedValue = null;
459 }
460
461 @Override
462 public void onChange(boolean selfChange, Uri uri, int userId) {
463 invalidate();
464 super.onChange(selfChange, uri, userId);
465 }
466
467 @Override
468 protected Uri getUriFor(String settingName) {
469 return Settings.Global.getUriFor(settingName);
470 }
471 }
472}