0%

Android_LocationFudger_GeofenceManager

LocationFudger 与 GeofenceManager

1
2
3
4
5
6
7
8
深入理解Android wifi卷 第九章提到过 LocationFudger 和 GeofenceManager ,故拿出来看看。
翻译用的 deepl。

LocationFudger 源码:
https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/core/java/com/android/server/location/fudger/LocationFudger.java

GeofenceManager 源码:
https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/core/java/com/android/server/location/geofence/GeofenceManager.java

LocationFudger

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
/*
* Copyright (C) 2020 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.server.location.fudger;

import android.annotation.Nullable;
import android.location.Location;
import android.location.LocationResult;
import android.os.SystemClock;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;

import java.security.SecureRandom;
import java.time.Clock;
import java.util.Random;

/**
* Contains the logic to obfuscate (fudge) locations for coarse applications. The goal is just to
* prevent applications with only the coarse location permission from receiving a fine location.
*/
// 包含混淆(fudge)位置的逻辑,供粗略的应用程序使用。其目的只是为了防止只有粗略位置权限的应用程序收到精细的位置。

public class LocationFudger {

// minimum accuracy a coarsened location can have
// 粗化的位置可以有的最小精度
private static final float MIN_ACCURACY_M = 200.0f;

// how often random offsets are updated
// 随机偏移量的更新频率
@VisibleForTesting
static final long OFFSET_UPDATE_INTERVAL_MS = 60 * 60 * 1000;

// the percentage that we change the random offset at every interval. 0.0 indicates the random
// offset doesn't change. 1.0 indicates the random offset is completely replaced every interval
// 我们在每个间隔内改变随机偏移量的百分比。0.0表示随机偏移量不改变。1.0表示每隔一段时间就完全替换随机偏移量。
private static final double CHANGE_PER_INTERVAL = 0.03; // 3% change

// weights used to move the random offset. the goal is to iterate on the previous offset, but
// keep the resulting standard deviation the same. the variance of two gaussian distributions
// summed together is equal to the sum of the variance of each distribution. so some quick
// algebra results in the following sqrt calculation to weight in a new offset while keeping the
// final standard deviation unchanged.
// 用于移动随机偏移量的权重。目标是在以前的偏移量上进行迭代,但保持结果的标准差不变。两个高斯分布的方差相加等于每个分布的方差之和。因此,一些快速代数的结果是,在保持最终标准差不变的情况下,用下面的sqrt计算来增加新的偏移量。
private static final double NEW_WEIGHT = CHANGE_PER_INTERVAL;
private static final double OLD_WEIGHT = Math.sqrt(1 - NEW_WEIGHT * NEW_WEIGHT);

// this number actually varies because the earth is not round, but 111,000 meters is considered
// generally acceptable
// 这个数字实际上是变化的,因为地球不是圆的,但111000米被认为是普遍可以接受的。
private static final int APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR = 111_000;

// we pick a value 1 meter away from 90.0 degrees in order to keep cosine(MAX_LATITUDE) to a
// non-zero value, so that we avoid divide by zero errors
// 我们选择一个离90.0度1米远的值,以保持余弦(MAX_LATITUDE)为非零值,这样可以避免除以零的错误。
private static final double MAX_LATITUDE =
90.0 - (1.0 / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR);

private final float mAccuracyM;
private final Clock mClock;
private final Random mRandom;

@GuardedBy("this")
private double mLatitudeOffsetM;
@GuardedBy("this")
private double mLongitudeOffsetM;
@GuardedBy("this")
private long mNextUpdateRealtimeMs;

@GuardedBy("this")
@Nullable private Location mCachedFineLocation;
@GuardedBy("this")
@Nullable private Location mCachedCoarseLocation;

@GuardedBy("this")
@Nullable private LocationResult mCachedFineLocationResult;
@GuardedBy("this")
@Nullable private LocationResult mCachedCoarseLocationResult;

public LocationFudger(float accuracyM) {
this(accuracyM, SystemClock.elapsedRealtimeClock(), new SecureRandom());
}

@VisibleForTesting
LocationFudger(float accuracyM, Clock clock, Random random) {
mClock = clock;
mRandom = random;
mAccuracyM = Math.max(accuracyM, MIN_ACCURACY_M);

resetOffsets();
}

/**
* Resets the random offsets completely.
*/
// 完全重设随机偏移量。
public void resetOffsets() {
mLatitudeOffsetM = nextRandomOffset();
mLongitudeOffsetM = nextRandomOffset();
mNextUpdateRealtimeMs = mClock.millis() + OFFSET_UPDATE_INTERVAL_MS;
}

/**
* Coarsens a LocationResult by coarsening every location within the location result with
* {@link #createCoarse(Location)}.
*/
// 对LocationResult进行粗化,用{@link #createCoarse(Location)}粗化定位结果中的每个位置。
public LocationResult createCoarse(LocationResult fineLocationResult) {
synchronized (this) {
if (fineLocationResult == mCachedFineLocationResult
|| fineLocationResult == mCachedCoarseLocationResult) {
return mCachedCoarseLocationResult;
}
}

LocationResult coarseLocationResult = fineLocationResult.map(this::createCoarse);

synchronized (this) {
mCachedFineLocationResult = fineLocationResult;
mCachedCoarseLocationResult = coarseLocationResult;
}

return coarseLocationResult;
}

/**
* Create a coarse location using two technique, random offsets and snap-to-grid.
*
* First we add a random offset to mitigate against detecting grid transitions. Without a random
* offset it is possible to detect a user's position quite accurately when they cross a grid
* boundary. The random offset changes very slowly over time, to mitigate against taking many
* location samples and averaging them out. Second we snap-to-grid (quantize). This has the nice
* property of producing stable results, and mitigating against taking many samples to average
* out a random offset.
*/
// 使用两种技术创建一个粗略的位置,随机偏移和snap-to-grid。
// 首先,我们添加了一个随机偏移,以减轻检测网格转换的难度。如果没有随机偏移,当用户越过网格边界时,就有可能相当准确地检测到他们的位置。随机偏移量随时间变化非常缓慢,以减轻采取许多位置样本和平均化的影响。其次,我们要进行snap-to-grid (quantize)。这有一个很好的特性,即产生稳定的结果,并减轻了采取许多样本来平均化随机偏移的情况。
public Location createCoarse(Location fine) {
synchronized (this) {
if (fine == mCachedFineLocation || fine == mCachedCoarseLocation) {
return mCachedCoarseLocation;
}
}

// update the offsets in use
// 更新使用中的偏移量
updateOffsets();

Location coarse = new Location(fine);

// clear any fields that could leak more detailed location information
// 清除任何可能泄露更详细位置信息的字段
coarse.removeBearing();
coarse.removeSpeed();
coarse.removeAltitude();
coarse.setExtras(null);

double latitude = wrapLatitude(coarse.getLatitude());
double longitude = wrapLongitude(coarse.getLongitude());

// add offsets - update longitude first using the non-offset latitude
// 增加偏移量--先用非偏移的纬度更新经度
longitude += wrapLongitude(metersToDegreesLongitude(mLongitudeOffsetM, latitude));
latitude += wrapLatitude(metersToDegreesLatitude(mLatitudeOffsetM));

// quantize location by snapping to a grid. this is the primary means of obfuscation. it
// gives nice consistent results and is very effective at hiding the true location (as long
// as you are not sitting on a grid boundary, which the random offsets mitigate).
//
// note that we quantize the latitude first, since the longitude quantization depends on the
// latitude value and so leaks information about the latitude
// 这是混淆的主要手段。它提供了很好的一致的结果,并且在隐藏真实位置方面非常有效(只要你不坐在网格边界上,而随机偏移可以缓解)。
// 注意,我们先对纬度进行量化,因为经度量化取决于纬度值,所以会泄露纬度的信息。
double latGranularity = metersToDegreesLatitude(mAccuracyM);
latitude = wrapLatitude(Math.round(latitude / latGranularity) * latGranularity);
double lonGranularity = metersToDegreesLongitude(mAccuracyM, latitude);
longitude = wrapLongitude(Math.round(longitude / lonGranularity) * lonGranularity);

coarse.setLatitude(latitude);
coarse.setLongitude(longitude);
coarse.setAccuracy(Math.max(mAccuracyM, coarse.getAccuracy()));

synchronized (this) {
mCachedFineLocation = fine;
mCachedCoarseLocation = coarse;
}

return coarse;
}

/**
* Update the random offsets over time.
*
* If the random offset was reset for every location fix then an application could more easily
* average location results over time, especially when the location is near a grid boundary. On
* the other hand if the random offset is constant then if an application finds a way to reverse
* engineer the offset they would be able to detect location at grid boundaries very accurately.
* So we choose a random offset and then very slowly move it, to make both approaches very hard.
* The random offset does not need to be large, because snap-to-grid is the primary obfuscation
* mechanism. It just needs to be large enough to stop information leakage as we cross grid
* boundaries.
*/
// 随着时间的推移更新随机偏移量。
// 如果随机偏移量在每次定位时都被重置,那么应用程序可以更容易地在一段时间内平均定位结果,特别是当位置在网格边界附近时。另一方面,如果随机偏移量是恒定的,那么如果一个应用程序找到一种方法来反向设计偏移量,他们将能够非常准确地检测网格边界的位置。 因此,我们选择一个随机偏移量,然后非常缓慢地移动它,以使这两种方法都非常困难。这个随机偏移量不需要很大,因为 "快照到网格 "是主要的混淆机制。它只需要足够大,以便在我们跨越网格边界时阻止信息泄露。
private synchronized void updateOffsets() {
long now = mClock.millis();
if (now < mNextUpdateRealtimeMs) {
return;
}

mLatitudeOffsetM = (OLD_WEIGHT * mLatitudeOffsetM) + (NEW_WEIGHT * nextRandomOffset());
mLongitudeOffsetM = (OLD_WEIGHT * mLongitudeOffsetM) + (NEW_WEIGHT * nextRandomOffset());
mNextUpdateRealtimeMs = now + OFFSET_UPDATE_INTERVAL_MS;
}

private double nextRandomOffset() {
return mRandom.nextGaussian() * (mAccuracyM / 4.0);
}

private static double wrapLatitude(double lat) {
if (lat > MAX_LATITUDE) {
lat = MAX_LATITUDE;
}
if (lat < -MAX_LATITUDE) {
lat = -MAX_LATITUDE;
}
return lat;
}

private static double wrapLongitude(double lon) {
lon %= 360.0; // wraps into range (-360.0, +360.0) 包入范围(-360.0, +360.0)。
if (lon >= 180.0) {
lon -= 360.0;
}
if (lon < -180.0) {
lon += 360.0;
}
return lon;
}

private static double metersToDegreesLatitude(double distance) {
return distance / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR;
}

// requires latitude since longitudinal distances change with distance from equator.
// 需要纬度,因为纵向距离随着离赤道的距离而变化。
private static double metersToDegreesLongitude(double distance, double lat) {
return distance / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR / Math.cos(Math.toRadians(lat));
}
}

GeofenceManager

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
/*
* Copyright (C) 2020 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.server.location.geofence;

import static android.location.LocationManager.FUSED_PROVIDER;
import static android.location.LocationManager.KEY_PROXIMITY_ENTERING;

import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
import static com.android.server.location.LocationPermissions.PERMISSION_FINE;

import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.location.Geofence;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.location.LocationRequest;
import android.location.util.identity.CallerIdentity;
import android.os.Binder;
import android.os.PowerManager;
import android.os.SystemClock;
import android.os.WorkSource;
import android.stats.location.LocationStatsEnums;
import android.util.ArraySet;

import com.android.internal.annotations.GuardedBy;
import com.android.server.PendingIntentUtils;
import com.android.server.location.LocationPermissions;
import com.android.server.location.injector.Injector;
import com.android.server.location.injector.LocationPermissionsHelper;
import com.android.server.location.injector.LocationUsageLogger;
import com.android.server.location.injector.SettingsHelper;
import com.android.server.location.injector.UserInfoHelper;
import com.android.server.location.injector.UserInfoHelper.UserListener;
import com.android.server.location.listeners.ListenerMultiplexer;
import com.android.server.location.listeners.PendingIntentListenerRegistration;

import java.util.Collection;
import java.util.Objects;

/**
* Manages all geofences.
*/
// 管理所有的地理围栏。
public class GeofenceManager extends
ListenerMultiplexer<GeofenceKey, PendingIntent, GeofenceManager.GeofenceRegistration,
LocationRequest> implements
LocationListener {

private static final String TAG = "GeofenceManager";

private static final String ATTRIBUTION_TAG = "GeofencingService";

private static final long WAKELOCK_TIMEOUT_MS = 30000;

private static final int MAX_SPEED_M_S = 100; // 360 km/hr (high speed train)
private static final long MAX_LOCATION_AGE_MS = 5 * 60 * 1000L; // five minutes
private static final long MAX_LOCATION_INTERVAL_MS = 2 * 60 * 60 * 1000; // two hours

protected final class GeofenceRegistration extends
PendingIntentListenerRegistration<Geofence, PendingIntent> {

private static final int STATE_UNKNOWN = 0;
private static final int STATE_INSIDE = 1;
private static final int STATE_OUTSIDE = 2;

private final Location mCenter;
private final PowerManager.WakeLock mWakeLock;

private int mGeofenceState;

// we store these values because we don't trust the listeners not to give us dupes, not to
// spam us, and because checking the values may be more expensive
// 我们存储这些值是因为我们不相信听众不会给我们提供重复的信息,不会给我们发送垃圾邮件,而且检查这些值可能更昂贵。
private boolean mPermitted;

private @Nullable Location mCachedLocation;
private float mCachedLocationDistanceM;

protected GeofenceRegistration(Geofence geofence, CallerIdentity identity,
PendingIntent pendingIntent) {
super(geofence, identity, pendingIntent);

mCenter = new Location("");
mCenter.setLatitude(geofence.getLatitude());
mCenter.setLongitude(geofence.getLongitude());

mWakeLock = Objects.requireNonNull(mContext.getSystemService(PowerManager.class))
.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
TAG + ":" + identity.getPackageName());
mWakeLock.setReferenceCounted(true);
mWakeLock.setWorkSource(identity.addToWorkSource(null));
}

@Override
protected GeofenceManager getOwner() {
return GeofenceManager.this;
}

@Override
protected void onPendingIntentListenerRegister() {
mGeofenceState = STATE_UNKNOWN;
mPermitted = mLocationPermissionsHelper.hasLocationPermissions(PERMISSION_FINE,
getIdentity());
}

@Override
protected void onActive() {
Location location = getLastLocation();
if (location != null) {
executeOperation(onLocationChanged(location));
}
}

boolean isPermitted() {
return mPermitted;
}

boolean onLocationPermissionsChanged(String packageName) {
if (getIdentity().getPackageName().equals(packageName)) {
return onLocationPermissionsChanged();
}

return false;
}

boolean onLocationPermissionsChanged(int uid) {
if (getIdentity().getUid() == uid) {
return onLocationPermissionsChanged();
}

return false;
}

private boolean onLocationPermissionsChanged() {
boolean permitted = mLocationPermissionsHelper.hasLocationPermissions(PERMISSION_FINE,
getIdentity());
if (permitted != mPermitted) {
mPermitted = permitted;
return true;
}

return false;
}

double getDistanceToBoundary(Location location) {
if (!location.equals(mCachedLocation)) {
mCachedLocation = location;
mCachedLocationDistanceM = mCenter.distanceTo(mCachedLocation);
}

return Math.abs(getRequest().getRadius() - mCachedLocationDistanceM);
}

ListenerOperation<PendingIntent> onLocationChanged(Location location) {
// remove expired fences
// 拆除过期的栅栏
if (getRequest().isExpired()) {
remove();
return null;
}

mCachedLocation = location;
mCachedLocationDistanceM = mCenter.distanceTo(mCachedLocation);

int oldState = mGeofenceState;
float radius = Math.max(getRequest().getRadius(), location.getAccuracy());
if (mCachedLocationDistanceM <= radius) {
mGeofenceState = STATE_INSIDE;
if (oldState != STATE_INSIDE) {
return pendingIntent -> sendIntent(pendingIntent, true);
}
} else {
mGeofenceState = STATE_OUTSIDE;
if (oldState == STATE_INSIDE) {
// return exit only if previously entered
// 只有在先前输入的情况下才会返回出口
return pendingIntent -> sendIntent(pendingIntent, false);
}
}

return null;
}

private void sendIntent(PendingIntent pendingIntent, boolean entering) {
Intent intent = new Intent().putExtra(KEY_PROXIMITY_ENTERING, entering);

mWakeLock.acquire(WAKELOCK_TIMEOUT_MS);
try {
// send() only enforces permissions for broadcast intents, but since clients can
// select any kind of pending intent we do not rely on send() to enforce permissions
// send()只对广播意图执行权限,但由于客户端可以选择任何类型的待定意图,我们不依靠send()来执行权限。
pendingIntent.send(mContext, 0, intent, (pI, i, rC, rD, rE) -> mWakeLock.release(),
null, null, PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
} catch (PendingIntent.CanceledException e) {
mWakeLock.release();
removeRegistration(new GeofenceKey(pendingIntent, getRequest()), this);
}
}

@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append(getIdentity());

ArraySet<String> flags = new ArraySet<>(1);
if (!mPermitted) {
flags.add("na");
}
if (!flags.isEmpty()) {
builder.append(" ").append(flags);
}

builder.append(" ").append(getRequest());
return builder.toString();
}
}

final Object mLock = new Object();

protected final Context mContext;

private final UserListener mUserChangedListener = this::onUserChanged;
private final SettingsHelper.UserSettingChangedListener mLocationEnabledChangedListener =
this::onLocationEnabledChanged;
private final SettingsHelper.UserSettingChangedListener
mLocationPackageBlacklistChangedListener =
this::onLocationPackageBlacklistChanged;
private final LocationPermissionsHelper.LocationPermissionsListener
mLocationPermissionsListener =
new LocationPermissionsHelper.LocationPermissionsListener() {
@Override
public void onLocationPermissionsChanged(String packageName) {
GeofenceManager.this.onLocationPermissionsChanged(packageName);
}

@Override
public void onLocationPermissionsChanged(int uid) {
GeofenceManager.this.onLocationPermissionsChanged(uid);
}
};

protected final UserInfoHelper mUserInfoHelper;
protected final LocationPermissionsHelper mLocationPermissionsHelper;
protected final SettingsHelper mSettingsHelper;
protected final LocationUsageLogger mLocationUsageLogger;

@GuardedBy("mLock")
private @Nullable LocationManager mLocationManager;

@GuardedBy("mLock")
private @Nullable Location mLastLocation;

public GeofenceManager(Context context, Injector injector) {
mContext = context.createAttributionContext(ATTRIBUTION_TAG);
mUserInfoHelper = injector.getUserInfoHelper();
mSettingsHelper = injector.getSettingsHelper();
mLocationPermissionsHelper = injector.getLocationPermissionsHelper();
mLocationUsageLogger = injector.getLocationUsageLogger();
}

@Override
public String getTag() {
return TAG;
}

private LocationManager getLocationManager() {
synchronized (mLock) {
if (mLocationManager == null) {
mLocationManager = Objects.requireNonNull(
mContext.getSystemService(LocationManager.class));
}

return mLocationManager;
}
}

/**
* Adds a new geofence, replacing any geofence already associated with the PendingIntent. It
* doesn't make any real sense to register multiple geofences with the same pending intent, but
* we continue to allow this for backwards compatibility.
*/
// 添加一个新的地理围栏,替换已经与PendingIntent相关的任何地理围栏。用同一个待定Intent注册多个地理围栏没有任何实际意义,但为了向后兼容,我们继续允许这样做。
public void addGeofence(Geofence geofence, PendingIntent pendingIntent, String packageName,
@Nullable String attributionTag) {
LocationPermissions.enforceCallingOrSelfLocationPermission(mContext, PERMISSION_FINE);

CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName,
attributionTag, AppOpsManager.toReceiverId(pendingIntent));

final long ident = Binder.clearCallingIdentity();
try {
putRegistration(new GeofenceKey(pendingIntent, geofence),
new GeofenceRegistration(geofence, identity, pendingIntent));
} finally {
Binder.restoreCallingIdentity(ident);
}
}

/**
* Removes the geofence associated with the PendingIntent.
*/
// 移除与PendingIntent相关的地理围栏。
public void removeGeofence(PendingIntent pendingIntent) {
final long identity = Binder.clearCallingIdentity();
try {
removeRegistrationIf(key -> key.getPendingIntent().equals(pendingIntent));
} finally {
Binder.restoreCallingIdentity(identity);
}
}

@Override
protected boolean isActive(GeofenceRegistration registration) {
return registration.isPermitted() && isActive(registration.getIdentity());
}

private boolean isActive(CallerIdentity identity) {
if (identity.isSystemServer()) {
if (!mSettingsHelper.isLocationEnabled(mUserInfoHelper.getCurrentUserId())) {
return false;
}
} else {
if (!mSettingsHelper.isLocationEnabled(identity.getUserId())) {
return false;
}
if (!mUserInfoHelper.isCurrentUserId(identity.getUserId())) {
return false;
}
if (mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(),
identity.getPackageName())) {
return false;
}
}

return true;
}

@Override
protected void onRegister() {
mUserInfoHelper.addListener(mUserChangedListener);
mSettingsHelper.addOnLocationEnabledChangedListener(mLocationEnabledChangedListener);
mSettingsHelper.addOnLocationPackageBlacklistChangedListener(
mLocationPackageBlacklistChangedListener);
mLocationPermissionsHelper.addListener(mLocationPermissionsListener);
}

@Override
protected void onUnregister() {
mUserInfoHelper.removeListener(mUserChangedListener);
mSettingsHelper.removeOnLocationEnabledChangedListener(mLocationEnabledChangedListener);
mSettingsHelper.removeOnLocationPackageBlacklistChangedListener(
mLocationPackageBlacklistChangedListener);
mLocationPermissionsHelper.removeListener(mLocationPermissionsListener);
}

@Override
protected void onRegistrationAdded(GeofenceKey key, GeofenceRegistration registration) {
mLocationUsageLogger.logLocationApiUsage(
LocationStatsEnums.USAGE_ENDED,
LocationStatsEnums.API_REQUEST_GEOFENCE,
registration.getIdentity().getPackageName(),
registration.getIdentity().getAttributionTag(),
null,
/* LocationRequest= */ null,
/* hasListener= */ false,
true,
registration.getRequest(), true);
}

@Override
protected void onRegistrationRemoved(GeofenceKey key, GeofenceRegistration registration) {
mLocationUsageLogger.logLocationApiUsage(
LocationStatsEnums.USAGE_ENDED,
LocationStatsEnums.API_REQUEST_GEOFENCE,
registration.getIdentity().getPackageName(),
registration.getIdentity().getAttributionTag(),
null,
/* LocationRequest= */ null,
/* hasListener= */ false,
true,
registration.getRequest(), true);
}

@Override
protected boolean registerWithService(LocationRequest locationRequest,
Collection<GeofenceRegistration> registrations) {
getLocationManager().requestLocationUpdates(FUSED_PROVIDER, locationRequest,
DIRECT_EXECUTOR, this);
return true;
}

@Override
protected void unregisterWithService() {
synchronized (mLock) {
getLocationManager().removeUpdates(this);
mLastLocation = null;
}
}

@Override
protected LocationRequest mergeRegistrations(Collection<GeofenceRegistration> registrations) {
Location location = getLastLocation();

long realtimeMs = SystemClock.elapsedRealtime();

WorkSource workSource = null;
double minFenceDistanceM = Double.MAX_VALUE;
for (GeofenceRegistration registration : registrations) {
if (registration.getRequest().isExpired(realtimeMs)) {
continue;
}

workSource = registration.getIdentity().addToWorkSource(workSource);

if (location != null) {
double fenceDistanceM = registration.getDistanceToBoundary(location);
if (fenceDistanceM < minFenceDistanceM) {
minFenceDistanceM = fenceDistanceM;
}
}
}

long intervalMs;
if (Double.compare(minFenceDistanceM, Double.MAX_VALUE) < 0) {
intervalMs = (long) Math.min(MAX_LOCATION_INTERVAL_MS,
Math.max(
mSettingsHelper.getBackgroundThrottleProximityAlertIntervalMs(),
minFenceDistanceM * 1000 / MAX_SPEED_M_S));
} else {
intervalMs = mSettingsHelper.getBackgroundThrottleProximityAlertIntervalMs();
}

return new LocationRequest.Builder(intervalMs)
.setMinUpdateIntervalMillis(0)
.setHiddenFromAppOps(true)
.setWorkSource(workSource)
.build();
}


@Override
public void onLocationChanged(Location location) {
synchronized (mLock) {
mLastLocation = location;
}

deliverToListeners(registration -> {
return registration.onLocationChanged(location);
});
updateService();
}

@Nullable Location getLastLocation() {
Location location;
synchronized (mLock) {
location = mLastLocation;
}

if (location == null) {
location = getLocationManager().getLastLocation();
}

if (location != null) {
if (location.getElapsedRealtimeAgeMillis() > MAX_LOCATION_AGE_MS) {
location = null;
}
}

return location;
}

void onUserChanged(int userId, int change) {
if (change == UserListener.CURRENT_USER_CHANGED) {
updateRegistrations(registration -> registration.getIdentity().getUserId() == userId);
}
}

void onLocationEnabledChanged(int userId) {
updateRegistrations(registration -> registration.getIdentity().getUserId() == userId);
}

void onLocationPackageBlacklistChanged(int userId) {
updateRegistrations(registration -> registration.getIdentity().getUserId() == userId);
}

void onLocationPermissionsChanged(String packageName) {
updateRegistrations(registration -> registration.onLocationPermissionsChanged(packageName));
}

void onLocationPermissionsChanged(int uid) {
updateRegistrations(registration -> registration.onLocationPermissionsChanged(uid));
}
}