blob: a6c3860884785d38611c86db0b901fcec335d928 [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
790 if (providerRequest.locationRequests == null) {
791 providerRequest.locationRequests = new ArrayList<LocationRequest>();
792 }
793
794 providerRequest.locationRequests.add(locationRequest);
795 if (locationRequest.getInterval() < providerRequest.interval) {
796 providerRequest.reportLocation = true;
797 providerRequest.interval = locationRequest.getInterval();
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700798 }
799 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700800
801 if (providerRequest.reportLocation) {
802 // calculate who to blame for power
803 // This is somewhat arbitrary. We pick a threshold interval
804 // that is slightly higher that the minimum interval, and
805 // spread the blame across all applications with a request
806 // under that threshold.
807 long thresholdInterval = (providerRequest.interval + 1000) * 3 / 2;
808 for (UpdateRecord record : records) {
809 LocationRequest locationRequest = record.mRequest;
810 if (locationRequest.getInterval() <= thresholdInterval) {
811 worksource.add(record.mReceiver.mUid);
812 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700813 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800814 }
815 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700816
817 if (D) Log.d(TAG, "provider request: " + provider + " " + providerRequest);
818 p.setRequest(providerRequest, worksource);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800819 }
820
821 private class UpdateRecord {
822 final String mProvider;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700823 final LocationRequest mRequest;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800824 final Receiver mReceiver;
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400825 Location mLastFixBroadcast;
826 long mLastStatusBroadcast;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800827
828 /**
829 * Note: must be constructed with lock held.
830 */
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700831 UpdateRecord(String provider, LocationRequest request, Receiver receiver) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800832 mProvider = provider;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700833 mRequest = request;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800834 mReceiver = receiver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800835
836 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
837 if (records == null) {
838 records = new ArrayList<UpdateRecord>();
839 mRecordsByProvider.put(provider, records);
840 }
841 if (!records.contains(this)) {
842 records.add(this);
843 }
844 }
845
846 /**
847 * Method to be called when a record will no longer be used. Calling this multiple times
848 * must have the same effect as calling it once.
849 */
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700850 void disposeLocked(boolean removeReceiver) {
851 // remove from mRecordsByProvider
852 ArrayList<UpdateRecord> globalRecords = mRecordsByProvider.get(this.mProvider);
853 if (globalRecords != null) {
854 globalRecords.remove(this);
855 }
856
857 if (!removeReceiver) return; // the caller will handle the rest
858
859 // remove from Receiver#mUpdateRecords
860 HashMap<String, UpdateRecord> receiverRecords = mReceiver.mUpdateRecords;
861 if (receiverRecords != null) {
862 receiverRecords.remove(this.mProvider);
863
864 // and also remove the Receiver if it has no more update records
865 if (removeReceiver && receiverRecords.size() == 0) {
866 removeUpdatesLocked(mReceiver);
867 }
Mike Lockwood3a76fd62009-09-01 07:26:56 -0400868 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800869 }
870
871 @Override
872 public String toString() {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700873 StringBuilder s = new StringBuilder();
874 s.append("UpdateRecord[");
875 s.append(mProvider);
876 s.append(' ').append(mReceiver.mPackageName).append('(');
877 s.append(mReceiver.mUid).append(')');
878 s.append(' ').append(mRequest);
879 s.append(']');
880 return s.toString();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800881 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800882 }
883
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700884 private Receiver getReceiver(ILocationListener listener, int pid, int uid, String packageName) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400885 IBinder binder = listener.asBinder();
886 Receiver receiver = mReceivers.get(binder);
887 if (receiver == null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700888 receiver = new Receiver(listener, null, pid, uid, packageName);
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400889 mReceivers.put(binder, receiver);
890
891 try {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700892 receiver.getListener().asBinder().linkToDeath(receiver, 0);
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400893 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800894 Slog.e(TAG, "linkToDeath failed:", e);
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400895 return null;
896 }
897 }
898 return receiver;
899 }
900
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700901 private Receiver getReceiver(PendingIntent intent, int pid, int uid, String packageName) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400902 Receiver receiver = mReceivers.get(intent);
903 if (receiver == null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700904 receiver = new Receiver(null, intent, pid, uid, packageName);
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400905 mReceivers.put(intent, receiver);
906 }
907 return receiver;
908 }
909
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700910 private String checkPermissionAndRequest(LocationRequest request) {
911 String perm = checkPermission();
912
913 if (ACCESS_COARSE_LOCATION.equals(perm)) {
914 request.applyCoarsePermissionRestrictions();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400915 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700916 return perm;
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400917 }
918
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700919 private void checkPackageName(String packageName) {
Nick Pellye0fd6932012-07-11 10:26:13 -0700920 if (packageName == null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700921 throw new SecurityException("invalid package name: " + packageName);
Nick Pellye0fd6932012-07-11 10:26:13 -0700922 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700923 int uid = Binder.getCallingUid();
Nick Pellye0fd6932012-07-11 10:26:13 -0700924 String[] packages = mPackageManager.getPackagesForUid(uid);
925 if (packages == null) {
926 throw new SecurityException("invalid UID " + uid);
927 }
928 for (String pkg : packages) {
929 if (packageName.equals(pkg)) return;
930 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700931 throw new SecurityException("invalid package name: " + packageName);
Nick Pellye0fd6932012-07-11 10:26:13 -0700932 }
933
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700934 private void checkPendingIntent(PendingIntent intent) {
935 if (intent == null) {
936 throw new IllegalArgumentException("invalid pending intent: " + intent);
Dianne Hackborn6c418d52011-06-29 14:05:33 -0700937 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700938 }
939
940 private Receiver checkListenerOrIntent(ILocationListener listener, PendingIntent intent,
941 int pid, int uid, String packageName) {
942 if (intent == null && listener == null) {
943 throw new IllegalArgumentException("need eiter listener or intent");
944 } else if (intent != null && listener != null) {
945 throw new IllegalArgumentException("cannot register both listener and intent");
946 } else if (intent != null) {
947 checkPendingIntent(intent);
948 return getReceiver(intent, pid, uid, packageName);
949 } else {
950 return getReceiver(listener, pid, uid, packageName);
951 }
Dianne Hackborn6c418d52011-06-29 14:05:33 -0700952 }
953
Nick Pellye0fd6932012-07-11 10:26:13 -0700954 @Override
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700955 public void requestLocationUpdates(LocationRequest request, ILocationListener listener,
956 PendingIntent intent, String packageName) {
957 if (request == null) request = DEFAULT_LOCATION_REQUEST;
958 checkPackageName(packageName);
959 checkPermissionAndRequest(request);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800960
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700961 final int pid = Binder.getCallingPid();
962 final int uid = Binder.getCallingUid();
963 Receiver recevier = checkListenerOrIntent(listener, intent, pid, uid, packageName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800964
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700965 // so wakelock calls will succeed (not totally sure this is still needed)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800966 long identity = Binder.clearCallingIdentity();
967 try {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700968 synchronized (mLock) {
969 requestLocationUpdatesLocked(request, recevier, pid, uid, packageName);
Mike Lockwood2d4d1bf2010-10-18 17:06:26 -0400970 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800971 } finally {
972 Binder.restoreCallingIdentity(identity);
973 }
974 }
975
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700976 private void requestLocationUpdatesLocked(LocationRequest request, Receiver receiver,
977 int pid, int uid, String packageName) {
978 // Figure out the provider. Either its explicitly request (legacy use cases), or
979 // use the fused provider
980 if (request == null) request = DEFAULT_LOCATION_REQUEST;
981 String name = request.getProvider();
982 if (name == null) name = LocationManager.FUSED_PROVIDER;
983 LocationProviderInterface provider = mProvidersByName.get(name);
984 if (provider == null) {
985 throw new IllegalArgumentException("provider doesn't exisit: " + provider);
986 }
987
988 Log.i(TAG, "request " + Integer.toHexString(System.identityHashCode(receiver)) + " " +
989 name + " " + request + " from " + packageName + "(" + uid + ")");
990
991 UpdateRecord record = new UpdateRecord(name, request, receiver);
992 UpdateRecord oldRecord = receiver.mUpdateRecords.put(name, record);
993 if (oldRecord != null) {
994 oldRecord.disposeLocked(false);
995 }
996
997 boolean isProviderEnabled = isAllowedBySettingsLocked(name);
998 if (isProviderEnabled) {
999 applyRequirementsLocked(name);
1000 } else {
1001 // Notify the listener that updates are currently disabled
1002 receiver.callProviderEnabledLocked(name, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001003 }
1004 }
1005
Nick Pellye0fd6932012-07-11 10:26:13 -07001006 @Override
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001007 public void removeUpdates(ILocationListener listener, PendingIntent intent,
1008 String packageName) {
1009 checkPackageName(packageName);
1010 checkPermission();
1011 final int pid = Binder.getCallingPid();
1012 final int uid = Binder.getCallingUid();
1013 Receiver receiver = checkListenerOrIntent(listener, intent, pid, uid, packageName);
1014
1015 // so wakelock calls will succeed (not totally sure this is still needed)
1016 long identity = Binder.clearCallingIdentity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001017 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001018 synchronized (mLock) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001019 removeUpdatesLocked(receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001020 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001021 } finally {
1022 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001023 }
1024 }
1025
1026 private void removeUpdatesLocked(Receiver receiver) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001027 Log.i(TAG, "remove " + Integer.toHexString(System.identityHashCode(receiver)));
1028
1029 if (mReceivers.remove(receiver.mKey) != null && receiver.isListener()) {
1030 receiver.getListener().asBinder().unlinkToDeath(receiver, 0);
1031 synchronized (receiver) {
1032 if (receiver.mPendingBroadcasts > 0) {
1033 decrementPendingBroadcasts();
1034 receiver.mPendingBroadcasts = 0;
1035 }
1036 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001037 }
1038
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001039 // Record which providers were associated with this listener
1040 HashSet<String> providers = new HashSet<String>();
1041 HashMap<String, UpdateRecord> oldRecords = receiver.mUpdateRecords;
1042 if (oldRecords != null) {
1043 // Call dispose() on the obsolete update records.
1044 for (UpdateRecord record : oldRecords.values()) {
1045 record.disposeLocked(false);
1046 }
1047 // Accumulate providers
1048 providers.addAll(oldRecords.keySet());
1049 }
1050
1051 // update provider
1052 for (String provider : providers) {
1053 // If provider is already disabled, don't need to do anything
1054 if (!isAllowedBySettingsLocked(provider)) {
1055 continue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001056 }
1057
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001058 applyRequirementsLocked(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001059 }
1060 }
1061
Nick Pellye0fd6932012-07-11 10:26:13 -07001062 @Override
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001063 public Location getLastLocation(LocationRequest request) {
1064 if (D) Log.d(TAG, "getLastLocation: " + request);
1065 if (request == null) request = DEFAULT_LOCATION_REQUEST;
1066 String perm = checkPermissionAndRequest(request);
1067
1068 synchronized (mLock) {
1069 // Figure out the provider. Either its explicitly request (deprecated API's),
1070 // or use the fused provider
1071 String name = request.getProvider();
1072 if (name == null) name = LocationManager.FUSED_PROVIDER;
1073 LocationProviderInterface provider = mProvidersByName.get(name);
1074 if (provider == null) return null;
1075
1076 if (!isAllowedBySettingsLocked(name)) return null;
1077
1078 Location location = mLastLocation.get(name);
1079 if (ACCESS_FINE_LOCATION.equals(perm)) {
1080 return location;
1081 } else {
1082 return getCoarseLocationExtra(location);
1083 }
1084 }
1085 }
1086
1087 @Override
1088 public void requestGeofence(LocationRequest request, Geofence geofence, PendingIntent intent,
1089 String packageName) {
1090 if (request == null) request = DEFAULT_LOCATION_REQUEST;
1091 checkPermissionAndRequest(request);
1092 checkPendingIntent(intent);
1093 checkPackageName(packageName);
1094
1095 if (D) Log.d(TAG, "requestGeofence: " + request + " " + geofence + " " + intent);
1096
1097 mGeofenceManager.addFence(request, geofence, intent, Binder.getCallingUid(), packageName);
1098 }
1099
1100 @Override
1101 public void removeGeofence(Geofence geofence, PendingIntent intent, String packageName) {
1102 checkPermission();
1103 checkPendingIntent(intent);
1104 checkPackageName(packageName);
1105
1106 if (D) Log.d(TAG, "removeGeofence: " + geofence + " " + intent);
1107
1108 mGeofenceManager.removeFence(geofence, intent);
1109 }
1110
1111
1112 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001113 public boolean addGpsStatusListener(IGpsStatusListener listener) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001114 if (mGpsStatusProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001115 return false;
1116 }
Mike Lockwoodb7e99222009-07-07 13:18:21 -04001117 if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) !=
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001118 PackageManager.PERMISSION_GRANTED) {
1119 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
1120 }
1121
1122 try {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001123 mGpsStatusProvider.addGpsStatusListener(listener);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001124 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001125 Slog.e(TAG, "mGpsStatusProvider.addGpsStatusListener failed", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001126 return false;
1127 }
1128 return true;
1129 }
1130
Nick Pellye0fd6932012-07-11 10:26:13 -07001131 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001132 public void removeGpsStatusListener(IGpsStatusListener listener) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001133 synchronized (mLock) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001134 try {
1135 mGpsStatusProvider.removeGpsStatusListener(listener);
1136 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001137 Slog.e(TAG, "mGpsStatusProvider.removeGpsStatusListener failed", e);
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001138 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001139 }
1140 }
1141
Nick Pellye0fd6932012-07-11 10:26:13 -07001142 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001143 public boolean sendExtraCommand(String provider, String command, Bundle extras) {
Mike Lockwoodc6cc8362009-08-17 13:16:08 -04001144 if (provider == null) {
1145 // throw NullPointerException to remain compatible with previous implementation
1146 throw new NullPointerException();
1147 }
1148
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001149 checkPermission();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001150 // and check for ACCESS_LOCATION_EXTRA_COMMANDS
Mike Lockwoodb7e99222009-07-07 13:18:21 -04001151 if ((mContext.checkCallingOrSelfPermission(ACCESS_LOCATION_EXTRA_COMMANDS)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001152 != PackageManager.PERMISSION_GRANTED)) {
1153 throw new SecurityException("Requires ACCESS_LOCATION_EXTRA_COMMANDS permission");
1154 }
1155
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001156 synchronized (mLock) {
Mike Lockwoodd03ff942010-02-09 08:46:14 -05001157 LocationProviderInterface p = mProvidersByName.get(provider);
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001158 if (p == null) return false;
Nick Pellye0fd6932012-07-11 10:26:13 -07001159
Mike Lockwoodd03ff942010-02-09 08:46:14 -05001160 return p.sendExtraCommand(command, extras);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001161 }
1162 }
1163
Nick Pellye0fd6932012-07-11 10:26:13 -07001164 @Override
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001165 public boolean sendNiResponse(int notifId, int userResponse) {
Mike Lockwood18ad9f62009-08-27 14:01:23 -07001166 if (Binder.getCallingUid() != Process.myUid()) {
1167 throw new SecurityException(
1168 "calling sendNiResponse from outside of the system is not allowed");
1169 }
Danke Xie22d1f9f2009-08-18 18:28:45 -04001170 try {
1171 return mNetInitiatedListener.sendNiResponse(notifId, userResponse);
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001172 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001173 Slog.e(TAG, "RemoteException in LocationManagerService.sendNiResponse");
Danke Xie22d1f9f2009-08-18 18:28:45 -04001174 return false;
1175 }
1176 }
1177
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001178 /**
Mike Lockwood628fd6d2010-01-25 22:46:13 -05001179 * @return null if the provider does not exist
Alexey Tarasovf2db9fb2009-09-01 02:37:07 +11001180 * @throws SecurityException if the provider is not allowed to be
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001181 * accessed by the caller
1182 */
Nick Pellye0fd6932012-07-11 10:26:13 -07001183 @Override
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001184 public ProviderProperties getProviderProperties(String provider) {
1185 checkPermission();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001186
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001187 LocationProviderInterface p;
1188 synchronized (mLock) {
1189 p = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001190 }
1191
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001192 if (p == null) return null;
1193 return p.getProperties();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001194 }
1195
Nick Pellye0fd6932012-07-11 10:26:13 -07001196 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001197 public boolean isProviderEnabled(String provider) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001198 checkPermission();
1199 if (LocationManager.FUSED_PROVIDER.equals(provider)) return false;
1200
1201 synchronized (mLock) {
1202 LocationProviderInterface p = mProvidersByName.get(provider);
1203 if (p == null) return false;
1204
1205 return isAllowedBySettingsLocked(provider);
1206 }
1207 }
1208
1209 private void checkCallerIsProvider() {
1210 if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER)
1211 == PackageManager.PERMISSION_GRANTED) {
1212 return;
1213 }
1214
1215 // Previously we only used the INSTALL_LOCATION_PROVIDER
1216 // check. But that is system or signature
1217 // protection level which is not flexible enough for
1218 // providers installed oustide the system image. So
1219 // also allow providers with a UID matching the
1220 // currently bound package name
1221
1222 int uid = Binder.getCallingUid();
1223
1224 if (mGeocodeProvider != null) {
1225 if (doesPackageHaveUid(uid, mGeocodeProvider.getConnectedPackageName())) return;
1226 }
1227 for (LocationProviderProxy proxy : mProxyProviders) {
1228 if (doesPackageHaveUid(uid, proxy.getConnectedPackageName())) return;
1229 }
1230 throw new SecurityException("need INSTALL_LOCATION_PROVIDER permission, " +
1231 "or UID of a currently bound location provider");
1232 }
1233
1234 private boolean doesPackageHaveUid(int uid, String packageName) {
1235 if (packageName == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001236 return false;
1237 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001238 try {
1239 ApplicationInfo appInfo = mPackageManager.getApplicationInfo(packageName, 0);
1240 if (appInfo.uid != uid) {
1241 return false;
1242 }
1243 } catch (NameNotFoundException e) {
1244 return false;
1245 }
1246 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001247 }
1248
Nick Pellye0fd6932012-07-11 10:26:13 -07001249 @Override
Mike Lockwooda4903f252010-02-17 06:42:23 -05001250 public void reportLocation(Location location, boolean passive) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001251 checkCallerIsProvider();
Mike Lockwood275555c2009-05-01 11:30:34 -04001252
Nick Pelly2eeeec22012-07-18 13:13:37 -07001253 if (!location.isComplete()) {
1254 Log.w(TAG, "Dropping incomplete location: " + location);
1255 return;
1256 }
1257
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001258 mLocationHandler.removeMessages(MSG_LOCATION_CHANGED, location);
1259 Message m = Message.obtain(mLocationHandler, MSG_LOCATION_CHANGED, location);
Mike Lockwooda4903f252010-02-17 06:42:23 -05001260 m.arg1 = (passive ? 1 : 0);
Mike Lockwood4e50b782009-04-03 08:24:43 -07001261 mLocationHandler.sendMessageAtFrontOfQueue(m);
1262 }
1263
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001264
1265 private static boolean shouldBroadcastSafe(Location loc, Location lastLoc, UpdateRecord record) {
1266 // Always broadcast the first update
1267 if (lastLoc == null) {
1268 return true;
1269 }
1270
Nick Pellyf1be6862012-05-15 10:53:42 -07001271 // Check whether sufficient time has passed
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001272 long minTime = record.mRequest.getFastestInterval();
Nick Pelly2eeeec22012-07-18 13:13:37 -07001273 long delta = (loc.getElapsedRealtimeNano() - lastLoc.getElapsedRealtimeNano()) / 1000000L;
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001274 if (delta < minTime - MAX_PROVIDER_SCHEDULING_JITTER_MS) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001275 return false;
1276 }
1277
1278 // Check whether sufficient distance has been traveled
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001279 double minDistance = record.mRequest.getSmallestDisplacement();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001280 if (minDistance > 0.0) {
1281 if (loc.distanceTo(lastLoc) <= minDistance) {
1282 return false;
1283 }
1284 }
1285
1286 return true;
1287 }
1288
Mike Lockwooda4903f252010-02-17 06:42:23 -05001289 private void handleLocationChangedLocked(Location location, boolean passive) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001290 long now = SystemClock.elapsedRealtime();
Mike Lockwooda4903f252010-02-17 06:42:23 -05001291 String provider = (passive ? LocationManager.PASSIVE_PROVIDER : location.getProvider());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001292 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001293 if (records == null || records.size() == 0) return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001294
Mike Lockwoodd03ff942010-02-09 08:46:14 -05001295 LocationProviderInterface p = mProvidersByName.get(provider);
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001296 if (p == null) return;
1297
1298 // Add the coarse location as an extra, if not already present
1299 Location coarse = getCoarseLocationExtra(location);
1300 if (coarse == null) {
1301 coarse = addCoarseLocationExtra(location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001302 }
1303
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001304 // Update last known locations
1305 Location lastLocation = mLastLocation.get(provider);
Mike Lockwood4e50b782009-04-03 08:24:43 -07001306 if (lastLocation == null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001307 lastLocation = new Location(provider);
1308 mLastLocation.put(provider, lastLocation);
Mike Lockwood4e50b782009-04-03 08:24:43 -07001309 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001310 lastLocation.set(location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001311
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001312 // Fetch latest status update time
1313 long newStatusUpdateTime = p.getStatusUpdateTime();
1314
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001315 // Get latest status
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001316 Bundle extras = new Bundle();
1317 int status = p.getStatus(extras);
1318
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001319 ArrayList<Receiver> deadReceivers = null;
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001320 ArrayList<UpdateRecord> deadUpdateRecords = null;
Nick Pellye0fd6932012-07-11 10:26:13 -07001321
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001322 // Broadcast location or status to all listeners
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001323 for (UpdateRecord r : records) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001324 Receiver receiver = r.mReceiver;
Mike Lockwood03ca2162010-04-01 08:10:09 -07001325 boolean receiverDead = false;
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001326 if (ACCESS_FINE_LOCATION.equals(receiver.mPermission)) {
1327 location = lastLocation; // use fine location
1328 } else {
1329 location = coarse; // use coarse location
1330 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001331
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001332 Location lastLoc = r.mLastFixBroadcast;
Mike Lockwood4e50b782009-04-03 08:24:43 -07001333 if ((lastLoc == null) || shouldBroadcastSafe(location, lastLoc, r)) {
1334 if (lastLoc == null) {
1335 lastLoc = new Location(location);
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001336 r.mLastFixBroadcast = lastLoc;
Mike Lockwood4e50b782009-04-03 08:24:43 -07001337 } else {
1338 lastLoc.set(location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001339 }
Mike Lockwood4e50b782009-04-03 08:24:43 -07001340 if (!receiver.callLocationChangedLocked(location)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001341 Slog.w(TAG, "RemoteException calling onLocationChanged on " + receiver);
Mike Lockwood03ca2162010-04-01 08:10:09 -07001342 receiverDead = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001343 }
1344 }
1345
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001346 long prevStatusUpdateTime = r.mLastStatusBroadcast;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001347 if ((newStatusUpdateTime > prevStatusUpdateTime) &&
1348 (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) {
1349
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001350 r.mLastStatusBroadcast = newStatusUpdateTime;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001351 if (!receiver.callStatusChangedLocked(provider, status, extras)) {
Mike Lockwood03ca2162010-04-01 08:10:09 -07001352 receiverDead = true;
Joe Onorato8a9b2202010-02-26 18:56:32 -08001353 Slog.w(TAG, "RemoteException calling onStatusChanged on " + receiver);
Mike Lockwood03ca2162010-04-01 08:10:09 -07001354 }
1355 }
1356
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001357 // track expired records
1358 if (r.mRequest.getNumUpdates() == 0 || r.mRequest.getExpireAt() < now) {
1359 if (deadUpdateRecords == null) {
1360 deadUpdateRecords = new ArrayList<UpdateRecord>();
1361 }
1362 deadUpdateRecords.add(r);
1363 }
1364 // track dead receivers
1365 if (receiverDead) {
Mike Lockwood03ca2162010-04-01 08:10:09 -07001366 if (deadReceivers == null) {
1367 deadReceivers = new ArrayList<Receiver>();
1368 }
1369 if (!deadReceivers.contains(receiver)) {
1370 deadReceivers.add(receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001371 }
1372 }
1373 }
Nick Pellye0fd6932012-07-11 10:26:13 -07001374
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001375 // remove dead records and receivers outside the loop
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001376 if (deadReceivers != null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001377 for (Receiver receiver : deadReceivers) {
1378 removeUpdatesLocked(receiver);
1379 }
1380 }
1381 if (deadUpdateRecords != null) {
1382 for (UpdateRecord r : deadUpdateRecords) {
1383 r.disposeLocked(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001384 }
1385 }
1386 }
1387
1388 private class LocationWorkerHandler extends Handler {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001389 @Override
1390 public void handleMessage(Message msg) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001391 switch (msg.what) {
1392 case MSG_LOCATION_CHANGED:
1393 handleLocationChanged((Location) msg.obj, msg.arg1 == 1);
1394 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001395 }
1396 }
1397 }
1398
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001399 private void handleLocationChanged(Location location, boolean passive) {
1400 String provider = location.getProvider();
Jeff Sharkey5e613312012-01-30 11:16:20 -08001401
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001402 if (!passive) {
1403 // notify passive provider of the new location
1404 mPassiveProvider.updateLocation(location);
1405 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001406
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001407 synchronized (mLock) {
1408 if (isAllowedBySettingsLocked(provider)) {
1409 handleLocationChangedLocked(location, passive);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001410 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001411 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001412 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001413
Mike Lockwoode97ae402010-09-29 15:23:46 -04001414 private final PackageMonitor mPackageMonitor = new PackageMonitor() {
1415 @Override
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001416 public void onPackageDisappeared(String packageName, int reason) {
1417 // remove all receivers associated with this package name
1418 synchronized (mLock) {
1419 ArrayList<Receiver> deadReceivers = null;
1420
1421 for (Receiver receiver : mReceivers.values()) {
1422 if (receiver.mPackageName.equals(packageName)) {
1423 if (deadReceivers == null) {
1424 deadReceivers = new ArrayList<Receiver>();
1425 }
1426 deadReceivers.add(receiver);
1427 }
1428 }
1429
1430 // perform removal outside of mReceivers loop
1431 if (deadReceivers != null) {
1432 for (Receiver receiver : deadReceivers) {
1433 removeUpdatesLocked(receiver);
1434 }
1435 }
1436 }
Nick Pellye0fd6932012-07-11 10:26:13 -07001437 }
Mike Lockwoode97ae402010-09-29 15:23:46 -04001438 };
1439
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001440 // Wake locks
1441
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001442 private void incrementPendingBroadcasts() {
1443 synchronized (mWakeLock) {
1444 if (mPendingBroadcasts++ == 0) {
1445 try {
1446 mWakeLock.acquire();
1447 log("Acquired wakelock");
1448 } catch (Exception e) {
1449 // This is to catch a runtime exception thrown when we try to release an
1450 // already released lock.
Joe Onorato8a9b2202010-02-26 18:56:32 -08001451 Slog.e(TAG, "exception in acquireWakeLock()", e);
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001452 }
1453 }
Mike Lockwood48f17512009-04-23 09:12:08 -07001454 }
1455 }
1456
1457 private void decrementPendingBroadcasts() {
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001458 synchronized (mWakeLock) {
Mike Lockwood48f17512009-04-23 09:12:08 -07001459 if (--mPendingBroadcasts == 0) {
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001460 try {
1461 // Release wake lock
1462 if (mWakeLock.isHeld()) {
1463 mWakeLock.release();
1464 log("Released wakelock");
1465 } else {
1466 log("Can't release wakelock again!");
1467 }
1468 } catch (Exception e) {
1469 // This is to catch a runtime exception thrown when we try to release an
1470 // already released lock.
Joe Onorato8a9b2202010-02-26 18:56:32 -08001471 Slog.e(TAG, "exception in releaseWakeLock()", e);
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001472 }
Mike Lockwood48f17512009-04-23 09:12:08 -07001473 }
1474 }
1475 }
1476
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001477 // Geocoder
1478
Nick Pellye0fd6932012-07-11 10:26:13 -07001479 @Override
Mike Lockwoode15735a2010-09-20 17:48:47 -04001480 public boolean geocoderIsPresent() {
Mark Vandevoorde01ac80b2010-05-21 15:43:26 -07001481 return mGeocodeProvider != null;
1482 }
1483
Nick Pellye0fd6932012-07-11 10:26:13 -07001484 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001485 public String getFromLocation(double latitude, double longitude, int maxResults,
Mike Lockwood34901402010-01-04 12:14:21 -05001486 GeocoderParams params, List<Address> addrs) {
Mike Lockwooda55c3212009-04-15 11:10:11 -04001487 if (mGeocodeProvider != null) {
Mike Lockwood628fd6d2010-01-25 22:46:13 -05001488 return mGeocodeProvider.getFromLocation(latitude, longitude, maxResults,
1489 params, addrs);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001490 }
Mike Lockwooda55c3212009-04-15 11:10:11 -04001491 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001492 }
1493
Mike Lockwooda55c3212009-04-15 11:10:11 -04001494
Nick Pellye0fd6932012-07-11 10:26:13 -07001495 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001496 public String getFromLocationName(String locationName,
Mike Lockwooda55c3212009-04-15 11:10:11 -04001497 double lowerLeftLatitude, double lowerLeftLongitude,
1498 double upperRightLatitude, double upperRightLongitude, int maxResults,
Mike Lockwood34901402010-01-04 12:14:21 -05001499 GeocoderParams params, List<Address> addrs) {
Mike Lockwooda55c3212009-04-15 11:10:11 -04001500
1501 if (mGeocodeProvider != null) {
Mike Lockwood628fd6d2010-01-25 22:46:13 -05001502 return mGeocodeProvider.getFromLocationName(locationName, lowerLeftLatitude,
1503 lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
1504 maxResults, params, addrs);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001505 }
Mike Lockwooda55c3212009-04-15 11:10:11 -04001506 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001507 }
1508
1509 // Mock Providers
1510
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001511 private void checkMockPermissionsSafe() {
1512 boolean allowMocks = Settings.Secure.getInt(mContext.getContentResolver(),
1513 Settings.Secure.ALLOW_MOCK_LOCATION, 0) == 1;
1514 if (!allowMocks) {
1515 throw new SecurityException("Requires ACCESS_MOCK_LOCATION secure setting");
1516 }
1517
1518 if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) !=
1519 PackageManager.PERMISSION_GRANTED) {
1520 throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission");
Nick Pellye0fd6932012-07-11 10:26:13 -07001521 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001522 }
1523
Nick Pellye0fd6932012-07-11 10:26:13 -07001524 @Override
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001525 public void addTestProvider(String name, ProviderProperties properties) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001526 checkMockPermissionsSafe();
1527
Mike Lockwooda4903f252010-02-17 06:42:23 -05001528 if (LocationManager.PASSIVE_PROVIDER.equals(name)) {
1529 throw new IllegalArgumentException("Cannot mock the passive location provider");
1530 }
1531
Mike Lockwood86328a92009-10-23 08:38:25 -04001532 long identity = Binder.clearCallingIdentity();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001533 synchronized (mLock) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001534 MockProvider provider = new MockProvider(name, this, properties);
Mike Lockwood7566c1d2009-08-25 10:05:18 -07001535 // remove the real provider if we are replacing GPS or network provider
1536 if (LocationManager.GPS_PROVIDER.equals(name)
1537 || LocationManager.NETWORK_PROVIDER.equals(name)) {
Mike Lockwoodd03ff942010-02-09 08:46:14 -05001538 LocationProviderInterface p = mProvidersByName.get(name);
1539 if (p != null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001540 removeProviderLocked(p);
Mike Lockwood7566c1d2009-08-25 10:05:18 -07001541 }
1542 }
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001543 if (mProvidersByName.get(name) != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001544 throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
1545 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001546 addProviderLocked(provider);
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001547 mMockProviders.put(name, provider);
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001548 mLastLocation.put(name, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001549 updateProvidersLocked();
1550 }
Mike Lockwood86328a92009-10-23 08:38:25 -04001551 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001552 }
1553
Nick Pellye0fd6932012-07-11 10:26:13 -07001554 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001555 public void removeTestProvider(String provider) {
1556 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001557 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001558 MockProvider mockProvider = mMockProviders.get(provider);
1559 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001560 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1561 }
Mike Lockwood86328a92009-10-23 08:38:25 -04001562 long identity = Binder.clearCallingIdentity();
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001563 removeProviderLocked(mProvidersByName.get(provider));
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001564 mMockProviders.remove(mockProvider);
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001565
1566 // reinstate real provider if available
1567 LocationProviderInterface realProvider = mRealProviders.get(provider);
1568 if (realProvider != null) {
1569 addProviderLocked(realProvider);
Mike Lockwood7566c1d2009-08-25 10:05:18 -07001570 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001571 mLastLocation.put(provider, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001572 updateProvidersLocked();
Mike Lockwood86328a92009-10-23 08:38:25 -04001573 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001574 }
1575 }
1576
Nick Pellye0fd6932012-07-11 10:26:13 -07001577 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001578 public void setTestProviderLocation(String provider, Location loc) {
1579 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001580 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001581 MockProvider mockProvider = mMockProviders.get(provider);
1582 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001583 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1584 }
Mike Lockwood95427cd2009-05-07 13:27:54 -04001585 // clear calling identity so INSTALL_LOCATION_PROVIDER permission is not required
1586 long identity = Binder.clearCallingIdentity();
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001587 mockProvider.setLocation(loc);
Mike Lockwood95427cd2009-05-07 13:27:54 -04001588 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001589 }
1590 }
1591
Nick Pellye0fd6932012-07-11 10:26:13 -07001592 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001593 public void clearTestProviderLocation(String provider) {
1594 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001595 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001596 MockProvider mockProvider = mMockProviders.get(provider);
1597 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001598 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1599 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001600 mockProvider.clearLocation();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001601 }
1602 }
1603
Nick Pellye0fd6932012-07-11 10:26:13 -07001604 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001605 public void setTestProviderEnabled(String provider, boolean enabled) {
1606 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001607 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001608 MockProvider mockProvider = mMockProviders.get(provider);
1609 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001610 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1611 }
Mike Lockwood86328a92009-10-23 08:38:25 -04001612 long identity = Binder.clearCallingIdentity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001613 if (enabled) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001614 mockProvider.enable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001615 mEnabledProviders.add(provider);
1616 mDisabledProviders.remove(provider);
1617 } else {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001618 mockProvider.disable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001619 mEnabledProviders.remove(provider);
1620 mDisabledProviders.add(provider);
1621 }
1622 updateProvidersLocked();
Mike Lockwood86328a92009-10-23 08:38:25 -04001623 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001624 }
1625 }
1626
Nick Pellye0fd6932012-07-11 10:26:13 -07001627 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001628 public void clearTestProviderEnabled(String provider) {
1629 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001630 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001631 MockProvider mockProvider = mMockProviders.get(provider);
1632 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001633 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1634 }
Mike Lockwood86328a92009-10-23 08:38:25 -04001635 long identity = Binder.clearCallingIdentity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001636 mEnabledProviders.remove(provider);
1637 mDisabledProviders.remove(provider);
1638 updateProvidersLocked();
Mike Lockwood86328a92009-10-23 08:38:25 -04001639 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001640 }
1641 }
1642
Nick Pellye0fd6932012-07-11 10:26:13 -07001643 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001644 public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
1645 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001646 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001647 MockProvider mockProvider = mMockProviders.get(provider);
1648 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001649 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1650 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001651 mockProvider.setStatus(status, extras, updateTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001652 }
1653 }
1654
Nick Pellye0fd6932012-07-11 10:26:13 -07001655 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001656 public void clearTestProviderStatus(String provider) {
1657 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001658 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001659 MockProvider mockProvider = mMockProviders.get(provider);
1660 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001661 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1662 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001663 mockProvider.clearStatus();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001664 }
1665 }
1666
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001667 private static double wrapLatitude(double lat) {
1668 if (lat > MAX_LATITUDE) lat = MAX_LATITUDE;
1669 if (lat < -MAX_LATITUDE) lat = -MAX_LATITUDE;
1670 return lat;
1671 }
1672
1673 private static double wrapLongitude(double lon) {
1674 if (lon >= 180.0) lon -= 360.0;
1675 if (lon < -180.0) lon += 360.0;
1676 return lon;
1677 }
1678
1679 private static double distanceToDegreesLatitude(double distance) {
1680 return distance / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR;
1681 }
1682
1683 /**
1684 * Requires latitude since longitudinal distances change with distance from equator.
1685 */
1686 private static double distanceToDegreesLongitude(double distance, double lat) {
1687 return distance / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR / Math.cos(lat);
1688 }
1689
1690 /**
1691 * Fudge a location into a coarse location.
1692 * <p>Add a random offset, then quantize the result (snap-to-grid).
1693 * Random offsets alone can be low-passed pretty easily.
1694 * Snap-to-grid on its own is excellent unless you are sitting on a
1695 * grid boundary and bouncing between quantizations.
1696 * The combination is quite hard to reverse engineer.
1697 * <p>The random offset used is smaller than the goal accuracy
1698 * ({@link #COARSE_ACCURACY_M}), in order to give relatively stable
1699 * results after quantization.
1700 */
1701 private static Location createCoarse(Location fine) {
1702 Location coarse = new Location(fine);
1703
1704 coarse.removeBearing();
1705 coarse.removeSpeed();
1706 coarse.removeAltitude();
1707
1708 double lat = coarse.getLatitude();
1709 double lon = coarse.getLongitude();
1710
1711 // wrap
1712 lat = wrapLatitude(lat);
1713 lon = wrapLongitude(lon);
1714
1715 if (coarse.getAccuracy() < COARSE_ACCURACY_M / 2) {
1716 // apply a random offset
1717 double fudgeDistance = COARSE_ACCURACY_M / 2.0 - coarse.getAccuracy();
1718 lat += (Math.random() - 0.5) * distanceToDegreesLatitude(fudgeDistance);
1719 lon += (Math.random() - 0.5) * distanceToDegreesLongitude(fudgeDistance, lat);
1720 }
1721
1722 // wrap
1723 lat = wrapLatitude(lat);
1724 lon = wrapLongitude(lon);
1725
1726 // quantize (snap-to-grid)
1727 double latGranularity = distanceToDegreesLatitude(COARSE_ACCURACY_M);
1728 double lonGranularity = distanceToDegreesLongitude(COARSE_ACCURACY_M, lat);
1729 long latQuantized = Math.round(lat / latGranularity);
1730 long lonQuantized = Math.round(lon / lonGranularity);
1731 lat = latQuantized * latGranularity;
1732 lon = lonQuantized * lonGranularity;
1733
1734 // wrap again
1735 lat = wrapLatitude(lat);
1736 lon = wrapLongitude(lon);
1737
1738 // apply
1739 coarse.setLatitude(lat);
1740 coarse.setLongitude(lon);
1741 coarse.setAccuracy((float)COARSE_ACCURACY_M);
1742
1743 return coarse;
1744 }
1745
1746
1747 private static Location getCoarseLocationExtra(Location location) {
1748 Bundle extras = location.getExtras();
1749 if (extras == null) return null;
1750 Parcelable parcel = extras.getParcelable(EXTRA_COARSE_LOCATION);
1751 if (parcel == null) return null;
1752 if (!(parcel instanceof Location)) return null;
1753 Location coarse = (Location) parcel;
1754 if (coarse.getAccuracy() < COARSE_ACCURACY_M) return null;
1755 return coarse;
1756 }
1757
1758 private static Location addCoarseLocationExtra(Location location) {
1759 Bundle extras = location.getExtras();
1760 if (extras == null) extras = new Bundle();
1761 Location coarse = createCoarse(location);
1762 extras.putParcelable(EXTRA_COARSE_LOCATION, coarse);
1763 location.setExtras(extras);
1764 return coarse;
1765 }
1766
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001767 private void log(String log) {
1768 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001769 Slog.d(TAG, log);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001770 }
1771 }
Nick Pellye0fd6932012-07-11 10:26:13 -07001772
1773 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001774 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1775 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1776 != PackageManager.PERMISSION_GRANTED) {
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001777 pw.println("Permission Denial: can't dump LocationManagerService from from pid="
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001778 + Binder.getCallingPid()
1779 + ", uid=" + Binder.getCallingUid());
1780 return;
1781 }
Nick Pellye0fd6932012-07-11 10:26:13 -07001782
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001783 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001784 pw.println("Current Location Manager state:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001785 pw.println(" Location Listeners:");
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001786 for (Receiver receiver : mReceivers.values()) {
1787 pw.println(" " + receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001788 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001789 pw.println(" Records by Provider:");
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001790 for (Map.Entry<String, ArrayList<UpdateRecord>> entry : mRecordsByProvider.entrySet()) {
1791 pw.println(" " + entry.getKey() + ":");
1792 for (UpdateRecord record : entry.getValue()) {
1793 pw.println(" " + record);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001794 }
1795 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001796 pw.println(" Last Known Locations:");
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001797 for (Map.Entry<String, Location> entry : mLastLocation.entrySet()) {
1798 String provider = entry.getKey();
1799 Location location = entry.getValue();
1800 pw.println(" " + provider + ": " + location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001801 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001802
Nick Pellye0fd6932012-07-11 10:26:13 -07001803 mGeofenceManager.dump(pw);
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001804
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001805 if (mEnabledProviders.size() > 0) {
1806 pw.println(" Enabled Providers:");
1807 for (String i : mEnabledProviders) {
1808 pw.println(" " + i);
1809 }
Nick Pellye0fd6932012-07-11 10:26:13 -07001810
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001811 }
1812 if (mDisabledProviders.size() > 0) {
1813 pw.println(" Disabled Providers:");
1814 for (String i : mDisabledProviders) {
1815 pw.println(" " + i);
1816 }
Nick Pellye0fd6932012-07-11 10:26:13 -07001817
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001818 }
1819 if (mMockProviders.size() > 0) {
1820 pw.println(" Mock Providers:");
1821 for (Map.Entry<String, MockProvider> i : mMockProviders.entrySet()) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001822 i.getValue().dump(pw, " ");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001823 }
1824 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001825
1826 if (args.length > 0 && "short".equals(args[0])) {
1827 return;
1828 }
Fred Fettinger3c8fbdf2010-01-04 15:38:13 -06001829 for (LocationProviderInterface provider: mProviders) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001830 pw.print(provider.getName() + " Internal State");
1831 if (provider instanceof LocationProviderProxy) {
1832 LocationProviderProxy proxy = (LocationProviderProxy) provider;
1833 pw.print(" (" + proxy.getConnectedPackageName() + ")");
Fred Fettinger3c8fbdf2010-01-04 15:38:13 -06001834 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001835 pw.println(":");
1836 provider.dump(fd, pw, args);
Fred Fettinger3c8fbdf2010-01-04 15:38:13 -06001837 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001838 }
1839 }
1840}