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