blob: e219e8d7d0ce5124f027464c137959ae332fae8b [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080019import android.app.PendingIntent;
Mike Lockwood9637d472009-04-02 21:41:57 -070020import android.content.ContentQueryMap;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080021import android.content.ContentResolver;
22import android.content.Context;
23import android.content.Intent;
24import android.content.IntentFilter;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070025import android.content.pm.ApplicationInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026import android.content.pm.PackageManager;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070027import android.content.pm.PackageManager.NameNotFoundException;
Mike Lockwood628fd6d2010-01-25 22:46:13 -050028import android.content.res.Resources;
Mike Lockwood9637d472009-04-02 21:41:57 -070029import android.database.Cursor;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import android.location.Address;
Mike Lockwood03ca2162010-04-01 08:10:09 -070031import android.location.Criteria;
Mike Lockwood34901402010-01-04 12:14:21 -050032import android.location.GeocoderParams;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070033import android.location.Geofence;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034import android.location.IGpsStatusListener;
Mike Lockwood15e3d0f2009-05-01 07:53:28 -040035import android.location.IGpsStatusProvider;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036import android.location.ILocationListener;
37import android.location.ILocationManager;
Danke Xie22d1f9f2009-08-18 18:28:45 -040038import android.location.INetInitiatedListener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039import android.location.Location;
40import android.location.LocationManager;
41import android.location.LocationProvider;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070042import android.location.LocationRequest;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043import android.net.ConnectivityManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044import android.os.Binder;
45import android.os.Bundle;
46import android.os.Handler;
47import android.os.IBinder;
Mike Lockwood3d12b512009-04-21 23:25:35 -070048import android.os.Looper;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049import android.os.Message;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070050import android.os.Parcelable;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051import android.os.PowerManager;
Mike Lockwoode932f7f2009-04-06 10:51:26 -070052import android.os.Process;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053import android.os.RemoteException;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070054import android.os.SystemClock;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070055import android.os.WorkSource;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056import android.provider.Settings;
Nick Pellye0fd6932012-07-11 10:26:13 -070057import android.provider.Settings.NameValueTable;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058import android.util.Log;
Joe Onorato8a9b2202010-02-26 18:56:32 -080059import android.util.Slog;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060
Mike Lockwoode97ae402010-09-29 15:23:46 -040061import com.android.internal.content.PackageMonitor;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070062import com.android.internal.location.ProviderProperties;
63import com.android.internal.location.ProviderRequest;
Mike Lockwood43e33f22010-03-26 10:41:48 -040064import com.android.server.location.GeocoderProxy;
Nick Pellye0fd6932012-07-11 10:26:13 -070065import com.android.server.location.GeofenceManager;
Mike Lockwood43e33f22010-03-26 10:41:48 -040066import com.android.server.location.GpsLocationProvider;
67import com.android.server.location.LocationProviderInterface;
68import com.android.server.location.LocationProviderProxy;
69import com.android.server.location.MockProvider;
70import com.android.server.location.PassiveProvider;
71
72import java.io.FileDescriptor;
73import java.io.PrintWriter;
74import java.util.ArrayList;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070075import java.util.Arrays;
Mike Lockwood43e33f22010-03-26 10:41:48 -040076import java.util.HashMap;
77import java.util.HashSet;
78import java.util.List;
79import java.util.Map;
80import java.util.Observable;
81import java.util.Observer;
82import java.util.Set;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080083
84/**
85 * The service class that manages LocationProviders and issues location
86 * updates and alerts.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080087 */
Nick Pelly6fa9ad42012-07-16 12:18:23 -070088public class LocationManagerService extends ILocationManager.Stub implements Observer, Runnable {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080089 private static final String TAG = "LocationManagerService";
Nick Pelly6fa9ad42012-07-16 12:18:23 -070090 public static final boolean D = false;
91
92 private static final String WAKELOCK_KEY = TAG;
93 private static final String THREAD_NAME = TAG;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080094
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080095 private static final String ACCESS_FINE_LOCATION =
Nick Pelly6fa9ad42012-07-16 12:18:23 -070096 android.Manifest.permission.ACCESS_FINE_LOCATION;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080097 private static final String ACCESS_COARSE_LOCATION =
Nick Pelly6fa9ad42012-07-16 12:18:23 -070098 android.Manifest.permission.ACCESS_COARSE_LOCATION;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080099 private static final String ACCESS_MOCK_LOCATION =
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700100 android.Manifest.permission.ACCESS_MOCK_LOCATION;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800101 private static final String ACCESS_LOCATION_EXTRA_COMMANDS =
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700102 android.Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS;
Mike Lockwood275555c2009-05-01 11:30:34 -0400103 private static final String INSTALL_LOCATION_PROVIDER =
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700104 android.Manifest.permission.INSTALL_LOCATION_PROVIDER;
105
106 private static final String NETWORK_LOCATION_SERVICE_ACTION =
107 "com.android.location.service.v2.NetworkLocationProvider";
108 private static final String FUSED_LOCATION_SERVICE_ACTION =
109 "com.android.location.service.FusedLocationProvider";
110
111 private static final int MSG_LOCATION_CHANGED = 1;
112
113 // Accuracy in meters above which a location is considered coarse
114 private static final double COARSE_ACCURACY_M = 100.0;
115 private static final String EXTRA_COARSE_LOCATION = "coarseLocation";
116
117 private static final int APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR = 111000;
118
119 /**
120 * Maximum latitude of 1 meter from the pole.
121 * This keeps cosine(MAX_LATITUDE) to a non-zero value;
122 */
123 private static final double MAX_LATITUDE =
124 90.0 - (1.0 / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800125
Nick Pellyf1be6862012-05-15 10:53:42 -0700126 // Location Providers may sometimes deliver location updates
127 // slightly faster that requested - provide grace period so
128 // we don't unnecessarily filter events that are otherwise on
129 // time
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700130 private static final int MAX_PROVIDER_SCHEDULING_JITTER_MS = 100;
Nick Pellyf1be6862012-05-15 10:53:42 -0700131
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700132 private static final LocationRequest DEFAULT_LOCATION_REQUEST = new LocationRequest();
133
134 private final Context mContext;
135
136 // used internally for synchronization
137 private final Object mLock = new Object();
138
139 // --- fields below are final after init() ---
140 private GeofenceManager mGeofenceManager;
141 private PowerManager.WakeLock mWakeLock;
142 private PackageManager mPackageManager;
143 private GeocoderProxy mGeocodeProvider;
144 private IGpsStatusProvider mGpsStatusProvider;
145 private INetInitiatedListener mNetInitiatedListener;
146 private LocationWorkerHandler mLocationHandler;
147 // track the passive provider for some special cases
148 private PassiveProvider mPassiveProvider;
149
150 // --- fields below are protected by mWakeLock ---
151 private int mPendingBroadcasts;
152
153 // --- fields below are protected by mLock ---
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800154 // Set of providers that are explicitly enabled
155 private final Set<String> mEnabledProviders = new HashSet<String>();
156
157 // Set of providers that are explicitly disabled
158 private final Set<String> mDisabledProviders = new HashSet<String>();
159
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700160 // Mock (test) providers
161 private final HashMap<String, MockProvider> mMockProviders =
162 new HashMap<String, MockProvider>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800163
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700164 // all receivers
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400165 private final HashMap<Object, Receiver> mReceivers = new HashMap<Object, Receiver>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800166
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700167 // currently installed providers (with mocks replacing real providers)
Mike Lockwoodd03ff942010-02-09 08:46:14 -0500168 private final ArrayList<LocationProviderInterface> mProviders =
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700169 new ArrayList<LocationProviderInterface>();
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400170
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700171 // real providers, saved here when mocked out
172 private final HashMap<String, LocationProviderInterface> mRealProviders =
173 new HashMap<String, LocationProviderInterface>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800174
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700175 // mapping from provider name to provider
176 private final HashMap<String, LocationProviderInterface> mProvidersByName =
177 new HashMap<String, LocationProviderInterface>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800178
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700179 // mapping from provider name to all its UpdateRecords
180 private final HashMap<String, ArrayList<UpdateRecord>> mRecordsByProvider =
181 new HashMap<String, ArrayList<UpdateRecord>>();
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700182
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700183 // mapping from provider name to last known location
184 private final HashMap<String, Location> mLastLocation = new HashMap<String, Location>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800185
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700186 // all providers that operate over proxy, for authorizing incoming location
187 private final ArrayList<LocationProviderProxy> mProxyProviders =
188 new ArrayList<LocationProviderProxy>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800189
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700190 public LocationManagerService(Context context) {
191 super();
192 mContext = context;
The Android Open Source Project4df24232009-03-05 14:34:35 -0800193
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700194 if (D) Log.d(TAG, "Constructed");
195
196 // most startup is deferred until systemReady()
197 }
198
199 public void systemReady() {
200 Thread thread = new Thread(null, this, THREAD_NAME);
201 thread.start();
202 }
203
204 @Override
205 public void run() {
206 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
207 Looper.prepare();
208 mLocationHandler = new LocationWorkerHandler();
209 init();
210 Looper.loop();
211 }
212
213 private void init() {
214 if (D) Log.d(TAG, "init()");
215
216 PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
217 mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
218 mPackageManager = mContext.getPackageManager();
219
220 synchronized (mLock) {
221 loadProvidersLocked();
222 }
223 mGeofenceManager = new GeofenceManager(mContext);
224
225 // Register for Network (Wifi or Mobile) updates
226 IntentFilter filter = new IntentFilter();
227 filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
228
229 // listen for settings changes
230 ContentResolver resolver = mContext.getContentResolver();
231 Cursor settingsCursor = resolver.query(Settings.Secure.CONTENT_URI, null,
232 "(" + NameValueTable.NAME + "=?)",
233 new String[]{Settings.Secure.LOCATION_PROVIDERS_ALLOWED}, null);
234 ContentQueryMap query = new ContentQueryMap(settingsCursor, NameValueTable.NAME, true,
235 mLocationHandler);
236 settingsCursor.close();
237 query.addObserver(this);
238 mPackageMonitor.register(mContext, Looper.myLooper(), true);
239 }
240
241 private void loadProvidersLocked() {
242 if (GpsLocationProvider.isSupported()) {
243 // Create a gps location provider
244 GpsLocationProvider gpsProvider = new GpsLocationProvider(mContext, this);
245 mGpsStatusProvider = gpsProvider.getGpsStatusProvider();
246 mNetInitiatedListener = gpsProvider.getNetInitiatedListener();
247 addProviderLocked(gpsProvider);
248 mRealProviders.put(LocationManager.GPS_PROVIDER, gpsProvider);
249 }
250
251 // create a passive location provider, which is always enabled
252 PassiveProvider passiveProvider = new PassiveProvider(this);
253 addProviderLocked(passiveProvider);
254 mEnabledProviders.add(passiveProvider.getName());
255 mPassiveProvider = passiveProvider;
256
257 /*
258 Load package name(s) containing location provider support.
259 These packages can contain services implementing location providers:
260 Geocoder Provider, Network Location Provider, and
261 Fused Location Provider. They will each be searched for
262 service components implementing these providers.
263 The location framework also has support for installation
264 of new location providers at run-time. The new package does not
265 have to be explicitly listed here, however it must have a signature
266 that matches the signature of at least one package on this list.
267 */
268 Resources resources = mContext.getResources();
269 ArrayList<String> providerPackageNames = new ArrayList<String>();
270 String[] pkgs1 = resources.getStringArray(
271 com.android.internal.R.array.config_locationProviderPackageNames);
272 String[] pkgs2 = resources.getStringArray(
273 com.android.internal.R.array.config_overlay_locationProviderPackageNames);
274 if (D) Log.d(TAG, "built-in location providers: " + Arrays.toString(pkgs1));
275 if (D) Log.d(TAG, "overlay location providers: " + Arrays.toString(pkgs2));
276 if (pkgs1 != null) providerPackageNames.addAll(Arrays.asList(pkgs1));
277 if (pkgs2 != null) providerPackageNames.addAll(Arrays.asList(pkgs2));
278
279 // bind to network provider
280 LocationProviderProxy networkProvider = LocationProviderProxy.createAndBind(
281 mContext,
282 LocationManager.NETWORK_PROVIDER,
283 NETWORK_LOCATION_SERVICE_ACTION,
284 providerPackageNames, mLocationHandler);
285 if (networkProvider != null) {
286 mRealProviders.put(LocationManager.NETWORK_PROVIDER, networkProvider);
287 mProxyProviders.add(networkProvider);
288 addProviderLocked(networkProvider);
289 } else {
290 Slog.w(TAG, "no network location provider found");
291 }
292
293 // bind to fused provider
294 LocationProviderProxy fusedLocationProvider = LocationProviderProxy.createAndBind(
295 mContext,
296 LocationManager.FUSED_PROVIDER,
297 FUSED_LOCATION_SERVICE_ACTION,
298 providerPackageNames, mLocationHandler);
299 if (fusedLocationProvider != null) {
300 addProviderLocked(fusedLocationProvider);
301 mProxyProviders.add(fusedLocationProvider);
302 mEnabledProviders.add(fusedLocationProvider.getName());
303 } else {
304 Slog.e(TAG, "no fused location provider found",
305 new IllegalStateException("Location service needs a fused location provider"));
306 }
307
308 // bind to geocoder provider
309 mGeocodeProvider = GeocoderProxy.createAndBind(mContext, providerPackageNames);
310 if (mGeocodeProvider == null) {
311 Slog.e(TAG, "no geocoder provider found");
312 }
313
314 updateProvidersLocked();
315 }
Mike Lockwood9637d472009-04-02 21:41:57 -0700316
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800317 /**
318 * A wrapper class holding either an ILocationListener or a PendingIntent to receive
319 * location updates.
320 */
Mike Lockwood48f17512009-04-23 09:12:08 -0700321 private final class Receiver implements IBinder.DeathRecipient, PendingIntent.OnFinished {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700322 final int mUid; // uid of receiver
323 final int mPid; // pid of receiver
324 final String mPackageName; // package name of receiver
325 final String mPermission; // best permission that receiver has
326
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800327 final ILocationListener mListener;
328 final PendingIntent mPendingIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800329 final Object mKey;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700330
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400331 final HashMap<String,UpdateRecord> mUpdateRecords = new HashMap<String,UpdateRecord>();
Nick Pellyf1be6862012-05-15 10:53:42 -0700332
Mike Lockwood48f17512009-04-23 09:12:08 -0700333 int mPendingBroadcasts;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800334
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700335 Receiver(ILocationListener listener, PendingIntent intent, int pid, int uid,
336 String packageName) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800337 mListener = listener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800338 mPendingIntent = intent;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700339 if (listener != null) {
340 mKey = listener.asBinder();
341 } else {
342 mKey = intent;
343 }
344 mPermission = checkPermission();
345 mUid = uid;
346 mPid = pid;
347 mPackageName = packageName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800348 }
349
350 @Override
351 public boolean equals(Object otherObj) {
352 if (otherObj instanceof Receiver) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700353 return mKey.equals(((Receiver)otherObj).mKey);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800354 }
355 return false;
356 }
357
358 @Override
359 public int hashCode() {
360 return mKey.hashCode();
361 }
Mike Lockwood3681f262009-05-12 10:52:03 -0400362
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800363 @Override
364 public String toString() {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700365 StringBuilder s = new StringBuilder();
366 s.append("Reciever[");
367 s.append(Integer.toHexString(System.identityHashCode(this)));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800368 if (mListener != null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700369 s.append(" listener");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800370 } else {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700371 s.append(" intent");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800372 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700373 for (String p : mUpdateRecords.keySet()) {
374 s.append(" ").append(mUpdateRecords.get(p).toString());
375 }
376 s.append("]");
377 return s.toString();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800378 }
379
380 public boolean isListener() {
381 return mListener != null;
382 }
383
384 public boolean isPendingIntent() {
385 return mPendingIntent != null;
386 }
387
388 public ILocationListener getListener() {
389 if (mListener != null) {
390 return mListener;
391 }
392 throw new IllegalStateException("Request for non-existent listener");
393 }
394
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800395 public boolean callStatusChangedLocked(String provider, int status, Bundle extras) {
396 if (mListener != null) {
397 try {
Mike Lockwood48f17512009-04-23 09:12:08 -0700398 synchronized (this) {
399 // synchronize to ensure incrementPendingBroadcastsLocked()
400 // is called before decrementPendingBroadcasts()
401 mListener.onStatusChanged(provider, status, extras);
Nick Pellye0fd6932012-07-11 10:26:13 -0700402 // call this after broadcasting so we do not increment
403 // if we throw an exeption.
404 incrementPendingBroadcastsLocked();
Mike Lockwood48f17512009-04-23 09:12:08 -0700405 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800406 } catch (RemoteException e) {
407 return false;
408 }
409 } else {
410 Intent statusChanged = new Intent();
411 statusChanged.putExtras(extras);
412 statusChanged.putExtra(LocationManager.KEY_STATUS_CHANGED, status);
413 try {
Mike Lockwood48f17512009-04-23 09:12:08 -0700414 synchronized (this) {
415 // synchronize to ensure incrementPendingBroadcastsLocked()
416 // is called before decrementPendingBroadcasts()
Dianne Hackborn6c418d52011-06-29 14:05:33 -0700417 mPendingIntent.send(mContext, 0, statusChanged, this, mLocationHandler,
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700418 mPermission);
Mike Lockwood48f17512009-04-23 09:12:08 -0700419 // call this after broadcasting so we do not increment
420 // if we throw an exeption.
421 incrementPendingBroadcastsLocked();
422 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800423 } catch (PendingIntent.CanceledException e) {
424 return false;
425 }
426 }
427 return true;
428 }
429
430 public boolean callLocationChangedLocked(Location location) {
431 if (mListener != null) {
432 try {
Mike Lockwood48f17512009-04-23 09:12:08 -0700433 synchronized (this) {
434 // synchronize to ensure incrementPendingBroadcastsLocked()
435 // is called before decrementPendingBroadcasts()
436 mListener.onLocationChanged(location);
Nick Pellye0fd6932012-07-11 10:26:13 -0700437 // call this after broadcasting so we do not increment
438 // if we throw an exeption.
439 incrementPendingBroadcastsLocked();
Mike Lockwood48f17512009-04-23 09:12:08 -0700440 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800441 } catch (RemoteException e) {
442 return false;
443 }
444 } else {
445 Intent locationChanged = new Intent();
446 locationChanged.putExtra(LocationManager.KEY_LOCATION_CHANGED, location);
447 try {
Mike Lockwood48f17512009-04-23 09:12:08 -0700448 synchronized (this) {
449 // synchronize to ensure incrementPendingBroadcastsLocked()
450 // is called before decrementPendingBroadcasts()
Dianne Hackborn6c418d52011-06-29 14:05:33 -0700451 mPendingIntent.send(mContext, 0, locationChanged, this, mLocationHandler,
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700452 mPermission);
Mike Lockwood48f17512009-04-23 09:12:08 -0700453 // call this after broadcasting so we do not increment
454 // if we throw an exeption.
455 incrementPendingBroadcastsLocked();
456 }
457 } catch (PendingIntent.CanceledException e) {
458 return false;
459 }
460 }
461 return true;
462 }
463
464 public boolean callProviderEnabledLocked(String provider, boolean enabled) {
465 if (mListener != null) {
466 try {
467 synchronized (this) {
468 // synchronize to ensure incrementPendingBroadcastsLocked()
469 // is called before decrementPendingBroadcasts()
470 if (enabled) {
471 mListener.onProviderEnabled(provider);
472 } else {
473 mListener.onProviderDisabled(provider);
474 }
Nick Pellye0fd6932012-07-11 10:26:13 -0700475 // call this after broadcasting so we do not increment
476 // if we throw an exeption.
477 incrementPendingBroadcastsLocked();
Mike Lockwood48f17512009-04-23 09:12:08 -0700478 }
479 } catch (RemoteException e) {
480 return false;
481 }
482 } else {
483 Intent providerIntent = new Intent();
484 providerIntent.putExtra(LocationManager.KEY_PROVIDER_ENABLED, enabled);
485 try {
486 synchronized (this) {
487 // synchronize to ensure incrementPendingBroadcastsLocked()
488 // is called before decrementPendingBroadcasts()
Dianne Hackborn6c418d52011-06-29 14:05:33 -0700489 mPendingIntent.send(mContext, 0, providerIntent, this, mLocationHandler,
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700490 mPermission);
Mike Lockwood48f17512009-04-23 09:12:08 -0700491 // call this after broadcasting so we do not increment
492 // if we throw an exeption.
493 incrementPendingBroadcastsLocked();
494 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800495 } catch (PendingIntent.CanceledException e) {
496 return false;
497 }
498 }
499 return true;
500 }
501
Nick Pellyf1be6862012-05-15 10:53:42 -0700502 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800503 public void binderDied() {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700504 if (D) Log.d(TAG, "Location listener died");
505
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400506 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800507 removeUpdatesLocked(this);
508 }
Mike Lockwood48f17512009-04-23 09:12:08 -0700509 synchronized (this) {
510 if (mPendingBroadcasts > 0) {
511 LocationManagerService.this.decrementPendingBroadcasts();
512 mPendingBroadcasts = 0;
513 }
514 }
515 }
516
Nick Pellye0fd6932012-07-11 10:26:13 -0700517 @Override
Mike Lockwood48f17512009-04-23 09:12:08 -0700518 public void onSendFinished(PendingIntent pendingIntent, Intent intent,
519 int resultCode, String resultData, Bundle resultExtras) {
Mike Lockwood0528b9b2009-05-07 10:12:54 -0400520 synchronized (this) {
521 decrementPendingBroadcastsLocked();
Mike Lockwood48f17512009-04-23 09:12:08 -0700522 }
523 }
524
Mike Lockwood0528b9b2009-05-07 10:12:54 -0400525 // this must be called while synchronized by caller in a synchronized block
526 // containing the sending of the broadcaset
527 private void incrementPendingBroadcastsLocked() {
528 if (mPendingBroadcasts++ == 0) {
529 LocationManagerService.this.incrementPendingBroadcasts();
530 }
531 }
532
533 private void decrementPendingBroadcastsLocked() {
534 if (--mPendingBroadcasts == 0) {
535 LocationManagerService.this.decrementPendingBroadcasts();
Mike Lockwood48f17512009-04-23 09:12:08 -0700536 }
537 }
538 }
539
Nick Pellye0fd6932012-07-11 10:26:13 -0700540 @Override
Mike Lockwood48f17512009-04-23 09:12:08 -0700541 public void locationCallbackFinished(ILocationListener listener) {
Joshua Bartel080b61b2009-10-05 12:44:46 -0400542 //Do not use getReceiver here as that will add the ILocationListener to
543 //the receiver list if it is not found. If it is not found then the
544 //LocationListener was removed when it had a pending broadcast and should
545 //not be added back.
546 IBinder binder = listener.asBinder();
547 Receiver receiver = mReceivers.get(binder);
Mike Lockwood48f17512009-04-23 09:12:08 -0700548 if (receiver != null) {
Mike Lockwood0528b9b2009-05-07 10:12:54 -0400549 synchronized (receiver) {
550 // so wakelock calls will succeed
551 long identity = Binder.clearCallingIdentity();
552 receiver.decrementPendingBroadcastsLocked();
553 Binder.restoreCallingIdentity(identity);
554 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800555 }
556 }
557
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700558 /** Settings Observer callback */
559 @Override
560 public void update(Observable o, Object arg) {
561 synchronized (mLock) {
562 updateProvidersLocked();
Mike Lockwood9637d472009-04-02 21:41:57 -0700563 }
564 }
565
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700566 private void addProviderLocked(LocationProviderInterface provider) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400567 mProviders.add(provider);
568 mProvidersByName.put(provider.getName(), provider);
569 }
570
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700571 private void removeProviderLocked(LocationProviderInterface provider) {
572 provider.disable();
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400573 mProviders.remove(provider);
574 mProvidersByName.remove(provider.getName());
575 }
576
Mike Lockwood3d12b512009-04-21 23:25:35 -0700577
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800578 private boolean isAllowedBySettingsLocked(String provider) {
579 if (mEnabledProviders.contains(provider)) {
580 return true;
581 }
582 if (mDisabledProviders.contains(provider)) {
583 return false;
584 }
585 // Use system settings
586 ContentResolver resolver = mContext.getContentResolver();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800587
Brad Larson8eb3ea62009-12-29 11:47:55 -0600588 return Settings.Secure.isLocationProviderEnabled(resolver, provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800589 }
590
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700591 /**
592 * Throw SecurityException if caller has neither COARSE or FINE.
593 * Otherwise, return the best permission.
594 */
595 private String checkPermission() {
596 if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) ==
597 PackageManager.PERMISSION_GRANTED) {
Dianne Hackborn6c418d52011-06-29 14:05:33 -0700598 return ACCESS_FINE_LOCATION;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700599 } else if (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION) ==
600 PackageManager.PERMISSION_GRANTED) {
601 return ACCESS_COARSE_LOCATION;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800602 }
Dianne Hackborn6c418d52011-06-29 14:05:33 -0700603
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700604 throw new SecurityException("Location requires either ACCESS_COARSE_LOCATION or" +
605 "ACCESS_FINE_LOCATION permission");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800606 }
607
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700608 /**
609 * Returns all providers by name, including passive, but excluding
610 * fused.
611 */
Nick Pellye0fd6932012-07-11 10:26:13 -0700612 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800613 public List<String> getAllProviders() {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700614 checkPermission();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800615
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700616 ArrayList<String> out;
617 synchronized (mLock) {
618 out = new ArrayList<String>(mProviders.size());
619 for (LocationProviderInterface provider : mProviders) {
620 String name = provider.getName();
621 if (LocationManager.FUSED_PROVIDER.equals(name)) {
Mike Lockwood03ca2162010-04-01 08:10:09 -0700622 continue;
623 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800624 out.add(name);
625 }
626 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700627
628 if (D) Log.d(TAG, "getAllProviders()=" + out);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800629 return out;
630 }
631
Mike Lockwood03ca2162010-04-01 08:10:09 -0700632 /**
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700633 * Return all providers by name, that match criteria and are optionally
634 * enabled.
635 * Can return passive provider, but never returns fused provider.
Mike Lockwood03ca2162010-04-01 08:10:09 -0700636 */
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700637 @Override
638 public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
639 checkPermission();
Mike Lockwood03ca2162010-04-01 08:10:09 -0700640
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700641 ArrayList<String> out;
Mike Lockwood03ca2162010-04-01 08:10:09 -0700642 synchronized (mLock) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700643 out = new ArrayList<String>(mProviders.size());
644 for (LocationProviderInterface provider : mProviders) {
645 String name = provider.getName();
646 if (LocationManager.FUSED_PROVIDER.equals(name)) {
647 continue;
648 }
649 if (enabledOnly && !isAllowedBySettingsLocked(name)) {
650 continue;
651 }
652 if (criteria != null && !LocationProvider.propertiesMeetCriteria(
653 name, provider.getProperties(), criteria)) {
654 continue;
655 }
656 out.add(name);
Mike Lockwood03ca2162010-04-01 08:10:09 -0700657 }
658 }
659
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700660 if (D) Log.d(TAG, "getProviders()=" + out);
661 return out;
Mike Lockwood03ca2162010-04-01 08:10:09 -0700662 }
663
664 /**
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700665 * Return the name of the best provider given a Criteria object.
666 * This method has been deprecated from the public API,
667 * and the whole LoactionProvider (including #meetsCriteria)
668 * has been deprecated as well. So this method now uses
669 * some simplified logic.
Mike Lockwood03ca2162010-04-01 08:10:09 -0700670 */
Nick Pellye0fd6932012-07-11 10:26:13 -0700671 @Override
Mike Lockwood03ca2162010-04-01 08:10:09 -0700672 public String getBestProvider(Criteria criteria, boolean enabledOnly) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700673 String result = null;
674 checkPermission();
675
676 List<String> providers = getProviders(criteria, enabledOnly);
677 if (providers.size() < 1) {
678 result = pickBest(providers);
679 if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
680 return result;
681 }
682 providers = getProviders(null, enabledOnly);
683 if (providers.size() >= 1) {
684 result = pickBest(providers);
685 if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
686 return result;
Mike Lockwood03ca2162010-04-01 08:10:09 -0700687 }
688
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700689 if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
Mike Lockwood03ca2162010-04-01 08:10:09 -0700690 return null;
691 }
692
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700693 private String pickBest(List<String> providers) {
694 if (providers.contains(LocationManager.NETWORK_PROVIDER)) {
695 return LocationManager.NETWORK_PROVIDER;
696 } else if (providers.contains(LocationManager.GPS_PROVIDER)) {
697 return LocationManager.GPS_PROVIDER;
698 } else {
699 return providers.get(0);
700 }
701 }
702
Nick Pellye0fd6932012-07-11 10:26:13 -0700703 @Override
Mike Lockwood03ca2162010-04-01 08:10:09 -0700704 public boolean providerMeetsCriteria(String provider, Criteria criteria) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700705 checkPermission();
706
Mike Lockwood03ca2162010-04-01 08:10:09 -0700707 LocationProviderInterface p = mProvidersByName.get(provider);
708 if (p == null) {
709 throw new IllegalArgumentException("provider=" + provider);
710 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700711
712 boolean result = LocationProvider.propertiesMeetCriteria(
713 p.getName(), p.getProperties(), criteria);
714 if (D) Log.d(TAG, "providerMeetsCriteria(" + provider + ", " + criteria + ")=" + result);
715 return result;
Mike Lockwood03ca2162010-04-01 08:10:09 -0700716 }
717
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800718 private void updateProvidersLocked() {
Brad Fitzpatrick0c5a0402010-08-27 14:01:23 -0700719 boolean changesMade = false;
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400720 for (int i = mProviders.size() - 1; i >= 0; i--) {
Mike Lockwoodd03ff942010-02-09 08:46:14 -0500721 LocationProviderInterface p = mProviders.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800722 boolean isEnabled = p.isEnabled();
723 String name = p.getName();
724 boolean shouldBeEnabled = isAllowedBySettingsLocked(name);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800725 if (isEnabled && !shouldBeEnabled) {
726 updateProviderListenersLocked(name, false);
Brad Fitzpatrick0c5a0402010-08-27 14:01:23 -0700727 changesMade = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800728 } else if (!isEnabled && shouldBeEnabled) {
729 updateProviderListenersLocked(name, true);
Brad Fitzpatrick0c5a0402010-08-27 14:01:23 -0700730 changesMade = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800731 }
Brad Fitzpatrick0c5a0402010-08-27 14:01:23 -0700732 }
733 if (changesMade) {
734 mContext.sendBroadcast(new Intent(LocationManager.PROVIDERS_CHANGED_ACTION));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800735 }
736 }
737
738 private void updateProviderListenersLocked(String provider, boolean enabled) {
739 int listeners = 0;
740
Mike Lockwoodd03ff942010-02-09 08:46:14 -0500741 LocationProviderInterface p = mProvidersByName.get(provider);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700742 if (p == null) return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800743
744 ArrayList<Receiver> deadReceivers = null;
Nick Pellye0fd6932012-07-11 10:26:13 -0700745
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800746 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
747 if (records != null) {
748 final int N = records.size();
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700749 for (int i = 0; i < N; i++) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800750 UpdateRecord record = records.get(i);
751 // Sends a notification message to the receiver
Mike Lockwood48f17512009-04-23 09:12:08 -0700752 if (!record.mReceiver.callProviderEnabledLocked(provider, enabled)) {
753 if (deadReceivers == null) {
754 deadReceivers = new ArrayList<Receiver>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800755 }
Simon Schoar46866572009-06-10 21:12:10 +0200756 deadReceivers.add(record.mReceiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800757 }
758 listeners++;
759 }
760 }
761
762 if (deadReceivers != null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700763 for (int i = deadReceivers.size() - 1; i >= 0; i--) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800764 removeUpdatesLocked(deadReceivers.get(i));
765 }
766 }
Nick Pellye0fd6932012-07-11 10:26:13 -0700767
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800768 if (enabled) {
769 p.enable();
770 if (listeners > 0) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700771 applyRequirementsLocked(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800772 }
773 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800774 p.disable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800775 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800776 }
777
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700778 private void applyRequirementsLocked(String provider) {
779 LocationProviderInterface p = mProvidersByName.get(provider);
780 if (p == null) return;
781
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800782 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700783 WorkSource worksource = new WorkSource();
784 ProviderRequest providerRequest = new ProviderRequest();
785
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800786 if (records != null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700787 for (UpdateRecord record : records) {
788 LocationRequest locationRequest = record.mRequest;
789
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700790 providerRequest.locationRequests.add(locationRequest);
791 if (locationRequest.getInterval() < providerRequest.interval) {
792 providerRequest.reportLocation = true;
793 providerRequest.interval = locationRequest.getInterval();
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700794 }
795 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700796
797 if (providerRequest.reportLocation) {
798 // calculate who to blame for power
799 // This is somewhat arbitrary. We pick a threshold interval
800 // that is slightly higher that the minimum interval, and
801 // spread the blame across all applications with a request
802 // under that threshold.
803 long thresholdInterval = (providerRequest.interval + 1000) * 3 / 2;
804 for (UpdateRecord record : records) {
805 LocationRequest locationRequest = record.mRequest;
806 if (locationRequest.getInterval() <= thresholdInterval) {
807 worksource.add(record.mReceiver.mUid);
808 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700809 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800810 }
811 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700812
813 if (D) Log.d(TAG, "provider request: " + provider + " " + providerRequest);
814 p.setRequest(providerRequest, worksource);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800815 }
816
817 private class UpdateRecord {
818 final String mProvider;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700819 final LocationRequest mRequest;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800820 final Receiver mReceiver;
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400821 Location mLastFixBroadcast;
822 long mLastStatusBroadcast;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800823
824 /**
825 * Note: must be constructed with lock held.
826 */
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700827 UpdateRecord(String provider, LocationRequest request, Receiver receiver) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800828 mProvider = provider;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700829 mRequest = request;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800830 mReceiver = receiver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800831
832 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
833 if (records == null) {
834 records = new ArrayList<UpdateRecord>();
835 mRecordsByProvider.put(provider, records);
836 }
837 if (!records.contains(this)) {
838 records.add(this);
839 }
840 }
841
842 /**
843 * Method to be called when a record will no longer be used. Calling this multiple times
844 * must have the same effect as calling it once.
845 */
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700846 void disposeLocked(boolean removeReceiver) {
847 // remove from mRecordsByProvider
848 ArrayList<UpdateRecord> globalRecords = mRecordsByProvider.get(this.mProvider);
849 if (globalRecords != null) {
850 globalRecords.remove(this);
851 }
852
853 if (!removeReceiver) return; // the caller will handle the rest
854
855 // remove from Receiver#mUpdateRecords
856 HashMap<String, UpdateRecord> receiverRecords = mReceiver.mUpdateRecords;
857 if (receiverRecords != null) {
858 receiverRecords.remove(this.mProvider);
859
860 // and also remove the Receiver if it has no more update records
861 if (removeReceiver && receiverRecords.size() == 0) {
862 removeUpdatesLocked(mReceiver);
863 }
Mike Lockwood3a76fd62009-09-01 07:26:56 -0400864 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800865 }
866
867 @Override
868 public String toString() {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700869 StringBuilder s = new StringBuilder();
870 s.append("UpdateRecord[");
871 s.append(mProvider);
872 s.append(' ').append(mReceiver.mPackageName).append('(');
873 s.append(mReceiver.mUid).append(')');
874 s.append(' ').append(mRequest);
875 s.append(']');
876 return s.toString();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800877 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800878 }
879
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700880 private Receiver getReceiver(ILocationListener listener, int pid, int uid, String packageName) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400881 IBinder binder = listener.asBinder();
882 Receiver receiver = mReceivers.get(binder);
883 if (receiver == null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700884 receiver = new Receiver(listener, null, pid, uid, packageName);
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400885 mReceivers.put(binder, receiver);
886
887 try {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700888 receiver.getListener().asBinder().linkToDeath(receiver, 0);
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400889 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800890 Slog.e(TAG, "linkToDeath failed:", e);
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400891 return null;
892 }
893 }
894 return receiver;
895 }
896
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700897 private Receiver getReceiver(PendingIntent intent, int pid, int uid, String packageName) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400898 Receiver receiver = mReceivers.get(intent);
899 if (receiver == null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700900 receiver = new Receiver(null, intent, pid, uid, packageName);
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400901 mReceivers.put(intent, receiver);
902 }
903 return receiver;
904 }
905
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700906 private String checkPermissionAndRequest(LocationRequest request) {
907 String perm = checkPermission();
908
909 if (ACCESS_COARSE_LOCATION.equals(perm)) {
910 request.applyCoarsePermissionRestrictions();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400911 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700912 return perm;
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400913 }
914
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700915 private void checkPackageName(String packageName) {
Nick Pellye0fd6932012-07-11 10:26:13 -0700916 if (packageName == null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700917 throw new SecurityException("invalid package name: " + packageName);
Nick Pellye0fd6932012-07-11 10:26:13 -0700918 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700919 int uid = Binder.getCallingUid();
Nick Pellye0fd6932012-07-11 10:26:13 -0700920 String[] packages = mPackageManager.getPackagesForUid(uid);
921 if (packages == null) {
922 throw new SecurityException("invalid UID " + uid);
923 }
924 for (String pkg : packages) {
925 if (packageName.equals(pkg)) return;
926 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700927 throw new SecurityException("invalid package name: " + packageName);
Nick Pellye0fd6932012-07-11 10:26:13 -0700928 }
929
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700930 private void checkPendingIntent(PendingIntent intent) {
931 if (intent == null) {
932 throw new IllegalArgumentException("invalid pending intent: " + intent);
Dianne Hackborn6c418d52011-06-29 14:05:33 -0700933 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700934 }
935
936 private Receiver checkListenerOrIntent(ILocationListener listener, PendingIntent intent,
937 int pid, int uid, String packageName) {
938 if (intent == null && listener == null) {
939 throw new IllegalArgumentException("need eiter listener or intent");
940 } else if (intent != null && listener != null) {
941 throw new IllegalArgumentException("cannot register both listener and intent");
942 } else if (intent != null) {
943 checkPendingIntent(intent);
944 return getReceiver(intent, pid, uid, packageName);
945 } else {
946 return getReceiver(listener, pid, uid, packageName);
947 }
Dianne Hackborn6c418d52011-06-29 14:05:33 -0700948 }
949
Nick Pellye0fd6932012-07-11 10:26:13 -0700950 @Override
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700951 public void requestLocationUpdates(LocationRequest request, ILocationListener listener,
952 PendingIntent intent, String packageName) {
953 if (request == null) request = DEFAULT_LOCATION_REQUEST;
954 checkPackageName(packageName);
955 checkPermissionAndRequest(request);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800956
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700957 final int pid = Binder.getCallingPid();
958 final int uid = Binder.getCallingUid();
959 Receiver recevier = checkListenerOrIntent(listener, intent, pid, uid, packageName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800960
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700961 // so wakelock calls will succeed (not totally sure this is still needed)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800962 long identity = Binder.clearCallingIdentity();
963 try {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700964 synchronized (mLock) {
965 requestLocationUpdatesLocked(request, recevier, pid, uid, packageName);
Mike Lockwood2d4d1bf2010-10-18 17:06:26 -0400966 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800967 } finally {
968 Binder.restoreCallingIdentity(identity);
969 }
970 }
971
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700972 private void requestLocationUpdatesLocked(LocationRequest request, Receiver receiver,
973 int pid, int uid, String packageName) {
974 // Figure out the provider. Either its explicitly request (legacy use cases), or
975 // use the fused provider
976 if (request == null) request = DEFAULT_LOCATION_REQUEST;
977 String name = request.getProvider();
978 if (name == null) name = LocationManager.FUSED_PROVIDER;
979 LocationProviderInterface provider = mProvidersByName.get(name);
980 if (provider == null) {
981 throw new IllegalArgumentException("provider doesn't exisit: " + provider);
982 }
983
984 Log.i(TAG, "request " + Integer.toHexString(System.identityHashCode(receiver)) + " " +
985 name + " " + request + " from " + packageName + "(" + uid + ")");
986
987 UpdateRecord record = new UpdateRecord(name, request, receiver);
988 UpdateRecord oldRecord = receiver.mUpdateRecords.put(name, record);
989 if (oldRecord != null) {
990 oldRecord.disposeLocked(false);
991 }
992
993 boolean isProviderEnabled = isAllowedBySettingsLocked(name);
994 if (isProviderEnabled) {
995 applyRequirementsLocked(name);
996 } else {
997 // Notify the listener that updates are currently disabled
998 receiver.callProviderEnabledLocked(name, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800999 }
1000 }
1001
Nick Pellye0fd6932012-07-11 10:26:13 -07001002 @Override
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001003 public void removeUpdates(ILocationListener listener, PendingIntent intent,
1004 String packageName) {
1005 checkPackageName(packageName);
1006 checkPermission();
1007 final int pid = Binder.getCallingPid();
1008 final int uid = Binder.getCallingUid();
1009 Receiver receiver = checkListenerOrIntent(listener, intent, pid, uid, packageName);
1010
1011 // so wakelock calls will succeed (not totally sure this is still needed)
1012 long identity = Binder.clearCallingIdentity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001013 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001014 synchronized (mLock) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001015 removeUpdatesLocked(receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001016 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001017 } finally {
1018 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001019 }
1020 }
1021
1022 private void removeUpdatesLocked(Receiver receiver) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001023 Log.i(TAG, "remove " + Integer.toHexString(System.identityHashCode(receiver)));
1024
1025 if (mReceivers.remove(receiver.mKey) != null && receiver.isListener()) {
1026 receiver.getListener().asBinder().unlinkToDeath(receiver, 0);
1027 synchronized (receiver) {
1028 if (receiver.mPendingBroadcasts > 0) {
1029 decrementPendingBroadcasts();
1030 receiver.mPendingBroadcasts = 0;
1031 }
1032 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001033 }
1034
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001035 // Record which providers were associated with this listener
1036 HashSet<String> providers = new HashSet<String>();
1037 HashMap<String, UpdateRecord> oldRecords = receiver.mUpdateRecords;
1038 if (oldRecords != null) {
1039 // Call dispose() on the obsolete update records.
1040 for (UpdateRecord record : oldRecords.values()) {
1041 record.disposeLocked(false);
1042 }
1043 // Accumulate providers
1044 providers.addAll(oldRecords.keySet());
1045 }
1046
1047 // update provider
1048 for (String provider : providers) {
1049 // If provider is already disabled, don't need to do anything
1050 if (!isAllowedBySettingsLocked(provider)) {
1051 continue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001052 }
1053
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001054 applyRequirementsLocked(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001055 }
1056 }
1057
Nick Pellye0fd6932012-07-11 10:26:13 -07001058 @Override
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001059 public Location getLastLocation(LocationRequest request) {
1060 if (D) Log.d(TAG, "getLastLocation: " + request);
1061 if (request == null) request = DEFAULT_LOCATION_REQUEST;
1062 String perm = checkPermissionAndRequest(request);
1063
1064 synchronized (mLock) {
1065 // Figure out the provider. Either its explicitly request (deprecated API's),
1066 // or use the fused provider
1067 String name = request.getProvider();
1068 if (name == null) name = LocationManager.FUSED_PROVIDER;
1069 LocationProviderInterface provider = mProvidersByName.get(name);
1070 if (provider == null) return null;
1071
1072 if (!isAllowedBySettingsLocked(name)) return null;
1073
1074 Location location = mLastLocation.get(name);
1075 if (ACCESS_FINE_LOCATION.equals(perm)) {
1076 return location;
1077 } else {
1078 return getCoarseLocationExtra(location);
1079 }
1080 }
1081 }
1082
1083 @Override
1084 public void requestGeofence(LocationRequest request, Geofence geofence, PendingIntent intent,
1085 String packageName) {
1086 if (request == null) request = DEFAULT_LOCATION_REQUEST;
1087 checkPermissionAndRequest(request);
1088 checkPendingIntent(intent);
1089 checkPackageName(packageName);
1090
1091 if (D) Log.d(TAG, "requestGeofence: " + request + " " + geofence + " " + intent);
1092
1093 mGeofenceManager.addFence(request, geofence, intent, Binder.getCallingUid(), packageName);
1094 }
1095
1096 @Override
1097 public void removeGeofence(Geofence geofence, PendingIntent intent, String packageName) {
1098 checkPermission();
1099 checkPendingIntent(intent);
1100 checkPackageName(packageName);
1101
1102 if (D) Log.d(TAG, "removeGeofence: " + geofence + " " + intent);
1103
1104 mGeofenceManager.removeFence(geofence, intent);
1105 }
1106
1107
1108 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001109 public boolean addGpsStatusListener(IGpsStatusListener listener) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001110 if (mGpsStatusProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001111 return false;
1112 }
Mike Lockwoodb7e99222009-07-07 13:18:21 -04001113 if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) !=
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001114 PackageManager.PERMISSION_GRANTED) {
1115 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
1116 }
1117
1118 try {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001119 mGpsStatusProvider.addGpsStatusListener(listener);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001120 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001121 Slog.e(TAG, "mGpsStatusProvider.addGpsStatusListener failed", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001122 return false;
1123 }
1124 return true;
1125 }
1126
Nick Pellye0fd6932012-07-11 10:26:13 -07001127 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001128 public void removeGpsStatusListener(IGpsStatusListener listener) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001129 synchronized (mLock) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001130 try {
1131 mGpsStatusProvider.removeGpsStatusListener(listener);
1132 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001133 Slog.e(TAG, "mGpsStatusProvider.removeGpsStatusListener failed", e);
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001134 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001135 }
1136 }
1137
Nick Pellye0fd6932012-07-11 10:26:13 -07001138 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001139 public boolean sendExtraCommand(String provider, String command, Bundle extras) {
Mike Lockwoodc6cc8362009-08-17 13:16:08 -04001140 if (provider == null) {
1141 // throw NullPointerException to remain compatible with previous implementation
1142 throw new NullPointerException();
1143 }
1144
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001145 checkPermission();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001146 // and check for ACCESS_LOCATION_EXTRA_COMMANDS
Mike Lockwoodb7e99222009-07-07 13:18:21 -04001147 if ((mContext.checkCallingOrSelfPermission(ACCESS_LOCATION_EXTRA_COMMANDS)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001148 != PackageManager.PERMISSION_GRANTED)) {
1149 throw new SecurityException("Requires ACCESS_LOCATION_EXTRA_COMMANDS permission");
1150 }
1151
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001152 synchronized (mLock) {
Mike Lockwoodd03ff942010-02-09 08:46:14 -05001153 LocationProviderInterface p = mProvidersByName.get(provider);
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001154 if (p == null) return false;
Nick Pellye0fd6932012-07-11 10:26:13 -07001155
Mike Lockwoodd03ff942010-02-09 08:46:14 -05001156 return p.sendExtraCommand(command, extras);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001157 }
1158 }
1159
Nick Pellye0fd6932012-07-11 10:26:13 -07001160 @Override
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001161 public boolean sendNiResponse(int notifId, int userResponse) {
Mike Lockwood18ad9f62009-08-27 14:01:23 -07001162 if (Binder.getCallingUid() != Process.myUid()) {
1163 throw new SecurityException(
1164 "calling sendNiResponse from outside of the system is not allowed");
1165 }
Danke Xie22d1f9f2009-08-18 18:28:45 -04001166 try {
1167 return mNetInitiatedListener.sendNiResponse(notifId, userResponse);
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001168 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001169 Slog.e(TAG, "RemoteException in LocationManagerService.sendNiResponse");
Danke Xie22d1f9f2009-08-18 18:28:45 -04001170 return false;
1171 }
1172 }
1173
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001174 /**
Mike Lockwood628fd6d2010-01-25 22:46:13 -05001175 * @return null if the provider does not exist
Alexey Tarasovf2db9fb2009-09-01 02:37:07 +11001176 * @throws SecurityException if the provider is not allowed to be
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001177 * accessed by the caller
1178 */
Nick Pellye0fd6932012-07-11 10:26:13 -07001179 @Override
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001180 public ProviderProperties getProviderProperties(String provider) {
1181 checkPermission();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001182
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001183 LocationProviderInterface p;
1184 synchronized (mLock) {
1185 p = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001186 }
1187
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001188 if (p == null) return null;
1189 return p.getProperties();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001190 }
1191
Nick Pellye0fd6932012-07-11 10:26:13 -07001192 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001193 public boolean isProviderEnabled(String provider) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001194 checkPermission();
1195 if (LocationManager.FUSED_PROVIDER.equals(provider)) return false;
1196
1197 synchronized (mLock) {
1198 LocationProviderInterface p = mProvidersByName.get(provider);
1199 if (p == null) return false;
1200
1201 return isAllowedBySettingsLocked(provider);
1202 }
1203 }
1204
1205 private void checkCallerIsProvider() {
1206 if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER)
1207 == PackageManager.PERMISSION_GRANTED) {
1208 return;
1209 }
1210
1211 // Previously we only used the INSTALL_LOCATION_PROVIDER
1212 // check. But that is system or signature
1213 // protection level which is not flexible enough for
1214 // providers installed oustide the system image. So
1215 // also allow providers with a UID matching the
1216 // currently bound package name
1217
1218 int uid = Binder.getCallingUid();
1219
1220 if (mGeocodeProvider != null) {
1221 if (doesPackageHaveUid(uid, mGeocodeProvider.getConnectedPackageName())) return;
1222 }
1223 for (LocationProviderProxy proxy : mProxyProviders) {
1224 if (doesPackageHaveUid(uid, proxy.getConnectedPackageName())) return;
1225 }
1226 throw new SecurityException("need INSTALL_LOCATION_PROVIDER permission, " +
1227 "or UID of a currently bound location provider");
1228 }
1229
1230 private boolean doesPackageHaveUid(int uid, String packageName) {
1231 if (packageName == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001232 return false;
1233 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001234 try {
1235 ApplicationInfo appInfo = mPackageManager.getApplicationInfo(packageName, 0);
1236 if (appInfo.uid != uid) {
1237 return false;
1238 }
1239 } catch (NameNotFoundException e) {
1240 return false;
1241 }
1242 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001243 }
1244
Nick Pellye0fd6932012-07-11 10:26:13 -07001245 @Override
Mike Lockwooda4903f252010-02-17 06:42:23 -05001246 public void reportLocation(Location location, boolean passive) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001247 checkCallerIsProvider();
Mike Lockwood275555c2009-05-01 11:30:34 -04001248
Nick Pelly2eeeec22012-07-18 13:13:37 -07001249 if (!location.isComplete()) {
1250 Log.w(TAG, "Dropping incomplete location: " + location);
1251 return;
1252 }
1253
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001254 mLocationHandler.removeMessages(MSG_LOCATION_CHANGED, location);
1255 Message m = Message.obtain(mLocationHandler, MSG_LOCATION_CHANGED, location);
Mike Lockwooda4903f252010-02-17 06:42:23 -05001256 m.arg1 = (passive ? 1 : 0);
Mike Lockwood4e50b782009-04-03 08:24:43 -07001257 mLocationHandler.sendMessageAtFrontOfQueue(m);
1258 }
1259
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001260
1261 private static boolean shouldBroadcastSafe(Location loc, Location lastLoc, UpdateRecord record) {
1262 // Always broadcast the first update
1263 if (lastLoc == null) {
1264 return true;
1265 }
1266
Nick Pellyf1be6862012-05-15 10:53:42 -07001267 // Check whether sufficient time has passed
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001268 long minTime = record.mRequest.getFastestInterval();
Nick Pelly2eeeec22012-07-18 13:13:37 -07001269 long delta = (loc.getElapsedRealtimeNano() - lastLoc.getElapsedRealtimeNano()) / 1000000L;
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001270 if (delta < minTime - MAX_PROVIDER_SCHEDULING_JITTER_MS) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001271 return false;
1272 }
1273
1274 // Check whether sufficient distance has been traveled
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001275 double minDistance = record.mRequest.getSmallestDisplacement();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001276 if (minDistance > 0.0) {
1277 if (loc.distanceTo(lastLoc) <= minDistance) {
1278 return false;
1279 }
1280 }
1281
1282 return true;
1283 }
1284
Mike Lockwooda4903f252010-02-17 06:42:23 -05001285 private void handleLocationChangedLocked(Location location, boolean passive) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001286 long now = SystemClock.elapsedRealtime();
Mike Lockwooda4903f252010-02-17 06:42:23 -05001287 String provider = (passive ? LocationManager.PASSIVE_PROVIDER : location.getProvider());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001288 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001289 if (records == null || records.size() == 0) return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001290
Mike Lockwoodd03ff942010-02-09 08:46:14 -05001291 LocationProviderInterface p = mProvidersByName.get(provider);
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001292 if (p == null) return;
1293
1294 // Add the coarse location as an extra, if not already present
1295 Location coarse = getCoarseLocationExtra(location);
1296 if (coarse == null) {
1297 coarse = addCoarseLocationExtra(location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001298 }
1299
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001300 // Update last known locations
1301 Location lastLocation = mLastLocation.get(provider);
Mike Lockwood4e50b782009-04-03 08:24:43 -07001302 if (lastLocation == null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001303 lastLocation = new Location(provider);
1304 mLastLocation.put(provider, lastLocation);
Mike Lockwood4e50b782009-04-03 08:24:43 -07001305 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001306 lastLocation.set(location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001307
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001308 // Fetch latest status update time
1309 long newStatusUpdateTime = p.getStatusUpdateTime();
1310
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001311 // Get latest status
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001312 Bundle extras = new Bundle();
1313 int status = p.getStatus(extras);
1314
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001315 ArrayList<Receiver> deadReceivers = null;
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001316 ArrayList<UpdateRecord> deadUpdateRecords = null;
Nick Pellye0fd6932012-07-11 10:26:13 -07001317
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001318 // Broadcast location or status to all listeners
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001319 for (UpdateRecord r : records) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001320 Receiver receiver = r.mReceiver;
Mike Lockwood03ca2162010-04-01 08:10:09 -07001321 boolean receiverDead = false;
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001322 if (ACCESS_FINE_LOCATION.equals(receiver.mPermission)) {
1323 location = lastLocation; // use fine location
1324 } else {
1325 location = coarse; // use coarse location
1326 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001327
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001328 Location lastLoc = r.mLastFixBroadcast;
Mike Lockwood4e50b782009-04-03 08:24:43 -07001329 if ((lastLoc == null) || shouldBroadcastSafe(location, lastLoc, r)) {
1330 if (lastLoc == null) {
1331 lastLoc = new Location(location);
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001332 r.mLastFixBroadcast = lastLoc;
Mike Lockwood4e50b782009-04-03 08:24:43 -07001333 } else {
1334 lastLoc.set(location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001335 }
Mike Lockwood4e50b782009-04-03 08:24:43 -07001336 if (!receiver.callLocationChangedLocked(location)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001337 Slog.w(TAG, "RemoteException calling onLocationChanged on " + receiver);
Mike Lockwood03ca2162010-04-01 08:10:09 -07001338 receiverDead = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001339 }
1340 }
1341
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001342 long prevStatusUpdateTime = r.mLastStatusBroadcast;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001343 if ((newStatusUpdateTime > prevStatusUpdateTime) &&
1344 (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) {
1345
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001346 r.mLastStatusBroadcast = newStatusUpdateTime;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001347 if (!receiver.callStatusChangedLocked(provider, status, extras)) {
Mike Lockwood03ca2162010-04-01 08:10:09 -07001348 receiverDead = true;
Joe Onorato8a9b2202010-02-26 18:56:32 -08001349 Slog.w(TAG, "RemoteException calling onStatusChanged on " + receiver);
Mike Lockwood03ca2162010-04-01 08:10:09 -07001350 }
1351 }
1352
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001353 // track expired records
1354 if (r.mRequest.getNumUpdates() == 0 || r.mRequest.getExpireAt() < now) {
1355 if (deadUpdateRecords == null) {
1356 deadUpdateRecords = new ArrayList<UpdateRecord>();
1357 }
1358 deadUpdateRecords.add(r);
1359 }
1360 // track dead receivers
1361 if (receiverDead) {
Mike Lockwood03ca2162010-04-01 08:10:09 -07001362 if (deadReceivers == null) {
1363 deadReceivers = new ArrayList<Receiver>();
1364 }
1365 if (!deadReceivers.contains(receiver)) {
1366 deadReceivers.add(receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001367 }
1368 }
1369 }
Nick Pellye0fd6932012-07-11 10:26:13 -07001370
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001371 // remove dead records and receivers outside the loop
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001372 if (deadReceivers != null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001373 for (Receiver receiver : deadReceivers) {
1374 removeUpdatesLocked(receiver);
1375 }
1376 }
1377 if (deadUpdateRecords != null) {
1378 for (UpdateRecord r : deadUpdateRecords) {
1379 r.disposeLocked(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001380 }
1381 }
1382 }
1383
1384 private class LocationWorkerHandler extends Handler {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001385 @Override
1386 public void handleMessage(Message msg) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001387 switch (msg.what) {
1388 case MSG_LOCATION_CHANGED:
1389 handleLocationChanged((Location) msg.obj, msg.arg1 == 1);
1390 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001391 }
1392 }
1393 }
1394
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001395 private void handleLocationChanged(Location location, boolean passive) {
1396 String provider = location.getProvider();
Jeff Sharkey5e613312012-01-30 11:16:20 -08001397
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001398 if (!passive) {
1399 // notify passive provider of the new location
1400 mPassiveProvider.updateLocation(location);
1401 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001402
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001403 synchronized (mLock) {
1404 if (isAllowedBySettingsLocked(provider)) {
1405 handleLocationChangedLocked(location, passive);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001406 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001407 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001408 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001409
Mike Lockwoode97ae402010-09-29 15:23:46 -04001410 private final PackageMonitor mPackageMonitor = new PackageMonitor() {
1411 @Override
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001412 public void onPackageDisappeared(String packageName, int reason) {
1413 // remove all receivers associated with this package name
1414 synchronized (mLock) {
1415 ArrayList<Receiver> deadReceivers = null;
1416
1417 for (Receiver receiver : mReceivers.values()) {
1418 if (receiver.mPackageName.equals(packageName)) {
1419 if (deadReceivers == null) {
1420 deadReceivers = new ArrayList<Receiver>();
1421 }
1422 deadReceivers.add(receiver);
1423 }
1424 }
1425
1426 // perform removal outside of mReceivers loop
1427 if (deadReceivers != null) {
1428 for (Receiver receiver : deadReceivers) {
1429 removeUpdatesLocked(receiver);
1430 }
1431 }
1432 }
Nick Pellye0fd6932012-07-11 10:26:13 -07001433 }
Mike Lockwoode97ae402010-09-29 15:23:46 -04001434 };
1435
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001436 // Wake locks
1437
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001438 private void incrementPendingBroadcasts() {
1439 synchronized (mWakeLock) {
1440 if (mPendingBroadcasts++ == 0) {
1441 try {
1442 mWakeLock.acquire();
1443 log("Acquired wakelock");
1444 } catch (Exception e) {
1445 // This is to catch a runtime exception thrown when we try to release an
1446 // already released lock.
Joe Onorato8a9b2202010-02-26 18:56:32 -08001447 Slog.e(TAG, "exception in acquireWakeLock()", e);
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001448 }
1449 }
Mike Lockwood48f17512009-04-23 09:12:08 -07001450 }
1451 }
1452
1453 private void decrementPendingBroadcasts() {
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001454 synchronized (mWakeLock) {
Mike Lockwood48f17512009-04-23 09:12:08 -07001455 if (--mPendingBroadcasts == 0) {
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001456 try {
1457 // Release wake lock
1458 if (mWakeLock.isHeld()) {
1459 mWakeLock.release();
1460 log("Released wakelock");
1461 } else {
1462 log("Can't release wakelock again!");
1463 }
1464 } catch (Exception e) {
1465 // This is to catch a runtime exception thrown when we try to release an
1466 // already released lock.
Joe Onorato8a9b2202010-02-26 18:56:32 -08001467 Slog.e(TAG, "exception in releaseWakeLock()", e);
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001468 }
Mike Lockwood48f17512009-04-23 09:12:08 -07001469 }
1470 }
1471 }
1472
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001473 // Geocoder
1474
Nick Pellye0fd6932012-07-11 10:26:13 -07001475 @Override
Mike Lockwoode15735a2010-09-20 17:48:47 -04001476 public boolean geocoderIsPresent() {
Mark Vandevoorde01ac80b2010-05-21 15:43:26 -07001477 return mGeocodeProvider != null;
1478 }
1479
Nick Pellye0fd6932012-07-11 10:26:13 -07001480 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001481 public String getFromLocation(double latitude, double longitude, int maxResults,
Mike Lockwood34901402010-01-04 12:14:21 -05001482 GeocoderParams params, List<Address> addrs) {
Mike Lockwooda55c3212009-04-15 11:10:11 -04001483 if (mGeocodeProvider != null) {
Mike Lockwood628fd6d2010-01-25 22:46:13 -05001484 return mGeocodeProvider.getFromLocation(latitude, longitude, maxResults,
1485 params, addrs);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001486 }
Mike Lockwooda55c3212009-04-15 11:10:11 -04001487 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001488 }
1489
Mike Lockwooda55c3212009-04-15 11:10:11 -04001490
Nick Pellye0fd6932012-07-11 10:26:13 -07001491 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001492 public String getFromLocationName(String locationName,
Mike Lockwooda55c3212009-04-15 11:10:11 -04001493 double lowerLeftLatitude, double lowerLeftLongitude,
1494 double upperRightLatitude, double upperRightLongitude, int maxResults,
Mike Lockwood34901402010-01-04 12:14:21 -05001495 GeocoderParams params, List<Address> addrs) {
Mike Lockwooda55c3212009-04-15 11:10:11 -04001496
1497 if (mGeocodeProvider != null) {
Mike Lockwood628fd6d2010-01-25 22:46:13 -05001498 return mGeocodeProvider.getFromLocationName(locationName, lowerLeftLatitude,
1499 lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
1500 maxResults, params, addrs);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001501 }
Mike Lockwooda55c3212009-04-15 11:10:11 -04001502 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001503 }
1504
1505 // Mock Providers
1506
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001507 private void checkMockPermissionsSafe() {
1508 boolean allowMocks = Settings.Secure.getInt(mContext.getContentResolver(),
1509 Settings.Secure.ALLOW_MOCK_LOCATION, 0) == 1;
1510 if (!allowMocks) {
1511 throw new SecurityException("Requires ACCESS_MOCK_LOCATION secure setting");
1512 }
1513
1514 if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) !=
1515 PackageManager.PERMISSION_GRANTED) {
1516 throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission");
Nick Pellye0fd6932012-07-11 10:26:13 -07001517 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001518 }
1519
Nick Pellye0fd6932012-07-11 10:26:13 -07001520 @Override
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001521 public void addTestProvider(String name, ProviderProperties properties) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001522 checkMockPermissionsSafe();
1523
Mike Lockwooda4903f252010-02-17 06:42:23 -05001524 if (LocationManager.PASSIVE_PROVIDER.equals(name)) {
1525 throw new IllegalArgumentException("Cannot mock the passive location provider");
1526 }
1527
Mike Lockwood86328a92009-10-23 08:38:25 -04001528 long identity = Binder.clearCallingIdentity();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001529 synchronized (mLock) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001530 MockProvider provider = new MockProvider(name, this, properties);
Mike Lockwood7566c1d2009-08-25 10:05:18 -07001531 // remove the real provider if we are replacing GPS or network provider
1532 if (LocationManager.GPS_PROVIDER.equals(name)
1533 || LocationManager.NETWORK_PROVIDER.equals(name)) {
Mike Lockwoodd03ff942010-02-09 08:46:14 -05001534 LocationProviderInterface p = mProvidersByName.get(name);
1535 if (p != null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001536 removeProviderLocked(p);
Mike Lockwood7566c1d2009-08-25 10:05:18 -07001537 }
1538 }
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001539 if (mProvidersByName.get(name) != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001540 throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
1541 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001542 addProviderLocked(provider);
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001543 mMockProviders.put(name, provider);
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001544 mLastLocation.put(name, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001545 updateProvidersLocked();
1546 }
Mike Lockwood86328a92009-10-23 08:38:25 -04001547 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001548 }
1549
Nick Pellye0fd6932012-07-11 10:26:13 -07001550 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001551 public void removeTestProvider(String provider) {
1552 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001553 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001554 MockProvider mockProvider = mMockProviders.get(provider);
1555 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001556 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1557 }
Mike Lockwood86328a92009-10-23 08:38:25 -04001558 long identity = Binder.clearCallingIdentity();
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001559 removeProviderLocked(mProvidersByName.get(provider));
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001560 mMockProviders.remove(mockProvider);
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001561
1562 // reinstate real provider if available
1563 LocationProviderInterface realProvider = mRealProviders.get(provider);
1564 if (realProvider != null) {
1565 addProviderLocked(realProvider);
Mike Lockwood7566c1d2009-08-25 10:05:18 -07001566 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001567 mLastLocation.put(provider, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001568 updateProvidersLocked();
Mike Lockwood86328a92009-10-23 08:38:25 -04001569 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001570 }
1571 }
1572
Nick Pellye0fd6932012-07-11 10:26:13 -07001573 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001574 public void setTestProviderLocation(String provider, Location loc) {
1575 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001576 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001577 MockProvider mockProvider = mMockProviders.get(provider);
1578 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001579 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1580 }
Mike Lockwood95427cd2009-05-07 13:27:54 -04001581 // clear calling identity so INSTALL_LOCATION_PROVIDER permission is not required
1582 long identity = Binder.clearCallingIdentity();
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001583 mockProvider.setLocation(loc);
Mike Lockwood95427cd2009-05-07 13:27:54 -04001584 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001585 }
1586 }
1587
Nick Pellye0fd6932012-07-11 10:26:13 -07001588 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001589 public void clearTestProviderLocation(String provider) {
1590 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001591 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001592 MockProvider mockProvider = mMockProviders.get(provider);
1593 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001594 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1595 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001596 mockProvider.clearLocation();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001597 }
1598 }
1599
Nick Pellye0fd6932012-07-11 10:26:13 -07001600 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001601 public void setTestProviderEnabled(String provider, boolean enabled) {
1602 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001603 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001604 MockProvider mockProvider = mMockProviders.get(provider);
1605 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001606 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1607 }
Mike Lockwood86328a92009-10-23 08:38:25 -04001608 long identity = Binder.clearCallingIdentity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001609 if (enabled) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001610 mockProvider.enable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001611 mEnabledProviders.add(provider);
1612 mDisabledProviders.remove(provider);
1613 } else {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001614 mockProvider.disable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001615 mEnabledProviders.remove(provider);
1616 mDisabledProviders.add(provider);
1617 }
1618 updateProvidersLocked();
Mike Lockwood86328a92009-10-23 08:38:25 -04001619 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001620 }
1621 }
1622
Nick Pellye0fd6932012-07-11 10:26:13 -07001623 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001624 public void clearTestProviderEnabled(String provider) {
1625 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001626 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001627 MockProvider mockProvider = mMockProviders.get(provider);
1628 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001629 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1630 }
Mike Lockwood86328a92009-10-23 08:38:25 -04001631 long identity = Binder.clearCallingIdentity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001632 mEnabledProviders.remove(provider);
1633 mDisabledProviders.remove(provider);
1634 updateProvidersLocked();
Mike Lockwood86328a92009-10-23 08:38:25 -04001635 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001636 }
1637 }
1638
Nick Pellye0fd6932012-07-11 10:26:13 -07001639 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001640 public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
1641 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001642 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001643 MockProvider mockProvider = mMockProviders.get(provider);
1644 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001645 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1646 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001647 mockProvider.setStatus(status, extras, updateTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001648 }
1649 }
1650
Nick Pellye0fd6932012-07-11 10:26:13 -07001651 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001652 public void clearTestProviderStatus(String provider) {
1653 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001654 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001655 MockProvider mockProvider = mMockProviders.get(provider);
1656 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001657 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1658 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001659 mockProvider.clearStatus();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001660 }
1661 }
1662
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001663 private static double wrapLatitude(double lat) {
1664 if (lat > MAX_LATITUDE) lat = MAX_LATITUDE;
1665 if (lat < -MAX_LATITUDE) lat = -MAX_LATITUDE;
1666 return lat;
1667 }
1668
1669 private static double wrapLongitude(double lon) {
1670 if (lon >= 180.0) lon -= 360.0;
1671 if (lon < -180.0) lon += 360.0;
1672 return lon;
1673 }
1674
1675 private static double distanceToDegreesLatitude(double distance) {
1676 return distance / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR;
1677 }
1678
1679 /**
1680 * Requires latitude since longitudinal distances change with distance from equator.
1681 */
1682 private static double distanceToDegreesLongitude(double distance, double lat) {
1683 return distance / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR / Math.cos(lat);
1684 }
1685
1686 /**
1687 * Fudge a location into a coarse location.
1688 * <p>Add a random offset, then quantize the result (snap-to-grid).
1689 * Random offsets alone can be low-passed pretty easily.
1690 * Snap-to-grid on its own is excellent unless you are sitting on a
1691 * grid boundary and bouncing between quantizations.
1692 * The combination is quite hard to reverse engineer.
1693 * <p>The random offset used is smaller than the goal accuracy
1694 * ({@link #COARSE_ACCURACY_M}), in order to give relatively stable
1695 * results after quantization.
1696 */
1697 private static Location createCoarse(Location fine) {
1698 Location coarse = new Location(fine);
1699
1700 coarse.removeBearing();
1701 coarse.removeSpeed();
1702 coarse.removeAltitude();
1703
1704 double lat = coarse.getLatitude();
1705 double lon = coarse.getLongitude();
1706
1707 // wrap
1708 lat = wrapLatitude(lat);
1709 lon = wrapLongitude(lon);
1710
1711 if (coarse.getAccuracy() < COARSE_ACCURACY_M / 2) {
1712 // apply a random offset
1713 double fudgeDistance = COARSE_ACCURACY_M / 2.0 - coarse.getAccuracy();
1714 lat += (Math.random() - 0.5) * distanceToDegreesLatitude(fudgeDistance);
1715 lon += (Math.random() - 0.5) * distanceToDegreesLongitude(fudgeDistance, lat);
1716 }
1717
1718 // wrap
1719 lat = wrapLatitude(lat);
1720 lon = wrapLongitude(lon);
1721
1722 // quantize (snap-to-grid)
1723 double latGranularity = distanceToDegreesLatitude(COARSE_ACCURACY_M);
1724 double lonGranularity = distanceToDegreesLongitude(COARSE_ACCURACY_M, lat);
1725 long latQuantized = Math.round(lat / latGranularity);
1726 long lonQuantized = Math.round(lon / lonGranularity);
1727 lat = latQuantized * latGranularity;
1728 lon = lonQuantized * lonGranularity;
1729
1730 // wrap again
1731 lat = wrapLatitude(lat);
1732 lon = wrapLongitude(lon);
1733
1734 // apply
1735 coarse.setLatitude(lat);
1736 coarse.setLongitude(lon);
1737 coarse.setAccuracy((float)COARSE_ACCURACY_M);
1738
1739 return coarse;
1740 }
1741
1742
1743 private static Location getCoarseLocationExtra(Location location) {
1744 Bundle extras = location.getExtras();
1745 if (extras == null) return null;
1746 Parcelable parcel = extras.getParcelable(EXTRA_COARSE_LOCATION);
1747 if (parcel == null) return null;
1748 if (!(parcel instanceof Location)) return null;
1749 Location coarse = (Location) parcel;
1750 if (coarse.getAccuracy() < COARSE_ACCURACY_M) return null;
1751 return coarse;
1752 }
1753
1754 private static Location addCoarseLocationExtra(Location location) {
1755 Bundle extras = location.getExtras();
1756 if (extras == null) extras = new Bundle();
1757 Location coarse = createCoarse(location);
1758 extras.putParcelable(EXTRA_COARSE_LOCATION, coarse);
1759 location.setExtras(extras);
1760 return coarse;
1761 }
1762
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001763 private void log(String log) {
1764 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001765 Slog.d(TAG, log);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001766 }
1767 }
Nick Pellye0fd6932012-07-11 10:26:13 -07001768
1769 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001770 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1771 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1772 != PackageManager.PERMISSION_GRANTED) {
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001773 pw.println("Permission Denial: can't dump LocationManagerService from from pid="
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001774 + Binder.getCallingPid()
1775 + ", uid=" + Binder.getCallingUid());
1776 return;
1777 }
Nick Pellye0fd6932012-07-11 10:26:13 -07001778
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001779 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001780 pw.println("Current Location Manager state:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001781 pw.println(" Location Listeners:");
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001782 for (Receiver receiver : mReceivers.values()) {
1783 pw.println(" " + receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001784 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001785 pw.println(" Records by Provider:");
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001786 for (Map.Entry<String, ArrayList<UpdateRecord>> entry : mRecordsByProvider.entrySet()) {
1787 pw.println(" " + entry.getKey() + ":");
1788 for (UpdateRecord record : entry.getValue()) {
1789 pw.println(" " + record);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001790 }
1791 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001792 pw.println(" Last Known Locations:");
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001793 for (Map.Entry<String, Location> entry : mLastLocation.entrySet()) {
1794 String provider = entry.getKey();
1795 Location location = entry.getValue();
1796 pw.println(" " + provider + ": " + location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001797 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001798
Nick Pellye0fd6932012-07-11 10:26:13 -07001799 mGeofenceManager.dump(pw);
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001800
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001801 if (mEnabledProviders.size() > 0) {
1802 pw.println(" Enabled Providers:");
1803 for (String i : mEnabledProviders) {
1804 pw.println(" " + i);
1805 }
Nick Pellye0fd6932012-07-11 10:26:13 -07001806
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001807 }
1808 if (mDisabledProviders.size() > 0) {
1809 pw.println(" Disabled Providers:");
1810 for (String i : mDisabledProviders) {
1811 pw.println(" " + i);
1812 }
Nick Pellye0fd6932012-07-11 10:26:13 -07001813
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001814 }
1815 if (mMockProviders.size() > 0) {
1816 pw.println(" Mock Providers:");
1817 for (Map.Entry<String, MockProvider> i : mMockProviders.entrySet()) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001818 i.getValue().dump(pw, " ");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001819 }
1820 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001821
1822 if (args.length > 0 && "short".equals(args[0])) {
1823 return;
1824 }
Fred Fettinger3c8fbdf2010-01-04 15:38:13 -06001825 for (LocationProviderInterface provider: mProviders) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001826 pw.print(provider.getName() + " Internal State");
1827 if (provider instanceof LocationProviderProxy) {
1828 LocationProviderProxy proxy = (LocationProviderProxy) provider;
1829 pw.print(" (" + proxy.getConnectedPackageName() + ")");
Fred Fettinger3c8fbdf2010-01-04 15:38:13 -06001830 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001831 pw.println(":");
1832 provider.dump(fd, pw, args);
Fred Fettinger3c8fbdf2010-01-04 15:38:13 -06001833 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001834 }
1835 }
1836}