blob: 024d8da52b9d10d4f5a7f288ce38249291c17596 [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;
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -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;
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -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;
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -070038import android.database.Cursor;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039import android.location.Address;
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -070040import android.location.IGeocodeProvider;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041import android.location.IGpsStatusListener;
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -070042import android.location.IGpsStatusProvider;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043import android.location.ILocationListener;
44import android.location.ILocationManager;
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -070045import android.location.ILocationProvider;
Jean-Baptiste Queru9db3d072009-11-12 18:45:53 -080046import 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;
Jean-Baptiste Queru9db3d072009-11-12 18:45:53 -080051import 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;
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -070057import android.os.Looper;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058import android.os.Message;
59import android.os.PowerManager;
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -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;
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -070067import com.android.internal.location.LocationProviderProxy;
68import com.android.internal.location.MockProvider;
Jean-Baptiste Queru9db3d072009-11-12 18:45:53 -080069import 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 */
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -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;
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -070092 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
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -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;
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700107 private IGeocodeProvider mGeocodeProvider;
108 private IGpsStatusProvider mGpsStatusProvider;
Jean-Baptiste Queru9db3d072009-11-12 18:45:53 -0800109 private INetInitiatedListener mNetInitiatedListener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110 private LocationWorkerHandler mLocationHandler;
111
Jean-Baptiste Queru72b1f372009-08-31 09:17:57 -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
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700117 private static final int MESSAGE_LOCATION_CHANGED = 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800118
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700119 // 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;
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700122 private int mPendingBroadcasts;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800123
124 /**
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700125 * List of all receivers.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800126 */
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700127 private final HashMap<Object, Receiver> mReceivers = new HashMap<Object, Receiver>();
128
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800129
130 /**
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700131 * List of location providers.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800132 */
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700133 private final ArrayList<LocationProviderProxy> mProviders =
134 new ArrayList<LocationProviderProxy>();
135 private final HashMap<String, LocationProviderProxy> mProvidersByName
136 = new HashMap<String, LocationProviderProxy>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800137
138 /**
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700139 * Object used internally for synchronization
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800140 */
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700141 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
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -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;
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700162
163 // for Settings change notification
164 private ContentQueryMap mSettings;
The Android Open Source Project4df24232009-03-05 14:34:35 -0800165
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 */
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -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;
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700174 final HashMap<String,UpdateRecord> mUpdateRecords = new HashMap<String,UpdateRecord>();
175 int mPendingBroadcasts;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800176
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700177 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
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700183 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 }
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700202
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))
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700208 + " Listener " + mKey + "}";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800209 } else {
210 return "Receiver{"
211 + Integer.toHexString(System.identityHashCode(this))
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700212 + " 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 {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -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 {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -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 {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -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 {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -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 }
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700352 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800353 removeUpdatesLocked(this);
354 }
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -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) {
365 synchronized (this) {
366 decrementPendingBroadcastsLocked();
367 }
368 }
369
370 // 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();
381 }
382 }
383 }
384
385 public void locationCallbackFinished(ILocationListener listener) {
Joshua Bartel9ff67a52009-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);
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700392 if (receiver != null) {
393 synchronized (receiver) {
394 // so wakelock calls will succeed
395 long identity = Binder.clearCallingIdentity();
396 receiver.decrementPendingBroadcastsLocked();
397 Binder.restoreCallingIdentity(identity);
398 }
399 }
400 }
401
402 private final class SettingsObserver implements Observer {
403 public void update(Observable o, Object arg) {
404 synchronized (mLock) {
405 updateProvidersLocked();
406 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800407 }
408 }
409
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700410 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);
Jean-Baptiste Querucf4550c2009-07-21 11:16:54 -0700417 provider.unlinkProvider();
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700418 mProvidersByName.remove(provider.getName());
419 }
420
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800421 private void loadProviders() {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700422 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
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700445 GpsLocationProvider provider = new GpsLocationProvider(mContext, this);
446 mGpsStatusProvider = provider.getGpsStatusProvider();
Jean-Baptiste Queru9db3d072009-11-12 18:45:53 -0800447 mNetInitiatedListener = provider.getNetInitiatedListener();
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700448 LocationProviderProxy proxy = new LocationProviderProxy(LocationManager.GPS_PROVIDER, provider);
449 addProvider(proxy);
Jean-Baptiste Queru72b1f372009-08-31 09:17:57 -0700450 mGpsLocationProvider = proxy;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800451 }
452
453 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;
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -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 }
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700469 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800470
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -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);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800475
476 // 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();
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700481 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);
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700485 mContext.registerReceiver(mBroadcastReceiver, intentFilter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800486
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -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
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -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
507 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");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800511 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800512
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700513 synchronized (mLock) {
514 // 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
525 LocationProviderProxy proxy = new LocationProviderProxy(name, provider);
526 addProvider(proxy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800527 updateProvidersLocked();
Jean-Baptiste Queru72b1f372009-08-31 09:17:57 -0700528 if (LocationManager.NETWORK_PROVIDER.equals(name)) {
529 mNetworkLocationProvider = proxy;
530 }
The Android Open Source Project4df24232009-03-05 14:34:35 -0800531
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700532 // notify provider of current network state
Jean-Baptiste Queru9db3d072009-11-12 18:45:53 -0800533 proxy.updateNetworkState(mNetworkState, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800534 }
535 }
536
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700537 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");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800541 }
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700542
543 mGeocodeProvider = provider;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800544 }
545
546 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)
Jean-Baptiste Querucf4550c2009-07-21 11:16:54 -0700563 && (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)
Jean-Baptiste Querucf4550c2009-07-21 11:16:54 -0700568 && (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800569 != PackageManager.PERMISSION_GRANTED)
Jean-Baptiste Querucf4550c2009-07-21 11:16:54 -0700570 && (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)
Jean-Baptiste Querucf4550c2009-07-21 11:16:54 -0700579 && (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)
Jean-Baptiste Querucf4550c2009-07-21 11:16:54 -0700584 && (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800585 != PackageManager.PERMISSION_GRANTED)
Jean-Baptiste Querucf4550c2009-07-21 11:16:54 -0700586 && (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 {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700596 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 }
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700611 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 {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700621 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 }
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700636 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() {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700651 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
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700669 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
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -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 }
Jean-Baptiste Querucf4550c2009-07-21 11:16:54 -0700686 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 }
708 }
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;
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700727 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,
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700734 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);
Jean-Baptiste Queru9db3d072009-11-12 18:45:53 -0800757 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);
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700773 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
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700780 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 }
818 for (ProximityAlert alert : mProximityAlerts.values()) {
819 if (alert.mUid == uid) {
820 return true;
821 }
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 {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700830 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 {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700843 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
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700859 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();
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700868 boolean newUid = !providerHasListener(provider, callingUid, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800869 long identity = Binder.clearCallingIdentity();
870 try {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700871 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
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700877 if (newUid) {
878 proxy.addListener(callingUid);
879 }
880
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800881 boolean isProviderEnabled = isAllowedBySettingsLocked(provider);
882 if (isProviderEnabled) {
883 long minTimeForProvider = getMinTimeLocked(provider);
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700884 proxy.setMinTime(minTimeForProvider);
885 proxy.enableLocationTracking(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800886 } else {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -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 {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700897 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 {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700909 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 {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700928 if (mReceivers.remove(receiver.mKey) != null && receiver.isListener()) {
929 receiver.getListener().asBinder().unlinkToDeath(receiver, 0);
Joshua Bartel9ff67a52009-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>();
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700940 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()) {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700944 if (!providerHasListener(record.mProvider, callingUid, receiver)) {
945 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
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700971 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) {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700986 if (mGpsStatusProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800987 return false;
988 }
Jean-Baptiste Querucf4550c2009-07-21 11:16:54 -0700989 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 {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700995 mGpsStatusProvider.addGpsStatusListener(listener);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800996 } catch (RemoteException e) {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700997 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) {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001004 synchronized (mLock) {
1005 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) {
Jean-Baptiste Queru9db3d072009-11-12 18:45:53 -08001014 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
Jean-Baptiste Querucf4550c2009-07-21 11:16:54 -07001022 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
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001027 synchronized (mLock) {
1028 LocationProviderProxy proxy = mProvidersByName.get(provider);
Jean-Baptiste Queru9db3d072009-11-12 18:45:53 -08001029 if (proxy == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001030 return false;
1031 }
1032
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001033 return proxy.sendExtraCommand(command, extras);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001034 }
1035 }
1036
Jean-Baptiste Queru9db3d072009-11-12 18:45:53 -08001037 public boolean sendNiResponse(int notifId, int userResponse)
1038 {
1039 if (Binder.getCallingUid() != Process.myUid()) {
1040 throw new SecurityException(
1041 "calling sendNiResponse from outside of the system is not allowed");
1042 }
1043 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. Mobile5550ef42009-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. Mobile5550ef42009-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
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -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. Mobile5550ef42009-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. Mobile5550ef42009-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 {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001149 synchronized (this) {
1150 // synchronize to ensure incrementPendingBroadcasts()
1151 // 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.
1155 incrementPendingBroadcasts();
1156 }
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 {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001174 synchronized (this) {
1175 // synchronize to ensure incrementPendingBroadcasts()
1176 // 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.
1180 incrementPendingBroadcasts();
1181 }
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) {
Mike Lockwood19563cc2010-01-19 11:48:14 -05001207 ProximityAlert alert = mProximityAlerts.remove(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001208 mProximitiesEntered.remove(alert);
1209 }
1210 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001211 }
1212
1213 // Note: this is called with the lock held.
1214 public void onProviderDisabled(String provider) {
1215 if (provider.equals(LocationManager.GPS_PROVIDER)) {
1216 isGpsAvailable = false;
1217 }
1218 }
1219
1220 // Note: this is called with the lock held.
1221 public void onProviderEnabled(String provider) {
1222 // ignore
1223 }
1224
1225 // Note: this is called with the lock held.
1226 public void onStatusChanged(String provider, int status, Bundle extras) {
1227 if ((provider.equals(LocationManager.GPS_PROVIDER)) &&
1228 (status != LocationProvider.AVAILABLE)) {
1229 isGpsAvailable = false;
1230 }
1231 }
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001232
1233 public void onSendFinished(PendingIntent pendingIntent, Intent intent,
1234 int resultCode, String resultData, Bundle resultExtras) {
1235 // synchronize to ensure incrementPendingBroadcasts()
1236 // is called before decrementPendingBroadcasts()
1237 synchronized (this) {
1238 decrementPendingBroadcasts();
1239 }
1240 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001241 }
1242
1243 public void addProximityAlert(double latitude, double longitude,
1244 float radius, long expiration, PendingIntent intent) {
1245 try {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001246 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001247 addProximityAlertLocked(latitude, longitude, radius, expiration, intent);
1248 }
1249 } catch (SecurityException se) {
1250 throw se;
1251 } catch (Exception e) {
1252 Log.e(TAG, "addProximityAlert got exception:", e);
1253 }
1254 }
1255
1256 private void addProximityAlertLocked(double latitude, double longitude,
1257 float radius, long expiration, PendingIntent intent) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001258 if (LOCAL_LOGV) {
1259 Log.v(TAG, "addProximityAlert: latitude = " + latitude +
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001260 ", longitude = " + longitude +
1261 ", expiration = " + expiration +
1262 ", intent = " + intent);
1263 }
1264
1265 // Require ability to access all providers for now
1266 if (!isAllowedProviderSafe(LocationManager.GPS_PROVIDER) ||
1267 !isAllowedProviderSafe(LocationManager.NETWORK_PROVIDER)) {
1268 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
1269 }
1270
1271 if (expiration != -1) {
1272 expiration += System.currentTimeMillis();
1273 }
1274 ProximityAlert alert = new ProximityAlert(Binder.getCallingUid(),
1275 latitude, longitude, radius, expiration, intent);
1276 mProximityAlerts.put(intent, alert);
1277
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001278 if (mProximityReceiver == null) {
1279 mProximityListener = new ProximityListener();
1280 mProximityReceiver = new Receiver(mProximityListener);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001281
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001282 for (int i = mProviders.size() - 1; i >= 0; i--) {
1283 LocationProviderProxy provider = mProviders.get(i);
1284 requestLocationUpdatesLocked(provider.getName(), 1000L, 1.0f, mProximityReceiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001285 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001286 }
1287 }
1288
1289 public void removeProximityAlert(PendingIntent intent) {
1290 try {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001291 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001292 removeProximityAlertLocked(intent);
1293 }
1294 } catch (SecurityException se) {
1295 throw se;
1296 } catch (Exception e) {
1297 Log.e(TAG, "removeProximityAlert got exception:", e);
1298 }
1299 }
1300
1301 private void removeProximityAlertLocked(PendingIntent intent) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001302 if (LOCAL_LOGV) {
1303 Log.v(TAG, "removeProximityAlert: intent = " + intent);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001304 }
1305
1306 mProximityAlerts.remove(intent);
1307 if (mProximityAlerts.size() == 0) {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001308 removeUpdatesLocked(mProximityReceiver);
1309 mProximityReceiver = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001310 mProximityListener = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001311 }
1312 }
1313
1314 /**
1315 * @return null if the provider does not exits
Alexey Tarasovf2db9fb2009-09-01 02:37:07 +11001316 * @throws SecurityException if the provider is not allowed to be
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001317 * accessed by the caller
1318 */
1319 public Bundle getProviderInfo(String provider) {
1320 try {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001321 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001322 return _getProviderInfoLocked(provider);
1323 }
1324 } catch (SecurityException se) {
1325 throw se;
1326 } catch (Exception e) {
1327 Log.e(TAG, "_getProviderInfo got exception:", e);
1328 return null;
1329 }
1330 }
1331
1332 private Bundle _getProviderInfoLocked(String provider) {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001333 LocationProviderProxy p = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001334 if (p == null) {
1335 return null;
1336 }
1337
1338 checkPermissionsSafe(provider);
1339
1340 Bundle b = new Bundle();
1341 b.putBoolean("network", p.requiresNetwork());
1342 b.putBoolean("satellite", p.requiresSatellite());
1343 b.putBoolean("cell", p.requiresCell());
1344 b.putBoolean("cost", p.hasMonetaryCost());
1345 b.putBoolean("altitude", p.supportsAltitude());
1346 b.putBoolean("speed", p.supportsSpeed());
1347 b.putBoolean("bearing", p.supportsBearing());
1348 b.putInt("power", p.getPowerRequirement());
1349 b.putInt("accuracy", p.getAccuracy());
1350
1351 return b;
1352 }
1353
1354 public boolean isProviderEnabled(String provider) {
1355 try {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001356 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001357 return _isProviderEnabledLocked(provider);
1358 }
1359 } catch (SecurityException se) {
1360 throw se;
1361 } catch (Exception e) {
1362 Log.e(TAG, "isProviderEnabled got exception:", e);
1363 return false;
1364 }
1365 }
1366
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001367 public void reportLocation(Location location) {
1368 if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER)
1369 != PackageManager.PERMISSION_GRANTED) {
1370 throw new SecurityException("Requires INSTALL_LOCATION_PROVIDER permission");
1371 }
1372
1373 mLocationHandler.removeMessages(MESSAGE_LOCATION_CHANGED, location);
1374 Message m = Message.obtain(mLocationHandler, MESSAGE_LOCATION_CHANGED, location);
1375 mLocationHandler.sendMessageAtFrontOfQueue(m);
1376 }
1377
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001378 private boolean _isProviderEnabledLocked(String provider) {
1379 checkPermissionsSafe(provider);
1380
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001381 LocationProviderProxy p = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001382 if (p == null) {
1383 throw new IllegalArgumentException("provider=" + provider);
1384 }
1385 return isAllowedBySettingsLocked(provider);
1386 }
1387
1388 public Location getLastKnownLocation(String provider) {
1389 try {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001390 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001391 return _getLastKnownLocationLocked(provider);
1392 }
1393 } catch (SecurityException se) {
1394 throw se;
1395 } catch (Exception e) {
1396 Log.e(TAG, "getLastKnownLocation got exception:", e);
1397 return null;
1398 }
1399 }
1400
1401 private Location _getLastKnownLocationLocked(String provider) {
1402 checkPermissionsSafe(provider);
1403
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001404 LocationProviderProxy p = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001405 if (p == null) {
1406 throw new IllegalArgumentException("provider=" + provider);
1407 }
1408
1409 if (!isAllowedBySettingsLocked(provider)) {
1410 return null;
1411 }
1412
Jean-Baptiste Queru9db3d072009-11-12 18:45:53 -08001413 return mLastKnownLocation.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001414 }
1415
1416 private static boolean shouldBroadcastSafe(Location loc, Location lastLoc, UpdateRecord record) {
1417 // Always broadcast the first update
1418 if (lastLoc == null) {
1419 return true;
1420 }
1421
1422 // Don't broadcast same location again regardless of condition
1423 // TODO - we should probably still rebroadcast if user explicitly sets a minTime > 0
1424 if (loc.getTime() == lastLoc.getTime()) {
1425 return false;
1426 }
1427
1428 // Check whether sufficient distance has been traveled
1429 double minDistance = record.mMinDistance;
1430 if (minDistance > 0.0) {
1431 if (loc.distanceTo(lastLoc) <= minDistance) {
1432 return false;
1433 }
1434 }
1435
1436 return true;
1437 }
1438
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001439 private void handleLocationChangedLocked(Location location) {
1440 String provider = location.getProvider();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001441 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
1442 if (records == null || records.size() == 0) {
1443 return;
1444 }
1445
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001446 LocationProviderProxy p = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001447 if (p == null) {
1448 return;
1449 }
1450
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001451 // Update last known location for provider
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001452 Location lastLocation = mLastKnownLocation.get(provider);
1453 if (lastLocation == null) {
1454 mLastKnownLocation.put(provider, new Location(location));
1455 } else {
1456 lastLocation.set(location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001457 }
1458
1459 // Fetch latest status update time
1460 long newStatusUpdateTime = p.getStatusUpdateTime();
1461
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001462 // Get latest status
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001463 Bundle extras = new Bundle();
1464 int status = p.getStatus(extras);
1465
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001466 ArrayList<Receiver> deadReceivers = null;
1467
1468 // Broadcast location or status to all listeners
1469 final int N = records.size();
1470 for (int i=0; i<N; i++) {
1471 UpdateRecord r = records.get(i);
1472 Receiver receiver = r.mReceiver;
1473
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001474 Location lastLoc = r.mLastFixBroadcast;
1475 if ((lastLoc == null) || shouldBroadcastSafe(location, lastLoc, r)) {
1476 if (lastLoc == null) {
1477 lastLoc = new Location(location);
1478 r.mLastFixBroadcast = lastLoc;
1479 } else {
1480 lastLoc.set(location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001481 }
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001482 if (!receiver.callLocationChangedLocked(location)) {
1483 Log.w(TAG, "RemoteException calling onLocationChanged on " + receiver);
1484 if (deadReceivers == null) {
1485 deadReceivers = new ArrayList<Receiver>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001486 }
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001487 deadReceivers.add(receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001488 }
1489 }
1490
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001491 long prevStatusUpdateTime = r.mLastStatusBroadcast;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001492 if ((newStatusUpdateTime > prevStatusUpdateTime) &&
1493 (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) {
1494
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001495 r.mLastStatusBroadcast = newStatusUpdateTime;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001496 if (!receiver.callStatusChangedLocked(provider, status, extras)) {
1497 Log.w(TAG, "RemoteException calling onStatusChanged on " + receiver);
1498 if (deadReceivers == null) {
1499 deadReceivers = new ArrayList<Receiver>();
1500 }
1501 if (!deadReceivers.contains(receiver)) {
1502 deadReceivers.add(receiver);
1503 }
1504 }
1505 }
1506 }
1507
1508 if (deadReceivers != null) {
1509 for (int i=deadReceivers.size()-1; i>=0; i--) {
1510 removeUpdatesLocked(deadReceivers.get(i));
1511 }
1512 }
1513 }
1514
1515 private class LocationWorkerHandler extends Handler {
1516
1517 @Override
1518 public void handleMessage(Message msg) {
1519 try {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001520 if (msg.what == MESSAGE_LOCATION_CHANGED) {
1521 // log("LocationWorkerHandler: MESSAGE_LOCATION_CHANGED!");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001522
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001523 synchronized (mLock) {
1524 Location location = (Location) msg.obj;
Jean-Baptiste Querucf4550c2009-07-21 11:16:54 -07001525 String provider = location.getProvider();
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001526
Jean-Baptiste Querucf4550c2009-07-21 11:16:54 -07001527 // notify other providers of the new location
1528 for (int i = mProviders.size() - 1; i >= 0; i--) {
1529 LocationProviderProxy proxy = mProviders.get(i);
1530 if (!provider.equals(proxy.getName())) {
1531 proxy.updateLocation(location);
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001532 }
1533 }
1534
Jean-Baptiste Querucf4550c2009-07-21 11:16:54 -07001535 if (isAllowedBySettingsLocked(provider)) {
1536 handleLocationChangedLocked(location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001537 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001538 }
1539 }
1540 } catch (Exception e) {
1541 // Log, don't crash!
1542 Log.e(TAG, "Exception in LocationWorkerHandler.handleMessage:", e);
1543 }
1544 }
1545 }
1546
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001547 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001548 @Override
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001549 public void onReceive(Context context, Intent intent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001550 String action = intent.getAction();
1551
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001552 if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001553 || action.equals(Intent.ACTION_PACKAGE_RESTARTED)) {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001554 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001555 int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
1556 if (uid >= 0) {
1557 ArrayList<Receiver> removedRecs = null;
1558 for (ArrayList<UpdateRecord> i : mRecordsByProvider.values()) {
1559 for (int j=i.size()-1; j>=0; j--) {
1560 UpdateRecord ur = i.get(j);
1561 if (ur.mReceiver.isPendingIntent() && ur.mUid == uid) {
1562 if (removedRecs == null) {
1563 removedRecs = new ArrayList<Receiver>();
1564 }
1565 if (!removedRecs.contains(ur.mReceiver)) {
1566 removedRecs.add(ur.mReceiver);
1567 }
1568 }
1569 }
1570 }
1571 ArrayList<ProximityAlert> removedAlerts = null;
1572 for (ProximityAlert i : mProximityAlerts.values()) {
1573 if (i.mUid == uid) {
1574 if (removedAlerts == null) {
1575 removedAlerts = new ArrayList<ProximityAlert>();
1576 }
1577 if (!removedAlerts.contains(i)) {
1578 removedAlerts.add(i);
1579 }
1580 }
1581 }
1582 if (removedRecs != null) {
1583 for (int i=removedRecs.size()-1; i>=0; i--) {
1584 removeUpdatesLocked(removedRecs.get(i));
1585 }
1586 }
1587 if (removedAlerts != null) {
1588 for (int i=removedAlerts.size()-1; i>=0; i--) {
1589 removeProximityAlertLocked(removedAlerts.get(i).mIntent);
1590 }
1591 }
1592 }
1593 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001594 } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001595 boolean noConnectivity =
1596 intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
1597 if (!noConnectivity) {
The Android Open Source Project4df24232009-03-05 14:34:35 -08001598 mNetworkState = LocationProvider.AVAILABLE;
1599 } else {
1600 mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001601 }
Jean-Baptiste Queru9db3d072009-11-12 18:45:53 -08001602 NetworkInfo info =
1603 (NetworkInfo)intent.getExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001604
1605 // Notify location providers of current network state
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001606 synchronized (mLock) {
1607 for (int i = mProviders.size() - 1; i >= 0; i--) {
1608 LocationProviderProxy provider = mProviders.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001609 if (provider.requiresNetwork()) {
Jean-Baptiste Queru9db3d072009-11-12 18:45:53 -08001610 provider.updateNetworkState(mNetworkState, info);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001611 }
1612 }
1613 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001614 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001615 }
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001616 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001617
1618 // Wake locks
1619
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001620 private void incrementPendingBroadcasts() {
1621 synchronized (mWakeLock) {
1622 if (mPendingBroadcasts++ == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001623 try {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001624 mWakeLock.acquire();
1625 log("Acquired wakelock");
1626 } catch (Exception e) {
1627 // This is to catch a runtime exception thrown when we try to release an
1628 // already released lock.
1629 Log.e(TAG, "exception in acquireWakeLock()", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001630 }
1631 }
1632 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001633 }
1634
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001635 private void decrementPendingBroadcasts() {
1636 synchronized (mWakeLock) {
1637 if (--mPendingBroadcasts == 0) {
1638 try {
1639 // Release wake lock
1640 if (mWakeLock.isHeld()) {
1641 mWakeLock.release();
1642 log("Released wakelock");
1643 } else {
1644 log("Can't release wakelock again!");
1645 }
1646 } catch (Exception e) {
1647 // This is to catch a runtime exception thrown when we try to release an
1648 // already released lock.
1649 Log.e(TAG, "exception in releaseWakeLock()", e);
1650 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001651 }
1652 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001653 }
1654
1655 // Geocoder
1656
1657 public String getFromLocation(double latitude, double longitude, int maxResults,
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001658 String language, String country, String variant, String appName, List<Address> addrs) {
1659 if (mGeocodeProvider != null) {
1660 try {
1661 return mGeocodeProvider.getFromLocation(latitude, longitude, maxResults, language, country,
1662 variant, appName, addrs);
1663 } catch (RemoteException e) {
1664 Log.e(TAG, "getFromLocation failed", e);
1665 mGeocodeProvider = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001666 }
1667 }
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001668 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001669 }
1670
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001671
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001672 public String getFromLocationName(String locationName,
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001673 double lowerLeftLatitude, double lowerLeftLongitude,
1674 double upperRightLatitude, double upperRightLongitude, int maxResults,
1675 String language, String country, String variant, String appName, List<Address> addrs) {
1676
1677 if (mGeocodeProvider != null) {
1678 try {
1679 return mGeocodeProvider.getFromLocationName(locationName, lowerLeftLatitude,
1680 lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
1681 maxResults, language, country, variant, appName, addrs);
1682 } catch (RemoteException e) {
1683 Log.e(TAG, "getFromLocationName failed", e);
1684 mGeocodeProvider = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001685 }
1686 }
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001687 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001688 }
1689
1690 // Mock Providers
1691
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001692 private void checkMockPermissionsSafe() {
1693 boolean allowMocks = Settings.Secure.getInt(mContext.getContentResolver(),
1694 Settings.Secure.ALLOW_MOCK_LOCATION, 0) == 1;
1695 if (!allowMocks) {
1696 throw new SecurityException("Requires ACCESS_MOCK_LOCATION secure setting");
1697 }
1698
1699 if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) !=
1700 PackageManager.PERMISSION_GRANTED) {
1701 throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission");
1702 }
1703 }
1704
1705 public void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite,
1706 boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
1707 boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
1708 checkMockPermissionsSafe();
1709
Jean-Baptiste Queru9db3d072009-11-12 18:45:53 -08001710 long identity = Binder.clearCallingIdentity();
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001711 synchronized (mLock) {
1712 MockProvider provider = new MockProvider(name, this,
1713 requiresNetwork, requiresSatellite,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001714 requiresCell, hasMonetaryCost, supportsAltitude,
1715 supportsSpeed, supportsBearing, powerRequirement, accuracy);
Jean-Baptiste Queru72b1f372009-08-31 09:17:57 -07001716 // remove the real provider if we are replacing GPS or network provider
1717 if (LocationManager.GPS_PROVIDER.equals(name)
1718 || LocationManager.NETWORK_PROVIDER.equals(name)) {
1719 LocationProviderProxy proxy = mProvidersByName.get(name);
1720 if (proxy != null) {
1721 proxy.enableLocationTracking(false);
1722 removeProvider(proxy);
1723 }
1724 }
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001725 if (mProvidersByName.get(name) != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001726 throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
1727 }
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001728 addProvider(new LocationProviderProxy(name, provider));
1729 mMockProviders.put(name, provider);
Jean-Baptiste Queru72b1f372009-08-31 09:17:57 -07001730 mLastKnownLocation.put(name, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001731 updateProvidersLocked();
1732 }
Jean-Baptiste Queru9db3d072009-11-12 18:45:53 -08001733 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001734 }
1735
1736 public void removeTestProvider(String provider) {
1737 checkMockPermissionsSafe();
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001738 synchronized (mLock) {
1739 MockProvider mockProvider = mMockProviders.get(provider);
1740 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001741 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1742 }
Jean-Baptiste Queru9db3d072009-11-12 18:45:53 -08001743 long identity = Binder.clearCallingIdentity();
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001744 removeProvider(mProvidersByName.get(provider));
1745 mMockProviders.remove(mockProvider);
Jean-Baptiste Queru72b1f372009-08-31 09:17:57 -07001746 // reinstall real provider if we were mocking GPS or network provider
1747 if (LocationManager.GPS_PROVIDER.equals(provider) &&
1748 mGpsLocationProvider != null) {
1749 addProvider(mGpsLocationProvider);
1750 } else if (LocationManager.NETWORK_PROVIDER.equals(provider) &&
1751 mNetworkLocationProvider != null) {
1752 addProvider(mNetworkLocationProvider);
1753 }
1754 mLastKnownLocation.put(provider, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001755 updateProvidersLocked();
Jean-Baptiste Queru9db3d072009-11-12 18:45:53 -08001756 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001757 }
1758 }
1759
1760 public void setTestProviderLocation(String provider, Location loc) {
1761 checkMockPermissionsSafe();
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001762 synchronized (mLock) {
1763 MockProvider mockProvider = mMockProviders.get(provider);
1764 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001765 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1766 }
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001767 // clear calling identity so INSTALL_LOCATION_PROVIDER permission is not required
1768 long identity = Binder.clearCallingIdentity();
1769 mockProvider.setLocation(loc);
1770 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001771 }
1772 }
1773
1774 public void clearTestProviderLocation(String provider) {
1775 checkMockPermissionsSafe();
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001776 synchronized (mLock) {
1777 MockProvider mockProvider = mMockProviders.get(provider);
1778 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001779 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1780 }
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001781 mockProvider.clearLocation();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001782 }
1783 }
1784
1785 public void setTestProviderEnabled(String provider, boolean enabled) {
1786 checkMockPermissionsSafe();
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001787 synchronized (mLock) {
1788 MockProvider mockProvider = mMockProviders.get(provider);
1789 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001790 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1791 }
Jean-Baptiste Queru9db3d072009-11-12 18:45:53 -08001792 long identity = Binder.clearCallingIdentity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001793 if (enabled) {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001794 mockProvider.enable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001795 mEnabledProviders.add(provider);
1796 mDisabledProviders.remove(provider);
1797 } else {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001798 mockProvider.disable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001799 mEnabledProviders.remove(provider);
1800 mDisabledProviders.add(provider);
1801 }
1802 updateProvidersLocked();
Jean-Baptiste Queru9db3d072009-11-12 18:45:53 -08001803 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001804 }
1805 }
1806
1807 public void clearTestProviderEnabled(String provider) {
1808 checkMockPermissionsSafe();
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001809 synchronized (mLock) {
1810 MockProvider mockProvider = mMockProviders.get(provider);
1811 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001812 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1813 }
Jean-Baptiste Queru9db3d072009-11-12 18:45:53 -08001814 long identity = Binder.clearCallingIdentity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001815 mEnabledProviders.remove(provider);
1816 mDisabledProviders.remove(provider);
1817 updateProvidersLocked();
Jean-Baptiste Queru9db3d072009-11-12 18:45:53 -08001818 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001819 }
1820 }
1821
1822 public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
1823 checkMockPermissionsSafe();
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001824 synchronized (mLock) {
1825 MockProvider mockProvider = mMockProviders.get(provider);
1826 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001827 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1828 }
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001829 mockProvider.setStatus(status, extras, updateTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001830 }
1831 }
1832
1833 public void clearTestProviderStatus(String provider) {
1834 checkMockPermissionsSafe();
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001835 synchronized (mLock) {
1836 MockProvider mockProvider = mMockProviders.get(provider);
1837 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001838 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1839 }
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001840 mockProvider.clearStatus();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001841 }
1842 }
1843
1844 private void log(String log) {
1845 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1846 Log.d(TAG, log);
1847 }
1848 }
1849
1850 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1851 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1852 != PackageManager.PERMISSION_GRANTED) {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001853 pw.println("Permission Denial: can't dump LocationManagerService from from pid="
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001854 + Binder.getCallingPid()
1855 + ", uid=" + Binder.getCallingUid());
1856 return;
1857 }
1858
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001859 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001860 pw.println("Current Location Manager state:");
1861 pw.println(" sProvidersLoaded=" + sProvidersLoaded);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001862 pw.println(" Listeners:");
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001863 int N = mReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001864 for (int i=0; i<N; i++) {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001865 pw.println(" " + mReceivers.get(i));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001866 }
1867 pw.println(" Location Listeners:");
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001868 for (Receiver i : mReceivers.values()) {
1869 pw.println(" " + i + ":");
1870 for (Map.Entry<String,UpdateRecord> j : i.mUpdateRecords.entrySet()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001871 pw.println(" " + j.getKey() + ":");
1872 j.getValue().dump(pw, " ");
1873 }
1874 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001875 pw.println(" Records by Provider:");
1876 for (Map.Entry<String, ArrayList<UpdateRecord>> i
1877 : mRecordsByProvider.entrySet()) {
1878 pw.println(" " + i.getKey() + ":");
1879 for (UpdateRecord j : i.getValue()) {
1880 pw.println(" " + j + ":");
1881 j.dump(pw, " ");
1882 }
1883 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001884 pw.println(" Last Known Locations:");
1885 for (Map.Entry<String, Location> i
1886 : mLastKnownLocation.entrySet()) {
1887 pw.println(" " + i.getKey() + ":");
1888 i.getValue().dump(new PrintWriterPrinter(pw), " ");
1889 }
1890 if (mProximityAlerts.size() > 0) {
1891 pw.println(" Proximity Alerts:");
1892 for (Map.Entry<PendingIntent, ProximityAlert> i
1893 : mProximityAlerts.entrySet()) {
1894 pw.println(" " + i.getKey() + ":");
1895 i.getValue().dump(pw, " ");
1896 }
1897 }
1898 if (mProximitiesEntered.size() > 0) {
1899 pw.println(" Proximities Entered:");
1900 for (ProximityAlert i : mProximitiesEntered) {
1901 pw.println(" " + i + ":");
1902 i.dump(pw, " ");
1903 }
1904 }
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001905 pw.println(" mProximityReceiver=" + mProximityReceiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001906 pw.println(" mProximityListener=" + mProximityListener);
1907 if (mEnabledProviders.size() > 0) {
1908 pw.println(" Enabled Providers:");
1909 for (String i : mEnabledProviders) {
1910 pw.println(" " + i);
1911 }
1912
1913 }
1914 if (mDisabledProviders.size() > 0) {
1915 pw.println(" Disabled Providers:");
1916 for (String i : mDisabledProviders) {
1917 pw.println(" " + i);
1918 }
1919
1920 }
1921 if (mMockProviders.size() > 0) {
1922 pw.println(" Mock Providers:");
1923 for (Map.Entry<String, MockProvider> i : mMockProviders.entrySet()) {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001924 i.getValue().dump(pw, " ");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001925 }
1926 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001927 }
1928 }
1929}