blob: bbb43d79d3a20ca0a59b825a4d372c3c5d7dc6fa [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 java.io.FileDescriptor;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080020import java.io.PrintWriter;
21import java.util.ArrayList;
22import java.util.HashMap;
23import java.util.HashSet;
24import java.util.List;
25import java.util.Map;
Mike Lockwood9637d472009-04-02 21:41:57 -070026import java.util.Observable;
27import java.util.Observer;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import java.util.Set;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import android.app.PendingIntent;
31import android.content.BroadcastReceiver;
Mike Lockwood9637d472009-04-02 21:41:57 -070032import android.content.ContentQueryMap;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033import android.content.ContentResolver;
34import android.content.Context;
35import android.content.Intent;
36import android.content.IntentFilter;
37import android.content.pm.PackageManager;
Mike Lockwood9637d472009-04-02 21:41:57 -070038import android.database.Cursor;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039import android.location.Address;
Mike Lockwooda55c3212009-04-15 11:10:11 -040040import android.location.IGeocodeProvider;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041import android.location.IGpsStatusListener;
Mike Lockwood15e3d0f2009-05-01 07:53:28 -040042import android.location.IGpsStatusProvider;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043import android.location.ILocationListener;
44import android.location.ILocationManager;
Mike Lockwoode932f7f2009-04-06 10:51:26 -070045import android.location.ILocationProvider;
Danke Xie22d1f9f2009-08-18 18:28:45 -040046import android.location.INetInitiatedListener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047import android.location.Location;
48import android.location.LocationManager;
49import android.location.LocationProvider;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050import android.net.ConnectivityManager;
Mike Lockwood03d24672009-10-08 15:45:03 -040051import android.net.NetworkInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052import android.net.Uri;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053import android.os.Binder;
54import android.os.Bundle;
55import android.os.Handler;
56import android.os.IBinder;
Mike Lockwood3d12b512009-04-21 23:25:35 -070057import android.os.Looper;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058import android.os.Message;
59import android.os.PowerManager;
Mike Lockwoode932f7f2009-04-06 10:51:26 -070060import android.os.Process;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080061import android.os.RemoteException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062import android.provider.Settings;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080063import android.util.Log;
64import android.util.PrintWriterPrinter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080065
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080066import com.android.internal.location.GpsLocationProvider;
Mike Lockwoode932f7f2009-04-06 10:51:26 -070067import com.android.internal.location.LocationProviderProxy;
Mike Lockwood7ec434e2009-03-27 07:46:48 -070068import com.android.internal.location.MockProvider;
Danke Xie22d1f9f2009-08-18 18:28:45 -040069import com.android.internal.location.GpsNetInitiatedHandler;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070
71/**
72 * The service class that manages LocationProviders and issues location
73 * updates and alerts.
74 *
75 * {@hide}
76 */
Mike Lockwood3d12b512009-04-21 23:25:35 -070077public class LocationManagerService extends ILocationManager.Stub implements Runnable {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078 private static final String TAG = "LocationManagerService";
The Android Open Source Project10592532009-03-18 17:39:46 -070079 private static final boolean LOCAL_LOGV = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080080
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081 // The last time a location was written, by provider name.
82 private HashMap<String,Long> mLastWriteTime = new HashMap<String,Long>();
83
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084 private static final String ACCESS_FINE_LOCATION =
85 android.Manifest.permission.ACCESS_FINE_LOCATION;
86 private static final String ACCESS_COARSE_LOCATION =
87 android.Manifest.permission.ACCESS_COARSE_LOCATION;
88 private static final String ACCESS_MOCK_LOCATION =
89 android.Manifest.permission.ACCESS_MOCK_LOCATION;
90 private static final String ACCESS_LOCATION_EXTRA_COMMANDS =
91 android.Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS;
Mike Lockwood275555c2009-05-01 11:30:34 -040092 private static final String INSTALL_LOCATION_PROVIDER =
93 android.Manifest.permission.INSTALL_LOCATION_PROVIDER;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080094
95 // Set of providers that are explicitly enabled
96 private final Set<String> mEnabledProviders = new HashSet<String>();
97
98 // Set of providers that are explicitly disabled
99 private final Set<String> mDisabledProviders = new HashSet<String>();
100
101 // Locations, status values, and extras for mock providers
Mike Lockwood7ec434e2009-03-27 07:46:48 -0700102 private final HashMap<String,MockProvider> mMockProviders = new HashMap<String,MockProvider>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800103
104 private static boolean sProvidersLoaded = false;
105
106 private final Context mContext;
Mike Lockwooda55c3212009-04-15 11:10:11 -0400107 private IGeocodeProvider mGeocodeProvider;
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400108 private IGpsStatusProvider mGpsStatusProvider;
Danke Xie22d1f9f2009-08-18 18:28:45 -0400109 private INetInitiatedListener mNetInitiatedListener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110 private LocationWorkerHandler mLocationHandler;
111
Mike Lockwood7566c1d2009-08-25 10:05:18 -0700112 // Cache the real providers for use in addTestProvider() and removeTestProvider()
113 LocationProviderProxy mNetworkLocationProvider;
114 LocationProviderProxy mGpsLocationProvider;
115
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800116 // Handler messages
Mike Lockwood4e50b782009-04-03 08:24:43 -0700117 private static final int MESSAGE_LOCATION_CHANGED = 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800118
Mike Lockwood0528b9b2009-05-07 10:12:54 -0400119 // wakelock variables
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800120 private final static String WAKELOCK_KEY = "LocationManagerService";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121 private PowerManager.WakeLock mWakeLock = null;
Mike Lockwood48f17512009-04-23 09:12:08 -0700122 private int mPendingBroadcasts;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800123
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800124 /**
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400125 * List of all receivers.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800126 */
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400127 private final HashMap<Object, Receiver> mReceivers = new HashMap<Object, Receiver>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800128
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400129
130 /**
131 * List of location providers.
132 */
133 private final ArrayList<LocationProviderProxy> mProviders =
134 new ArrayList<LocationProviderProxy>();
135 private final HashMap<String, LocationProviderProxy> mProvidersByName
136 = new HashMap<String, LocationProviderProxy>();
137
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800138 /**
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400139 * Object used internally for synchronization
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800140 */
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400141 private final Object mLock = new Object();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800142
143 /**
144 * Mapping from provider name to all its UpdateRecords
145 */
146 private final HashMap<String,ArrayList<UpdateRecord>> mRecordsByProvider =
147 new HashMap<String,ArrayList<UpdateRecord>>();
148
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800149 // Proximity listeners
Mike Lockwood48f17512009-04-23 09:12:08 -0700150 private Receiver mProximityReceiver = null;
151 private ILocationListener mProximityListener = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800152 private HashMap<PendingIntent,ProximityAlert> mProximityAlerts =
153 new HashMap<PendingIntent,ProximityAlert>();
154 private HashSet<ProximityAlert> mProximitiesEntered =
155 new HashSet<ProximityAlert>();
156
157 // Last known location for each provider
158 private HashMap<String,Location> mLastKnownLocation =
159 new HashMap<String,Location>();
160
The Android Open Source Project4df24232009-03-05 14:34:35 -0800161 private int mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
The Android Open Source Project4df24232009-03-05 14:34:35 -0800162
Mike Lockwood9637d472009-04-02 21:41:57 -0700163 // for Settings change notification
164 private ContentQueryMap mSettings;
165
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800166 /**
167 * A wrapper class holding either an ILocationListener or a PendingIntent to receive
168 * location updates.
169 */
Mike Lockwood48f17512009-04-23 09:12:08 -0700170 private final class Receiver implements IBinder.DeathRecipient, PendingIntent.OnFinished {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800171 final ILocationListener mListener;
172 final PendingIntent mPendingIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800173 final Object mKey;
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400174 final HashMap<String,UpdateRecord> mUpdateRecords = new HashMap<String,UpdateRecord>();
Mike Lockwood48f17512009-04-23 09:12:08 -0700175 int mPendingBroadcasts;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800176
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400177 Receiver(ILocationListener listener) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800178 mListener = listener;
179 mPendingIntent = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800180 mKey = listener.asBinder();
181 }
182
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400183 Receiver(PendingIntent intent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800184 mPendingIntent = intent;
185 mListener = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800186 mKey = intent;
187 }
188
189 @Override
190 public boolean equals(Object otherObj) {
191 if (otherObj instanceof Receiver) {
192 return mKey.equals(
193 ((Receiver)otherObj).mKey);
194 }
195 return false;
196 }
197
198 @Override
199 public int hashCode() {
200 return mKey.hashCode();
201 }
Mike Lockwood3681f262009-05-12 10:52:03 -0400202
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800203 @Override
204 public String toString() {
205 if (mListener != null) {
206 return "Receiver{"
207 + Integer.toHexString(System.identityHashCode(this))
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400208 + " Listener " + mKey + "}";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800209 } else {
210 return "Receiver{"
211 + Integer.toHexString(System.identityHashCode(this))
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400212 + " Intent " + mKey + "}";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800213 }
214 }
215
216 public boolean isListener() {
217 return mListener != null;
218 }
219
220 public boolean isPendingIntent() {
221 return mPendingIntent != null;
222 }
223
224 public ILocationListener getListener() {
225 if (mListener != null) {
226 return mListener;
227 }
228 throw new IllegalStateException("Request for non-existent listener");
229 }
230
231 public PendingIntent getPendingIntent() {
232 if (mPendingIntent != null) {
233 return mPendingIntent;
234 }
235 throw new IllegalStateException("Request for non-existent intent");
236 }
237
238 public boolean callStatusChangedLocked(String provider, int status, Bundle extras) {
239 if (mListener != null) {
240 try {
Mike Lockwood48f17512009-04-23 09:12:08 -0700241 synchronized (this) {
242 // synchronize to ensure incrementPendingBroadcastsLocked()
243 // is called before decrementPendingBroadcasts()
244 mListener.onStatusChanged(provider, status, extras);
245 if (mListener != mProximityListener) {
246 // call this after broadcasting so we do not increment
247 // if we throw an exeption.
248 incrementPendingBroadcastsLocked();
249 }
250 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800251 } catch (RemoteException e) {
252 return false;
253 }
254 } else {
255 Intent statusChanged = new Intent();
256 statusChanged.putExtras(extras);
257 statusChanged.putExtra(LocationManager.KEY_STATUS_CHANGED, status);
258 try {
Mike Lockwood48f17512009-04-23 09:12:08 -0700259 synchronized (this) {
260 // synchronize to ensure incrementPendingBroadcastsLocked()
261 // is called before decrementPendingBroadcasts()
262 mPendingIntent.send(mContext, 0, statusChanged, this, mLocationHandler);
263 // call this after broadcasting so we do not increment
264 // if we throw an exeption.
265 incrementPendingBroadcastsLocked();
266 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800267 } catch (PendingIntent.CanceledException e) {
268 return false;
269 }
270 }
271 return true;
272 }
273
274 public boolean callLocationChangedLocked(Location location) {
275 if (mListener != null) {
276 try {
Mike Lockwood48f17512009-04-23 09:12:08 -0700277 synchronized (this) {
278 // synchronize to ensure incrementPendingBroadcastsLocked()
279 // is called before decrementPendingBroadcasts()
280 mListener.onLocationChanged(location);
281 if (mListener != mProximityListener) {
282 // call this after broadcasting so we do not increment
283 // if we throw an exeption.
284 incrementPendingBroadcastsLocked();
285 }
286 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800287 } catch (RemoteException e) {
288 return false;
289 }
290 } else {
291 Intent locationChanged = new Intent();
292 locationChanged.putExtra(LocationManager.KEY_LOCATION_CHANGED, location);
293 try {
Mike Lockwood48f17512009-04-23 09:12:08 -0700294 synchronized (this) {
295 // synchronize to ensure incrementPendingBroadcastsLocked()
296 // is called before decrementPendingBroadcasts()
297 mPendingIntent.send(mContext, 0, locationChanged, this, mLocationHandler);
298 // call this after broadcasting so we do not increment
299 // if we throw an exeption.
300 incrementPendingBroadcastsLocked();
301 }
302 } catch (PendingIntent.CanceledException e) {
303 return false;
304 }
305 }
306 return true;
307 }
308
309 public boolean callProviderEnabledLocked(String provider, boolean enabled) {
310 if (mListener != null) {
311 try {
312 synchronized (this) {
313 // synchronize to ensure incrementPendingBroadcastsLocked()
314 // is called before decrementPendingBroadcasts()
315 if (enabled) {
316 mListener.onProviderEnabled(provider);
317 } else {
318 mListener.onProviderDisabled(provider);
319 }
320 if (mListener != mProximityListener) {
321 // call this after broadcasting so we do not increment
322 // if we throw an exeption.
323 incrementPendingBroadcastsLocked();
324 }
325 }
326 } catch (RemoteException e) {
327 return false;
328 }
329 } else {
330 Intent providerIntent = new Intent();
331 providerIntent.putExtra(LocationManager.KEY_PROVIDER_ENABLED, enabled);
332 try {
333 synchronized (this) {
334 // synchronize to ensure incrementPendingBroadcastsLocked()
335 // is called before decrementPendingBroadcasts()
336 mPendingIntent.send(mContext, 0, providerIntent, this, mLocationHandler);
337 // call this after broadcasting so we do not increment
338 // if we throw an exeption.
339 incrementPendingBroadcastsLocked();
340 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800341 } catch (PendingIntent.CanceledException e) {
342 return false;
343 }
344 }
345 return true;
346 }
347
348 public void binderDied() {
The Android Open Source Project10592532009-03-18 17:39:46 -0700349 if (LOCAL_LOGV) {
350 Log.v(TAG, "Location listener died");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800351 }
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400352 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800353 removeUpdatesLocked(this);
354 }
Mike Lockwood48f17512009-04-23 09:12:08 -0700355 synchronized (this) {
356 if (mPendingBroadcasts > 0) {
357 LocationManagerService.this.decrementPendingBroadcasts();
358 mPendingBroadcasts = 0;
359 }
360 }
361 }
362
363 public void onSendFinished(PendingIntent pendingIntent, Intent intent,
364 int resultCode, String resultData, Bundle resultExtras) {
Mike Lockwood0528b9b2009-05-07 10:12:54 -0400365 synchronized (this) {
366 decrementPendingBroadcastsLocked();
Mike Lockwood48f17512009-04-23 09:12:08 -0700367 }
368 }
369
Mike Lockwood0528b9b2009-05-07 10:12:54 -0400370 // this must be called while synchronized by caller in a synchronized block
371 // containing the sending of the broadcaset
372 private void incrementPendingBroadcastsLocked() {
373 if (mPendingBroadcasts++ == 0) {
374 LocationManagerService.this.incrementPendingBroadcasts();
375 }
376 }
377
378 private void decrementPendingBroadcastsLocked() {
379 if (--mPendingBroadcasts == 0) {
380 LocationManagerService.this.decrementPendingBroadcasts();
Mike Lockwood48f17512009-04-23 09:12:08 -0700381 }
382 }
383 }
384
385 public void locationCallbackFinished(ILocationListener listener) {
Joshua Bartel080b61b2009-10-05 12:44:46 -0400386 //Do not use getReceiver here as that will add the ILocationListener to
387 //the receiver list if it is not found. If it is not found then the
388 //LocationListener was removed when it had a pending broadcast and should
389 //not be added back.
390 IBinder binder = listener.asBinder();
391 Receiver receiver = mReceivers.get(binder);
Mike Lockwood48f17512009-04-23 09:12:08 -0700392 if (receiver != null) {
Mike Lockwood0528b9b2009-05-07 10:12:54 -0400393 synchronized (receiver) {
394 // so wakelock calls will succeed
395 long identity = Binder.clearCallingIdentity();
396 receiver.decrementPendingBroadcastsLocked();
397 Binder.restoreCallingIdentity(identity);
398 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800399 }
400 }
401
Mike Lockwood9637d472009-04-02 21:41:57 -0700402 private final class SettingsObserver implements Observer {
403 public void update(Observable o, Object arg) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400404 synchronized (mLock) {
Mike Lockwood9637d472009-04-02 21:41:57 -0700405 updateProvidersLocked();
406 }
407 }
408 }
409
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400410 private void addProvider(LocationProviderProxy provider) {
411 mProviders.add(provider);
412 mProvidersByName.put(provider.getName(), provider);
413 }
414
415 private void removeProvider(LocationProviderProxy provider) {
416 mProviders.remove(provider);
Suchi Amalapurapufff2fda2009-06-30 21:36:16 -0700417 provider.unlinkProvider();
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400418 mProvidersByName.remove(provider.getName());
419 }
420
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800421 private void loadProviders() {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400422 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800423 if (sProvidersLoaded) {
424 return;
425 }
426
427 // Load providers
428 loadProvidersLocked();
429 sProvidersLoaded = true;
430 }
431 }
432
433 private void loadProvidersLocked() {
434 try {
435 _loadProvidersLocked();
436 } catch (Exception e) {
437 Log.e(TAG, "Exception loading providers:", e);
438 }
439 }
440
441 private void _loadProvidersLocked() {
442 // Attempt to load "real" providers first
443 if (GpsLocationProvider.isSupported()) {
444 // Create a gps location provider
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400445 GpsLocationProvider provider = new GpsLocationProvider(mContext, this);
446 mGpsStatusProvider = provider.getGpsStatusProvider();
Danke Xie22d1f9f2009-08-18 18:28:45 -0400447 mNetInitiatedListener = provider.getNetInitiatedListener();
Mike Lockwood8dfe5d82009-05-07 11:49:01 -0400448 LocationProviderProxy proxy = new LocationProviderProxy(LocationManager.GPS_PROVIDER, provider);
449 addProvider(proxy);
Mike Lockwood7566c1d2009-08-25 10:05:18 -0700450 mGpsLocationProvider = proxy;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800451 }
452
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800453 updateProvidersLocked();
454 }
455
456 /**
457 * @param context the context that the LocationManagerService runs in
458 */
459 public LocationManagerService(Context context) {
460 super();
461 mContext = context;
Mike Lockwood3d12b512009-04-21 23:25:35 -0700462
463 Thread thread = new Thread(null, this, "LocationManagerService");
464 thread.start();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800465
The Android Open Source Project10592532009-03-18 17:39:46 -0700466 if (LOCAL_LOGV) {
467 Log.v(TAG, "Constructed LocationManager Service");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800468 }
Mike Lockwood3d12b512009-04-21 23:25:35 -0700469 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800470
Mike Lockwood3d12b512009-04-21 23:25:35 -0700471 private void initialize() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800472 // Create a wake lock, needs to be done before calling loadProviders() below
473 PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
474 mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
Mike Lockwood0528b9b2009-05-07 10:12:54 -0400475
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800476 // Load providers
477 loadProviders();
478
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800479 // Register for Network (Wifi or Mobile) updates
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800480 IntentFilter intentFilter = new IntentFilter();
Mike Lockwood0528b9b2009-05-07 10:12:54 -0400481 intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
482 // Register for Package Manager updates
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800483 intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
484 intentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
Mike Lockwood0528b9b2009-05-07 10:12:54 -0400485 mContext.registerReceiver(mBroadcastReceiver, intentFilter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800486
Mike Lockwood9637d472009-04-02 21:41:57 -0700487 // listen for settings changes
488 ContentResolver resolver = mContext.getContentResolver();
489 Cursor settingsCursor = resolver.query(Settings.Secure.CONTENT_URI, null,
490 "(" + Settings.System.NAME + "=?)",
491 new String[]{Settings.Secure.LOCATION_PROVIDERS_ALLOWED},
492 null);
493 mSettings = new ContentQueryMap(settingsCursor, Settings.System.NAME, true, mLocationHandler);
494 SettingsObserver settingsObserver = new SettingsObserver();
495 mSettings.addObserver(settingsObserver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800496 }
497
Mike Lockwood3d12b512009-04-21 23:25:35 -0700498 public void run()
499 {
500 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
501 Looper.prepare();
502 mLocationHandler = new LocationWorkerHandler();
503 initialize();
504 Looper.loop();
505 }
506
Mike Lockwood275555c2009-05-01 11:30:34 -0400507 public void installLocationProvider(String name, ILocationProvider provider) {
508 if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER)
509 != PackageManager.PERMISSION_GRANTED) {
510 throw new SecurityException("Requires INSTALL_LOCATION_PROVIDER permission");
Mike Lockwoode932f7f2009-04-06 10:51:26 -0700511 }
512
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400513 synchronized (mLock) {
Mike Lockwood3681f262009-05-12 10:52:03 -0400514 // check to see if we are reinstalling a dead provider
515 LocationProviderProxy oldProvider = mProvidersByName.get(name);
516 if (oldProvider != null) {
517 if (oldProvider.isDead()) {
518 Log.d(TAG, "replacing dead provider");
519 removeProvider(oldProvider);
520 } else {
521 throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
522 }
523 }
524
Mike Lockwood8dfe5d82009-05-07 11:49:01 -0400525 LocationProviderProxy proxy = new LocationProviderProxy(name, provider);
526 addProvider(proxy);
527 updateProvidersLocked();
Mike Lockwood7566c1d2009-08-25 10:05:18 -0700528 if (LocationManager.NETWORK_PROVIDER.equals(name)) {
529 mNetworkLocationProvider = proxy;
530 }
Mike Lockwood275555c2009-05-01 11:30:34 -0400531
Mike Lockwood8dfe5d82009-05-07 11:49:01 -0400532 // notify provider of current network state
Mike Lockwood03d24672009-10-08 15:45:03 -0400533 proxy.updateNetworkState(mNetworkState, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800534 }
535 }
536
Mike Lockwood275555c2009-05-01 11:30:34 -0400537 public void installGeocodeProvider(IGeocodeProvider provider) {
538 if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER)
539 != PackageManager.PERMISSION_GRANTED) {
540 throw new SecurityException("Requires INSTALL_LOCATION_PROVIDER permission");
Mike Lockwooda55c3212009-04-15 11:10:11 -0400541 }
542
543 mGeocodeProvider = provider;
544 }
545
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800546 private boolean isAllowedBySettingsLocked(String provider) {
547 if (mEnabledProviders.contains(provider)) {
548 return true;
549 }
550 if (mDisabledProviders.contains(provider)) {
551 return false;
552 }
553 // Use system settings
554 ContentResolver resolver = mContext.getContentResolver();
555 String allowedProviders = Settings.Secure.getString(resolver,
556 Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
557
558 return ((allowedProviders != null) && (allowedProviders.contains(provider)));
559 }
560
561 private void checkPermissionsSafe(String provider) {
562 if (LocationManager.GPS_PROVIDER.equals(provider)
Mike Lockwoodb7e99222009-07-07 13:18:21 -0400563 && (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800564 != PackageManager.PERMISSION_GRANTED)) {
565 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
566 }
567 if (LocationManager.NETWORK_PROVIDER.equals(provider)
Mike Lockwoodb7e99222009-07-07 13:18:21 -0400568 && (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800569 != PackageManager.PERMISSION_GRANTED)
Mike Lockwoodb7e99222009-07-07 13:18:21 -0400570 && (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800571 != PackageManager.PERMISSION_GRANTED)) {
572 throw new SecurityException(
573 "Requires ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission");
574 }
575 }
576
577 private boolean isAllowedProviderSafe(String provider) {
578 if (LocationManager.GPS_PROVIDER.equals(provider)
Mike Lockwoodb7e99222009-07-07 13:18:21 -0400579 && (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800580 != PackageManager.PERMISSION_GRANTED)) {
581 return false;
582 }
583 if (LocationManager.NETWORK_PROVIDER.equals(provider)
Mike Lockwoodb7e99222009-07-07 13:18:21 -0400584 && (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800585 != PackageManager.PERMISSION_GRANTED)
Mike Lockwoodb7e99222009-07-07 13:18:21 -0400586 && (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800587 != PackageManager.PERMISSION_GRANTED)) {
588 return false;
589 }
590
591 return true;
592 }
593
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800594 public List<String> getAllProviders() {
595 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400596 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800597 return _getAllProvidersLocked();
598 }
599 } catch (SecurityException se) {
600 throw se;
601 } catch (Exception e) {
602 Log.e(TAG, "getAllProviders got exception:", e);
603 return null;
604 }
605 }
606
607 private List<String> _getAllProvidersLocked() {
The Android Open Source Project10592532009-03-18 17:39:46 -0700608 if (LOCAL_LOGV) {
609 Log.v(TAG, "getAllProviders");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800610 }
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400611 ArrayList<String> out = new ArrayList<String>(mProviders.size());
612 for (int i = mProviders.size() - 1; i >= 0; i--) {
613 LocationProviderProxy p = mProviders.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800614 out.add(p.getName());
615 }
616 return out;
617 }
618
619 public List<String> getProviders(boolean enabledOnly) {
620 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400621 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800622 return _getProvidersLocked(enabledOnly);
623 }
624 } catch (SecurityException se) {
625 throw se;
626 } catch (Exception e) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700627 Log.e(TAG, "getProviders got exception:", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800628 return null;
629 }
630 }
631
632 private List<String> _getProvidersLocked(boolean enabledOnly) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700633 if (LOCAL_LOGV) {
634 Log.v(TAG, "getProviders");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800635 }
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400636 ArrayList<String> out = new ArrayList<String>(mProviders.size());
637 for (int i = mProviders.size() - 1; i >= 0; i--) {
638 LocationProviderProxy p = mProviders.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800639 String name = p.getName();
640 if (isAllowedProviderSafe(name)) {
641 if (enabledOnly && !isAllowedBySettingsLocked(name)) {
642 continue;
643 }
644 out.add(name);
645 }
646 }
647 return out;
648 }
649
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800650 private void updateProvidersLocked() {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400651 for (int i = mProviders.size() - 1; i >= 0; i--) {
652 LocationProviderProxy p = mProviders.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800653 boolean isEnabled = p.isEnabled();
654 String name = p.getName();
655 boolean shouldBeEnabled = isAllowedBySettingsLocked(name);
656
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800657 if (isEnabled && !shouldBeEnabled) {
658 updateProviderListenersLocked(name, false);
659 } else if (!isEnabled && shouldBeEnabled) {
660 updateProviderListenersLocked(name, true);
661 }
662
663 }
664 }
665
666 private void updateProviderListenersLocked(String provider, boolean enabled) {
667 int listeners = 0;
668
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400669 LocationProviderProxy p = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800670 if (p == null) {
671 return;
672 }
673
674 ArrayList<Receiver> deadReceivers = null;
675
676 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
677 if (records != null) {
678 final int N = records.size();
679 for (int i=0; i<N; i++) {
680 UpdateRecord record = records.get(i);
681 // Sends a notification message to the receiver
Mike Lockwood48f17512009-04-23 09:12:08 -0700682 if (!record.mReceiver.callProviderEnabledLocked(provider, enabled)) {
683 if (deadReceivers == null) {
684 deadReceivers = new ArrayList<Receiver>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800685 }
Simon Schoar46866572009-06-10 21:12:10 +0200686 deadReceivers.add(record.mReceiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800687 }
688 listeners++;
689 }
690 }
691
692 if (deadReceivers != null) {
693 for (int i=deadReceivers.size()-1; i>=0; i--) {
694 removeUpdatesLocked(deadReceivers.get(i));
695 }
696 }
697
698 if (enabled) {
699 p.enable();
700 if (listeners > 0) {
701 p.setMinTime(getMinTimeLocked(provider));
702 p.enableLocationTracking(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800703 }
704 } else {
705 p.enableLocationTracking(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800706 p.disable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800707 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800708 }
709
710 private long getMinTimeLocked(String provider) {
711 long minTime = Long.MAX_VALUE;
712 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
713 if (records != null) {
714 for (int i=records.size()-1; i>=0; i--) {
715 minTime = Math.min(minTime, records.get(i).mMinTime);
716 }
717 }
718 return minTime;
719 }
720
721 private class UpdateRecord {
722 final String mProvider;
723 final Receiver mReceiver;
724 final long mMinTime;
725 final float mMinDistance;
726 final int mUid;
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400727 Location mLastFixBroadcast;
728 long mLastStatusBroadcast;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800729
730 /**
731 * Note: must be constructed with lock held.
732 */
733 UpdateRecord(String provider, long minTime, float minDistance,
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400734 Receiver receiver, int uid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800735 mProvider = provider;
736 mReceiver = receiver;
737 mMinTime = minTime;
738 mMinDistance = minDistance;
739 mUid = uid;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800740
741 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
742 if (records == null) {
743 records = new ArrayList<UpdateRecord>();
744 mRecordsByProvider.put(provider, records);
745 }
746 if (!records.contains(this)) {
747 records.add(this);
748 }
749 }
750
751 /**
752 * Method to be called when a record will no longer be used. Calling this multiple times
753 * must have the same effect as calling it once.
754 */
755 void disposeLocked() {
756 ArrayList<UpdateRecord> records = mRecordsByProvider.get(this.mProvider);
Mike Lockwood3a76fd62009-09-01 07:26:56 -0400757 if (records != null) {
758 records.remove(this);
759 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800760 }
761
762 @Override
763 public String toString() {
764 return "UpdateRecord{"
765 + Integer.toHexString(System.identityHashCode(this))
766 + " " + mProvider + " " + mReceiver + "}";
767 }
768
769 void dump(PrintWriter pw, String prefix) {
770 pw.println(prefix + this);
771 pw.println(prefix + "mProvider=" + mProvider + " mReceiver=" + mReceiver);
772 pw.println(prefix + "mMinTime=" + mMinTime + " mMinDistance=" + mMinDistance);
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400773 pw.println(prefix + "mUid=" + mUid);
774 pw.println(prefix + "mLastFixBroadcast:");
775 mLastFixBroadcast.dump(new PrintWriterPrinter(pw), prefix + " ");
776 pw.println(prefix + "mLastStatusBroadcast=" + mLastStatusBroadcast);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800777 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800778 }
779
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400780 private Receiver getReceiver(ILocationListener listener) {
781 IBinder binder = listener.asBinder();
782 Receiver receiver = mReceivers.get(binder);
783 if (receiver == null) {
784 receiver = new Receiver(listener);
785 mReceivers.put(binder, receiver);
786
787 try {
788 if (receiver.isListener()) {
789 receiver.getListener().asBinder().linkToDeath(receiver, 0);
790 }
791 } catch (RemoteException e) {
792 Log.e(TAG, "linkToDeath failed:", e);
793 return null;
794 }
795 }
796 return receiver;
797 }
798
799 private Receiver getReceiver(PendingIntent intent) {
800 Receiver receiver = mReceivers.get(intent);
801 if (receiver == null) {
802 receiver = new Receiver(intent);
803 mReceivers.put(intent, receiver);
804 }
805 return receiver;
806 }
807
808 private boolean providerHasListener(String provider, int uid, Receiver excludedReceiver) {
809 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
810 if (records != null) {
811 for (int i = records.size() - 1; i >= 0; i--) {
812 UpdateRecord record = records.get(i);
813 if (record.mUid == uid && record.mReceiver != excludedReceiver) {
814 return true;
815 }
816 }
817 }
Mike Lockwood95427cd2009-05-07 13:27:54 -0400818 for (ProximityAlert alert : mProximityAlerts.values()) {
819 if (alert.mUid == uid) {
820 return true;
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400821 }
822 }
823 return false;
824 }
825
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800826 public void requestLocationUpdates(String provider,
827 long minTime, float minDistance, ILocationListener listener) {
828
829 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400830 synchronized (mLock) {
831 requestLocationUpdatesLocked(provider, minTime, minDistance, getReceiver(listener));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800832 }
833 } catch (SecurityException se) {
834 throw se;
835 } catch (Exception e) {
836 Log.e(TAG, "requestUpdates got exception:", e);
837 }
838 }
839
840 public void requestLocationUpdatesPI(String provider,
841 long minTime, float minDistance, PendingIntent intent) {
842 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400843 synchronized (mLock) {
844 requestLocationUpdatesLocked(provider, minTime, minDistance, getReceiver(intent));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800845 }
846 } catch (SecurityException se) {
847 throw se;
848 } catch (Exception e) {
849 Log.e(TAG, "requestUpdates got exception:", e);
850 }
851 }
852
853 private void requestLocationUpdatesLocked(String provider,
854 long minTime, float minDistance, Receiver receiver) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700855 if (LOCAL_LOGV) {
856 Log.v(TAG, "_requestLocationUpdates: listener = " + receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800857 }
858
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400859 LocationProviderProxy proxy = mProvidersByName.get(provider);
860 if (proxy == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800861 throw new IllegalArgumentException("provider=" + provider);
862 }
863
864 checkPermissionsSafe(provider);
865
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800866 // so wakelock calls will succeed
867 final int callingUid = Binder.getCallingUid();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400868 boolean newUid = !providerHasListener(provider, callingUid, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800869 long identity = Binder.clearCallingIdentity();
870 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400871 UpdateRecord r = new UpdateRecord(provider, minTime, minDistance, receiver, callingUid);
872 UpdateRecord oldRecord = receiver.mUpdateRecords.put(provider, r);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800873 if (oldRecord != null) {
874 oldRecord.disposeLocked();
875 }
876
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400877 if (newUid) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400878 proxy.addListener(callingUid);
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400879 }
880
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800881 boolean isProviderEnabled = isAllowedBySettingsLocked(provider);
882 if (isProviderEnabled) {
883 long minTimeForProvider = getMinTimeLocked(provider);
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400884 proxy.setMinTime(minTimeForProvider);
885 proxy.enableLocationTracking(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800886 } else {
Mike Lockwood48f17512009-04-23 09:12:08 -0700887 // Notify the listener that updates are currently disabled
888 receiver.callProviderEnabledLocked(provider, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800889 }
890 } finally {
891 Binder.restoreCallingIdentity(identity);
892 }
893 }
894
895 public void removeUpdates(ILocationListener listener) {
896 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400897 synchronized (mLock) {
898 removeUpdatesLocked(getReceiver(listener));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800899 }
900 } catch (SecurityException se) {
901 throw se;
902 } catch (Exception e) {
903 Log.e(TAG, "removeUpdates got exception:", e);
904 }
905 }
906
907 public void removeUpdatesPI(PendingIntent intent) {
908 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400909 synchronized (mLock) {
910 removeUpdatesLocked(getReceiver(intent));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800911 }
912 } catch (SecurityException se) {
913 throw se;
914 } catch (Exception e) {
915 Log.e(TAG, "removeUpdates got exception:", e);
916 }
917 }
918
919 private void removeUpdatesLocked(Receiver receiver) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700920 if (LOCAL_LOGV) {
921 Log.v(TAG, "_removeUpdates: listener = " + receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800922 }
923
924 // so wakelock calls will succeed
925 final int callingUid = Binder.getCallingUid();
926 long identity = Binder.clearCallingIdentity();
927 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400928 if (mReceivers.remove(receiver.mKey) != null && receiver.isListener()) {
929 receiver.getListener().asBinder().unlinkToDeath(receiver, 0);
Joshua Bartel080b61b2009-10-05 12:44:46 -0400930 synchronized(receiver) {
931 if(receiver.mPendingBroadcasts > 0) {
932 decrementPendingBroadcasts();
933 receiver.mPendingBroadcasts = 0;
934 }
935 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800936 }
937
938 // Record which providers were associated with this listener
939 HashSet<String> providers = new HashSet<String>();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400940 HashMap<String,UpdateRecord> oldRecords = receiver.mUpdateRecords;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800941 if (oldRecords != null) {
942 // Call dispose() on the obsolete update records.
943 for (UpdateRecord record : oldRecords.values()) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400944 if (!providerHasListener(record.mProvider, callingUid, receiver)) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400945 LocationProviderProxy proxy = mProvidersByName.get(record.mProvider);
946 if (proxy != null) {
947 proxy.removeListener(callingUid);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800948 }
949 }
950 record.disposeLocked();
951 }
952 // Accumulate providers
953 providers.addAll(oldRecords.keySet());
954 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800955
956 // See if the providers associated with this listener have any
957 // other listeners; if one does, inform it of the new smallest minTime
958 // value; if one does not, disable location tracking for it
959 for (String provider : providers) {
960 // If provider is already disabled, don't need to do anything
961 if (!isAllowedBySettingsLocked(provider)) {
962 continue;
963 }
964
965 boolean hasOtherListener = false;
966 ArrayList<UpdateRecord> recordsForProvider = mRecordsByProvider.get(provider);
967 if (recordsForProvider != null && recordsForProvider.size() > 0) {
968 hasOtherListener = true;
969 }
970
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400971 LocationProviderProxy p = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800972 if (p != null) {
973 if (hasOtherListener) {
974 p.setMinTime(getMinTimeLocked(provider));
975 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800976 p.enableLocationTracking(false);
977 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800978 }
979 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800980 } finally {
981 Binder.restoreCallingIdentity(identity);
982 }
983 }
984
985 public boolean addGpsStatusListener(IGpsStatusListener listener) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400986 if (mGpsStatusProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800987 return false;
988 }
Mike Lockwoodb7e99222009-07-07 13:18:21 -0400989 if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) !=
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800990 PackageManager.PERMISSION_GRANTED) {
991 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
992 }
993
994 try {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400995 mGpsStatusProvider.addGpsStatusListener(listener);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800996 } catch (RemoteException e) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400997 Log.e(TAG, "mGpsStatusProvider.addGpsStatusListener failed", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800998 return false;
999 }
1000 return true;
1001 }
1002
1003 public void removeGpsStatusListener(IGpsStatusListener listener) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001004 synchronized (mLock) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001005 try {
1006 mGpsStatusProvider.removeGpsStatusListener(listener);
1007 } catch (Exception e) {
1008 Log.e(TAG, "mGpsStatusProvider.removeGpsStatusListener failed", e);
1009 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001010 }
1011 }
1012
1013 public boolean sendExtraCommand(String provider, String command, Bundle extras) {
Mike Lockwoodc6cc8362009-08-17 13:16:08 -04001014 if (provider == null) {
1015 // throw NullPointerException to remain compatible with previous implementation
1016 throw new NullPointerException();
1017 }
1018
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001019 // first check for permission to the provider
1020 checkPermissionsSafe(provider);
1021 // and check for ACCESS_LOCATION_EXTRA_COMMANDS
Mike Lockwoodb7e99222009-07-07 13:18:21 -04001022 if ((mContext.checkCallingOrSelfPermission(ACCESS_LOCATION_EXTRA_COMMANDS)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001023 != PackageManager.PERMISSION_GRANTED)) {
1024 throw new SecurityException("Requires ACCESS_LOCATION_EXTRA_COMMANDS permission");
1025 }
1026
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001027 synchronized (mLock) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001028 LocationProviderProxy proxy = mProvidersByName.get(provider);
Mike Lockwood6ba7ae12009-08-17 08:39:12 -04001029 if (proxy == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001030 return false;
1031 }
1032
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001033 return proxy.sendExtraCommand(command, extras);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001034 }
1035 }
1036
Danke Xie22d1f9f2009-08-18 18:28:45 -04001037 public boolean sendNiResponse(int notifId, int userResponse)
1038 {
Mike Lockwood18ad9f62009-08-27 14:01:23 -07001039 if (Binder.getCallingUid() != Process.myUid()) {
1040 throw new SecurityException(
1041 "calling sendNiResponse from outside of the system is not allowed");
1042 }
Danke Xie22d1f9f2009-08-18 18:28:45 -04001043 try {
1044 return mNetInitiatedListener.sendNiResponse(notifId, userResponse);
1045 }
1046 catch (RemoteException e)
1047 {
1048 Log.e(TAG, "RemoteException in LocationManagerService.sendNiResponse");
1049 return false;
1050 }
1051 }
1052
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001053 class ProximityAlert {
1054 final int mUid;
1055 final double mLatitude;
1056 final double mLongitude;
1057 final float mRadius;
1058 final long mExpiration;
1059 final PendingIntent mIntent;
1060 final Location mLocation;
1061
1062 public ProximityAlert(int uid, double latitude, double longitude,
1063 float radius, long expiration, PendingIntent intent) {
1064 mUid = uid;
1065 mLatitude = latitude;
1066 mLongitude = longitude;
1067 mRadius = radius;
1068 mExpiration = expiration;
1069 mIntent = intent;
1070
1071 mLocation = new Location("");
1072 mLocation.setLatitude(latitude);
1073 mLocation.setLongitude(longitude);
1074 }
1075
1076 long getExpiration() {
1077 return mExpiration;
1078 }
1079
1080 PendingIntent getIntent() {
1081 return mIntent;
1082 }
1083
Yusuf T. Mobile2d2078a2009-07-14 15:46:26 -07001084 boolean isInProximity(double latitude, double longitude, float accuracy) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001085 Location loc = new Location("");
1086 loc.setLatitude(latitude);
1087 loc.setLongitude(longitude);
1088
1089 double radius = loc.distanceTo(mLocation);
Yusuf T. Mobile2d2078a2009-07-14 15:46:26 -07001090 return radius <= Math.max(mRadius,accuracy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001091 }
1092
1093 @Override
1094 public String toString() {
1095 return "ProximityAlert{"
1096 + Integer.toHexString(System.identityHashCode(this))
1097 + " uid " + mUid + mIntent + "}";
1098 }
1099
1100 void dump(PrintWriter pw, String prefix) {
1101 pw.println(prefix + this);
1102 pw.println(prefix + "mLatitude=" + mLatitude + " mLongitude=" + mLongitude);
1103 pw.println(prefix + "mRadius=" + mRadius + " mExpiration=" + mExpiration);
1104 pw.println(prefix + "mIntent=" + mIntent);
1105 pw.println(prefix + "mLocation:");
1106 mLocation.dump(new PrintWriterPrinter(pw), prefix + " ");
1107 }
1108 }
1109
1110 // Listener for receiving locations to trigger proximity alerts
Mike Lockwood48f17512009-04-23 09:12:08 -07001111 class ProximityListener extends ILocationListener.Stub implements PendingIntent.OnFinished {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001112
1113 boolean isGpsAvailable = false;
1114
1115 // Note: this is called with the lock held.
1116 public void onLocationChanged(Location loc) {
1117
1118 // If Gps is available, then ignore updates from NetworkLocationProvider
1119 if (loc.getProvider().equals(LocationManager.GPS_PROVIDER)) {
1120 isGpsAvailable = true;
1121 }
1122 if (isGpsAvailable && loc.getProvider().equals(LocationManager.NETWORK_PROVIDER)) {
1123 return;
1124 }
1125
1126 // Process proximity alerts
1127 long now = System.currentTimeMillis();
1128 double latitude = loc.getLatitude();
1129 double longitude = loc.getLongitude();
Yusuf T. Mobile2d2078a2009-07-14 15:46:26 -07001130 float accuracy = loc.getAccuracy();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001131 ArrayList<PendingIntent> intentsToRemove = null;
1132
1133 for (ProximityAlert alert : mProximityAlerts.values()) {
1134 PendingIntent intent = alert.getIntent();
1135 long expiration = alert.getExpiration();
1136
1137 if ((expiration == -1) || (now <= expiration)) {
1138 boolean entered = mProximitiesEntered.contains(alert);
1139 boolean inProximity =
Yusuf T. Mobile2d2078a2009-07-14 15:46:26 -07001140 alert.isInProximity(latitude, longitude, accuracy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001141 if (!entered && inProximity) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001142 if (LOCAL_LOGV) {
1143 Log.v(TAG, "Entered alert");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001144 }
1145 mProximitiesEntered.add(alert);
1146 Intent enteredIntent = new Intent();
1147 enteredIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, true);
1148 try {
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001149 synchronized (this) {
1150 // synchronize to ensure incrementPendingBroadcasts()
Mike Lockwood48f17512009-04-23 09:12:08 -07001151 // is called before decrementPendingBroadcasts()
1152 intent.send(mContext, 0, enteredIntent, this, mLocationHandler);
1153 // call this after broadcasting so we do not increment
1154 // if we throw an exeption.
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001155 incrementPendingBroadcasts();
Mike Lockwood48f17512009-04-23 09:12:08 -07001156 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001157 } catch (PendingIntent.CanceledException e) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001158 if (LOCAL_LOGV) {
1159 Log.v(TAG, "Canceled proximity alert: " + alert, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001160 }
1161 if (intentsToRemove == null) {
1162 intentsToRemove = new ArrayList<PendingIntent>();
1163 }
1164 intentsToRemove.add(intent);
1165 }
1166 } else if (entered && !inProximity) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001167 if (LOCAL_LOGV) {
1168 Log.v(TAG, "Exited alert");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001169 }
1170 mProximitiesEntered.remove(alert);
1171 Intent exitedIntent = new Intent();
1172 exitedIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, false);
1173 try {
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001174 synchronized (this) {
1175 // synchronize to ensure incrementPendingBroadcasts()
Mike Lockwood48f17512009-04-23 09:12:08 -07001176 // is called before decrementPendingBroadcasts()
1177 intent.send(mContext, 0, exitedIntent, this, mLocationHandler);
1178 // call this after broadcasting so we do not increment
1179 // if we throw an exeption.
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001180 incrementPendingBroadcasts();
Mike Lockwood48f17512009-04-23 09:12:08 -07001181 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001182 } catch (PendingIntent.CanceledException e) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001183 if (LOCAL_LOGV) {
1184 Log.v(TAG, "Canceled proximity alert: " + alert, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001185 }
1186 if (intentsToRemove == null) {
1187 intentsToRemove = new ArrayList<PendingIntent>();
1188 }
1189 intentsToRemove.add(intent);
1190 }
1191 }
1192 } else {
1193 // Mark alert for expiration
The Android Open Source Project10592532009-03-18 17:39:46 -07001194 if (LOCAL_LOGV) {
1195 Log.v(TAG, "Expiring proximity alert: " + alert);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001196 }
1197 if (intentsToRemove == null) {
1198 intentsToRemove = new ArrayList<PendingIntent>();
1199 }
1200 intentsToRemove.add(alert.getIntent());
1201 }
1202 }
1203
1204 // Remove expired alerts
1205 if (intentsToRemove != null) {
1206 for (PendingIntent i : intentsToRemove) {
1207 mProximityAlerts.remove(i);
1208 ProximityAlert alert = mProximityAlerts.get(i);
1209 mProximitiesEntered.remove(alert);
1210 }
1211 }
1212
1213 }
1214
1215 // Note: this is called with the lock held.
1216 public void onProviderDisabled(String provider) {
1217 if (provider.equals(LocationManager.GPS_PROVIDER)) {
1218 isGpsAvailable = false;
1219 }
1220 }
1221
1222 // Note: this is called with the lock held.
1223 public void onProviderEnabled(String provider) {
1224 // ignore
1225 }
1226
1227 // Note: this is called with the lock held.
1228 public void onStatusChanged(String provider, int status, Bundle extras) {
1229 if ((provider.equals(LocationManager.GPS_PROVIDER)) &&
1230 (status != LocationProvider.AVAILABLE)) {
1231 isGpsAvailable = false;
1232 }
1233 }
Mike Lockwood48f17512009-04-23 09:12:08 -07001234
1235 public void onSendFinished(PendingIntent pendingIntent, Intent intent,
1236 int resultCode, String resultData, Bundle resultExtras) {
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001237 // synchronize to ensure incrementPendingBroadcasts()
1238 // is called before decrementPendingBroadcasts()
1239 synchronized (this) {
1240 decrementPendingBroadcasts();
1241 }
Mike Lockwood48f17512009-04-23 09:12:08 -07001242 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001243 }
1244
1245 public void addProximityAlert(double latitude, double longitude,
1246 float radius, long expiration, PendingIntent intent) {
1247 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001248 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001249 addProximityAlertLocked(latitude, longitude, radius, expiration, intent);
1250 }
1251 } catch (SecurityException se) {
1252 throw se;
1253 } catch (Exception e) {
1254 Log.e(TAG, "addProximityAlert got exception:", e);
1255 }
1256 }
1257
1258 private void addProximityAlertLocked(double latitude, double longitude,
1259 float radius, long expiration, PendingIntent intent) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001260 if (LOCAL_LOGV) {
1261 Log.v(TAG, "addProximityAlert: latitude = " + latitude +
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001262 ", longitude = " + longitude +
1263 ", expiration = " + expiration +
1264 ", intent = " + intent);
1265 }
1266
1267 // Require ability to access all providers for now
1268 if (!isAllowedProviderSafe(LocationManager.GPS_PROVIDER) ||
1269 !isAllowedProviderSafe(LocationManager.NETWORK_PROVIDER)) {
1270 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
1271 }
1272
1273 if (expiration != -1) {
1274 expiration += System.currentTimeMillis();
1275 }
1276 ProximityAlert alert = new ProximityAlert(Binder.getCallingUid(),
1277 latitude, longitude, radius, expiration, intent);
1278 mProximityAlerts.put(intent, alert);
1279
Mike Lockwood48f17512009-04-23 09:12:08 -07001280 if (mProximityReceiver == null) {
1281 mProximityListener = new ProximityListener();
1282 mProximityReceiver = new Receiver(mProximityListener);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001283
Mike Lockwood95427cd2009-05-07 13:27:54 -04001284 for (int i = mProviders.size() - 1; i >= 0; i--) {
1285 LocationProviderProxy provider = mProviders.get(i);
Mike Lockwood48f17512009-04-23 09:12:08 -07001286 requestLocationUpdatesLocked(provider.getName(), 1000L, 1.0f, mProximityReceiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001287 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001288 }
1289 }
1290
1291 public void removeProximityAlert(PendingIntent intent) {
1292 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001293 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001294 removeProximityAlertLocked(intent);
1295 }
1296 } catch (SecurityException se) {
1297 throw se;
1298 } catch (Exception e) {
1299 Log.e(TAG, "removeProximityAlert got exception:", e);
1300 }
1301 }
1302
1303 private void removeProximityAlertLocked(PendingIntent intent) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001304 if (LOCAL_LOGV) {
1305 Log.v(TAG, "removeProximityAlert: intent = " + intent);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001306 }
1307
1308 mProximityAlerts.remove(intent);
1309 if (mProximityAlerts.size() == 0) {
Mike Lockwood48f17512009-04-23 09:12:08 -07001310 removeUpdatesLocked(mProximityReceiver);
1311 mProximityReceiver = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001312 mProximityListener = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001313 }
1314 }
1315
1316 /**
1317 * @return null if the provider does not exits
1318 * @throw SecurityException if the provider is not allowed to be
1319 * accessed by the caller
1320 */
1321 public Bundle getProviderInfo(String provider) {
1322 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001323 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001324 return _getProviderInfoLocked(provider);
1325 }
1326 } catch (SecurityException se) {
1327 throw se;
1328 } catch (Exception e) {
1329 Log.e(TAG, "_getProviderInfo got exception:", e);
1330 return null;
1331 }
1332 }
1333
1334 private Bundle _getProviderInfoLocked(String provider) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001335 LocationProviderProxy p = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001336 if (p == null) {
1337 return null;
1338 }
1339
1340 checkPermissionsSafe(provider);
1341
1342 Bundle b = new Bundle();
1343 b.putBoolean("network", p.requiresNetwork());
1344 b.putBoolean("satellite", p.requiresSatellite());
1345 b.putBoolean("cell", p.requiresCell());
1346 b.putBoolean("cost", p.hasMonetaryCost());
1347 b.putBoolean("altitude", p.supportsAltitude());
1348 b.putBoolean("speed", p.supportsSpeed());
1349 b.putBoolean("bearing", p.supportsBearing());
1350 b.putInt("power", p.getPowerRequirement());
1351 b.putInt("accuracy", p.getAccuracy());
1352
1353 return b;
1354 }
1355
1356 public boolean isProviderEnabled(String provider) {
1357 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001358 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001359 return _isProviderEnabledLocked(provider);
1360 }
1361 } catch (SecurityException se) {
1362 throw se;
1363 } catch (Exception e) {
1364 Log.e(TAG, "isProviderEnabled got exception:", e);
1365 return false;
1366 }
1367 }
1368
Mike Lockwood275555c2009-05-01 11:30:34 -04001369 public void reportLocation(Location location) {
1370 if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER)
1371 != PackageManager.PERMISSION_GRANTED) {
1372 throw new SecurityException("Requires INSTALL_LOCATION_PROVIDER permission");
1373 }
1374
Mike Lockwood4e50b782009-04-03 08:24:43 -07001375 mLocationHandler.removeMessages(MESSAGE_LOCATION_CHANGED, location);
1376 Message m = Message.obtain(mLocationHandler, MESSAGE_LOCATION_CHANGED, location);
1377 mLocationHandler.sendMessageAtFrontOfQueue(m);
1378 }
1379
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001380 private boolean _isProviderEnabledLocked(String provider) {
1381 checkPermissionsSafe(provider);
1382
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001383 LocationProviderProxy p = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001384 if (p == null) {
1385 throw new IllegalArgumentException("provider=" + provider);
1386 }
1387 return isAllowedBySettingsLocked(provider);
1388 }
1389
1390 public Location getLastKnownLocation(String provider) {
1391 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001392 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001393 return _getLastKnownLocationLocked(provider);
1394 }
1395 } catch (SecurityException se) {
1396 throw se;
1397 } catch (Exception e) {
1398 Log.e(TAG, "getLastKnownLocation got exception:", e);
1399 return null;
1400 }
1401 }
1402
1403 private Location _getLastKnownLocationLocked(String provider) {
1404 checkPermissionsSafe(provider);
1405
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001406 LocationProviderProxy p = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001407 if (p == null) {
1408 throw new IllegalArgumentException("provider=" + provider);
1409 }
1410
1411 if (!isAllowedBySettingsLocked(provider)) {
1412 return null;
1413 }
1414
Mike Lockwood9aa1fa22009-09-01 07:51:15 -04001415 return mLastKnownLocation.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001416 }
1417
1418 private static boolean shouldBroadcastSafe(Location loc, Location lastLoc, UpdateRecord record) {
1419 // Always broadcast the first update
1420 if (lastLoc == null) {
1421 return true;
1422 }
1423
1424 // Don't broadcast same location again regardless of condition
1425 // TODO - we should probably still rebroadcast if user explicitly sets a minTime > 0
1426 if (loc.getTime() == lastLoc.getTime()) {
1427 return false;
1428 }
1429
1430 // Check whether sufficient distance has been traveled
1431 double minDistance = record.mMinDistance;
1432 if (minDistance > 0.0) {
1433 if (loc.distanceTo(lastLoc) <= minDistance) {
1434 return false;
1435 }
1436 }
1437
1438 return true;
1439 }
1440
Mike Lockwood4e50b782009-04-03 08:24:43 -07001441 private void handleLocationChangedLocked(Location location) {
1442 String provider = location.getProvider();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001443 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
1444 if (records == null || records.size() == 0) {
1445 return;
1446 }
1447
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001448 LocationProviderProxy p = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001449 if (p == null) {
1450 return;
1451 }
1452
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001453 // Update last known location for provider
Mike Lockwood4e50b782009-04-03 08:24:43 -07001454 Location lastLocation = mLastKnownLocation.get(provider);
1455 if (lastLocation == null) {
1456 mLastKnownLocation.put(provider, new Location(location));
1457 } else {
1458 lastLocation.set(location);
1459 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001460
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001461 // Fetch latest status update time
1462 long newStatusUpdateTime = p.getStatusUpdateTime();
1463
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001464 // Get latest status
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001465 Bundle extras = new Bundle();
1466 int status = p.getStatus(extras);
1467
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001468 ArrayList<Receiver> deadReceivers = null;
1469
1470 // Broadcast location or status to all listeners
1471 final int N = records.size();
1472 for (int i=0; i<N; i++) {
1473 UpdateRecord r = records.get(i);
1474 Receiver receiver = r.mReceiver;
1475
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001476 Location lastLoc = r.mLastFixBroadcast;
Mike Lockwood4e50b782009-04-03 08:24:43 -07001477 if ((lastLoc == null) || shouldBroadcastSafe(location, lastLoc, r)) {
1478 if (lastLoc == null) {
1479 lastLoc = new Location(location);
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001480 r.mLastFixBroadcast = lastLoc;
Mike Lockwood4e50b782009-04-03 08:24:43 -07001481 } else {
1482 lastLoc.set(location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001483 }
Mike Lockwood4e50b782009-04-03 08:24:43 -07001484 if (!receiver.callLocationChangedLocked(location)) {
1485 Log.w(TAG, "RemoteException calling onLocationChanged on " + receiver);
1486 if (deadReceivers == null) {
1487 deadReceivers = new ArrayList<Receiver>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001488 }
Mike Lockwood4e50b782009-04-03 08:24:43 -07001489 deadReceivers.add(receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001490 }
1491 }
1492
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001493 long prevStatusUpdateTime = r.mLastStatusBroadcast;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001494 if ((newStatusUpdateTime > prevStatusUpdateTime) &&
1495 (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) {
1496
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001497 r.mLastStatusBroadcast = newStatusUpdateTime;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001498 if (!receiver.callStatusChangedLocked(provider, status, extras)) {
1499 Log.w(TAG, "RemoteException calling onStatusChanged on " + receiver);
1500 if (deadReceivers == null) {
1501 deadReceivers = new ArrayList<Receiver>();
1502 }
1503 if (!deadReceivers.contains(receiver)) {
1504 deadReceivers.add(receiver);
1505 }
1506 }
1507 }
1508 }
1509
1510 if (deadReceivers != null) {
1511 for (int i=deadReceivers.size()-1; i>=0; i--) {
1512 removeUpdatesLocked(deadReceivers.get(i));
1513 }
1514 }
1515 }
1516
1517 private class LocationWorkerHandler extends Handler {
1518
1519 @Override
1520 public void handleMessage(Message msg) {
1521 try {
Mike Lockwood4e50b782009-04-03 08:24:43 -07001522 if (msg.what == MESSAGE_LOCATION_CHANGED) {
1523 // log("LocationWorkerHandler: MESSAGE_LOCATION_CHANGED!");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001524
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001525 synchronized (mLock) {
Mike Lockwood4e50b782009-04-03 08:24:43 -07001526 Location location = (Location) msg.obj;
Mike Lockwoodfd6e5f02009-05-21 11:28:20 -04001527 String provider = location.getProvider();
Mike Lockwood98cb6672009-04-17 18:03:44 -04001528
Mike Lockwoodfd6e5f02009-05-21 11:28:20 -04001529 // notify other providers of the new location
1530 for (int i = mProviders.size() - 1; i >= 0; i--) {
1531 LocationProviderProxy proxy = mProviders.get(i);
1532 if (!provider.equals(proxy.getName())) {
1533 proxy.updateLocation(location);
Mike Lockwood98cb6672009-04-17 18:03:44 -04001534 }
1535 }
1536
Mike Lockwoodfd6e5f02009-05-21 11:28:20 -04001537 if (isAllowedBySettingsLocked(provider)) {
1538 handleLocationChangedLocked(location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001539 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001540 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001541 }
1542 } catch (Exception e) {
1543 // Log, don't crash!
1544 Log.e(TAG, "Exception in LocationWorkerHandler.handleMessage:", e);
1545 }
1546 }
1547 }
1548
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001549 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1550 @Override
1551 public void onReceive(Context context, Intent intent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001552 String action = intent.getAction();
1553
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001554 if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001555 || action.equals(Intent.ACTION_PACKAGE_RESTARTED)) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001556 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001557 int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
1558 if (uid >= 0) {
1559 ArrayList<Receiver> removedRecs = null;
1560 for (ArrayList<UpdateRecord> i : mRecordsByProvider.values()) {
1561 for (int j=i.size()-1; j>=0; j--) {
1562 UpdateRecord ur = i.get(j);
1563 if (ur.mReceiver.isPendingIntent() && ur.mUid == uid) {
1564 if (removedRecs == null) {
1565 removedRecs = new ArrayList<Receiver>();
1566 }
1567 if (!removedRecs.contains(ur.mReceiver)) {
1568 removedRecs.add(ur.mReceiver);
1569 }
1570 }
1571 }
1572 }
1573 ArrayList<ProximityAlert> removedAlerts = null;
1574 for (ProximityAlert i : mProximityAlerts.values()) {
1575 if (i.mUid == uid) {
1576 if (removedAlerts == null) {
1577 removedAlerts = new ArrayList<ProximityAlert>();
1578 }
1579 if (!removedAlerts.contains(i)) {
1580 removedAlerts.add(i);
1581 }
1582 }
1583 }
1584 if (removedRecs != null) {
1585 for (int i=removedRecs.size()-1; i>=0; i--) {
1586 removeUpdatesLocked(removedRecs.get(i));
1587 }
1588 }
1589 if (removedAlerts != null) {
1590 for (int i=removedAlerts.size()-1; i>=0; i--) {
1591 removeProximityAlertLocked(removedAlerts.get(i).mIntent);
1592 }
1593 }
1594 }
1595 }
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001596 } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001597 boolean noConnectivity =
1598 intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
1599 if (!noConnectivity) {
The Android Open Source Project4df24232009-03-05 14:34:35 -08001600 mNetworkState = LocationProvider.AVAILABLE;
1601 } else {
1602 mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001603 }
Mike Lockwood03d24672009-10-08 15:45:03 -04001604 NetworkInfo info =
1605 (NetworkInfo)intent.getExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001606
1607 // Notify location providers of current network state
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001608 synchronized (mLock) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001609 for (int i = mProviders.size() - 1; i >= 0; i--) {
1610 LocationProviderProxy provider = mProviders.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001611 if (provider.requiresNetwork()) {
Mike Lockwood03d24672009-10-08 15:45:03 -04001612 provider.updateNetworkState(mNetworkState, info);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001613 }
1614 }
1615 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001616 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001617 }
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001618 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001619
1620 // Wake locks
1621
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001622 private void incrementPendingBroadcasts() {
1623 synchronized (mWakeLock) {
1624 if (mPendingBroadcasts++ == 0) {
1625 try {
1626 mWakeLock.acquire();
1627 log("Acquired wakelock");
1628 } catch (Exception e) {
1629 // This is to catch a runtime exception thrown when we try to release an
1630 // already released lock.
1631 Log.e(TAG, "exception in acquireWakeLock()", e);
1632 }
1633 }
Mike Lockwood48f17512009-04-23 09:12:08 -07001634 }
1635 }
1636
1637 private void decrementPendingBroadcasts() {
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001638 synchronized (mWakeLock) {
Mike Lockwood48f17512009-04-23 09:12:08 -07001639 if (--mPendingBroadcasts == 0) {
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001640 try {
1641 // Release wake lock
1642 if (mWakeLock.isHeld()) {
1643 mWakeLock.release();
1644 log("Released wakelock");
1645 } else {
1646 log("Can't release wakelock again!");
1647 }
1648 } catch (Exception e) {
1649 // This is to catch a runtime exception thrown when we try to release an
1650 // already released lock.
1651 Log.e(TAG, "exception in releaseWakeLock()", e);
1652 }
Mike Lockwood48f17512009-04-23 09:12:08 -07001653 }
1654 }
1655 }
1656
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001657 // Geocoder
1658
1659 public String getFromLocation(double latitude, double longitude, int maxResults,
Mike Lockwooda55c3212009-04-15 11:10:11 -04001660 String language, String country, String variant, String appName, List<Address> addrs) {
1661 if (mGeocodeProvider != null) {
1662 try {
1663 return mGeocodeProvider.getFromLocation(latitude, longitude, maxResults, language, country,
1664 variant, appName, addrs);
1665 } catch (RemoteException e) {
1666 Log.e(TAG, "getFromLocation failed", e);
Mike Lockwood3681f262009-05-12 10:52:03 -04001667 mGeocodeProvider = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001668 }
1669 }
Mike Lockwooda55c3212009-04-15 11:10:11 -04001670 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001671 }
1672
Mike Lockwooda55c3212009-04-15 11:10:11 -04001673
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001674 public String getFromLocationName(String locationName,
Mike Lockwooda55c3212009-04-15 11:10:11 -04001675 double lowerLeftLatitude, double lowerLeftLongitude,
1676 double upperRightLatitude, double upperRightLongitude, int maxResults,
1677 String language, String country, String variant, String appName, List<Address> addrs) {
1678
1679 if (mGeocodeProvider != null) {
1680 try {
1681 return mGeocodeProvider.getFromLocationName(locationName, lowerLeftLatitude,
1682 lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
1683 maxResults, language, country, variant, appName, addrs);
1684 } catch (RemoteException e) {
1685 Log.e(TAG, "getFromLocationName failed", e);
Mike Lockwood3681f262009-05-12 10:52:03 -04001686 mGeocodeProvider = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001687 }
1688 }
Mike Lockwooda55c3212009-04-15 11:10:11 -04001689 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001690 }
1691
1692 // Mock Providers
1693
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001694 private void checkMockPermissionsSafe() {
1695 boolean allowMocks = Settings.Secure.getInt(mContext.getContentResolver(),
1696 Settings.Secure.ALLOW_MOCK_LOCATION, 0) == 1;
1697 if (!allowMocks) {
1698 throw new SecurityException("Requires ACCESS_MOCK_LOCATION secure setting");
1699 }
1700
1701 if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) !=
1702 PackageManager.PERMISSION_GRANTED) {
1703 throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission");
1704 }
1705 }
1706
1707 public void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite,
1708 boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
1709 boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
1710 checkMockPermissionsSafe();
1711
Mike Lockwood86328a92009-10-23 08:38:25 -04001712 long identity = Binder.clearCallingIdentity();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001713 synchronized (mLock) {
Mike Lockwood4e50b782009-04-03 08:24:43 -07001714 MockProvider provider = new MockProvider(name, this,
1715 requiresNetwork, requiresSatellite,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001716 requiresCell, hasMonetaryCost, supportsAltitude,
1717 supportsSpeed, supportsBearing, powerRequirement, accuracy);
Mike Lockwood7566c1d2009-08-25 10:05:18 -07001718 // remove the real provider if we are replacing GPS or network provider
1719 if (LocationManager.GPS_PROVIDER.equals(name)
1720 || LocationManager.NETWORK_PROVIDER.equals(name)) {
1721 LocationProviderProxy proxy = mProvidersByName.get(name);
1722 if (proxy != null) {
1723 proxy.enableLocationTracking(false);
1724 removeProvider(proxy);
1725 }
1726 }
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001727 if (mProvidersByName.get(name) != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001728 throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
1729 }
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001730 addProvider(new LocationProviderProxy(name, provider));
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001731 mMockProviders.put(name, provider);
Mike Lockwood7566c1d2009-08-25 10:05:18 -07001732 mLastKnownLocation.put(name, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001733 updateProvidersLocked();
1734 }
Mike Lockwood86328a92009-10-23 08:38:25 -04001735 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001736 }
1737
1738 public void removeTestProvider(String provider) {
1739 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001740 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001741 MockProvider mockProvider = mMockProviders.get(provider);
1742 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001743 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1744 }
Mike Lockwood86328a92009-10-23 08:38:25 -04001745 long identity = Binder.clearCallingIdentity();
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001746 removeProvider(mProvidersByName.get(provider));
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001747 mMockProviders.remove(mockProvider);
Mike Lockwood7566c1d2009-08-25 10:05:18 -07001748 // reinstall real provider if we were mocking GPS or network provider
1749 if (LocationManager.GPS_PROVIDER.equals(provider) &&
1750 mGpsLocationProvider != null) {
1751 addProvider(mGpsLocationProvider);
1752 } else if (LocationManager.NETWORK_PROVIDER.equals(provider) &&
1753 mNetworkLocationProvider != null) {
1754 addProvider(mNetworkLocationProvider);
1755 }
1756 mLastKnownLocation.put(provider, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001757 updateProvidersLocked();
Mike Lockwood86328a92009-10-23 08:38:25 -04001758 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001759 }
1760 }
1761
1762 public void setTestProviderLocation(String provider, Location loc) {
1763 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001764 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001765 MockProvider mockProvider = mMockProviders.get(provider);
1766 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001767 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1768 }
Mike Lockwood95427cd2009-05-07 13:27:54 -04001769 // clear calling identity so INSTALL_LOCATION_PROVIDER permission is not required
1770 long identity = Binder.clearCallingIdentity();
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001771 mockProvider.setLocation(loc);
Mike Lockwood95427cd2009-05-07 13:27:54 -04001772 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001773 }
1774 }
1775
1776 public void clearTestProviderLocation(String provider) {
1777 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001778 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001779 MockProvider mockProvider = mMockProviders.get(provider);
1780 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001781 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1782 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001783 mockProvider.clearLocation();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001784 }
1785 }
1786
1787 public void setTestProviderEnabled(String provider, boolean enabled) {
1788 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001789 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001790 MockProvider mockProvider = mMockProviders.get(provider);
1791 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001792 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1793 }
Mike Lockwood86328a92009-10-23 08:38:25 -04001794 long identity = Binder.clearCallingIdentity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001795 if (enabled) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001796 mockProvider.enable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001797 mEnabledProviders.add(provider);
1798 mDisabledProviders.remove(provider);
1799 } else {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001800 mockProvider.disable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001801 mEnabledProviders.remove(provider);
1802 mDisabledProviders.add(provider);
1803 }
1804 updateProvidersLocked();
Mike Lockwood86328a92009-10-23 08:38:25 -04001805 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001806 }
1807 }
1808
1809 public void clearTestProviderEnabled(String provider) {
1810 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001811 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001812 MockProvider mockProvider = mMockProviders.get(provider);
1813 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001814 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1815 }
Mike Lockwood86328a92009-10-23 08:38:25 -04001816 long identity = Binder.clearCallingIdentity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001817 mEnabledProviders.remove(provider);
1818 mDisabledProviders.remove(provider);
1819 updateProvidersLocked();
Mike Lockwood86328a92009-10-23 08:38:25 -04001820 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001821 }
1822 }
1823
1824 public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
1825 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001826 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001827 MockProvider mockProvider = mMockProviders.get(provider);
1828 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001829 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1830 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001831 mockProvider.setStatus(status, extras, updateTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001832 }
1833 }
1834
1835 public void clearTestProviderStatus(String provider) {
1836 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001837 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001838 MockProvider mockProvider = mMockProviders.get(provider);
1839 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001840 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1841 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001842 mockProvider.clearStatus();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001843 }
1844 }
1845
1846 private void log(String log) {
1847 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1848 Log.d(TAG, log);
1849 }
1850 }
1851
1852 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1853 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1854 != PackageManager.PERMISSION_GRANTED) {
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001855 pw.println("Permission Denial: can't dump LocationManagerService from from pid="
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001856 + Binder.getCallingPid()
1857 + ", uid=" + Binder.getCallingUid());
1858 return;
1859 }
1860
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001861 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001862 pw.println("Current Location Manager state:");
1863 pw.println(" sProvidersLoaded=" + sProvidersLoaded);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001864 pw.println(" Listeners:");
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001865 int N = mReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001866 for (int i=0; i<N; i++) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001867 pw.println(" " + mReceivers.get(i));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001868 }
1869 pw.println(" Location Listeners:");
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001870 for (Receiver i : mReceivers.values()) {
1871 pw.println(" " + i + ":");
1872 for (Map.Entry<String,UpdateRecord> j : i.mUpdateRecords.entrySet()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001873 pw.println(" " + j.getKey() + ":");
1874 j.getValue().dump(pw, " ");
1875 }
1876 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001877 pw.println(" Records by Provider:");
1878 for (Map.Entry<String, ArrayList<UpdateRecord>> i
1879 : mRecordsByProvider.entrySet()) {
1880 pw.println(" " + i.getKey() + ":");
1881 for (UpdateRecord j : i.getValue()) {
1882 pw.println(" " + j + ":");
1883 j.dump(pw, " ");
1884 }
1885 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001886 pw.println(" Last Known Locations:");
1887 for (Map.Entry<String, Location> i
1888 : mLastKnownLocation.entrySet()) {
1889 pw.println(" " + i.getKey() + ":");
1890 i.getValue().dump(new PrintWriterPrinter(pw), " ");
1891 }
1892 if (mProximityAlerts.size() > 0) {
1893 pw.println(" Proximity Alerts:");
1894 for (Map.Entry<PendingIntent, ProximityAlert> i
1895 : mProximityAlerts.entrySet()) {
1896 pw.println(" " + i.getKey() + ":");
1897 i.getValue().dump(pw, " ");
1898 }
1899 }
1900 if (mProximitiesEntered.size() > 0) {
1901 pw.println(" Proximities Entered:");
1902 for (ProximityAlert i : mProximitiesEntered) {
1903 pw.println(" " + i + ":");
1904 i.dump(pw, " ");
1905 }
1906 }
Mike Lockwood48f17512009-04-23 09:12:08 -07001907 pw.println(" mProximityReceiver=" + mProximityReceiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001908 pw.println(" mProximityListener=" + mProximityListener);
1909 if (mEnabledProviders.size() > 0) {
1910 pw.println(" Enabled Providers:");
1911 for (String i : mEnabledProviders) {
1912 pw.println(" " + i);
1913 }
1914
1915 }
1916 if (mDisabledProviders.size() > 0) {
1917 pw.println(" Disabled Providers:");
1918 for (String i : mDisabledProviders) {
1919 pw.println(" " + i);
1920 }
1921
1922 }
1923 if (mMockProviders.size() > 0) {
1924 pw.println(" Mock Providers:");
1925 for (Map.Entry<String, MockProvider> i : mMockProviders.entrySet()) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001926 i.getValue().dump(pw, " ");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001927 }
1928 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001929 }
1930 }
1931}