1919import java .io .FileDescriptor ;
2020import java .io .PrintWriter ;
2121import java .security .SecureRandom ;
22+ import android .content .Context ;
23+ import android .database .ContentObserver ;
2224import android .location .Location ;
2325import android .os .Bundle ;
26+ import android .os .Handler ;
2427import android .os .Parcelable ;
2528import android .os .SystemClock ;
29+ import android .provider .Settings ;
2630import android .util .Log ;
2731
2832
@@ -39,22 +43,19 @@ public class LocationFudger {
3943 private static final String EXTRA_COARSE_LOCATION = "coarseLocation" ;
4044
4145 /**
42- * This is the main control: Best location accuracy allowed for coarse applications .
46+ * Default coarse accuracy in meters .
4347 */
44- private static final float ACCURACY_METERS = 200 .0f ;
48+ private static final float DEFAULT_ACCURACY_IN_METERS = 2000 .0f ;
4549
4650 /**
47- * The distance between grids for snap-to-grid. See {@link #createCoarse} .
51+ * Minimum coarse accuracy in meters .
4852 */
49- private static final double GRID_SIZE_METERS = ACCURACY_METERS ;
53+ private static final float MINIMUM_ACCURACY_IN_METERS = 200.0f ;
5054
5155 /**
52- * Standard deviation of the (normally distributed) random offset applied
53- * to coarse locations. It does not need to be as large as
54- * {@link #COARSE_ACCURACY_METERS} because snap-to-grid is the primary obfuscation
55- * method. See further details in the implementation.
56+ * Secure settings key for coarse accuracy.
5657 */
57- private static final double STANDARD_DEVIATION_METERS = GRID_SIZE_METERS / 4.0 ;
58+ private static final String COARSE_ACCURACY_CONFIG_NAME = "locationCoarseAccuracy" ;
5859
5960 /**
6061 * This is the fastest interval that applications can receive coarse
@@ -106,43 +107,90 @@ public class LocationFudger {
106107 private final Object mLock = new Object ();
107108 private final SecureRandom mRandom = new SecureRandom ();
108109
110+ /**
111+ * Used to monitor coarse accuracy secure setting for changes.
112+ */
113+ private final ContentObserver mSettingsObserver ;
114+
115+ /**
116+ * Used to resolve coarse accuracy setting.
117+ */
118+ private final Context mContext ;
119+
109120 // all fields below protected by mLock
110121 private double mOffsetLatitudeMeters ;
111122 private double mOffsetLongitudeMeters ;
112123 private long mNextInterval ;
113124
114- public LocationFudger () {
115- mOffsetLatitudeMeters = nextOffset ();
116- mOffsetLongitudeMeters = nextOffset ();
117- mNextInterval = SystemClock .elapsedRealtime () + CHANGE_INTERVAL_MS ;
125+ /**
126+ * Best location accuracy allowed for coarse applications.
127+ * This value should only be set by {@link #setAccuracyInMetersLocked(float)}.
128+ */
129+ private float mAccuracyInMeters ;
130+
131+ /**
132+ * The distance between grids for snap-to-grid. See {@link #createCoarse}.
133+ * This value should only be set by {@link #setAccuracyInMetersLocked(float)}.
134+ */
135+ private double mGridSizeInMeters ;
136+
137+ /**
138+ * Standard deviation of the (normally distributed) random offset applied
139+ * to coarse locations. It does not need to be as large as
140+ * {@link #COARSE_ACCURACY_METERS} because snap-to-grid is the primary obfuscation
141+ * method. See further details in the implementation.
142+ * This value should only be set by {@link #setAccuracyInMetersLocked(float)}.
143+ */
144+ private double mStandardDeviationInMeters ;
145+
146+ public LocationFudger (Context context , Handler handler ) {
147+ mContext = context ;
148+ mSettingsObserver = new ContentObserver (handler ) {
149+ @ Override
150+ public void onChange (boolean selfChange ) {
151+ setAccuracyInMeters (loadCoarseAccuracy ());
152+ }
153+ };
154+ mContext .getContentResolver ().registerContentObserver (Settings .Secure .getUriFor (
155+ COARSE_ACCURACY_CONFIG_NAME ), false , mSettingsObserver );
156+
157+ float accuracy = loadCoarseAccuracy ();
158+ synchronized (mLock ) {
159+ setAccuracyInMetersLocked (accuracy );
160+ mOffsetLatitudeMeters = nextOffsetLocked ();
161+ mOffsetLongitudeMeters = nextOffsetLocked ();
162+ mNextInterval = SystemClock .elapsedRealtime () + CHANGE_INTERVAL_MS ;
163+ }
118164 }
119165
120166 /**
121167 * Get the cached coarse location, or generate a new one and cache it.
122168 */
123169 public Location getOrCreate (Location location ) {
124- Bundle extras = location .getExtras ();
125- if (extras == null ) {
126- return addCoarseLocationExtra (location );
127- }
128- Parcelable parcel = extras .getParcelable (EXTRA_COARSE_LOCATION );
129- if (parcel == null ) {
130- return addCoarseLocationExtra (location );
131- }
132- if (!(parcel instanceof Location )) {
133- return addCoarseLocationExtra (location );
134- }
135- Location coarse = (Location ) parcel ;
136- if (coarse .getAccuracy () < ACCURACY_METERS ) {
137- return addCoarseLocationExtra (location );
170+ synchronized (mLock ) {
171+ Bundle extras = location .getExtras ();
172+ if (extras == null ) {
173+ return addCoarseLocationExtraLocked (location );
174+ }
175+ Parcelable parcel = extras .getParcelable (EXTRA_COARSE_LOCATION );
176+ if (parcel == null ) {
177+ return addCoarseLocationExtraLocked (location );
178+ }
179+ if (!(parcel instanceof Location )) {
180+ return addCoarseLocationExtraLocked (location );
181+ }
182+ Location coarse = (Location ) parcel ;
183+ if (coarse .getAccuracy () < mAccuracyInMeters ) {
184+ return addCoarseLocationExtraLocked (location );
185+ }
186+ return coarse ;
138187 }
139- return coarse ;
140188 }
141189
142- private Location addCoarseLocationExtra (Location location ) {
190+ private Location addCoarseLocationExtraLocked (Location location ) {
143191 Bundle extras = location .getExtras ();
144192 if (extras == null ) extras = new Bundle ();
145- Location coarse = createCoarse (location );
193+ Location coarse = createCoarseLocked (location );
146194 extras .putParcelable (EXTRA_COARSE_LOCATION , coarse );
147195 location .setExtras (extras );
148196 return coarse ;
@@ -163,7 +211,7 @@ private Location addCoarseLocationExtra(Location location) {
163211 * producing stable results, and mitigating against taking many samples
164212 * to average out a random offset.
165213 */
166- private Location createCoarse (Location fine ) {
214+ private Location createCoarseLocked (Location fine ) {
167215 Location coarse = new Location (fine );
168216
169217 // clean all the optional information off the location, because
@@ -188,14 +236,12 @@ private Location createCoarse(Location fine) {
188236 //
189237 // We apply the offset even if the location already claims to be
190238 // inaccurate, because it may be more accurate than claimed.
191- synchronized (mLock ) {
192- updateRandomOffsetLocked ();
193- // perform lon first whilst lat is still within bounds
194- lon += metersToDegreesLongitude (mOffsetLongitudeMeters , lat );
195- lat += metersToDegreesLatitude (mOffsetLatitudeMeters );
196- if (D ) Log .d (TAG , String .format ("applied offset of %.0f, %.0f (meters)" ,
197- mOffsetLongitudeMeters , mOffsetLatitudeMeters ));
198- }
239+ updateRandomOffsetLocked ();
240+ // perform lon first whilst lat is still within bounds
241+ lon += metersToDegreesLongitude (mOffsetLongitudeMeters , lat );
242+ lat += metersToDegreesLatitude (mOffsetLatitudeMeters );
243+ if (D ) Log .d (TAG , String .format ("applied offset of %.0f, %.0f (meters)" ,
244+ mOffsetLongitudeMeters , mOffsetLatitudeMeters ));
199245
200246 // wrap
201247 lat = wrapLatitude (lat );
@@ -211,9 +257,9 @@ private Location createCoarse(Location fine) {
211257 // Note we quantize the latitude first, since the longitude
212258 // quantization depends on the latitude value and so leaks information
213259 // about the latitude
214- double latGranularity = metersToDegreesLatitude (GRID_SIZE_METERS );
260+ double latGranularity = metersToDegreesLatitude (mGridSizeInMeters );
215261 lat = Math .round (lat / latGranularity ) * latGranularity ;
216- double lonGranularity = metersToDegreesLongitude (GRID_SIZE_METERS , lat );
262+ double lonGranularity = metersToDegreesLongitude (mGridSizeInMeters , lat );
217263 lon = Math .round (lon / lonGranularity ) * lonGranularity ;
218264
219265 // wrap again
@@ -223,7 +269,7 @@ private Location createCoarse(Location fine) {
223269 // apply
224270 coarse .setLatitude (lat );
225271 coarse .setLongitude (lon );
226- coarse .setAccuracy (Math .max (ACCURACY_METERS , coarse .getAccuracy ()));
272+ coarse .setAccuracy (Math .max (mAccuracyInMeters , coarse .getAccuracy ()));
227273
228274 if (D ) Log .d (TAG , "fudged " + fine + " to " + coarse );
229275 return coarse ;
@@ -259,16 +305,16 @@ private void updateRandomOffsetLocked() {
259305 mNextInterval = now + CHANGE_INTERVAL_MS ;
260306
261307 mOffsetLatitudeMeters *= PREVIOUS_WEIGHT ;
262- mOffsetLatitudeMeters += NEW_WEIGHT * nextOffset ();
308+ mOffsetLatitudeMeters += NEW_WEIGHT * nextOffsetLocked ();
263309 mOffsetLongitudeMeters *= PREVIOUS_WEIGHT ;
264- mOffsetLongitudeMeters += NEW_WEIGHT * nextOffset ();
310+ mOffsetLongitudeMeters += NEW_WEIGHT * nextOffsetLocked ();
265311
266312 if (D ) Log .d (TAG , String .format ("new offset: %.0f, %.0f (meters)" ,
267313 mOffsetLongitudeMeters , mOffsetLatitudeMeters ));
268314 }
269315
270- private double nextOffset () {
271- return mRandom .nextGaussian () * STANDARD_DEVIATION_METERS ;
316+ private double nextOffsetLocked () {
317+ return mRandom .nextGaussian () * mStandardDeviationInMeters ;
272318 }
273319
274320 private static double wrapLatitude (double lat ) {
@@ -307,4 +353,45 @@ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
307353 pw .println (String .format ("offset: %.0f, %.0f (meters)" , mOffsetLongitudeMeters ,
308354 mOffsetLatitudeMeters ));
309355 }
356+
357+ /**
358+ * This is the main control: call this to set the best location accuracy
359+ * allowed for coarse applications and all derived values.
360+ */
361+ private void setAccuracyInMetersLocked (float accuracyInMeters ) {
362+ mAccuracyInMeters = Math .max (accuracyInMeters , MINIMUM_ACCURACY_IN_METERS );
363+ if (D ) {
364+ Log .d (TAG , "setAccuracyInMetersLocked: new accuracy = " + mAccuracyInMeters );
365+ }
366+ mGridSizeInMeters = mAccuracyInMeters ;
367+ mStandardDeviationInMeters = mGridSizeInMeters / 4.0 ;
368+ }
369+
370+ /**
371+ * Same as setAccuracyInMetersLocked without the pre-lock requirement.
372+ */
373+ private void setAccuracyInMeters (float accuracyInMeters ) {
374+ synchronized (mLock ) {
375+ setAccuracyInMetersLocked (accuracyInMeters );
376+ }
377+ }
378+
379+ /**
380+ * Loads the coarse accuracy value from secure settings.
381+ */
382+ private float loadCoarseAccuracy () {
383+ String newSetting = Settings .Secure .getString (mContext .getContentResolver (),
384+ COARSE_ACCURACY_CONFIG_NAME );
385+ if (D ) {
386+ Log .d (TAG , "loadCoarseAccuracy: newSetting = \" " + newSetting + "\" " );
387+ }
388+ if (newSetting == null ) {
389+ return DEFAULT_ACCURACY_IN_METERS ;
390+ }
391+ try {
392+ return Float .parseFloat (newSetting );
393+ } catch (NumberFormatException e ) {
394+ return DEFAULT_ACCURACY_IN_METERS ;
395+ }
396+ }
310397}
0 commit comments