blob: a4b6d97e947145d0e7101fb03ed0586d9dbc8d3d [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;
36
37import com.android.internal.util.IndentingPrintWriter;
38
39import java.io.FileDescriptor;
40import java.io.PrintWriter;
41import java.util.Arrays;
42import java.util.Collections;
43import java.util.List;
44import java.util.concurrent.CopyOnWriteArrayList;
45
46/**
47 * Provides accessors and listeners for all location related settings.
48 */
49public class LocationSettingsStore {
50
51 private static final String LOCATION_PACKAGE_BLACKLIST = "locationPackagePrefixBlacklist";
52 private static final String LOCATION_PACKAGE_WHITELIST = "locationPackagePrefixWhitelist";
53
54 private static final long DEFAULT_BACKGROUND_THROTTLE_INTERVAL_MS = 30 * 60 * 1000;
55 private static final long DEFAULT_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS =
56 30 * 60 * 1000;
57 private static final long DEFAULT_MAX_LAST_LOCATION_AGE_MS = 20 * 60 * 1000;
58
59 private final Context mContext;
60
61 private final IntegerSecureSetting mLocationMode;
62 private final StringListCachedSecureSetting mLocationProvidersAllowed;
63 private final LongGlobalSetting mBackgroundThrottleIntervalMs;
64 private final StringListCachedSecureSetting mLocationPackageBlacklist;
65 private final StringListCachedSecureSetting mLocationPackageWhitelist;
66 private final StringListCachedGlobalSetting mBackgroundThrottlePackageWhitelist;
67 private final StringListCachedGlobalSetting mIgnoreSettingsPackageWhitelist;
68
69 public LocationSettingsStore(Context context, Handler handler) {
70 mContext = context;
71
72 mLocationMode = new IntegerSecureSetting(context, LOCATION_MODE, handler);
73 mLocationProvidersAllowed = new StringListCachedSecureSetting(context,
74 LOCATION_PROVIDERS_ALLOWED, handler);
75 mBackgroundThrottleIntervalMs = new LongGlobalSetting(context,
76 LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS, handler);
77 mLocationPackageBlacklist = new StringListCachedSecureSetting(context,
78 LOCATION_PACKAGE_BLACKLIST, handler);
79 mLocationPackageWhitelist = new StringListCachedSecureSetting(context,
80 LOCATION_PACKAGE_WHITELIST, handler);
81 mBackgroundThrottlePackageWhitelist = new StringListCachedGlobalSetting(context,
82 LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST, handler);
83 mIgnoreSettingsPackageWhitelist = new StringListCachedGlobalSetting(context,
84 LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST, handler);
85 }
86
87 /**
88 * Retrieve if location is enabled or not.
89 */
90 public boolean isLocationEnabled(int userId) {
91 return mLocationMode.getValueForUser(LOCATION_MODE_OFF, userId) != LOCATION_MODE_OFF;
92 }
93
94 /**
95 * Add a listener for changes to the location enabled setting.
96 */
97 public void addOnLocationEnabledChangedListener(Runnable listener) {
98 mLocationMode.addListener(listener);
99 }
100
101 /**
102 * Remove a listener for changes to the location enabled setting.
103 */
104 public void removeOnLocationEnabledChangedListener(Runnable listener) {
105 mLocationMode.addListener(listener);
106 }
107
108 /**
109 * Retrieve the currently allowed location providers.
110 */
111 public List<String> getLocationProvidersAllowed(int userId) {
112 return mLocationProvidersAllowed.getValueForUser(userId);
113 }
114
115 /**
116 * Add a listener for changes to the currently allowed location providers.
117 */
118 public void addOnLocationProvidersAllowedChangedListener(Runnable runnable) {
119 mLocationProvidersAllowed.addListener(runnable);
120 }
121
122 /**
123 * Remove a listener for changes to the currently allowed location providers.
124 */
125 public void removeOnLocationProvidersAllowedChangedListener(Runnable runnable) {
126 mLocationProvidersAllowed.removeListener(runnable);
127 }
128
129 /**
130 * Retrieve the background throttle interval.
131 */
132 public long getBackgroundThrottleIntervalMs() {
133 return mBackgroundThrottleIntervalMs.getValue(DEFAULT_BACKGROUND_THROTTLE_INTERVAL_MS);
134 }
135
136 /**
137 * Add a listener for changes to the background throttle interval.
138 */
139 public void addOnBackgroundThrottleIntervalChangedListener(Runnable listener) {
140 mBackgroundThrottleIntervalMs.addListener(listener);
141 }
142
143 /**
144 * Remove a listener for changes to the background throttle interval.
145 */
146 public void removeOnBackgroundThrottleIntervalChangedListener(Runnable listener) {
147 mBackgroundThrottleIntervalMs.removeListener(listener);
148 }
149
150 /**
151 * Check if the given package is blacklisted for location access.
152 */
153 public boolean isLocationPackageBlacklisted(int userId, String packageName) {
154 List<String> locationPackageBlacklist = mLocationPackageBlacklist.getValueForUser(userId);
155 if (locationPackageBlacklist.isEmpty()) {
156 return false;
157 }
158
159 List<String> locationPackageWhitelist = mLocationPackageWhitelist.getValueForUser(userId);
160 for (String locationWhitelistPackage : locationPackageWhitelist) {
161 if (packageName.startsWith(locationWhitelistPackage)) {
162 return false;
163 }
164 }
165
166 for (String locationBlacklistPackage : locationPackageBlacklist) {
167 if (packageName.startsWith(locationBlacklistPackage)) {
168 return true;
169 }
170 }
171
172 return false;
173 }
174
175 /**
176 * Retrieve the background throttle package whitelist.
177 */
178 public List<String> getBackgroundThrottlePackageWhitelist() {
179 return mBackgroundThrottlePackageWhitelist.getValue();
180 }
181
182 /**
183 * Add a listener for changes to the background throttle package whitelist.
184 */
185 public void addOnBackgroundThrottlePackageWhitelistChangedListener(Runnable listener) {
186 mBackgroundThrottlePackageWhitelist.addListener(listener);
187 }
188
189 /**
190 * Remove a listener for changes to the background throttle package whitelist.
191 */
192 public void removeOnBackgroundThrottlePackageWhitelistChangedListener(Runnable listener) {
193 mBackgroundThrottlePackageWhitelist.removeListener(listener);
194 }
195
196 /**
197 * Retrieve the ignore settings package whitelist.
198 */
199 public List<String> getIgnoreSettingsPackageWhitelist() {
200 return mIgnoreSettingsPackageWhitelist.getValue();
201 }
202
203 /**
204 * Add a listener for changes to the ignore settings package whitelist.
205 */
206 public void addOnIgnoreSettingsPackageWhitelistChangedListener(Runnable listener) {
207 mIgnoreSettingsPackageWhitelist.addListener(listener);
208 }
209
210 /**
211 * Remove a listener for changes to the ignore settings package whitelist.
212 */
213 public void removeOnIgnoreSettingsPackageWhitelistChangedListener(Runnable listener) {
214 mIgnoreSettingsPackageWhitelist.removeListener(listener);
215 }
216
217 /**
218 * Retrieve the background throttling proximity alert interval.
219 */
220 public long getBackgroundThrottleProximityAlertIntervalMs() {
221 return Settings.Global.getLong(mContext.getContentResolver(),
222 LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS,
223 DEFAULT_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS);
224 }
225
226 public long getMaxLastLocationAgeMs() {
227 return Settings.Global.getLong(
228 mContext.getContentResolver(),
229 LOCATION_LAST_LOCATION_MAX_AGE_MILLIS,
230 DEFAULT_MAX_LAST_LOCATION_AGE_MS);
231 }
232
233 /**
234 * Dump info for debugging.
235 */
236 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
237 IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
238 int userId = ActivityManager.getCurrentUser();
239
240 ipw.println("--Location Settings--");
241 ipw.increaseIndent();
242
243 ipw.print("Location Enabled: ");
244 ipw.println(isLocationEnabled(userId));
245
246 ipw.print("Location Providers Allowed: ");
247 ipw.println(getLocationProvidersAllowed(userId));
248
249 List<String> locationPackageBlacklist = mLocationPackageBlacklist.getValueForUser(userId);
250 if (!locationPackageBlacklist.isEmpty()) {
251 ipw.println("Location Blacklisted Packages:");
252 ipw.increaseIndent();
253 for (String packageName : locationPackageBlacklist) {
254 ipw.println(packageName);
255 }
256 ipw.decreaseIndent();
257
258 List<String> locationPackageWhitelist = mLocationPackageWhitelist.getValueForUser(
259 userId);
260 if (!locationPackageWhitelist.isEmpty()) {
261 ipw.println("Location Whitelisted Packages:");
262 ipw.increaseIndent();
263 for (String packageName : locationPackageWhitelist) {
264 ipw.println(packageName);
265 }
266 ipw.decreaseIndent();
267 }
268 }
269
270 List<String> backgroundThrottlePackageWhitelist =
271 mBackgroundThrottlePackageWhitelist.getValue();
272 if (!backgroundThrottlePackageWhitelist.isEmpty()) {
273 ipw.println("Throttling Whitelisted Packages:");
274 ipw.increaseIndent();
275 for (String packageName : backgroundThrottlePackageWhitelist) {
276 ipw.println(packageName);
277 }
278 ipw.decreaseIndent();
279 }
280
281 List<String> ignoreSettingsPackageWhitelist = mIgnoreSettingsPackageWhitelist.getValue();
282 if (!ignoreSettingsPackageWhitelist.isEmpty()) {
283 ipw.println("Bypass Whitelisted Packages:");
284 ipw.increaseIndent();
285 for (String packageName : ignoreSettingsPackageWhitelist) {
286 ipw.println(packageName);
287 }
288 ipw.decreaseIndent();
289 }
290 }
291
292 private abstract static class ObservingSetting extends ContentObserver {
293
294 private final CopyOnWriteArrayList<Runnable> mListeners;
295
296 private ObservingSetting(Context context, String settingName, Handler handler) {
297 super(handler);
298 mListeners = new CopyOnWriteArrayList<>();
299
300 context.getContentResolver().registerContentObserver(
301 getUriFor(settingName), false, this, UserHandle.USER_ALL);
302 }
303
304 public void addListener(Runnable listener) {
305 mListeners.add(listener);
306 }
307
308 public void removeListener(Runnable listener) {
309 mListeners.remove(listener);
310 }
311
312 protected abstract Uri getUriFor(String settingName);
313
314 @Override
315 public void onChange(boolean selfChange, Uri uri, int userId) {
316 for (Runnable listener : mListeners) {
317 listener.run();
318 }
319 }
320 }
321
322 private static class IntegerSecureSetting extends ObservingSetting {
323
324 private final Context mContext;
325 private final String mSettingName;
326
327 private IntegerSecureSetting(Context context, String settingName, Handler handler) {
328 super(context, settingName, handler);
329 mContext = context;
330 mSettingName = settingName;
331 }
332
333 public int getValueForUser(int defaultValue, int userId) {
334 return Settings.Secure.getIntForUser(mContext.getContentResolver(), mSettingName,
335 defaultValue, userId);
336 }
337
338 @Override
339 protected Uri getUriFor(String settingName) {
340 return Settings.Secure.getUriFor(settingName);
341 }
342 }
343
344 private static class StringListCachedSecureSetting extends ObservingSetting {
345
346 private final Context mContext;
347 private final String mSettingName;
348
349 private int mCachedUserId = UserHandle.USER_NULL;
350 private List<String> mCachedValue;
351
352 private StringListCachedSecureSetting(Context context, String settingName,
353 Handler handler) {
354 super(context, settingName, handler);
355 mContext = context;
356 mSettingName = settingName;
357 }
358
359 public synchronized List<String> getValueForUser(int userId) {
360 if (userId != mCachedUserId) {
361 String setting = Settings.Secure.getStringForUser(mContext.getContentResolver(),
362 mSettingName, userId);
363 if (TextUtils.isEmpty(setting)) {
364 mCachedValue = Collections.emptyList();
365 } else {
366 mCachedValue = Arrays.asList(setting.split(","));
367 }
368 mCachedUserId = userId;
369 }
370
371 return mCachedValue;
372 }
373
374 public synchronized void invalidateForUser(int userId) {
375 if (mCachedUserId == userId) {
376 mCachedUserId = UserHandle.USER_NULL;
377 mCachedValue = null;
378 }
379 }
380
381 @Override
382 public void onChange(boolean selfChange, Uri uri, int userId) {
383 invalidateForUser(userId);
384 super.onChange(selfChange, uri, userId);
385 }
386
387 @Override
388 protected Uri getUriFor(String settingName) {
389 return Settings.Secure.getUriFor(settingName);
390 }
391 }
392
393 private static class LongGlobalSetting extends ObservingSetting {
394
395 private final Context mContext;
396 private final String mSettingName;
397
398 private LongGlobalSetting(Context context, String settingName, Handler handler) {
399 super(context, settingName, handler);
400 mContext = context;
401 mSettingName = settingName;
402 }
403
404 public long getValue(long defaultValue) {
405 return Settings.Global.getLong(mContext.getContentResolver(), mSettingName,
406 defaultValue);
407 }
408
409 @Override
410 protected Uri getUriFor(String settingName) {
411 return Settings.Global.getUriFor(settingName);
412 }
413 }
414
415 private static class StringListCachedGlobalSetting extends ObservingSetting {
416
417 private final Context mContext;
418 private final String mSettingName;
419
420 private boolean mValid;
421 private List<String> mCachedValue;
422
423 private StringListCachedGlobalSetting(Context context, String settingName,
424 Handler handler) {
425 super(context, settingName, handler);
426 mContext = context;
427 mSettingName = settingName;
428 }
429
430 public synchronized List<String> getValue() {
431 if (!mValid) {
432 String setting = Settings.Global.getString(mContext.getContentResolver(),
433 mSettingName);
434 if (TextUtils.isEmpty(setting)) {
435 mCachedValue = Collections.emptyList();
436 } else {
437 mCachedValue = Arrays.asList(setting.split(","));
438 }
439 mValid = true;
440 }
441
442 return mCachedValue;
443 }
444
445 public synchronized void invalidate() {
446 mValid = false;
447 mCachedValue = null;
448 }
449
450 @Override
451 public void onChange(boolean selfChange, Uri uri, int userId) {
452 invalidate();
453 super.onChange(selfChange, uri, userId);
454 }
455
456 @Override
457 protected Uri getUriFor(String settingName) {
458 return Settings.Global.getUriFor(settingName);
459 }
460 }
461}