blob: 12d6bff42523ef50b8582beb8b853b2f8978d0a4 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080019import android.app.PendingIntent;
Victoria Lease38389b62012-09-30 11:44:22 -070020import android.content.BroadcastReceiver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080021import android.content.ContentResolver;
22import android.content.Context;
23import android.content.Intent;
Victoria Lease38389b62012-09-30 11:44:22 -070024import android.content.IntentFilter;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070025import android.content.pm.ApplicationInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026import android.content.pm.PackageManager;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070027import android.content.pm.PackageManager.NameNotFoundException;
Mike Lockwood628fd6d2010-01-25 22:46:13 -050028import android.content.res.Resources;
Brian Muramatsubb95cb92012-08-29 10:43:21 -070029import android.database.ContentObserver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import android.location.Address;
Mike Lockwood03ca2162010-04-01 08:10:09 -070031import android.location.Criteria;
Mike Lockwood34901402010-01-04 12:14:21 -050032import android.location.GeocoderParams;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070033import android.location.Geofence;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034import android.location.IGpsStatusListener;
Mike Lockwood15e3d0f2009-05-01 07:53:28 -040035import android.location.IGpsStatusProvider;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036import android.location.ILocationListener;
37import android.location.ILocationManager;
Danke Xie22d1f9f2009-08-18 18:28:45 -040038import android.location.INetInitiatedListener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039import android.location.Location;
40import android.location.LocationManager;
41import android.location.LocationProvider;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070042import android.location.LocationRequest;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043import android.os.Binder;
44import android.os.Bundle;
45import android.os.Handler;
46import android.os.IBinder;
Mike Lockwood3d12b512009-04-21 23:25:35 -070047import android.os.Looper;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048import android.os.Message;
49import android.os.PowerManager;
Mike Lockwoode932f7f2009-04-06 10:51:26 -070050import android.os.Process;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051import android.os.RemoteException;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070052import android.os.SystemClock;
Dianne Hackborn5ac72a22012-08-29 18:32:08 -070053import android.os.UserHandle;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070054import android.os.WorkSource;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055import android.provider.Settings;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056import android.util.Log;
Joe Onorato8a9b2202010-02-26 18:56:32 -080057import android.util.Slog;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058
Mike Lockwoode97ae402010-09-29 15:23:46 -040059import com.android.internal.content.PackageMonitor;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070060import com.android.internal.location.ProviderProperties;
61import com.android.internal.location.ProviderRequest;
Mike Lockwood43e33f22010-03-26 10:41:48 -040062import com.android.server.location.GeocoderProxy;
Nick Pellye0fd6932012-07-11 10:26:13 -070063import com.android.server.location.GeofenceManager;
Mike Lockwood43e33f22010-03-26 10:41:48 -040064import com.android.server.location.GpsLocationProvider;
Nick Pelly4035f5a2012-08-17 14:43:49 -070065import com.android.server.location.LocationBlacklist;
Nick Pelly74fa7ea2012-08-13 19:36:38 -070066import com.android.server.location.LocationFudger;
Mike Lockwood43e33f22010-03-26 10:41:48 -040067import com.android.server.location.LocationProviderInterface;
68import com.android.server.location.LocationProviderProxy;
69import com.android.server.location.MockProvider;
70import com.android.server.location.PassiveProvider;
71
72import java.io.FileDescriptor;
73import java.io.PrintWriter;
74import java.util.ArrayList;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070075import java.util.Arrays;
Mike Lockwood43e33f22010-03-26 10:41:48 -040076import java.util.HashMap;
77import java.util.HashSet;
78import java.util.List;
79import java.util.Map;
Mike Lockwood43e33f22010-03-26 10:41:48 -040080import java.util.Set;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081
82/**
83 * The service class that manages LocationProviders and issues location
84 * updates and alerts.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080085 */
Brian Muramatsubb95cb92012-08-29 10:43:21 -070086public class LocationManagerService extends ILocationManager.Stub implements Runnable {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080087 private static final String TAG = "LocationManagerService";
Nick Pelly6fa9ad42012-07-16 12:18:23 -070088 public static final boolean D = false;
89
90 private static final String WAKELOCK_KEY = TAG;
91 private static final String THREAD_NAME = TAG;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080092
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080093 private static final String ACCESS_FINE_LOCATION =
Nick Pelly6fa9ad42012-07-16 12:18:23 -070094 android.Manifest.permission.ACCESS_FINE_LOCATION;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080095 private static final String ACCESS_COARSE_LOCATION =
Nick Pelly6fa9ad42012-07-16 12:18:23 -070096 android.Manifest.permission.ACCESS_COARSE_LOCATION;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080097 private static final String ACCESS_MOCK_LOCATION =
Nick Pelly6fa9ad42012-07-16 12:18:23 -070098 android.Manifest.permission.ACCESS_MOCK_LOCATION;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080099 private static final String ACCESS_LOCATION_EXTRA_COMMANDS =
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700100 android.Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS;
Mike Lockwood275555c2009-05-01 11:30:34 -0400101 private static final String INSTALL_LOCATION_PROVIDER =
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700102 android.Manifest.permission.INSTALL_LOCATION_PROVIDER;
103
104 private static final String NETWORK_LOCATION_SERVICE_ACTION =
105 "com.android.location.service.v2.NetworkLocationProvider";
106 private static final String FUSED_LOCATION_SERVICE_ACTION =
107 "com.android.location.service.FusedLocationProvider";
108
109 private static final int MSG_LOCATION_CHANGED = 1;
110
Nick Pellyf1be6862012-05-15 10:53:42 -0700111 // Location Providers may sometimes deliver location updates
112 // slightly faster that requested - provide grace period so
113 // we don't unnecessarily filter events that are otherwise on
114 // time
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700115 private static final int MAX_PROVIDER_SCHEDULING_JITTER_MS = 100;
Nick Pellyf1be6862012-05-15 10:53:42 -0700116
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700117 private static final LocationRequest DEFAULT_LOCATION_REQUEST = new LocationRequest();
118
119 private final Context mContext;
120
121 // used internally for synchronization
122 private final Object mLock = new Object();
123
124 // --- fields below are final after init() ---
Nick Pelly74fa7ea2012-08-13 19:36:38 -0700125 private LocationFudger mLocationFudger;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700126 private GeofenceManager mGeofenceManager;
127 private PowerManager.WakeLock mWakeLock;
128 private PackageManager mPackageManager;
129 private GeocoderProxy mGeocodeProvider;
130 private IGpsStatusProvider mGpsStatusProvider;
131 private INetInitiatedListener mNetInitiatedListener;
132 private LocationWorkerHandler mLocationHandler;
Nick Pelly4035f5a2012-08-17 14:43:49 -0700133 private PassiveProvider mPassiveProvider; // track passive provider for special cases
134 private LocationBlacklist mBlacklist;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700135
136 // --- fields below are protected by mWakeLock ---
137 private int mPendingBroadcasts;
138
139 // --- fields below are protected by mLock ---
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800140 // Set of providers that are explicitly enabled
141 private final Set<String> mEnabledProviders = new HashSet<String>();
142
143 // Set of providers that are explicitly disabled
144 private final Set<String> mDisabledProviders = new HashSet<String>();
145
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700146 // Mock (test) providers
147 private final HashMap<String, MockProvider> mMockProviders =
148 new HashMap<String, MockProvider>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800149
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700150 // all receivers
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400151 private final HashMap<Object, Receiver> mReceivers = new HashMap<Object, Receiver>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800152
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700153 // currently installed providers (with mocks replacing real providers)
Mike Lockwoodd03ff942010-02-09 08:46:14 -0500154 private final ArrayList<LocationProviderInterface> mProviders =
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700155 new ArrayList<LocationProviderInterface>();
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400156
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700157 // real providers, saved here when mocked out
158 private final HashMap<String, LocationProviderInterface> mRealProviders =
159 new HashMap<String, LocationProviderInterface>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800160
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700161 // mapping from provider name to provider
162 private final HashMap<String, LocationProviderInterface> mProvidersByName =
163 new HashMap<String, LocationProviderInterface>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800164
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700165 // mapping from provider name to all its UpdateRecords
166 private final HashMap<String, ArrayList<UpdateRecord>> mRecordsByProvider =
167 new HashMap<String, ArrayList<UpdateRecord>>();
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700168
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700169 // mapping from provider name to last known location
170 private final HashMap<String, Location> mLastLocation = new HashMap<String, Location>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800171
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700172 // all providers that operate over proxy, for authorizing incoming location
173 private final ArrayList<LocationProviderProxy> mProxyProviders =
174 new ArrayList<LocationProviderProxy>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800175
Victoria Lease38389b62012-09-30 11:44:22 -0700176 // current active user on the device - other users are denied location data
177 private int mCurrentUserId = UserHandle.USER_OWNER;
178
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700179 public LocationManagerService(Context context) {
180 super();
181 mContext = context;
The Android Open Source Project4df24232009-03-05 14:34:35 -0800182
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700183 if (D) Log.d(TAG, "Constructed");
184
185 // most startup is deferred until systemReady()
186 }
187
188 public void systemReady() {
189 Thread thread = new Thread(null, this, THREAD_NAME);
190 thread.start();
191 }
192
193 @Override
194 public void run() {
195 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
196 Looper.prepare();
197 mLocationHandler = new LocationWorkerHandler();
198 init();
199 Looper.loop();
200 }
201
202 private void init() {
203 if (D) Log.d(TAG, "init()");
204
205 PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
206 mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
207 mPackageManager = mContext.getPackageManager();
208
Brian Muramatsubb95cb92012-08-29 10:43:21 -0700209 mBlacklist = new LocationBlacklist(mContext, mLocationHandler);
210 mBlacklist.init();
Victoria Leasedf9ec612012-09-11 15:16:25 -0700211 mLocationFudger = new LocationFudger(mContext, mLocationHandler);
Brian Muramatsubb95cb92012-08-29 10:43:21 -0700212
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700213 synchronized (mLock) {
214 loadProvidersLocked();
215 }
Brian Muramatsubb95cb92012-08-29 10:43:21 -0700216
Nick Pelly4035f5a2012-08-17 14:43:49 -0700217 mGeofenceManager = new GeofenceManager(mContext, mBlacklist);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700218
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700219 // listen for settings changes
Brian Muramatsubb95cb92012-08-29 10:43:21 -0700220 mContext.getContentResolver().registerContentObserver(
221 Settings.Secure.getUriFor(Settings.Secure.LOCATION_PROVIDERS_ALLOWED), true,
222 new ContentObserver(mLocationHandler) {
223 @Override
224 public void onChange(boolean selfChange) {
225 synchronized (mLock) {
226 updateProvidersLocked();
227 }
228 }
229 });
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700230 mPackageMonitor.register(mContext, Looper.myLooper(), true);
Brian Muramatsubb95cb92012-08-29 10:43:21 -0700231
Victoria Lease38389b62012-09-30 11:44:22 -0700232 // listen for user change
233 IntentFilter intentFilter = new IntentFilter();
234 intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
235
236 mContext.registerReceiverAsUser(new BroadcastReceiver() {
237 @Override
238 public void onReceive(Context context, Intent intent) {
239 String action = intent.getAction();
240 if (Intent.ACTION_USER_SWITCHED.equals(action)) {
241 switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
242 }
243 }
244 }, UserHandle.ALL, intentFilter, null, null);
245
Brian Muramatsubb95cb92012-08-29 10:43:21 -0700246 updateProvidersLocked();
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700247 }
248
249 private void loadProvidersLocked() {
Victoria Lease5c24fd02012-10-01 11:00:50 -0700250 // create a passive location provider, which is always enabled
251 PassiveProvider passiveProvider = new PassiveProvider(this);
252 addProviderLocked(passiveProvider);
253 mEnabledProviders.add(passiveProvider.getName());
254 mPassiveProvider = passiveProvider;
255
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700256 if (GpsLocationProvider.isSupported()) {
257 // Create a gps location provider
258 GpsLocationProvider gpsProvider = new GpsLocationProvider(mContext, this);
259 mGpsStatusProvider = gpsProvider.getGpsStatusProvider();
260 mNetInitiatedListener = gpsProvider.getNetInitiatedListener();
261 addProviderLocked(gpsProvider);
262 mRealProviders.put(LocationManager.GPS_PROVIDER, gpsProvider);
263 }
264
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700265 /*
266 Load package name(s) containing location provider support.
267 These packages can contain services implementing location providers:
268 Geocoder Provider, Network Location Provider, and
269 Fused Location Provider. They will each be searched for
270 service components implementing these providers.
271 The location framework also has support for installation
272 of new location providers at run-time. The new package does not
273 have to be explicitly listed here, however it must have a signature
274 that matches the signature of at least one package on this list.
275 */
276 Resources resources = mContext.getResources();
277 ArrayList<String> providerPackageNames = new ArrayList<String>();
278 String[] pkgs1 = resources.getStringArray(
279 com.android.internal.R.array.config_locationProviderPackageNames);
280 String[] pkgs2 = resources.getStringArray(
281 com.android.internal.R.array.config_overlay_locationProviderPackageNames);
282 if (D) Log.d(TAG, "built-in location providers: " + Arrays.toString(pkgs1));
283 if (D) Log.d(TAG, "overlay location providers: " + Arrays.toString(pkgs2));
284 if (pkgs1 != null) providerPackageNames.addAll(Arrays.asList(pkgs1));
285 if (pkgs2 != null) providerPackageNames.addAll(Arrays.asList(pkgs2));
286
287 // bind to network provider
288 LocationProviderProxy networkProvider = LocationProviderProxy.createAndBind(
289 mContext,
290 LocationManager.NETWORK_PROVIDER,
291 NETWORK_LOCATION_SERVICE_ACTION,
292 providerPackageNames, mLocationHandler);
293 if (networkProvider != null) {
294 mRealProviders.put(LocationManager.NETWORK_PROVIDER, networkProvider);
295 mProxyProviders.add(networkProvider);
296 addProviderLocked(networkProvider);
297 } else {
298 Slog.w(TAG, "no network location provider found");
299 }
300
301 // bind to fused provider
302 LocationProviderProxy fusedLocationProvider = LocationProviderProxy.createAndBind(
303 mContext,
304 LocationManager.FUSED_PROVIDER,
305 FUSED_LOCATION_SERVICE_ACTION,
306 providerPackageNames, mLocationHandler);
307 if (fusedLocationProvider != null) {
308 addProviderLocked(fusedLocationProvider);
309 mProxyProviders.add(fusedLocationProvider);
310 mEnabledProviders.add(fusedLocationProvider.getName());
311 } else {
312 Slog.e(TAG, "no fused location provider found",
313 new IllegalStateException("Location service needs a fused location provider"));
314 }
315
316 // bind to geocoder provider
317 mGeocodeProvider = GeocoderProxy.createAndBind(mContext, providerPackageNames);
318 if (mGeocodeProvider == null) {
319 Slog.e(TAG, "no geocoder provider found");
320 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700321 }
Mike Lockwood9637d472009-04-02 21:41:57 -0700322
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800323 /**
Victoria Lease38389b62012-09-30 11:44:22 -0700324 * Called when the device's active user changes.
325 * @param userId the new active user's UserId
326 */
327 private void switchUser(int userId) {
Victoria Lease83762d22012-10-03 13:51:17 -0700328 mBlacklist.switchUser(userId);
Victoria Lease38389b62012-09-30 11:44:22 -0700329 //Log.d("LocationManagerService", "switchUser(" + mCurrentUserId + " -> " + userId + ")"); // TODO: remove this
330 synchronized (mLock) {
331 // TODO: inform previous user's Receivers that they will no longer receive updates
332 mCurrentUserId = userId;
333 // TODO: inform new user's Receivers that they are back on the update train
334 }
335 }
336
337 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800338 * A wrapper class holding either an ILocationListener or a PendingIntent to receive
339 * location updates.
340 */
Mike Lockwood48f17512009-04-23 09:12:08 -0700341 private final class Receiver implements IBinder.DeathRecipient, PendingIntent.OnFinished {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700342 final int mUid; // uid of receiver
343 final int mPid; // pid of receiver
344 final String mPackageName; // package name of receiver
345 final String mPermission; // best permission that receiver has
346
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800347 final ILocationListener mListener;
348 final PendingIntent mPendingIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800349 final Object mKey;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700350
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400351 final HashMap<String,UpdateRecord> mUpdateRecords = new HashMap<String,UpdateRecord>();
Nick Pellyf1be6862012-05-15 10:53:42 -0700352
Mike Lockwood48f17512009-04-23 09:12:08 -0700353 int mPendingBroadcasts;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800354
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700355 Receiver(ILocationListener listener, PendingIntent intent, int pid, int uid,
356 String packageName) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800357 mListener = listener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800358 mPendingIntent = intent;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700359 if (listener != null) {
360 mKey = listener.asBinder();
361 } else {
362 mKey = intent;
363 }
364 mPermission = checkPermission();
365 mUid = uid;
366 mPid = pid;
367 mPackageName = packageName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800368 }
369
370 @Override
371 public boolean equals(Object otherObj) {
372 if (otherObj instanceof Receiver) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700373 return mKey.equals(((Receiver)otherObj).mKey);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800374 }
375 return false;
376 }
377
378 @Override
379 public int hashCode() {
380 return mKey.hashCode();
381 }
Mike Lockwood3681f262009-05-12 10:52:03 -0400382
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800383 @Override
384 public String toString() {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700385 StringBuilder s = new StringBuilder();
386 s.append("Reciever[");
387 s.append(Integer.toHexString(System.identityHashCode(this)));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800388 if (mListener != null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700389 s.append(" listener");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800390 } else {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700391 s.append(" intent");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800392 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700393 for (String p : mUpdateRecords.keySet()) {
394 s.append(" ").append(mUpdateRecords.get(p).toString());
395 }
396 s.append("]");
397 return s.toString();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800398 }
399
400 public boolean isListener() {
401 return mListener != null;
402 }
403
404 public boolean isPendingIntent() {
405 return mPendingIntent != null;
406 }
407
408 public ILocationListener getListener() {
409 if (mListener != null) {
410 return mListener;
411 }
412 throw new IllegalStateException("Request for non-existent listener");
413 }
414
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800415 public boolean callStatusChangedLocked(String provider, int status, Bundle extras) {
416 if (mListener != null) {
417 try {
Mike Lockwood48f17512009-04-23 09:12:08 -0700418 synchronized (this) {
419 // synchronize to ensure incrementPendingBroadcastsLocked()
420 // is called before decrementPendingBroadcasts()
421 mListener.onStatusChanged(provider, status, extras);
Nick Pellye0fd6932012-07-11 10:26:13 -0700422 // call this after broadcasting so we do not increment
423 // if we throw an exeption.
424 incrementPendingBroadcastsLocked();
Mike Lockwood48f17512009-04-23 09:12:08 -0700425 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800426 } catch (RemoteException e) {
427 return false;
428 }
429 } else {
430 Intent statusChanged = new Intent();
431 statusChanged.putExtras(extras);
432 statusChanged.putExtra(LocationManager.KEY_STATUS_CHANGED, status);
433 try {
Mike Lockwood48f17512009-04-23 09:12:08 -0700434 synchronized (this) {
435 // synchronize to ensure incrementPendingBroadcastsLocked()
436 // is called before decrementPendingBroadcasts()
Dianne Hackborn6c418d52011-06-29 14:05:33 -0700437 mPendingIntent.send(mContext, 0, statusChanged, this, mLocationHandler,
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700438 mPermission);
Mike Lockwood48f17512009-04-23 09:12:08 -0700439 // call this after broadcasting so we do not increment
440 // if we throw an exeption.
441 incrementPendingBroadcastsLocked();
442 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800443 } catch (PendingIntent.CanceledException e) {
444 return false;
445 }
446 }
447 return true;
448 }
449
450 public boolean callLocationChangedLocked(Location location) {
451 if (mListener != null) {
452 try {
Mike Lockwood48f17512009-04-23 09:12:08 -0700453 synchronized (this) {
454 // synchronize to ensure incrementPendingBroadcastsLocked()
455 // is called before decrementPendingBroadcasts()
456 mListener.onLocationChanged(location);
Nick Pellye0fd6932012-07-11 10:26:13 -0700457 // call this after broadcasting so we do not increment
458 // if we throw an exeption.
459 incrementPendingBroadcastsLocked();
Mike Lockwood48f17512009-04-23 09:12:08 -0700460 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800461 } catch (RemoteException e) {
462 return false;
463 }
464 } else {
465 Intent locationChanged = new Intent();
466 locationChanged.putExtra(LocationManager.KEY_LOCATION_CHANGED, location);
467 try {
Mike Lockwood48f17512009-04-23 09:12:08 -0700468 synchronized (this) {
469 // synchronize to ensure incrementPendingBroadcastsLocked()
470 // is called before decrementPendingBroadcasts()
Dianne Hackborn6c418d52011-06-29 14:05:33 -0700471 mPendingIntent.send(mContext, 0, locationChanged, this, mLocationHandler,
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700472 mPermission);
Mike Lockwood48f17512009-04-23 09:12:08 -0700473 // call this after broadcasting so we do not increment
474 // if we throw an exeption.
475 incrementPendingBroadcastsLocked();
476 }
477 } catch (PendingIntent.CanceledException e) {
478 return false;
479 }
480 }
481 return true;
482 }
483
484 public boolean callProviderEnabledLocked(String provider, boolean enabled) {
485 if (mListener != null) {
486 try {
487 synchronized (this) {
488 // synchronize to ensure incrementPendingBroadcastsLocked()
489 // is called before decrementPendingBroadcasts()
490 if (enabled) {
491 mListener.onProviderEnabled(provider);
492 } else {
493 mListener.onProviderDisabled(provider);
494 }
Nick Pellye0fd6932012-07-11 10:26:13 -0700495 // call this after broadcasting so we do not increment
496 // if we throw an exeption.
497 incrementPendingBroadcastsLocked();
Mike Lockwood48f17512009-04-23 09:12:08 -0700498 }
499 } catch (RemoteException e) {
500 return false;
501 }
502 } else {
503 Intent providerIntent = new Intent();
504 providerIntent.putExtra(LocationManager.KEY_PROVIDER_ENABLED, enabled);
505 try {
506 synchronized (this) {
507 // synchronize to ensure incrementPendingBroadcastsLocked()
508 // is called before decrementPendingBroadcasts()
Dianne Hackborn6c418d52011-06-29 14:05:33 -0700509 mPendingIntent.send(mContext, 0, providerIntent, this, mLocationHandler,
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700510 mPermission);
Mike Lockwood48f17512009-04-23 09:12:08 -0700511 // call this after broadcasting so we do not increment
512 // if we throw an exeption.
513 incrementPendingBroadcastsLocked();
514 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800515 } catch (PendingIntent.CanceledException e) {
516 return false;
517 }
518 }
519 return true;
520 }
521
Nick Pellyf1be6862012-05-15 10:53:42 -0700522 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800523 public void binderDied() {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700524 if (D) Log.d(TAG, "Location listener died");
525
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400526 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800527 removeUpdatesLocked(this);
528 }
Mike Lockwood48f17512009-04-23 09:12:08 -0700529 synchronized (this) {
530 if (mPendingBroadcasts > 0) {
531 LocationManagerService.this.decrementPendingBroadcasts();
532 mPendingBroadcasts = 0;
533 }
534 }
535 }
536
Nick Pellye0fd6932012-07-11 10:26:13 -0700537 @Override
Mike Lockwood48f17512009-04-23 09:12:08 -0700538 public void onSendFinished(PendingIntent pendingIntent, Intent intent,
539 int resultCode, String resultData, Bundle resultExtras) {
Mike Lockwood0528b9b2009-05-07 10:12:54 -0400540 synchronized (this) {
541 decrementPendingBroadcastsLocked();
Mike Lockwood48f17512009-04-23 09:12:08 -0700542 }
543 }
544
Mike Lockwood0528b9b2009-05-07 10:12:54 -0400545 // this must be called while synchronized by caller in a synchronized block
546 // containing the sending of the broadcaset
547 private void incrementPendingBroadcastsLocked() {
548 if (mPendingBroadcasts++ == 0) {
549 LocationManagerService.this.incrementPendingBroadcasts();
550 }
551 }
552
553 private void decrementPendingBroadcastsLocked() {
554 if (--mPendingBroadcasts == 0) {
555 LocationManagerService.this.decrementPendingBroadcasts();
Mike Lockwood48f17512009-04-23 09:12:08 -0700556 }
557 }
558 }
559
Nick Pellye0fd6932012-07-11 10:26:13 -0700560 @Override
Mike Lockwood48f17512009-04-23 09:12:08 -0700561 public void locationCallbackFinished(ILocationListener listener) {
Joshua Bartel080b61b2009-10-05 12:44:46 -0400562 //Do not use getReceiver here as that will add the ILocationListener to
563 //the receiver list if it is not found. If it is not found then the
564 //LocationListener was removed when it had a pending broadcast and should
565 //not be added back.
566 IBinder binder = listener.asBinder();
567 Receiver receiver = mReceivers.get(binder);
Mike Lockwood48f17512009-04-23 09:12:08 -0700568 if (receiver != null) {
Mike Lockwood0528b9b2009-05-07 10:12:54 -0400569 synchronized (receiver) {
570 // so wakelock calls will succeed
571 long identity = Binder.clearCallingIdentity();
572 receiver.decrementPendingBroadcastsLocked();
573 Binder.restoreCallingIdentity(identity);
574 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800575 }
576 }
577
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700578 private void addProviderLocked(LocationProviderInterface provider) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400579 mProviders.add(provider);
580 mProvidersByName.put(provider.getName(), provider);
581 }
582
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700583 private void removeProviderLocked(LocationProviderInterface provider) {
584 provider.disable();
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400585 mProviders.remove(provider);
586 mProvidersByName.remove(provider.getName());
587 }
588
Mike Lockwood3d12b512009-04-21 23:25:35 -0700589
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800590 private boolean isAllowedBySettingsLocked(String provider) {
591 if (mEnabledProviders.contains(provider)) {
592 return true;
593 }
594 if (mDisabledProviders.contains(provider)) {
595 return false;
596 }
597 // Use system settings
598 ContentResolver resolver = mContext.getContentResolver();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800599
Brad Larson8eb3ea62009-12-29 11:47:55 -0600600 return Settings.Secure.isLocationProviderEnabled(resolver, provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800601 }
602
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700603 /**
604 * Throw SecurityException if caller has neither COARSE or FINE.
605 * Otherwise, return the best permission.
606 */
607 private String checkPermission() {
608 if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) ==
609 PackageManager.PERMISSION_GRANTED) {
Dianne Hackborn6c418d52011-06-29 14:05:33 -0700610 return ACCESS_FINE_LOCATION;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700611 } else if (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION) ==
612 PackageManager.PERMISSION_GRANTED) {
613 return ACCESS_COARSE_LOCATION;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800614 }
Dianne Hackborn6c418d52011-06-29 14:05:33 -0700615
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700616 throw new SecurityException("Location requires either ACCESS_COARSE_LOCATION or" +
Victoria Lease4fab68b2012-09-13 13:20:59 -0700617 " ACCESS_FINE_LOCATION permission");
618 }
619
620 /**
621 * Throw SecurityException if caller lacks permission to use Geofences.
622 */
623 private void checkGeofencePermission() {
624 if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) !=
625 PackageManager.PERMISSION_GRANTED) {
626 throw new SecurityException("Geofence usage requires ACCESS_FINE_LOCATION permission");
627 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800628 }
629
Victoria Lease8dbb6342012-09-21 16:55:53 -0700630 private boolean isAllowedProviderSafe(String provider) {
631 if (LocationManager.GPS_PROVIDER.equals(provider) ||
632 LocationManager.PASSIVE_PROVIDER.equals(provider)) {
633 // gps and passive providers require FINE permission
634 return mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
635 == PackageManager.PERMISSION_GRANTED;
636 } else if (LocationManager.NETWORK_PROVIDER.equals(provider) ||
637 LocationManager.FUSED_PROVIDER.equals(provider)) {
638 // network and fused providers are ok with COARSE or FINE
639 return (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
640 == PackageManager.PERMISSION_GRANTED) ||
641 (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION)
642 == PackageManager.PERMISSION_GRANTED);
643 }
644 return false;
645 }
646
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700647 /**
648 * Returns all providers by name, including passive, but excluding
Laurent Tu0d21e212012-10-02 15:33:48 -0700649 * fused, also including ones that are not permitted to
650 * be accessed by the calling activity or are currently disabled.
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700651 */
Nick Pellye0fd6932012-07-11 10:26:13 -0700652 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800653 public List<String> getAllProviders() {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700654 ArrayList<String> out;
655 synchronized (mLock) {
656 out = new ArrayList<String>(mProviders.size());
657 for (LocationProviderInterface provider : mProviders) {
658 String name = provider.getName();
659 if (LocationManager.FUSED_PROVIDER.equals(name)) {
Mike Lockwood03ca2162010-04-01 08:10:09 -0700660 continue;
661 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800662 out.add(name);
663 }
664 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700665
666 if (D) Log.d(TAG, "getAllProviders()=" + out);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800667 return out;
668 }
669
Mike Lockwood03ca2162010-04-01 08:10:09 -0700670 /**
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700671 * Return all providers by name, that match criteria and are optionally
672 * enabled.
673 * Can return passive provider, but never returns fused provider.
Mike Lockwood03ca2162010-04-01 08:10:09 -0700674 */
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700675 @Override
676 public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700677 ArrayList<String> out;
Mike Lockwood03ca2162010-04-01 08:10:09 -0700678 synchronized (mLock) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700679 out = new ArrayList<String>(mProviders.size());
680 for (LocationProviderInterface provider : mProviders) {
681 String name = provider.getName();
682 if (LocationManager.FUSED_PROVIDER.equals(name)) {
683 continue;
684 }
Victoria Lease8dbb6342012-09-21 16:55:53 -0700685 if (isAllowedProviderSafe(name)) {
686 if (enabledOnly && !isAllowedBySettingsLocked(name)) {
687 continue;
688 }
689 if (criteria != null && !LocationProvider.propertiesMeetCriteria(
690 name, provider.getProperties(), criteria)) {
691 continue;
692 }
693 out.add(name);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700694 }
Mike Lockwood03ca2162010-04-01 08:10:09 -0700695 }
696 }
697
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700698 if (D) Log.d(TAG, "getProviders()=" + out);
699 return out;
Mike Lockwood03ca2162010-04-01 08:10:09 -0700700 }
701
702 /**
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700703 * Return the name of the best provider given a Criteria object.
704 * This method has been deprecated from the public API,
Victoria Lease8dbb6342012-09-21 16:55:53 -0700705 * and the whole LocationProvider (including #meetsCriteria)
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700706 * has been deprecated as well. So this method now uses
707 * some simplified logic.
Mike Lockwood03ca2162010-04-01 08:10:09 -0700708 */
Nick Pellye0fd6932012-07-11 10:26:13 -0700709 @Override
Mike Lockwood03ca2162010-04-01 08:10:09 -0700710 public String getBestProvider(Criteria criteria, boolean enabledOnly) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700711 String result = null;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700712
713 List<String> providers = getProviders(criteria, enabledOnly);
Victoria Lease8dbb6342012-09-21 16:55:53 -0700714 if (!providers.isEmpty()) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700715 result = pickBest(providers);
716 if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
717 return result;
718 }
719 providers = getProviders(null, enabledOnly);
Victoria Lease8dbb6342012-09-21 16:55:53 -0700720 if (!providers.isEmpty()) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700721 result = pickBest(providers);
722 if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
723 return result;
Mike Lockwood03ca2162010-04-01 08:10:09 -0700724 }
725
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700726 if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
Mike Lockwood03ca2162010-04-01 08:10:09 -0700727 return null;
728 }
729
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700730 private String pickBest(List<String> providers) {
Victoria Lease1925e292012-09-24 17:00:18 -0700731 if (providers.contains(LocationManager.GPS_PROVIDER)) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700732 return LocationManager.GPS_PROVIDER;
Victoria Lease1925e292012-09-24 17:00:18 -0700733 } else if (providers.contains(LocationManager.NETWORK_PROVIDER)) {
734 return LocationManager.NETWORK_PROVIDER;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700735 } else {
736 return providers.get(0);
737 }
738 }
739
Nick Pellye0fd6932012-07-11 10:26:13 -0700740 @Override
Mike Lockwood03ca2162010-04-01 08:10:09 -0700741 public boolean providerMeetsCriteria(String provider, Criteria criteria) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700742 checkPermission();
743
Mike Lockwood03ca2162010-04-01 08:10:09 -0700744 LocationProviderInterface p = mProvidersByName.get(provider);
745 if (p == null) {
746 throw new IllegalArgumentException("provider=" + provider);
747 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700748
749 boolean result = LocationProvider.propertiesMeetCriteria(
750 p.getName(), p.getProperties(), criteria);
751 if (D) Log.d(TAG, "providerMeetsCriteria(" + provider + ", " + criteria + ")=" + result);
752 return result;
Mike Lockwood03ca2162010-04-01 08:10:09 -0700753 }
754
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800755 private void updateProvidersLocked() {
Brad Fitzpatrick0c5a0402010-08-27 14:01:23 -0700756 boolean changesMade = false;
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400757 for (int i = mProviders.size() - 1; i >= 0; i--) {
Mike Lockwoodd03ff942010-02-09 08:46:14 -0500758 LocationProviderInterface p = mProviders.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800759 boolean isEnabled = p.isEnabled();
760 String name = p.getName();
761 boolean shouldBeEnabled = isAllowedBySettingsLocked(name);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800762 if (isEnabled && !shouldBeEnabled) {
763 updateProviderListenersLocked(name, false);
Brad Fitzpatrick0c5a0402010-08-27 14:01:23 -0700764 changesMade = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800765 } else if (!isEnabled && shouldBeEnabled) {
766 updateProviderListenersLocked(name, true);
Brad Fitzpatrick0c5a0402010-08-27 14:01:23 -0700767 changesMade = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800768 }
Brad Fitzpatrick0c5a0402010-08-27 14:01:23 -0700769 }
770 if (changesMade) {
Dianne Hackborn5ac72a22012-08-29 18:32:08 -0700771 mContext.sendBroadcastAsUser(new Intent(LocationManager.PROVIDERS_CHANGED_ACTION),
772 UserHandle.ALL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800773 }
774 }
775
776 private void updateProviderListenersLocked(String provider, boolean enabled) {
777 int listeners = 0;
778
Mike Lockwoodd03ff942010-02-09 08:46:14 -0500779 LocationProviderInterface p = mProvidersByName.get(provider);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700780 if (p == null) return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800781
782 ArrayList<Receiver> deadReceivers = null;
Nick Pellye0fd6932012-07-11 10:26:13 -0700783
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800784 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
785 if (records != null) {
786 final int N = records.size();
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700787 for (int i = 0; i < N; i++) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800788 UpdateRecord record = records.get(i);
789 // Sends a notification message to the receiver
Mike Lockwood48f17512009-04-23 09:12:08 -0700790 if (!record.mReceiver.callProviderEnabledLocked(provider, enabled)) {
791 if (deadReceivers == null) {
792 deadReceivers = new ArrayList<Receiver>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800793 }
Simon Schoar46866572009-06-10 21:12:10 +0200794 deadReceivers.add(record.mReceiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800795 }
796 listeners++;
797 }
798 }
799
800 if (deadReceivers != null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700801 for (int i = deadReceivers.size() - 1; i >= 0; i--) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800802 removeUpdatesLocked(deadReceivers.get(i));
803 }
804 }
Nick Pellye0fd6932012-07-11 10:26:13 -0700805
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800806 if (enabled) {
807 p.enable();
808 if (listeners > 0) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700809 applyRequirementsLocked(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800810 }
811 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800812 p.disable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800813 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800814 }
815
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700816 private void applyRequirementsLocked(String provider) {
817 LocationProviderInterface p = mProvidersByName.get(provider);
818 if (p == null) return;
819
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800820 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700821 WorkSource worksource = new WorkSource();
822 ProviderRequest providerRequest = new ProviderRequest();
823
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800824 if (records != null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700825 for (UpdateRecord record : records) {
826 LocationRequest locationRequest = record.mRequest;
827
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700828 providerRequest.locationRequests.add(locationRequest);
829 if (locationRequest.getInterval() < providerRequest.interval) {
830 providerRequest.reportLocation = true;
831 providerRequest.interval = locationRequest.getInterval();
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700832 }
833 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700834
835 if (providerRequest.reportLocation) {
836 // calculate who to blame for power
837 // This is somewhat arbitrary. We pick a threshold interval
838 // that is slightly higher that the minimum interval, and
839 // spread the blame across all applications with a request
840 // under that threshold.
841 long thresholdInterval = (providerRequest.interval + 1000) * 3 / 2;
842 for (UpdateRecord record : records) {
843 LocationRequest locationRequest = record.mRequest;
844 if (locationRequest.getInterval() <= thresholdInterval) {
845 worksource.add(record.mReceiver.mUid);
846 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700847 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800848 }
849 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700850
851 if (D) Log.d(TAG, "provider request: " + provider + " " + providerRequest);
852 p.setRequest(providerRequest, worksource);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800853 }
854
855 private class UpdateRecord {
856 final String mProvider;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700857 final LocationRequest mRequest;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800858 final Receiver mReceiver;
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400859 Location mLastFixBroadcast;
860 long mLastStatusBroadcast;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800861
862 /**
863 * Note: must be constructed with lock held.
864 */
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700865 UpdateRecord(String provider, LocationRequest request, Receiver receiver) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800866 mProvider = provider;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700867 mRequest = request;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800868 mReceiver = receiver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800869
870 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
871 if (records == null) {
872 records = new ArrayList<UpdateRecord>();
873 mRecordsByProvider.put(provider, records);
874 }
875 if (!records.contains(this)) {
876 records.add(this);
877 }
878 }
879
880 /**
881 * Method to be called when a record will no longer be used. Calling this multiple times
882 * must have the same effect as calling it once.
883 */
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700884 void disposeLocked(boolean removeReceiver) {
885 // remove from mRecordsByProvider
886 ArrayList<UpdateRecord> globalRecords = mRecordsByProvider.get(this.mProvider);
887 if (globalRecords != null) {
888 globalRecords.remove(this);
889 }
890
891 if (!removeReceiver) return; // the caller will handle the rest
892
893 // remove from Receiver#mUpdateRecords
894 HashMap<String, UpdateRecord> receiverRecords = mReceiver.mUpdateRecords;
895 if (receiverRecords != null) {
896 receiverRecords.remove(this.mProvider);
897
898 // and also remove the Receiver if it has no more update records
899 if (removeReceiver && receiverRecords.size() == 0) {
900 removeUpdatesLocked(mReceiver);
901 }
Mike Lockwood3a76fd62009-09-01 07:26:56 -0400902 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800903 }
904
905 @Override
906 public String toString() {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700907 StringBuilder s = new StringBuilder();
908 s.append("UpdateRecord[");
909 s.append(mProvider);
910 s.append(' ').append(mReceiver.mPackageName).append('(');
911 s.append(mReceiver.mUid).append(')');
912 s.append(' ').append(mRequest);
913 s.append(']');
914 return s.toString();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800915 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800916 }
917
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700918 private Receiver getReceiver(ILocationListener listener, int pid, int uid, String packageName) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400919 IBinder binder = listener.asBinder();
920 Receiver receiver = mReceivers.get(binder);
921 if (receiver == null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700922 receiver = new Receiver(listener, null, pid, uid, packageName);
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400923 mReceivers.put(binder, receiver);
924
925 try {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700926 receiver.getListener().asBinder().linkToDeath(receiver, 0);
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400927 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800928 Slog.e(TAG, "linkToDeath failed:", e);
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400929 return null;
930 }
931 }
932 return receiver;
933 }
934
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700935 private Receiver getReceiver(PendingIntent intent, int pid, int uid, String packageName) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400936 Receiver receiver = mReceivers.get(intent);
937 if (receiver == null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700938 receiver = new Receiver(null, intent, pid, uid, packageName);
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400939 mReceivers.put(intent, receiver);
940 }
941 return receiver;
942 }
943
Victoria Lease09016ab2012-09-16 12:33:15 -0700944 private boolean isProviderAllowedByCoarsePermission(String provider) {
945 if (LocationManager.FUSED_PROVIDER.equals(provider)) {
946 return true;
947 }
948 if (LocationManager.PASSIVE_PROVIDER.equals(provider)) {
949 return true;
950 }
951 if (LocationManager.NETWORK_PROVIDER.equals(provider)) {
952 return true;
953 }
954 return false;
955 }
956
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700957 private String checkPermissionAndRequest(LocationRequest request) {
958 String perm = checkPermission();
959
960 if (ACCESS_COARSE_LOCATION.equals(perm)) {
Victoria Lease09016ab2012-09-16 12:33:15 -0700961 if (!isProviderAllowedByCoarsePermission(request.getProvider())) {
962 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
963 }
964 switch (request.getQuality()) {
965 case LocationRequest.ACCURACY_FINE:
966 request.setQuality(LocationRequest.ACCURACY_BLOCK);
967 break;
968 case LocationRequest.POWER_HIGH:
969 request.setQuality(LocationRequest.POWER_LOW);
970 break;
971 }
972 // throttle
973 if (request.getInterval() < LocationFudger.FASTEST_INTERVAL_MS) {
974 request.setInterval(LocationFudger.FASTEST_INTERVAL_MS);
975 }
976 if (request.getFastestInterval() < LocationFudger.FASTEST_INTERVAL_MS) {
977 request.setFastestInterval(LocationFudger.FASTEST_INTERVAL_MS);
978 }
Nick Pelly74fa7ea2012-08-13 19:36:38 -0700979 }
Nick Pelly4e31c4f2012-08-13 19:35:39 -0700980 // make getFastestInterval() the minimum of interval and fastest interval
981 if (request.getFastestInterval() > request.getInterval()) {
982 request.setFastestInterval(request.getInterval());
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400983 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700984 return perm;
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400985 }
986
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700987 private void checkPackageName(String packageName) {
Nick Pellye0fd6932012-07-11 10:26:13 -0700988 if (packageName == null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700989 throw new SecurityException("invalid package name: " + packageName);
Nick Pellye0fd6932012-07-11 10:26:13 -0700990 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700991 int uid = Binder.getCallingUid();
Nick Pellye0fd6932012-07-11 10:26:13 -0700992 String[] packages = mPackageManager.getPackagesForUid(uid);
993 if (packages == null) {
994 throw new SecurityException("invalid UID " + uid);
995 }
996 for (String pkg : packages) {
997 if (packageName.equals(pkg)) return;
998 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700999 throw new SecurityException("invalid package name: " + packageName);
Nick Pellye0fd6932012-07-11 10:26:13 -07001000 }
1001
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001002 private void checkPendingIntent(PendingIntent intent) {
1003 if (intent == null) {
1004 throw new IllegalArgumentException("invalid pending intent: " + intent);
Dianne Hackborn6c418d52011-06-29 14:05:33 -07001005 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001006 }
1007
1008 private Receiver checkListenerOrIntent(ILocationListener listener, PendingIntent intent,
1009 int pid, int uid, String packageName) {
1010 if (intent == null && listener == null) {
1011 throw new IllegalArgumentException("need eiter listener or intent");
1012 } else if (intent != null && listener != null) {
1013 throw new IllegalArgumentException("cannot register both listener and intent");
1014 } else if (intent != null) {
1015 checkPendingIntent(intent);
1016 return getReceiver(intent, pid, uid, packageName);
1017 } else {
1018 return getReceiver(listener, pid, uid, packageName);
1019 }
Dianne Hackborn6c418d52011-06-29 14:05:33 -07001020 }
1021
Nick Pellye0fd6932012-07-11 10:26:13 -07001022 @Override
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001023 public void requestLocationUpdates(LocationRequest request, ILocationListener listener,
1024 PendingIntent intent, String packageName) {
1025 if (request == null) request = DEFAULT_LOCATION_REQUEST;
1026 checkPackageName(packageName);
1027 checkPermissionAndRequest(request);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001028
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001029 final int pid = Binder.getCallingPid();
1030 final int uid = Binder.getCallingUid();
1031 Receiver recevier = checkListenerOrIntent(listener, intent, pid, uid, packageName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001032
Nick Pelly2b7a0d02012-08-17 15:09:44 -07001033 // providers may use public location API's, need to clear identity
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001034 long identity = Binder.clearCallingIdentity();
1035 try {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001036 synchronized (mLock) {
1037 requestLocationUpdatesLocked(request, recevier, pid, uid, packageName);
Mike Lockwood2d4d1bf2010-10-18 17:06:26 -04001038 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001039 } finally {
1040 Binder.restoreCallingIdentity(identity);
1041 }
1042 }
1043
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001044 private void requestLocationUpdatesLocked(LocationRequest request, Receiver receiver,
1045 int pid, int uid, String packageName) {
1046 // Figure out the provider. Either its explicitly request (legacy use cases), or
1047 // use the fused provider
1048 if (request == null) request = DEFAULT_LOCATION_REQUEST;
1049 String name = request.getProvider();
Victoria Lease09016ab2012-09-16 12:33:15 -07001050 if (name == null) {
1051 throw new IllegalArgumentException("provider name must not be null");
1052 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001053 LocationProviderInterface provider = mProvidersByName.get(name);
1054 if (provider == null) {
1055 throw new IllegalArgumentException("provider doesn't exisit: " + provider);
1056 }
1057
1058 Log.i(TAG, "request " + Integer.toHexString(System.identityHashCode(receiver)) + " " +
1059 name + " " + request + " from " + packageName + "(" + uid + ")");
1060
1061 UpdateRecord record = new UpdateRecord(name, request, receiver);
1062 UpdateRecord oldRecord = receiver.mUpdateRecords.put(name, record);
1063 if (oldRecord != null) {
1064 oldRecord.disposeLocked(false);
1065 }
1066
1067 boolean isProviderEnabled = isAllowedBySettingsLocked(name);
1068 if (isProviderEnabled) {
1069 applyRequirementsLocked(name);
1070 } else {
1071 // Notify the listener that updates are currently disabled
1072 receiver.callProviderEnabledLocked(name, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001073 }
1074 }
1075
Nick Pellye0fd6932012-07-11 10:26:13 -07001076 @Override
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001077 public void removeUpdates(ILocationListener listener, PendingIntent intent,
1078 String packageName) {
1079 checkPackageName(packageName);
1080 checkPermission();
1081 final int pid = Binder.getCallingPid();
1082 final int uid = Binder.getCallingUid();
1083 Receiver receiver = checkListenerOrIntent(listener, intent, pid, uid, packageName);
1084
Nick Pelly2b7a0d02012-08-17 15:09:44 -07001085 // providers may use public location API's, need to clear identity
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001086 long identity = Binder.clearCallingIdentity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001087 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001088 synchronized (mLock) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001089 removeUpdatesLocked(receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001090 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001091 } finally {
1092 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001093 }
1094 }
1095
1096 private void removeUpdatesLocked(Receiver receiver) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001097 Log.i(TAG, "remove " + Integer.toHexString(System.identityHashCode(receiver)));
1098
1099 if (mReceivers.remove(receiver.mKey) != null && receiver.isListener()) {
1100 receiver.getListener().asBinder().unlinkToDeath(receiver, 0);
1101 synchronized (receiver) {
1102 if (receiver.mPendingBroadcasts > 0) {
1103 decrementPendingBroadcasts();
1104 receiver.mPendingBroadcasts = 0;
1105 }
1106 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001107 }
1108
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001109 // Record which providers were associated with this listener
1110 HashSet<String> providers = new HashSet<String>();
1111 HashMap<String, UpdateRecord> oldRecords = receiver.mUpdateRecords;
1112 if (oldRecords != null) {
1113 // Call dispose() on the obsolete update records.
1114 for (UpdateRecord record : oldRecords.values()) {
1115 record.disposeLocked(false);
1116 }
1117 // Accumulate providers
1118 providers.addAll(oldRecords.keySet());
1119 }
1120
1121 // update provider
1122 for (String provider : providers) {
1123 // If provider is already disabled, don't need to do anything
1124 if (!isAllowedBySettingsLocked(provider)) {
1125 continue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001126 }
1127
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001128 applyRequirementsLocked(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001129 }
1130 }
1131
Nick Pellye0fd6932012-07-11 10:26:13 -07001132 @Override
Nick Pelly4035f5a2012-08-17 14:43:49 -07001133 public Location getLastLocation(LocationRequest request, String packageName) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001134 if (D) Log.d(TAG, "getLastLocation: " + request);
1135 if (request == null) request = DEFAULT_LOCATION_REQUEST;
1136 String perm = checkPermissionAndRequest(request);
Nick Pelly4035f5a2012-08-17 14:43:49 -07001137 checkPackageName(packageName);
1138
1139 if (mBlacklist.isBlacklisted(packageName)) {
1140 if (D) Log.d(TAG, "not returning last loc for blacklisted app: " +
1141 packageName);
1142 return null;
1143 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001144
1145 synchronized (mLock) {
1146 // Figure out the provider. Either its explicitly request (deprecated API's),
1147 // or use the fused provider
1148 String name = request.getProvider();
1149 if (name == null) name = LocationManager.FUSED_PROVIDER;
1150 LocationProviderInterface provider = mProvidersByName.get(name);
1151 if (provider == null) return null;
1152
1153 if (!isAllowedBySettingsLocked(name)) return null;
1154
1155 Location location = mLastLocation.get(name);
Victoria Lease09016ab2012-09-16 12:33:15 -07001156 if (location == null) {
1157 return null;
1158 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001159 if (ACCESS_FINE_LOCATION.equals(perm)) {
1160 return location;
1161 } else {
Victoria Lease09016ab2012-09-16 12:33:15 -07001162 Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
1163 if (noGPSLocation != null) {
1164 return mLocationFudger.getOrCreate(noGPSLocation);
1165 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001166 }
1167 }
Victoria Lease09016ab2012-09-16 12:33:15 -07001168 return null;
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001169 }
1170
1171 @Override
1172 public void requestGeofence(LocationRequest request, Geofence geofence, PendingIntent intent,
1173 String packageName) {
1174 if (request == null) request = DEFAULT_LOCATION_REQUEST;
Victoria Lease4fab68b2012-09-13 13:20:59 -07001175 checkGeofencePermission();
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001176 checkPermissionAndRequest(request);
1177 checkPendingIntent(intent);
1178 checkPackageName(packageName);
1179
1180 if (D) Log.d(TAG, "requestGeofence: " + request + " " + geofence + " " + intent);
1181
Nick Pelly2b7a0d02012-08-17 15:09:44 -07001182 // geo-fence manager uses the public location API, need to clear identity
1183 int uid = Binder.getCallingUid();
1184 long identity = Binder.clearCallingIdentity();
1185 try {
1186 mGeofenceManager.addFence(request, geofence, intent, uid, packageName);
1187 } finally {
1188 Binder.restoreCallingIdentity(identity);
1189 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001190 }
1191
1192 @Override
1193 public void removeGeofence(Geofence geofence, PendingIntent intent, String packageName) {
Victoria Lease4fab68b2012-09-13 13:20:59 -07001194 checkGeofencePermission();
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001195 checkPendingIntent(intent);
1196 checkPackageName(packageName);
1197
1198 if (D) Log.d(TAG, "removeGeofence: " + geofence + " " + intent);
1199
Nick Pelly2b7a0d02012-08-17 15:09:44 -07001200 // geo-fence manager uses the public location API, need to clear identity
1201 long identity = Binder.clearCallingIdentity();
1202 try {
1203 mGeofenceManager.removeFence(geofence, intent);
1204 } finally {
1205 Binder.restoreCallingIdentity(identity);
1206 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001207 }
1208
1209
1210 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001211 public boolean addGpsStatusListener(IGpsStatusListener listener) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001212 if (mGpsStatusProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001213 return false;
1214 }
Mike Lockwoodb7e99222009-07-07 13:18:21 -04001215 if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) !=
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001216 PackageManager.PERMISSION_GRANTED) {
1217 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
1218 }
1219
1220 try {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001221 mGpsStatusProvider.addGpsStatusListener(listener);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001222 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001223 Slog.e(TAG, "mGpsStatusProvider.addGpsStatusListener failed", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001224 return false;
1225 }
1226 return true;
1227 }
1228
Nick Pellye0fd6932012-07-11 10:26:13 -07001229 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001230 public void removeGpsStatusListener(IGpsStatusListener listener) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001231 synchronized (mLock) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001232 try {
1233 mGpsStatusProvider.removeGpsStatusListener(listener);
1234 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001235 Slog.e(TAG, "mGpsStatusProvider.removeGpsStatusListener failed", e);
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001236 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001237 }
1238 }
1239
Nick Pellye0fd6932012-07-11 10:26:13 -07001240 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001241 public boolean sendExtraCommand(String provider, String command, Bundle extras) {
Mike Lockwoodc6cc8362009-08-17 13:16:08 -04001242 if (provider == null) {
1243 // throw NullPointerException to remain compatible with previous implementation
1244 throw new NullPointerException();
1245 }
1246
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001247 checkPermission();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001248 // and check for ACCESS_LOCATION_EXTRA_COMMANDS
Mike Lockwoodb7e99222009-07-07 13:18:21 -04001249 if ((mContext.checkCallingOrSelfPermission(ACCESS_LOCATION_EXTRA_COMMANDS)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001250 != PackageManager.PERMISSION_GRANTED)) {
1251 throw new SecurityException("Requires ACCESS_LOCATION_EXTRA_COMMANDS permission");
1252 }
1253
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001254 synchronized (mLock) {
Mike Lockwoodd03ff942010-02-09 08:46:14 -05001255 LocationProviderInterface p = mProvidersByName.get(provider);
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001256 if (p == null) return false;
Nick Pellye0fd6932012-07-11 10:26:13 -07001257
Mike Lockwoodd03ff942010-02-09 08:46:14 -05001258 return p.sendExtraCommand(command, extras);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001259 }
1260 }
1261
Nick Pellye0fd6932012-07-11 10:26:13 -07001262 @Override
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001263 public boolean sendNiResponse(int notifId, int userResponse) {
Mike Lockwood18ad9f62009-08-27 14:01:23 -07001264 if (Binder.getCallingUid() != Process.myUid()) {
1265 throw new SecurityException(
1266 "calling sendNiResponse from outside of the system is not allowed");
1267 }
Danke Xie22d1f9f2009-08-18 18:28:45 -04001268 try {
1269 return mNetInitiatedListener.sendNiResponse(notifId, userResponse);
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001270 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001271 Slog.e(TAG, "RemoteException in LocationManagerService.sendNiResponse");
Danke Xie22d1f9f2009-08-18 18:28:45 -04001272 return false;
1273 }
1274 }
1275
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001276 /**
Mike Lockwood628fd6d2010-01-25 22:46:13 -05001277 * @return null if the provider does not exist
Alexey Tarasovf2db9fb2009-09-01 02:37:07 +11001278 * @throws SecurityException if the provider is not allowed to be
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001279 * accessed by the caller
1280 */
Nick Pellye0fd6932012-07-11 10:26:13 -07001281 @Override
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001282 public ProviderProperties getProviderProperties(String provider) {
1283 checkPermission();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001284
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001285 LocationProviderInterface p;
1286 synchronized (mLock) {
1287 p = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001288 }
1289
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001290 if (p == null) return null;
1291 return p.getProperties();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001292 }
1293
Nick Pellye0fd6932012-07-11 10:26:13 -07001294 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001295 public boolean isProviderEnabled(String provider) {
Victoria Leasef429921e2012-10-04 08:01:19 -07001296 String perms = checkPermission();
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001297 if (LocationManager.FUSED_PROVIDER.equals(provider)) return false;
Victoria Leasef429921e2012-10-04 08:01:19 -07001298 if (ACCESS_COARSE_LOCATION.equals(perms) &&
1299 !isProviderAllowedByCoarsePermission(provider)) {
1300 throw new SecurityException("The \"" + provider +
1301 "\" provider requires ACCESS_FINE_LOCATION permission");
1302 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001303
1304 synchronized (mLock) {
1305 LocationProviderInterface p = mProvidersByName.get(provider);
1306 if (p == null) return false;
1307
1308 return isAllowedBySettingsLocked(provider);
1309 }
1310 }
1311
1312 private void checkCallerIsProvider() {
1313 if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER)
1314 == PackageManager.PERMISSION_GRANTED) {
1315 return;
1316 }
1317
1318 // Previously we only used the INSTALL_LOCATION_PROVIDER
1319 // check. But that is system or signature
1320 // protection level which is not flexible enough for
1321 // providers installed oustide the system image. So
1322 // also allow providers with a UID matching the
1323 // currently bound package name
1324
1325 int uid = Binder.getCallingUid();
1326
1327 if (mGeocodeProvider != null) {
1328 if (doesPackageHaveUid(uid, mGeocodeProvider.getConnectedPackageName())) return;
1329 }
1330 for (LocationProviderProxy proxy : mProxyProviders) {
1331 if (doesPackageHaveUid(uid, proxy.getConnectedPackageName())) return;
1332 }
1333 throw new SecurityException("need INSTALL_LOCATION_PROVIDER permission, " +
1334 "or UID of a currently bound location provider");
1335 }
1336
1337 private boolean doesPackageHaveUid(int uid, String packageName) {
1338 if (packageName == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001339 return false;
1340 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001341 try {
1342 ApplicationInfo appInfo = mPackageManager.getApplicationInfo(packageName, 0);
1343 if (appInfo.uid != uid) {
1344 return false;
1345 }
1346 } catch (NameNotFoundException e) {
1347 return false;
1348 }
1349 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001350 }
1351
Nick Pellye0fd6932012-07-11 10:26:13 -07001352 @Override
Mike Lockwooda4903f22010-02-17 06:42:23 -05001353 public void reportLocation(Location location, boolean passive) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001354 checkCallerIsProvider();
Mike Lockwood275555c2009-05-01 11:30:34 -04001355
Nick Pelly2eeeec22012-07-18 13:13:37 -07001356 if (!location.isComplete()) {
1357 Log.w(TAG, "Dropping incomplete location: " + location);
1358 return;
1359 }
1360
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001361 mLocationHandler.removeMessages(MSG_LOCATION_CHANGED, location);
1362 Message m = Message.obtain(mLocationHandler, MSG_LOCATION_CHANGED, location);
Mike Lockwooda4903f22010-02-17 06:42:23 -05001363 m.arg1 = (passive ? 1 : 0);
Mike Lockwood4e50b782009-04-03 08:24:43 -07001364 mLocationHandler.sendMessageAtFrontOfQueue(m);
1365 }
1366
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001367
1368 private static boolean shouldBroadcastSafe(Location loc, Location lastLoc, UpdateRecord record) {
1369 // Always broadcast the first update
1370 if (lastLoc == null) {
1371 return true;
1372 }
1373
Nick Pellyf1be6862012-05-15 10:53:42 -07001374 // Check whether sufficient time has passed
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001375 long minTime = record.mRequest.getFastestInterval();
Philip Milne41180122012-09-26 11:29:25 -07001376 long delta = (loc.getElapsedRealtimeNanos() - lastLoc.getElapsedRealtimeNanos()) / 1000000L;
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001377 if (delta < minTime - MAX_PROVIDER_SCHEDULING_JITTER_MS) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001378 return false;
1379 }
1380
1381 // Check whether sufficient distance has been traveled
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001382 double minDistance = record.mRequest.getSmallestDisplacement();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001383 if (minDistance > 0.0) {
1384 if (loc.distanceTo(lastLoc) <= minDistance) {
1385 return false;
1386 }
1387 }
1388
1389 return true;
1390 }
1391
Mike Lockwooda4903f22010-02-17 06:42:23 -05001392 private void handleLocationChangedLocked(Location location, boolean passive) {
Nick Pelly4e31c4f2012-08-13 19:35:39 -07001393 if (D) Log.d(TAG, "incoming location: " + location);
1394
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001395 long now = SystemClock.elapsedRealtime();
Mike Lockwooda4903f22010-02-17 06:42:23 -05001396 String provider = (passive ? LocationManager.PASSIVE_PROVIDER : location.getProvider());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001397 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001398 if (records == null || records.size() == 0) return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001399
Mike Lockwoodd03ff942010-02-09 08:46:14 -05001400 LocationProviderInterface p = mProvidersByName.get(provider);
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001401 if (p == null) return;
1402
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001403 // Update last known locations
Victoria Lease09016ab2012-09-16 12:33:15 -07001404 Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
1405 Location lastNoGPSLocation = null;
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001406 Location lastLocation = mLastLocation.get(provider);
Mike Lockwood4e50b782009-04-03 08:24:43 -07001407 if (lastLocation == null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001408 lastLocation = new Location(provider);
1409 mLastLocation.put(provider, lastLocation);
Victoria Lease09016ab2012-09-16 12:33:15 -07001410 } else {
1411 lastNoGPSLocation = lastLocation.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
1412 if (noGPSLocation == null && lastNoGPSLocation != null) {
1413 // New location has no no-GPS location: adopt last no-GPS location. This is set
1414 // directly into location because we do not want to notify COARSE clients.
1415 location.setExtraLocation(Location.EXTRA_NO_GPS_LOCATION, lastNoGPSLocation);
1416 }
Mike Lockwood4e50b782009-04-03 08:24:43 -07001417 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001418 lastLocation.set(location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001419
Victoria Lease09016ab2012-09-16 12:33:15 -07001420 // Fetch coarse location
1421 Location coarseLocation = null;
1422 if (noGPSLocation != null && !noGPSLocation.equals(lastNoGPSLocation)) {
1423 coarseLocation = mLocationFudger.getOrCreate(noGPSLocation);
1424 }
1425
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001426 // Fetch latest status update time
1427 long newStatusUpdateTime = p.getStatusUpdateTime();
1428
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001429 // Get latest status
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001430 Bundle extras = new Bundle();
1431 int status = p.getStatus(extras);
1432
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001433 ArrayList<Receiver> deadReceivers = null;
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001434 ArrayList<UpdateRecord> deadUpdateRecords = null;
Nick Pellye0fd6932012-07-11 10:26:13 -07001435
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001436 // Broadcast location or status to all listeners
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001437 for (UpdateRecord r : records) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001438 Receiver receiver = r.mReceiver;
Mike Lockwood03ca2162010-04-01 08:10:09 -07001439 boolean receiverDead = false;
Nick Pelly4035f5a2012-08-17 14:43:49 -07001440
1441 if (mBlacklist.isBlacklisted(receiver.mPackageName)) {
1442 if (D) Log.d(TAG, "skipping loc update for blacklisted app: " +
1443 receiver.mPackageName);
1444 continue;
1445 }
1446
Victoria Lease09016ab2012-09-16 12:33:15 -07001447 Location notifyLocation = null;
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001448 if (ACCESS_FINE_LOCATION.equals(receiver.mPermission)) {
Victoria Lease09016ab2012-09-16 12:33:15 -07001449 notifyLocation = lastLocation; // use fine location
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001450 } else {
Victoria Lease09016ab2012-09-16 12:33:15 -07001451 notifyLocation = coarseLocation; // use coarse location if available
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001452 }
Victoria Lease09016ab2012-09-16 12:33:15 -07001453 if (notifyLocation != null) {
1454 Location lastLoc = r.mLastFixBroadcast;
1455 if ((lastLoc == null) || shouldBroadcastSafe(notifyLocation, lastLoc, r)) {
1456 if (lastLoc == null) {
1457 lastLoc = new Location(notifyLocation);
1458 r.mLastFixBroadcast = lastLoc;
1459 } else {
1460 lastLoc.set(notifyLocation);
1461 }
1462 if (!receiver.callLocationChangedLocked(notifyLocation)) {
1463 Slog.w(TAG, "RemoteException calling onLocationChanged on " + receiver);
1464 receiverDead = true;
1465 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001466 }
1467 }
1468
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001469 long prevStatusUpdateTime = r.mLastStatusBroadcast;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001470 if ((newStatusUpdateTime > prevStatusUpdateTime) &&
Victoria Lease09016ab2012-09-16 12:33:15 -07001471 (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001472
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001473 r.mLastStatusBroadcast = newStatusUpdateTime;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001474 if (!receiver.callStatusChangedLocked(provider, status, extras)) {
Mike Lockwood03ca2162010-04-01 08:10:09 -07001475 receiverDead = true;
Joe Onorato8a9b2202010-02-26 18:56:32 -08001476 Slog.w(TAG, "RemoteException calling onStatusChanged on " + receiver);
Mike Lockwood03ca2162010-04-01 08:10:09 -07001477 }
1478 }
1479
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001480 // track expired records
1481 if (r.mRequest.getNumUpdates() == 0 || r.mRequest.getExpireAt() < now) {
1482 if (deadUpdateRecords == null) {
1483 deadUpdateRecords = new ArrayList<UpdateRecord>();
1484 }
1485 deadUpdateRecords.add(r);
1486 }
1487 // track dead receivers
1488 if (receiverDead) {
Mike Lockwood03ca2162010-04-01 08:10:09 -07001489 if (deadReceivers == null) {
1490 deadReceivers = new ArrayList<Receiver>();
1491 }
1492 if (!deadReceivers.contains(receiver)) {
1493 deadReceivers.add(receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001494 }
1495 }
1496 }
Nick Pellye0fd6932012-07-11 10:26:13 -07001497
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001498 // remove dead records and receivers outside the loop
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001499 if (deadReceivers != null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001500 for (Receiver receiver : deadReceivers) {
1501 removeUpdatesLocked(receiver);
1502 }
1503 }
1504 if (deadUpdateRecords != null) {
1505 for (UpdateRecord r : deadUpdateRecords) {
1506 r.disposeLocked(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001507 }
1508 }
1509 }
1510
1511 private class LocationWorkerHandler extends Handler {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001512 @Override
1513 public void handleMessage(Message msg) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001514 switch (msg.what) {
1515 case MSG_LOCATION_CHANGED:
1516 handleLocationChanged((Location) msg.obj, msg.arg1 == 1);
1517 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001518 }
1519 }
1520 }
1521
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001522 private void handleLocationChanged(Location location, boolean passive) {
1523 String provider = location.getProvider();
Jeff Sharkey5e613312012-01-30 11:16:20 -08001524
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001525 if (!passive) {
1526 // notify passive provider of the new location
1527 mPassiveProvider.updateLocation(location);
1528 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001529
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001530 synchronized (mLock) {
1531 if (isAllowedBySettingsLocked(provider)) {
1532 handleLocationChangedLocked(location, passive);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001533 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001534 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001535 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001536
Mike Lockwoode97ae402010-09-29 15:23:46 -04001537 private final PackageMonitor mPackageMonitor = new PackageMonitor() {
1538 @Override
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001539 public void onPackageDisappeared(String packageName, int reason) {
1540 // remove all receivers associated with this package name
1541 synchronized (mLock) {
1542 ArrayList<Receiver> deadReceivers = null;
1543
1544 for (Receiver receiver : mReceivers.values()) {
1545 if (receiver.mPackageName.equals(packageName)) {
1546 if (deadReceivers == null) {
1547 deadReceivers = new ArrayList<Receiver>();
1548 }
1549 deadReceivers.add(receiver);
1550 }
1551 }
1552
1553 // perform removal outside of mReceivers loop
1554 if (deadReceivers != null) {
1555 for (Receiver receiver : deadReceivers) {
1556 removeUpdatesLocked(receiver);
1557 }
1558 }
1559 }
Nick Pellye0fd6932012-07-11 10:26:13 -07001560 }
Mike Lockwoode97ae402010-09-29 15:23:46 -04001561 };
1562
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001563 // Wake locks
1564
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001565 private void incrementPendingBroadcasts() {
1566 synchronized (mWakeLock) {
1567 if (mPendingBroadcasts++ == 0) {
1568 try {
1569 mWakeLock.acquire();
1570 log("Acquired wakelock");
1571 } catch (Exception e) {
1572 // This is to catch a runtime exception thrown when we try to release an
1573 // already released lock.
Joe Onorato8a9b2202010-02-26 18:56:32 -08001574 Slog.e(TAG, "exception in acquireWakeLock()", e);
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001575 }
1576 }
Mike Lockwood48f17512009-04-23 09:12:08 -07001577 }
1578 }
1579
1580 private void decrementPendingBroadcasts() {
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001581 synchronized (mWakeLock) {
Mike Lockwood48f17512009-04-23 09:12:08 -07001582 if (--mPendingBroadcasts == 0) {
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001583 try {
1584 // Release wake lock
1585 if (mWakeLock.isHeld()) {
1586 mWakeLock.release();
1587 log("Released wakelock");
1588 } else {
1589 log("Can't release wakelock again!");
1590 }
1591 } catch (Exception e) {
1592 // This is to catch a runtime exception thrown when we try to release an
1593 // already released lock.
Joe Onorato8a9b2202010-02-26 18:56:32 -08001594 Slog.e(TAG, "exception in releaseWakeLock()", e);
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001595 }
Mike Lockwood48f17512009-04-23 09:12:08 -07001596 }
1597 }
1598 }
1599
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001600 // Geocoder
1601
Nick Pellye0fd6932012-07-11 10:26:13 -07001602 @Override
Mike Lockwoode15735a2010-09-20 17:48:47 -04001603 public boolean geocoderIsPresent() {
Mark Vandevoorde01ac80b2010-05-21 15:43:26 -07001604 return mGeocodeProvider != null;
1605 }
1606
Nick Pellye0fd6932012-07-11 10:26:13 -07001607 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001608 public String getFromLocation(double latitude, double longitude, int maxResults,
Mike Lockwood34901402010-01-04 12:14:21 -05001609 GeocoderParams params, List<Address> addrs) {
Mike Lockwooda55c3212009-04-15 11:10:11 -04001610 if (mGeocodeProvider != null) {
Mike Lockwood628fd6d2010-01-25 22:46:13 -05001611 return mGeocodeProvider.getFromLocation(latitude, longitude, maxResults,
1612 params, addrs);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001613 }
Mike Lockwooda55c3212009-04-15 11:10:11 -04001614 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001615 }
1616
Mike Lockwooda55c3212009-04-15 11:10:11 -04001617
Nick Pellye0fd6932012-07-11 10:26:13 -07001618 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001619 public String getFromLocationName(String locationName,
Mike Lockwooda55c3212009-04-15 11:10:11 -04001620 double lowerLeftLatitude, double lowerLeftLongitude,
1621 double upperRightLatitude, double upperRightLongitude, int maxResults,
Mike Lockwood34901402010-01-04 12:14:21 -05001622 GeocoderParams params, List<Address> addrs) {
Mike Lockwooda55c3212009-04-15 11:10:11 -04001623
1624 if (mGeocodeProvider != null) {
Mike Lockwood628fd6d2010-01-25 22:46:13 -05001625 return mGeocodeProvider.getFromLocationName(locationName, lowerLeftLatitude,
1626 lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
1627 maxResults, params, addrs);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001628 }
Mike Lockwooda55c3212009-04-15 11:10:11 -04001629 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001630 }
1631
1632 // Mock Providers
1633
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001634 private void checkMockPermissionsSafe() {
1635 boolean allowMocks = Settings.Secure.getInt(mContext.getContentResolver(),
1636 Settings.Secure.ALLOW_MOCK_LOCATION, 0) == 1;
1637 if (!allowMocks) {
1638 throw new SecurityException("Requires ACCESS_MOCK_LOCATION secure setting");
1639 }
1640
1641 if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) !=
1642 PackageManager.PERMISSION_GRANTED) {
1643 throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission");
Nick Pellye0fd6932012-07-11 10:26:13 -07001644 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001645 }
1646
Nick Pellye0fd6932012-07-11 10:26:13 -07001647 @Override
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001648 public void addTestProvider(String name, ProviderProperties properties) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001649 checkMockPermissionsSafe();
1650
Mike Lockwooda4903f22010-02-17 06:42:23 -05001651 if (LocationManager.PASSIVE_PROVIDER.equals(name)) {
1652 throw new IllegalArgumentException("Cannot mock the passive location provider");
1653 }
1654
Mike Lockwood86328a92009-10-23 08:38:25 -04001655 long identity = Binder.clearCallingIdentity();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001656 synchronized (mLock) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001657 MockProvider provider = new MockProvider(name, this, properties);
Mike Lockwood7566c1d2009-08-25 10:05:18 -07001658 // remove the real provider if we are replacing GPS or network provider
1659 if (LocationManager.GPS_PROVIDER.equals(name)
Nick Pelly1332b532012-08-21 16:25:47 -07001660 || LocationManager.NETWORK_PROVIDER.equals(name)
1661 || LocationManager.FUSED_PROVIDER.equals(name)) {
Mike Lockwoodd03ff942010-02-09 08:46:14 -05001662 LocationProviderInterface p = mProvidersByName.get(name);
1663 if (p != null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001664 removeProviderLocked(p);
Mike Lockwood7566c1d2009-08-25 10:05:18 -07001665 }
1666 }
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001667 if (mProvidersByName.get(name) != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001668 throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
1669 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001670 addProviderLocked(provider);
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001671 mMockProviders.put(name, provider);
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001672 mLastLocation.put(name, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001673 updateProvidersLocked();
1674 }
Mike Lockwood86328a92009-10-23 08:38:25 -04001675 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001676 }
1677
Nick Pellye0fd6932012-07-11 10:26:13 -07001678 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001679 public void removeTestProvider(String provider) {
1680 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001681 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001682 MockProvider mockProvider = mMockProviders.get(provider);
1683 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001684 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1685 }
Mike Lockwood86328a92009-10-23 08:38:25 -04001686 long identity = Binder.clearCallingIdentity();
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001687 removeProviderLocked(mProvidersByName.get(provider));
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001688 mMockProviders.remove(mockProvider);
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001689
1690 // reinstate real provider if available
1691 LocationProviderInterface realProvider = mRealProviders.get(provider);
1692 if (realProvider != null) {
1693 addProviderLocked(realProvider);
Mike Lockwood7566c1d2009-08-25 10:05:18 -07001694 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001695 mLastLocation.put(provider, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001696 updateProvidersLocked();
Mike Lockwood86328a92009-10-23 08:38:25 -04001697 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001698 }
1699 }
1700
Nick Pellye0fd6932012-07-11 10:26:13 -07001701 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001702 public void setTestProviderLocation(String provider, Location loc) {
1703 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001704 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001705 MockProvider mockProvider = mMockProviders.get(provider);
1706 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001707 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1708 }
Mike Lockwood95427cd2009-05-07 13:27:54 -04001709 // clear calling identity so INSTALL_LOCATION_PROVIDER permission is not required
1710 long identity = Binder.clearCallingIdentity();
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001711 mockProvider.setLocation(loc);
Mike Lockwood95427cd2009-05-07 13:27:54 -04001712 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001713 }
1714 }
1715
Nick Pellye0fd6932012-07-11 10:26:13 -07001716 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001717 public void clearTestProviderLocation(String provider) {
1718 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001719 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001720 MockProvider mockProvider = mMockProviders.get(provider);
1721 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001722 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1723 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001724 mockProvider.clearLocation();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001725 }
1726 }
1727
Nick Pellye0fd6932012-07-11 10:26:13 -07001728 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001729 public void setTestProviderEnabled(String provider, boolean enabled) {
1730 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001731 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001732 MockProvider mockProvider = mMockProviders.get(provider);
1733 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001734 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1735 }
Mike Lockwood86328a92009-10-23 08:38:25 -04001736 long identity = Binder.clearCallingIdentity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001737 if (enabled) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001738 mockProvider.enable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001739 mEnabledProviders.add(provider);
1740 mDisabledProviders.remove(provider);
1741 } else {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001742 mockProvider.disable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001743 mEnabledProviders.remove(provider);
1744 mDisabledProviders.add(provider);
1745 }
1746 updateProvidersLocked();
Mike Lockwood86328a92009-10-23 08:38:25 -04001747 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001748 }
1749 }
1750
Nick Pellye0fd6932012-07-11 10:26:13 -07001751 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001752 public void clearTestProviderEnabled(String provider) {
1753 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001754 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001755 MockProvider mockProvider = mMockProviders.get(provider);
1756 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001757 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1758 }
Mike Lockwood86328a92009-10-23 08:38:25 -04001759 long identity = Binder.clearCallingIdentity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001760 mEnabledProviders.remove(provider);
1761 mDisabledProviders.remove(provider);
1762 updateProvidersLocked();
Mike Lockwood86328a92009-10-23 08:38:25 -04001763 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001764 }
1765 }
1766
Nick Pellye0fd6932012-07-11 10:26:13 -07001767 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001768 public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
1769 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001770 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001771 MockProvider mockProvider = mMockProviders.get(provider);
1772 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001773 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1774 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001775 mockProvider.setStatus(status, extras, updateTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001776 }
1777 }
1778
Nick Pellye0fd6932012-07-11 10:26:13 -07001779 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001780 public void clearTestProviderStatus(String provider) {
1781 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001782 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001783 MockProvider mockProvider = mMockProviders.get(provider);
1784 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001785 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1786 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001787 mockProvider.clearStatus();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001788 }
1789 }
1790
1791 private void log(String log) {
1792 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001793 Slog.d(TAG, log);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001794 }
1795 }
Nick Pellye0fd6932012-07-11 10:26:13 -07001796
1797 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001798 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1799 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1800 != PackageManager.PERMISSION_GRANTED) {
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001801 pw.println("Permission Denial: can't dump LocationManagerService from from pid="
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001802 + Binder.getCallingPid()
1803 + ", uid=" + Binder.getCallingUid());
1804 return;
1805 }
Nick Pellye0fd6932012-07-11 10:26:13 -07001806
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001807 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001808 pw.println("Current Location Manager state:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001809 pw.println(" Location Listeners:");
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001810 for (Receiver receiver : mReceivers.values()) {
1811 pw.println(" " + receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001812 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001813 pw.println(" Records by Provider:");
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001814 for (Map.Entry<String, ArrayList<UpdateRecord>> entry : mRecordsByProvider.entrySet()) {
1815 pw.println(" " + entry.getKey() + ":");
1816 for (UpdateRecord record : entry.getValue()) {
1817 pw.println(" " + record);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001818 }
1819 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001820 pw.println(" Last Known Locations:");
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001821 for (Map.Entry<String, Location> entry : mLastLocation.entrySet()) {
1822 String provider = entry.getKey();
1823 Location location = entry.getValue();
1824 pw.println(" " + provider + ": " + location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001825 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001826
Nick Pellye0fd6932012-07-11 10:26:13 -07001827 mGeofenceManager.dump(pw);
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001828
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001829 if (mEnabledProviders.size() > 0) {
1830 pw.println(" Enabled Providers:");
1831 for (String i : mEnabledProviders) {
1832 pw.println(" " + i);
1833 }
Nick Pellye0fd6932012-07-11 10:26:13 -07001834
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001835 }
1836 if (mDisabledProviders.size() > 0) {
1837 pw.println(" Disabled Providers:");
1838 for (String i : mDisabledProviders) {
1839 pw.println(" " + i);
1840 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001841 }
Nick Pelly4035f5a2012-08-17 14:43:49 -07001842 pw.append(" ");
1843 mBlacklist.dump(pw);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001844 if (mMockProviders.size() > 0) {
1845 pw.println(" Mock Providers:");
1846 for (Map.Entry<String, MockProvider> i : mMockProviders.entrySet()) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001847 i.getValue().dump(pw, " ");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001848 }
1849 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001850
Nick Pelly74fa7ea2012-08-13 19:36:38 -07001851 pw.append(" fudger: ");
1852 mLocationFudger.dump(fd, pw, args);
1853
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001854 if (args.length > 0 && "short".equals(args[0])) {
1855 return;
1856 }
Fred Fettinger3c8fbdf2010-01-04 15:38:13 -06001857 for (LocationProviderInterface provider: mProviders) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001858 pw.print(provider.getName() + " Internal State");
1859 if (provider instanceof LocationProviderProxy) {
1860 LocationProviderProxy proxy = (LocationProviderProxy) provider;
1861 pw.print(" (" + proxy.getConnectedPackageName() + ")");
Fred Fettinger3c8fbdf2010-01-04 15:38:13 -06001862 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001863 pw.println(":");
1864 provider.dump(fd, pw, args);
Fred Fettinger3c8fbdf2010-01-04 15:38:13 -06001865 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001866 }
1867 }
1868}