174fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly/* 274fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * Copyright (C) 2012 The Android Open Source Project 374fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * 474fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * Licensed under the Apache License, Version 2.0 (the "License"); 574fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * you may not use this file except in compliance with the License. 674fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * You may obtain a copy of the License at 774fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * 874fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * http://www.apache.org/licenses/LICENSE-2.0 974fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * 1074fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * Unless required by applicable law or agreed to in writing, software 1174fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * distributed under the License is distributed on an "AS IS" BASIS, 1274fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1374fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * See the License for the specific language governing permissions and 1474fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * limitations under the License. 1574fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly */ 1674fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly 1774fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pellypackage com.android.server.location; 1874fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly 1974fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pellyimport java.io.FileDescriptor; 2074fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pellyimport java.io.PrintWriter; 2174fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pellyimport java.security.SecureRandom; 22df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Leaseimport android.content.Context; 23df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Leaseimport android.database.ContentObserver; 2474fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pellyimport android.location.Location; 25df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Leaseimport android.os.Handler; 2674fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pellyimport android.os.SystemClock; 27df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Leaseimport android.provider.Settings; 2874fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pellyimport android.util.Log; 2974fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly 3074fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly 3174fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly/** 3274fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * Contains the logic to obfuscate (fudge) locations for coarse applications. 3374fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * 3474fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * <p>The goal is just to prevent applications with only 3574fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * the coarse location permission from receiving a fine location. 3674fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly */ 3774fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pellypublic class LocationFudger { 3874fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly private static final boolean D = false; 3974fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly private static final String TAG = "LocationFudge"; 4074fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly 4174fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly /** 42df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease * Default coarse accuracy in meters. 4374fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly */ 44df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease private static final float DEFAULT_ACCURACY_IN_METERS = 2000.0f; 4574fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly 4674fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly /** 47df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease * Minimum coarse accuracy in meters. 4874fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly */ 49df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease private static final float MINIMUM_ACCURACY_IN_METERS = 200.0f; 5074fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly 5174fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly /** 52df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease * Secure settings key for coarse accuracy. 5374fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly */ 54df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease private static final String COARSE_ACCURACY_CONFIG_NAME = "locationCoarseAccuracy"; 5574fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly 5674fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly /** 5774fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * This is the fastest interval that applications can receive coarse 5874fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * locations. 5974fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly */ 6074fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly public static final long FASTEST_INTERVAL_MS = 10 * 60 * 1000; // 10 minutes 6174fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly 6274fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly /** 6374fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * The duration until we change the random offset. 6474fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly */ 6574fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly private static final long CHANGE_INTERVAL_MS = 60 * 60 * 1000; // 1 hour 6674fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly 6774fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly /** 6874fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * The percentage that we change the random offset at every interval. 6974fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * 7074fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * <p>0.0 indicates the random offset doesn't change. 1.0 7174fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * indicates the random offset is completely replaced every interval. 7274fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly */ 7374fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly private static final double CHANGE_PER_INTERVAL = 0.03; // 3% change 7474fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly 7574fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly // Pre-calculated weights used to move the random offset. 7674fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly // 7774fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly // The goal is to iterate on the previous offset, but keep 7874fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly // the resulting standard deviation the same. The variance of 7974fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly // two gaussian distributions summed together is equal to the 8074fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly // sum of the variance of each distribution. So some quick 8174fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly // algebra results in the following sqrt calculation to 8274fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly // weigh in a new offset while keeping the final standard 8374fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly // deviation unchanged. 8474fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly private static final double NEW_WEIGHT = CHANGE_PER_INTERVAL; 8574fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly private static final double PREVIOUS_WEIGHT = Math.sqrt(1 - NEW_WEIGHT * NEW_WEIGHT); 8674fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly 8774fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly /** 8874fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * This number actually varies because the earth is not round, but 8974fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * 111,000 meters is considered generally acceptable. 9074fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly */ 9174fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly private static final int APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR = 111000; 9274fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly 9374fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly /** 9474fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * Maximum latitude. 9574fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * 9674fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * <p>We pick a value 1 meter away from 90.0 degrees in order 9774fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * to keep cosine(MAX_LATITUDE) to a non-zero value, so that we avoid 9874fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * divide by zero fails. 9974fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly */ 10074fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly private static final double MAX_LATITUDE = 90.0 - 10174fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly (1.0 / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR); 10274fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly 10374fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly private final Object mLock = new Object(); 10474fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly private final SecureRandom mRandom = new SecureRandom(); 10574fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly 106df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease /** 107df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease * Used to monitor coarse accuracy secure setting for changes. 108df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease */ 109df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease private final ContentObserver mSettingsObserver; 110df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease 111df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease /** 112df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease * Used to resolve coarse accuracy setting. 113df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease */ 114df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease private final Context mContext; 115df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease 11674fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly // all fields below protected by mLock 11774fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly private double mOffsetLatitudeMeters; 11874fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly private double mOffsetLongitudeMeters; 11974fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly private long mNextInterval; 12074fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly 121df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease /** 122df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease * Best location accuracy allowed for coarse applications. 123df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease * This value should only be set by {@link #setAccuracyInMetersLocked(float)}. 124df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease */ 125df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease private float mAccuracyInMeters; 126df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease 127df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease /** 128df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease * The distance between grids for snap-to-grid. See {@link #createCoarse}. 129df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease * This value should only be set by {@link #setAccuracyInMetersLocked(float)}. 130df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease */ 131df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease private double mGridSizeInMeters; 132df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease 133df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease /** 134df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease * Standard deviation of the (normally distributed) random offset applied 135df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease * to coarse locations. It does not need to be as large as 136df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease * {@link #COARSE_ACCURACY_METERS} because snap-to-grid is the primary obfuscation 137df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease * method. See further details in the implementation. 138df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease * This value should only be set by {@link #setAccuracyInMetersLocked(float)}. 139df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease */ 140df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease private double mStandardDeviationInMeters; 141df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease 142df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease public LocationFudger(Context context, Handler handler) { 143df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease mContext = context; 144df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease mSettingsObserver = new ContentObserver(handler) { 145df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease @Override 146df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease public void onChange(boolean selfChange) { 147df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease setAccuracyInMeters(loadCoarseAccuracy()); 148df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease } 149df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease }; 150df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease mContext.getContentResolver().registerContentObserver(Settings.Secure.getUriFor( 151df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease COARSE_ACCURACY_CONFIG_NAME), false, mSettingsObserver); 152df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease 153df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease float accuracy = loadCoarseAccuracy(); 154df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease synchronized (mLock) { 155df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease setAccuracyInMetersLocked(accuracy); 156df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease mOffsetLatitudeMeters = nextOffsetLocked(); 157df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease mOffsetLongitudeMeters = nextOffsetLocked(); 158df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease mNextInterval = SystemClock.elapsedRealtime() + CHANGE_INTERVAL_MS; 159df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease } 16074fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly } 16174fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly 16274fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly /** 16374fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * Get the cached coarse location, or generate a new one and cache it. 16474fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly */ 16574fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly public Location getOrCreate(Location location) { 166df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease synchronized (mLock) { 16709016ab4dd056a16809419d612cb865a14980880Victoria Lease Location coarse = location.getExtraLocation(Location.EXTRA_COARSE_LOCATION); 16809016ab4dd056a16809419d612cb865a14980880Victoria Lease if (coarse == null) { 169df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease return addCoarseLocationExtraLocked(location); 170df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease } 171df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease if (coarse.getAccuracy() < mAccuracyInMeters) { 172df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease return addCoarseLocationExtraLocked(location); 173df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease } 174df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease return coarse; 17574fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly } 17674fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly } 17774fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly 178df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease private Location addCoarseLocationExtraLocked(Location location) { 179df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease Location coarse = createCoarseLocked(location); 18009016ab4dd056a16809419d612cb865a14980880Victoria Lease location.setExtraLocation(Location.EXTRA_COARSE_LOCATION, coarse); 18174fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly return coarse; 18274fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly } 18374fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly 18474fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly /** 18574fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * Create a coarse location. 18674fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * 18774fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * <p>Two techniques are used: random offsets and snap-to-grid. 18874fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * 18974fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * <p>First we add a random offset. This mitigates against detecting 19074fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * grid transitions. Without a random offset it is possible to detect 19174fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * a users position very accurately when they cross a grid boundary. 19274fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * The random offset changes very slowly over time, to mitigate against 19374fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * taking many location samples and averaging them out. 19474fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * 19574fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * <p>Second we snap-to-grid (quantize). This has the nice property of 19674fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * producing stable results, and mitigating against taking many samples 19774fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * to average out a random offset. 19874fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly */ 199df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease private Location createCoarseLocked(Location fine) { 20074fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly Location coarse = new Location(fine); 20174fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly 20274fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly // clean all the optional information off the location, because 20374fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly // this can leak detailed location information 20474fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly coarse.removeBearing(); 20574fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly coarse.removeSpeed(); 20674fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly coarse.removeAltitude(); 20774fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly coarse.setExtras(null); 20874fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly 20974fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly double lat = coarse.getLatitude(); 21074fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly double lon = coarse.getLongitude(); 21174fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly 21274fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly // wrap 21374fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly lat = wrapLatitude(lat); 21474fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly lon = wrapLongitude(lon); 21574fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly 21674fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly // Step 1) apply a random offset 21774fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly // 21874fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly // The goal of the random offset is to prevent the application 21974fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly // from determining that the device is on a grid boundary 22074fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly // when it crosses from one grid to the next. 22174fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly // 22274fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly // We apply the offset even if the location already claims to be 22374fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly // inaccurate, because it may be more accurate than claimed. 224df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease updateRandomOffsetLocked(); 225df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease // perform lon first whilst lat is still within bounds 226df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease lon += metersToDegreesLongitude(mOffsetLongitudeMeters, lat); 227df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease lat += metersToDegreesLatitude(mOffsetLatitudeMeters); 228df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease if (D) Log.d(TAG, String.format("applied offset of %.0f, %.0f (meters)", 229df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease mOffsetLongitudeMeters, mOffsetLatitudeMeters)); 23074fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly 23174fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly // wrap 23274fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly lat = wrapLatitude(lat); 23374fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly lon = wrapLongitude(lon); 23474fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly 23574fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly // Step 2) Snap-to-grid (quantize) 23674fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly // 23774fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly // This is the primary means of obfuscation. It gives nice consistent 23874fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly // results and is very effective at hiding the true location 23974fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly // (as long as you are not sitting on a grid boundary, which 24074fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly // step 1 mitigates). 24174fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly // 24274fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly // Note we quantize the latitude first, since the longitude 24374fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly // quantization depends on the latitude value and so leaks information 24474fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly // about the latitude 245df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease double latGranularity = metersToDegreesLatitude(mGridSizeInMeters); 24674fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly lat = Math.round(lat / latGranularity) * latGranularity; 247df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease double lonGranularity = metersToDegreesLongitude(mGridSizeInMeters, lat); 24874fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly lon = Math.round(lon / lonGranularity) * lonGranularity; 24974fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly 25074fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly // wrap again 25174fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly lat = wrapLatitude(lat); 25274fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly lon = wrapLongitude(lon); 25374fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly 25474fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly // apply 25574fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly coarse.setLatitude(lat); 25674fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly coarse.setLongitude(lon); 257df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease coarse.setAccuracy(Math.max(mAccuracyInMeters, coarse.getAccuracy())); 25874fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly 25974fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly if (D) Log.d(TAG, "fudged " + fine + " to " + coarse); 26074fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly return coarse; 26174fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly } 26274fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly 26374fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly /** 26474fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * Update the random offset over time. 26574fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * 26674fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * <p>If the random offset was new for every location 26774fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * fix then an application can more easily average location results 26874fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * over time, 26974fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * especially when the location is near a grid boundary. On the 27074fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * other hand if the random offset is constant then if an application 27174fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * found a way to reverse engineer the offset they would be able 27274fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * to detect location at grid boundaries very accurately. So 27374fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * we choose a random offset and then very slowly move it, to 27474fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * make both approaches very hard. 27574fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * 27674fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * <p>The random offset does not need to be large, because snap-to-grid 27774fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * is the primary obfuscation mechanism. It just needs to be large 27874fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * enough to stop information leakage as we cross grid boundaries. 27974fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly */ 28074fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly private void updateRandomOffsetLocked() { 28174fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly long now = SystemClock.elapsedRealtime(); 28274fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly if (now < mNextInterval) { 28374fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly return; 28474fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly } 28574fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly 28674fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly if (D) Log.d(TAG, String.format("old offset: %.0f, %.0f (meters)", 28774fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly mOffsetLongitudeMeters, mOffsetLatitudeMeters)); 28874fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly 28974fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly // ok, need to update the random offset 29074fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly mNextInterval = now + CHANGE_INTERVAL_MS; 29174fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly 29274fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly mOffsetLatitudeMeters *= PREVIOUS_WEIGHT; 293df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease mOffsetLatitudeMeters += NEW_WEIGHT * nextOffsetLocked(); 29474fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly mOffsetLongitudeMeters *= PREVIOUS_WEIGHT; 295df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease mOffsetLongitudeMeters += NEW_WEIGHT * nextOffsetLocked(); 29674fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly 29774fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly if (D) Log.d(TAG, String.format("new offset: %.0f, %.0f (meters)", 29874fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly mOffsetLongitudeMeters, mOffsetLatitudeMeters)); 29974fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly } 30074fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly 301df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease private double nextOffsetLocked() { 302df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease return mRandom.nextGaussian() * mStandardDeviationInMeters; 30374fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly } 30474fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly 30574fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly private static double wrapLatitude(double lat) { 30674fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly if (lat > MAX_LATITUDE) { 30774fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly lat = MAX_LATITUDE; 30874fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly } 30974fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly if (lat < -MAX_LATITUDE) { 31074fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly lat = -MAX_LATITUDE; 31174fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly } 31274fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly return lat; 31374fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly } 31474fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly 31574fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly private static double wrapLongitude(double lon) { 31674fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly lon %= 360.0; // wraps into range (-360.0, +360.0) 31774fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly if (lon >= 180.0) { 31874fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly lon -= 360.0; 31974fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly } 32074fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly if (lon < -180.0) { 32174fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly lon += 360.0; 32274fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly } 32374fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly return lon; 32474fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly } 32574fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly 32674fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly private static double metersToDegreesLatitude(double distance) { 32774fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly return distance / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR; 32874fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly } 32974fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly 33074fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly /** 33174fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly * Requires latitude since longitudinal distances change with distance from equator. 33274fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly */ 33374fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly private static double metersToDegreesLongitude(double distance, double lat) { 33474fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly return distance / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR / Math.cos(Math.toRadians(lat)); 33574fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly } 33674fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly 33774fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 33874fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly pw.println(String.format("offset: %.0f, %.0f (meters)", mOffsetLongitudeMeters, 33974fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly mOffsetLatitudeMeters)); 34074fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly } 341df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease 342df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease /** 343df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease * This is the main control: call this to set the best location accuracy 344df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease * allowed for coarse applications and all derived values. 345df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease */ 346df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease private void setAccuracyInMetersLocked(float accuracyInMeters) { 347df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease mAccuracyInMeters = Math.max(accuracyInMeters, MINIMUM_ACCURACY_IN_METERS); 348df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease if (D) { 349df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease Log.d(TAG, "setAccuracyInMetersLocked: new accuracy = " + mAccuracyInMeters); 350df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease } 351df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease mGridSizeInMeters = mAccuracyInMeters; 352df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease mStandardDeviationInMeters = mGridSizeInMeters / 4.0; 353df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease } 354df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease 355df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease /** 356df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease * Same as setAccuracyInMetersLocked without the pre-lock requirement. 357df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease */ 358df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease private void setAccuracyInMeters(float accuracyInMeters) { 359df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease synchronized (mLock) { 360df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease setAccuracyInMetersLocked(accuracyInMeters); 361df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease } 362df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease } 363df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease 364df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease /** 365df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease * Loads the coarse accuracy value from secure settings. 366df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease */ 367df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease private float loadCoarseAccuracy() { 368df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease String newSetting = Settings.Secure.getString(mContext.getContentResolver(), 369df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease COARSE_ACCURACY_CONFIG_NAME); 370df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease if (D) { 371df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease Log.d(TAG, "loadCoarseAccuracy: newSetting = \"" + newSetting + "\""); 372df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease } 373df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease if (newSetting == null) { 374df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease return DEFAULT_ACCURACY_IN_METERS; 375df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease } 376df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease try { 377df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease return Float.parseFloat(newSetting); 378df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease } catch (NumberFormatException e) { 379df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease return DEFAULT_ACCURACY_IN_METERS; 380df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease } 381df9ec6171f6b7f3075d7f8174e9ae6ecf080c917Victoria Lease } 38274fa7eabda3d0c1a85e0b568e3fc4230ed4fe7a4Nick Pelly} 383