blob: bb05f932b301f0ee684ae986c1a14072701bf1c1 [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
649 * fused.
650 */
Nick Pellye0fd6932012-07-11 10:26:13 -0700651 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800652 public List<String> getAllProviders() {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700653 checkPermission();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800654
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700655 ArrayList<String> out;
656 synchronized (mLock) {
657 out = new ArrayList<String>(mProviders.size());
658 for (LocationProviderInterface provider : mProviders) {
659 String name = provider.getName();
660 if (LocationManager.FUSED_PROVIDER.equals(name)) {
Mike Lockwood03ca2162010-04-01 08:10:09 -0700661 continue;
662 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800663 out.add(name);
664 }
665 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700666
667 if (D) Log.d(TAG, "getAllProviders()=" + out);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800668 return out;
669 }
670
Mike Lockwood03ca2162010-04-01 08:10:09 -0700671 /**
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700672 * Return all providers by name, that match criteria and are optionally
673 * enabled.
674 * Can return passive provider, but never returns fused provider.
Mike Lockwood03ca2162010-04-01 08:10:09 -0700675 */
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700676 @Override
677 public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700678 ArrayList<String> out;
Mike Lockwood03ca2162010-04-01 08:10:09 -0700679 synchronized (mLock) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700680 out = new ArrayList<String>(mProviders.size());
681 for (LocationProviderInterface provider : mProviders) {
682 String name = provider.getName();
683 if (LocationManager.FUSED_PROVIDER.equals(name)) {
684 continue;
685 }
Victoria Lease8dbb6342012-09-21 16:55:53 -0700686 if (isAllowedProviderSafe(name)) {
687 if (enabledOnly && !isAllowedBySettingsLocked(name)) {
688 continue;
689 }
690 if (criteria != null && !LocationProvider.propertiesMeetCriteria(
691 name, provider.getProperties(), criteria)) {
692 continue;
693 }
694 out.add(name);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700695 }
Mike Lockwood03ca2162010-04-01 08:10:09 -0700696 }
697 }
698
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700699 if (D) Log.d(TAG, "getProviders()=" + out);
700 return out;
Mike Lockwood03ca2162010-04-01 08:10:09 -0700701 }
702
703 /**
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700704 * Return the name of the best provider given a Criteria object.
705 * This method has been deprecated from the public API,
Victoria Lease8dbb6342012-09-21 16:55:53 -0700706 * and the whole LocationProvider (including #meetsCriteria)
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700707 * has been deprecated as well. So this method now uses
708 * some simplified logic.
Mike Lockwood03ca2162010-04-01 08:10:09 -0700709 */
Nick Pellye0fd6932012-07-11 10:26:13 -0700710 @Override
Mike Lockwood03ca2162010-04-01 08:10:09 -0700711 public String getBestProvider(Criteria criteria, boolean enabledOnly) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700712 String result = null;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700713
714 List<String> providers = getProviders(criteria, enabledOnly);
Victoria Lease8dbb6342012-09-21 16:55:53 -0700715 if (!providers.isEmpty()) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700716 result = pickBest(providers);
717 if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
718 return result;
719 }
720 providers = getProviders(null, enabledOnly);
Victoria Lease8dbb6342012-09-21 16:55:53 -0700721 if (!providers.isEmpty()) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700722 result = pickBest(providers);
723 if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
724 return result;
Mike Lockwood03ca2162010-04-01 08:10:09 -0700725 }
726
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700727 if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
Mike Lockwood03ca2162010-04-01 08:10:09 -0700728 return null;
729 }
730
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700731 private String pickBest(List<String> providers) {
Victoria Lease1925e292012-09-24 17:00:18 -0700732 if (providers.contains(LocationManager.GPS_PROVIDER)) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700733 return LocationManager.GPS_PROVIDER;
Victoria Lease1925e292012-09-24 17:00:18 -0700734 } else if (providers.contains(LocationManager.NETWORK_PROVIDER)) {
735 return LocationManager.NETWORK_PROVIDER;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700736 } else {
737 return providers.get(0);
738 }
739 }
740
Nick Pellye0fd6932012-07-11 10:26:13 -0700741 @Override
Mike Lockwood03ca2162010-04-01 08:10:09 -0700742 public boolean providerMeetsCriteria(String provider, Criteria criteria) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700743 checkPermission();
744
Mike Lockwood03ca2162010-04-01 08:10:09 -0700745 LocationProviderInterface p = mProvidersByName.get(provider);
746 if (p == null) {
747 throw new IllegalArgumentException("provider=" + provider);
748 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700749
750 boolean result = LocationProvider.propertiesMeetCriteria(
751 p.getName(), p.getProperties(), criteria);
752 if (D) Log.d(TAG, "providerMeetsCriteria(" + provider + ", " + criteria + ")=" + result);
753 return result;
Mike Lockwood03ca2162010-04-01 08:10:09 -0700754 }
755
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800756 private void updateProvidersLocked() {
Brad Fitzpatrick0c5a0402010-08-27 14:01:23 -0700757 boolean changesMade = false;
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400758 for (int i = mProviders.size() - 1; i >= 0; i--) {
Mike Lockwoodd03ff942010-02-09 08:46:14 -0500759 LocationProviderInterface p = mProviders.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800760 boolean isEnabled = p.isEnabled();
761 String name = p.getName();
762 boolean shouldBeEnabled = isAllowedBySettingsLocked(name);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800763 if (isEnabled && !shouldBeEnabled) {
764 updateProviderListenersLocked(name, false);
Brad Fitzpatrick0c5a0402010-08-27 14:01:23 -0700765 changesMade = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800766 } else if (!isEnabled && shouldBeEnabled) {
767 updateProviderListenersLocked(name, true);
Brad Fitzpatrick0c5a0402010-08-27 14:01:23 -0700768 changesMade = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800769 }
Brad Fitzpatrick0c5a0402010-08-27 14:01:23 -0700770 }
771 if (changesMade) {
Dianne Hackborn5ac72a22012-08-29 18:32:08 -0700772 mContext.sendBroadcastAsUser(new Intent(LocationManager.PROVIDERS_CHANGED_ACTION),
773 UserHandle.ALL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800774 }
775 }
776
777 private void updateProviderListenersLocked(String provider, boolean enabled) {
778 int listeners = 0;
779
Mike Lockwoodd03ff942010-02-09 08:46:14 -0500780 LocationProviderInterface p = mProvidersByName.get(provider);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700781 if (p == null) return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800782
783 ArrayList<Receiver> deadReceivers = null;
Nick Pellye0fd6932012-07-11 10:26:13 -0700784
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800785 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
786 if (records != null) {
787 final int N = records.size();
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700788 for (int i = 0; i < N; i++) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800789 UpdateRecord record = records.get(i);
790 // Sends a notification message to the receiver
Mike Lockwood48f17512009-04-23 09:12:08 -0700791 if (!record.mReceiver.callProviderEnabledLocked(provider, enabled)) {
792 if (deadReceivers == null) {
793 deadReceivers = new ArrayList<Receiver>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800794 }
Simon Schoar46866572009-06-10 21:12:10 +0200795 deadReceivers.add(record.mReceiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800796 }
797 listeners++;
798 }
799 }
800
801 if (deadReceivers != null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700802 for (int i = deadReceivers.size() - 1; i >= 0; i--) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800803 removeUpdatesLocked(deadReceivers.get(i));
804 }
805 }
Nick Pellye0fd6932012-07-11 10:26:13 -0700806
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800807 if (enabled) {
808 p.enable();
809 if (listeners > 0) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700810 applyRequirementsLocked(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800811 }
812 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800813 p.disable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800814 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800815 }
816
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700817 private void applyRequirementsLocked(String provider) {
818 LocationProviderInterface p = mProvidersByName.get(provider);
819 if (p == null) return;
820
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800821 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700822 WorkSource worksource = new WorkSource();
823 ProviderRequest providerRequest = new ProviderRequest();
824
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800825 if (records != null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700826 for (UpdateRecord record : records) {
827 LocationRequest locationRequest = record.mRequest;
828
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700829 providerRequest.locationRequests.add(locationRequest);
830 if (locationRequest.getInterval() < providerRequest.interval) {
831 providerRequest.reportLocation = true;
832 providerRequest.interval = locationRequest.getInterval();
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700833 }
834 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700835
836 if (providerRequest.reportLocation) {
837 // calculate who to blame for power
838 // This is somewhat arbitrary. We pick a threshold interval
839 // that is slightly higher that the minimum interval, and
840 // spread the blame across all applications with a request
841 // under that threshold.
842 long thresholdInterval = (providerRequest.interval + 1000) * 3 / 2;
843 for (UpdateRecord record : records) {
844 LocationRequest locationRequest = record.mRequest;
845 if (locationRequest.getInterval() <= thresholdInterval) {
846 worksource.add(record.mReceiver.mUid);
847 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700848 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800849 }
850 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700851
852 if (D) Log.d(TAG, "provider request: " + provider + " " + providerRequest);
853 p.setRequest(providerRequest, worksource);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800854 }
855
856 private class UpdateRecord {
857 final String mProvider;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700858 final LocationRequest mRequest;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800859 final Receiver mReceiver;
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400860 Location mLastFixBroadcast;
861 long mLastStatusBroadcast;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800862
863 /**
864 * Note: must be constructed with lock held.
865 */
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700866 UpdateRecord(String provider, LocationRequest request, Receiver receiver) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800867 mProvider = provider;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700868 mRequest = request;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800869 mReceiver = receiver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800870
871 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
872 if (records == null) {
873 records = new ArrayList<UpdateRecord>();
874 mRecordsByProvider.put(provider, records);
875 }
876 if (!records.contains(this)) {
877 records.add(this);
878 }
879 }
880
881 /**
882 * Method to be called when a record will no longer be used. Calling this multiple times
883 * must have the same effect as calling it once.
884 */
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700885 void disposeLocked(boolean removeReceiver) {
886 // remove from mRecordsByProvider
887 ArrayList<UpdateRecord> globalRecords = mRecordsByProvider.get(this.mProvider);
888 if (globalRecords != null) {
889 globalRecords.remove(this);
890 }
891
892 if (!removeReceiver) return; // the caller will handle the rest
893
894 // remove from Receiver#mUpdateRecords
895 HashMap<String, UpdateRecord> receiverRecords = mReceiver.mUpdateRecords;
896 if (receiverRecords != null) {
897 receiverRecords.remove(this.mProvider);
898
899 // and also remove the Receiver if it has no more update records
900 if (removeReceiver && receiverRecords.size() == 0) {
901 removeUpdatesLocked(mReceiver);
902 }
Mike Lockwood3a76fd62009-09-01 07:26:56 -0400903 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800904 }
905
906 @Override
907 public String toString() {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700908 StringBuilder s = new StringBuilder();
909 s.append("UpdateRecord[");
910 s.append(mProvider);
911 s.append(' ').append(mReceiver.mPackageName).append('(');
912 s.append(mReceiver.mUid).append(')');
913 s.append(' ').append(mRequest);
914 s.append(']');
915 return s.toString();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800916 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800917 }
918
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700919 private Receiver getReceiver(ILocationListener listener, int pid, int uid, String packageName) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400920 IBinder binder = listener.asBinder();
921 Receiver receiver = mReceivers.get(binder);
922 if (receiver == null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700923 receiver = new Receiver(listener, null, pid, uid, packageName);
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400924 mReceivers.put(binder, receiver);
925
926 try {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700927 receiver.getListener().asBinder().linkToDeath(receiver, 0);
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400928 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800929 Slog.e(TAG, "linkToDeath failed:", e);
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400930 return null;
931 }
932 }
933 return receiver;
934 }
935
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700936 private Receiver getReceiver(PendingIntent intent, int pid, int uid, String packageName) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400937 Receiver receiver = mReceivers.get(intent);
938 if (receiver == null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700939 receiver = new Receiver(null, intent, pid, uid, packageName);
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400940 mReceivers.put(intent, receiver);
941 }
942 return receiver;
943 }
944
Victoria Lease09016ab2012-09-16 12:33:15 -0700945 private boolean isProviderAllowedByCoarsePermission(String provider) {
946 if (LocationManager.FUSED_PROVIDER.equals(provider)) {
947 return true;
948 }
949 if (LocationManager.PASSIVE_PROVIDER.equals(provider)) {
950 return true;
951 }
952 if (LocationManager.NETWORK_PROVIDER.equals(provider)) {
953 return true;
954 }
955 return false;
956 }
957
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700958 private String checkPermissionAndRequest(LocationRequest request) {
959 String perm = checkPermission();
960
961 if (ACCESS_COARSE_LOCATION.equals(perm)) {
Victoria Lease09016ab2012-09-16 12:33:15 -0700962 if (!isProviderAllowedByCoarsePermission(request.getProvider())) {
963 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
964 }
965 switch (request.getQuality()) {
966 case LocationRequest.ACCURACY_FINE:
967 request.setQuality(LocationRequest.ACCURACY_BLOCK);
968 break;
969 case LocationRequest.POWER_HIGH:
970 request.setQuality(LocationRequest.POWER_LOW);
971 break;
972 }
973 // throttle
974 if (request.getInterval() < LocationFudger.FASTEST_INTERVAL_MS) {
975 request.setInterval(LocationFudger.FASTEST_INTERVAL_MS);
976 }
977 if (request.getFastestInterval() < LocationFudger.FASTEST_INTERVAL_MS) {
978 request.setFastestInterval(LocationFudger.FASTEST_INTERVAL_MS);
979 }
Nick Pelly74fa7ea2012-08-13 19:36:38 -0700980 }
Nick Pelly4e31c4f2012-08-13 19:35:39 -0700981 // make getFastestInterval() the minimum of interval and fastest interval
982 if (request.getFastestInterval() > request.getInterval()) {
983 request.setFastestInterval(request.getInterval());
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400984 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700985 return perm;
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400986 }
987
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700988 private void checkPackageName(String packageName) {
Nick Pellye0fd6932012-07-11 10:26:13 -0700989 if (packageName == null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700990 throw new SecurityException("invalid package name: " + packageName);
Nick Pellye0fd6932012-07-11 10:26:13 -0700991 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700992 int uid = Binder.getCallingUid();
Nick Pellye0fd6932012-07-11 10:26:13 -0700993 String[] packages = mPackageManager.getPackagesForUid(uid);
994 if (packages == null) {
995 throw new SecurityException("invalid UID " + uid);
996 }
997 for (String pkg : packages) {
998 if (packageName.equals(pkg)) return;
999 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001000 throw new SecurityException("invalid package name: " + packageName);
Nick Pellye0fd6932012-07-11 10:26:13 -07001001 }
1002
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001003 private void checkPendingIntent(PendingIntent intent) {
1004 if (intent == null) {
1005 throw new IllegalArgumentException("invalid pending intent: " + intent);
Dianne Hackborn6c418d52011-06-29 14:05:33 -07001006 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001007 }
1008
1009 private Receiver checkListenerOrIntent(ILocationListener listener, PendingIntent intent,
1010 int pid, int uid, String packageName) {
1011 if (intent == null && listener == null) {
1012 throw new IllegalArgumentException("need eiter listener or intent");
1013 } else if (intent != null && listener != null) {
1014 throw new IllegalArgumentException("cannot register both listener and intent");
1015 } else if (intent != null) {
1016 checkPendingIntent(intent);
1017 return getReceiver(intent, pid, uid, packageName);
1018 } else {
1019 return getReceiver(listener, pid, uid, packageName);
1020 }
Dianne Hackborn6c418d52011-06-29 14:05:33 -07001021 }
1022
Nick Pellye0fd6932012-07-11 10:26:13 -07001023 @Override
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001024 public void requestLocationUpdates(LocationRequest request, ILocationListener listener,
1025 PendingIntent intent, String packageName) {
1026 if (request == null) request = DEFAULT_LOCATION_REQUEST;
1027 checkPackageName(packageName);
1028 checkPermissionAndRequest(request);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001029
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001030 final int pid = Binder.getCallingPid();
1031 final int uid = Binder.getCallingUid();
1032 Receiver recevier = checkListenerOrIntent(listener, intent, pid, uid, packageName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001033
Nick Pelly2b7a0d02012-08-17 15:09:44 -07001034 // providers may use public location API's, need to clear identity
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001035 long identity = Binder.clearCallingIdentity();
1036 try {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001037 synchronized (mLock) {
1038 requestLocationUpdatesLocked(request, recevier, pid, uid, packageName);
Mike Lockwood2d4d1bf2010-10-18 17:06:26 -04001039 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001040 } finally {
1041 Binder.restoreCallingIdentity(identity);
1042 }
1043 }
1044
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001045 private void requestLocationUpdatesLocked(LocationRequest request, Receiver receiver,
1046 int pid, int uid, String packageName) {
1047 // Figure out the provider. Either its explicitly request (legacy use cases), or
1048 // use the fused provider
1049 if (request == null) request = DEFAULT_LOCATION_REQUEST;
1050 String name = request.getProvider();
Victoria Lease09016ab2012-09-16 12:33:15 -07001051 if (name == null) {
1052 throw new IllegalArgumentException("provider name must not be null");
1053 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001054 LocationProviderInterface provider = mProvidersByName.get(name);
1055 if (provider == null) {
1056 throw new IllegalArgumentException("provider doesn't exisit: " + provider);
1057 }
1058
1059 Log.i(TAG, "request " + Integer.toHexString(System.identityHashCode(receiver)) + " " +
1060 name + " " + request + " from " + packageName + "(" + uid + ")");
1061
1062 UpdateRecord record = new UpdateRecord(name, request, receiver);
1063 UpdateRecord oldRecord = receiver.mUpdateRecords.put(name, record);
1064 if (oldRecord != null) {
1065 oldRecord.disposeLocked(false);
1066 }
1067
1068 boolean isProviderEnabled = isAllowedBySettingsLocked(name);
1069 if (isProviderEnabled) {
1070 applyRequirementsLocked(name);
1071 } else {
1072 // Notify the listener that updates are currently disabled
1073 receiver.callProviderEnabledLocked(name, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001074 }
1075 }
1076
Nick Pellye0fd6932012-07-11 10:26:13 -07001077 @Override
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001078 public void removeUpdates(ILocationListener listener, PendingIntent intent,
1079 String packageName) {
1080 checkPackageName(packageName);
1081 checkPermission();
1082 final int pid = Binder.getCallingPid();
1083 final int uid = Binder.getCallingUid();
1084 Receiver receiver = checkListenerOrIntent(listener, intent, pid, uid, packageName);
1085
Nick Pelly2b7a0d02012-08-17 15:09:44 -07001086 // providers may use public location API's, need to clear identity
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001087 long identity = Binder.clearCallingIdentity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001088 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001089 synchronized (mLock) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001090 removeUpdatesLocked(receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001091 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001092 } finally {
1093 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001094 }
1095 }
1096
1097 private void removeUpdatesLocked(Receiver receiver) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001098 Log.i(TAG, "remove " + Integer.toHexString(System.identityHashCode(receiver)));
1099
1100 if (mReceivers.remove(receiver.mKey) != null && receiver.isListener()) {
1101 receiver.getListener().asBinder().unlinkToDeath(receiver, 0);
1102 synchronized (receiver) {
1103 if (receiver.mPendingBroadcasts > 0) {
1104 decrementPendingBroadcasts();
1105 receiver.mPendingBroadcasts = 0;
1106 }
1107 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001108 }
1109
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001110 // Record which providers were associated with this listener
1111 HashSet<String> providers = new HashSet<String>();
1112 HashMap<String, UpdateRecord> oldRecords = receiver.mUpdateRecords;
1113 if (oldRecords != null) {
1114 // Call dispose() on the obsolete update records.
1115 for (UpdateRecord record : oldRecords.values()) {
1116 record.disposeLocked(false);
1117 }
1118 // Accumulate providers
1119 providers.addAll(oldRecords.keySet());
1120 }
1121
1122 // update provider
1123 for (String provider : providers) {
1124 // If provider is already disabled, don't need to do anything
1125 if (!isAllowedBySettingsLocked(provider)) {
1126 continue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001127 }
1128
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001129 applyRequirementsLocked(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001130 }
1131 }
1132
Nick Pellye0fd6932012-07-11 10:26:13 -07001133 @Override
Nick Pelly4035f5a2012-08-17 14:43:49 -07001134 public Location getLastLocation(LocationRequest request, String packageName) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001135 if (D) Log.d(TAG, "getLastLocation: " + request);
1136 if (request == null) request = DEFAULT_LOCATION_REQUEST;
1137 String perm = checkPermissionAndRequest(request);
Nick Pelly4035f5a2012-08-17 14:43:49 -07001138 checkPackageName(packageName);
1139
1140 if (mBlacklist.isBlacklisted(packageName)) {
1141 if (D) Log.d(TAG, "not returning last loc for blacklisted app: " +
1142 packageName);
1143 return null;
1144 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001145
1146 synchronized (mLock) {
1147 // Figure out the provider. Either its explicitly request (deprecated API's),
1148 // or use the fused provider
1149 String name = request.getProvider();
1150 if (name == null) name = LocationManager.FUSED_PROVIDER;
1151 LocationProviderInterface provider = mProvidersByName.get(name);
1152 if (provider == null) return null;
1153
1154 if (!isAllowedBySettingsLocked(name)) return null;
1155
1156 Location location = mLastLocation.get(name);
Victoria Lease09016ab2012-09-16 12:33:15 -07001157 if (location == null) {
1158 return null;
1159 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001160 if (ACCESS_FINE_LOCATION.equals(perm)) {
1161 return location;
1162 } else {
Victoria Lease09016ab2012-09-16 12:33:15 -07001163 Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
1164 if (noGPSLocation != null) {
1165 return mLocationFudger.getOrCreate(noGPSLocation);
1166 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001167 }
1168 }
Victoria Lease09016ab2012-09-16 12:33:15 -07001169 return null;
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001170 }
1171
1172 @Override
1173 public void requestGeofence(LocationRequest request, Geofence geofence, PendingIntent intent,
1174 String packageName) {
1175 if (request == null) request = DEFAULT_LOCATION_REQUEST;
Victoria Lease4fab68b2012-09-13 13:20:59 -07001176 checkGeofencePermission();
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001177 checkPermissionAndRequest(request);
1178 checkPendingIntent(intent);
1179 checkPackageName(packageName);
1180
1181 if (D) Log.d(TAG, "requestGeofence: " + request + " " + geofence + " " + intent);
1182
Nick Pelly2b7a0d02012-08-17 15:09:44 -07001183 // geo-fence manager uses the public location API, need to clear identity
1184 int uid = Binder.getCallingUid();
1185 long identity = Binder.clearCallingIdentity();
1186 try {
1187 mGeofenceManager.addFence(request, geofence, intent, uid, packageName);
1188 } finally {
1189 Binder.restoreCallingIdentity(identity);
1190 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001191 }
1192
1193 @Override
1194 public void removeGeofence(Geofence geofence, PendingIntent intent, String packageName) {
Victoria Lease4fab68b2012-09-13 13:20:59 -07001195 checkGeofencePermission();
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001196 checkPendingIntent(intent);
1197 checkPackageName(packageName);
1198
1199 if (D) Log.d(TAG, "removeGeofence: " + geofence + " " + intent);
1200
Nick Pelly2b7a0d02012-08-17 15:09:44 -07001201 // geo-fence manager uses the public location API, need to clear identity
1202 long identity = Binder.clearCallingIdentity();
1203 try {
1204 mGeofenceManager.removeFence(geofence, intent);
1205 } finally {
1206 Binder.restoreCallingIdentity(identity);
1207 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001208 }
1209
1210
1211 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001212 public boolean addGpsStatusListener(IGpsStatusListener listener) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001213 if (mGpsStatusProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001214 return false;
1215 }
Mike Lockwoodb7e99222009-07-07 13:18:21 -04001216 if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) !=
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001217 PackageManager.PERMISSION_GRANTED) {
1218 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
1219 }
1220
1221 try {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001222 mGpsStatusProvider.addGpsStatusListener(listener);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001223 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001224 Slog.e(TAG, "mGpsStatusProvider.addGpsStatusListener failed", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001225 return false;
1226 }
1227 return true;
1228 }
1229
Nick Pellye0fd6932012-07-11 10:26:13 -07001230 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001231 public void removeGpsStatusListener(IGpsStatusListener listener) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001232 synchronized (mLock) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001233 try {
1234 mGpsStatusProvider.removeGpsStatusListener(listener);
1235 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001236 Slog.e(TAG, "mGpsStatusProvider.removeGpsStatusListener failed", e);
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001237 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001238 }
1239 }
1240
Nick Pellye0fd6932012-07-11 10:26:13 -07001241 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001242 public boolean sendExtraCommand(String provider, String command, Bundle extras) {
Mike Lockwoodc6cc8362009-08-17 13:16:08 -04001243 if (provider == null) {
1244 // throw NullPointerException to remain compatible with previous implementation
1245 throw new NullPointerException();
1246 }
1247
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001248 checkPermission();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001249 // and check for ACCESS_LOCATION_EXTRA_COMMANDS
Mike Lockwoodb7e99222009-07-07 13:18:21 -04001250 if ((mContext.checkCallingOrSelfPermission(ACCESS_LOCATION_EXTRA_COMMANDS)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001251 != PackageManager.PERMISSION_GRANTED)) {
1252 throw new SecurityException("Requires ACCESS_LOCATION_EXTRA_COMMANDS permission");
1253 }
1254
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001255 synchronized (mLock) {
Mike Lockwoodd03ff942010-02-09 08:46:14 -05001256 LocationProviderInterface p = mProvidersByName.get(provider);
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001257 if (p == null) return false;
Nick Pellye0fd6932012-07-11 10:26:13 -07001258
Mike Lockwoodd03ff942010-02-09 08:46:14 -05001259 return p.sendExtraCommand(command, extras);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001260 }
1261 }
1262
Nick Pellye0fd6932012-07-11 10:26:13 -07001263 @Override
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001264 public boolean sendNiResponse(int notifId, int userResponse) {
Mike Lockwood18ad9f62009-08-27 14:01:23 -07001265 if (Binder.getCallingUid() != Process.myUid()) {
1266 throw new SecurityException(
1267 "calling sendNiResponse from outside of the system is not allowed");
1268 }
Danke Xie22d1f9f2009-08-18 18:28:45 -04001269 try {
1270 return mNetInitiatedListener.sendNiResponse(notifId, userResponse);
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001271 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001272 Slog.e(TAG, "RemoteException in LocationManagerService.sendNiResponse");
Danke Xie22d1f9f2009-08-18 18:28:45 -04001273 return false;
1274 }
1275 }
1276
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001277 /**
Mike Lockwood628fd6d2010-01-25 22:46:13 -05001278 * @return null if the provider does not exist
Alexey Tarasovf2db9fb2009-09-01 02:37:07 +11001279 * @throws SecurityException if the provider is not allowed to be
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001280 * accessed by the caller
1281 */
Nick Pellye0fd6932012-07-11 10:26:13 -07001282 @Override
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001283 public ProviderProperties getProviderProperties(String provider) {
1284 checkPermission();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001285
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001286 LocationProviderInterface p;
1287 synchronized (mLock) {
1288 p = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001289 }
1290
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001291 if (p == null) return null;
1292 return p.getProperties();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001293 }
1294
Nick Pellye0fd6932012-07-11 10:26:13 -07001295 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001296 public boolean isProviderEnabled(String provider) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001297 checkPermission();
1298 if (LocationManager.FUSED_PROVIDER.equals(provider)) return false;
1299
1300 synchronized (mLock) {
1301 LocationProviderInterface p = mProvidersByName.get(provider);
1302 if (p == null) return false;
1303
1304 return isAllowedBySettingsLocked(provider);
1305 }
1306 }
1307
1308 private void checkCallerIsProvider() {
1309 if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER)
1310 == PackageManager.PERMISSION_GRANTED) {
1311 return;
1312 }
1313
1314 // Previously we only used the INSTALL_LOCATION_PROVIDER
1315 // check. But that is system or signature
1316 // protection level which is not flexible enough for
1317 // providers installed oustide the system image. So
1318 // also allow providers with a UID matching the
1319 // currently bound package name
1320
1321 int uid = Binder.getCallingUid();
1322
1323 if (mGeocodeProvider != null) {
1324 if (doesPackageHaveUid(uid, mGeocodeProvider.getConnectedPackageName())) return;
1325 }
1326 for (LocationProviderProxy proxy : mProxyProviders) {
1327 if (doesPackageHaveUid(uid, proxy.getConnectedPackageName())) return;
1328 }
1329 throw new SecurityException("need INSTALL_LOCATION_PROVIDER permission, " +
1330 "or UID of a currently bound location provider");
1331 }
1332
1333 private boolean doesPackageHaveUid(int uid, String packageName) {
1334 if (packageName == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001335 return false;
1336 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001337 try {
1338 ApplicationInfo appInfo = mPackageManager.getApplicationInfo(packageName, 0);
1339 if (appInfo.uid != uid) {
1340 return false;
1341 }
1342 } catch (NameNotFoundException e) {
1343 return false;
1344 }
1345 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001346 }
1347
Nick Pellye0fd6932012-07-11 10:26:13 -07001348 @Override
Mike Lockwooda4903f22010-02-17 06:42:23 -05001349 public void reportLocation(Location location, boolean passive) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001350 checkCallerIsProvider();
Mike Lockwood275555c2009-05-01 11:30:34 -04001351
Nick Pelly2eeeec22012-07-18 13:13:37 -07001352 if (!location.isComplete()) {
1353 Log.w(TAG, "Dropping incomplete location: " + location);
1354 return;
1355 }
1356
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001357 mLocationHandler.removeMessages(MSG_LOCATION_CHANGED, location);
1358 Message m = Message.obtain(mLocationHandler, MSG_LOCATION_CHANGED, location);
Mike Lockwooda4903f22010-02-17 06:42:23 -05001359 m.arg1 = (passive ? 1 : 0);
Mike Lockwood4e50b782009-04-03 08:24:43 -07001360 mLocationHandler.sendMessageAtFrontOfQueue(m);
1361 }
1362
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001363
1364 private static boolean shouldBroadcastSafe(Location loc, Location lastLoc, UpdateRecord record) {
1365 // Always broadcast the first update
1366 if (lastLoc == null) {
1367 return true;
1368 }
1369
Nick Pellyf1be6862012-05-15 10:53:42 -07001370 // Check whether sufficient time has passed
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001371 long minTime = record.mRequest.getFastestInterval();
Philip Milne41180122012-09-26 11:29:25 -07001372 long delta = (loc.getElapsedRealtimeNanos() - lastLoc.getElapsedRealtimeNanos()) / 1000000L;
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001373 if (delta < minTime - MAX_PROVIDER_SCHEDULING_JITTER_MS) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001374 return false;
1375 }
1376
1377 // Check whether sufficient distance has been traveled
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001378 double minDistance = record.mRequest.getSmallestDisplacement();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001379 if (minDistance > 0.0) {
1380 if (loc.distanceTo(lastLoc) <= minDistance) {
1381 return false;
1382 }
1383 }
1384
1385 return true;
1386 }
1387
Mike Lockwooda4903f22010-02-17 06:42:23 -05001388 private void handleLocationChangedLocked(Location location, boolean passive) {
Nick Pelly4e31c4f2012-08-13 19:35:39 -07001389 if (D) Log.d(TAG, "incoming location: " + location);
1390
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001391 long now = SystemClock.elapsedRealtime();
Mike Lockwooda4903f22010-02-17 06:42:23 -05001392 String provider = (passive ? LocationManager.PASSIVE_PROVIDER : location.getProvider());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001393 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001394 if (records == null || records.size() == 0) return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001395
Mike Lockwoodd03ff942010-02-09 08:46:14 -05001396 LocationProviderInterface p = mProvidersByName.get(provider);
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001397 if (p == null) return;
1398
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001399 // Update last known locations
Victoria Lease09016ab2012-09-16 12:33:15 -07001400 Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
1401 Location lastNoGPSLocation = null;
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001402 Location lastLocation = mLastLocation.get(provider);
Mike Lockwood4e50b782009-04-03 08:24:43 -07001403 if (lastLocation == null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001404 lastLocation = new Location(provider);
1405 mLastLocation.put(provider, lastLocation);
Victoria Lease09016ab2012-09-16 12:33:15 -07001406 } else {
1407 lastNoGPSLocation = lastLocation.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
1408 if (noGPSLocation == null && lastNoGPSLocation != null) {
1409 // New location has no no-GPS location: adopt last no-GPS location. This is set
1410 // directly into location because we do not want to notify COARSE clients.
1411 location.setExtraLocation(Location.EXTRA_NO_GPS_LOCATION, lastNoGPSLocation);
1412 }
Mike Lockwood4e50b782009-04-03 08:24:43 -07001413 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001414 lastLocation.set(location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001415
Victoria Lease09016ab2012-09-16 12:33:15 -07001416 // Fetch coarse location
1417 Location coarseLocation = null;
1418 if (noGPSLocation != null && !noGPSLocation.equals(lastNoGPSLocation)) {
1419 coarseLocation = mLocationFudger.getOrCreate(noGPSLocation);
1420 }
1421
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001422 // Fetch latest status update time
1423 long newStatusUpdateTime = p.getStatusUpdateTime();
1424
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001425 // Get latest status
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001426 Bundle extras = new Bundle();
1427 int status = p.getStatus(extras);
1428
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001429 ArrayList<Receiver> deadReceivers = null;
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001430 ArrayList<UpdateRecord> deadUpdateRecords = null;
Nick Pellye0fd6932012-07-11 10:26:13 -07001431
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001432 // Broadcast location or status to all listeners
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001433 for (UpdateRecord r : records) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001434 Receiver receiver = r.mReceiver;
Mike Lockwood03ca2162010-04-01 08:10:09 -07001435 boolean receiverDead = false;
Nick Pelly4035f5a2012-08-17 14:43:49 -07001436
1437 if (mBlacklist.isBlacklisted(receiver.mPackageName)) {
1438 if (D) Log.d(TAG, "skipping loc update for blacklisted app: " +
1439 receiver.mPackageName);
1440 continue;
1441 }
1442
Victoria Lease09016ab2012-09-16 12:33:15 -07001443 Location notifyLocation = null;
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001444 if (ACCESS_FINE_LOCATION.equals(receiver.mPermission)) {
Victoria Lease09016ab2012-09-16 12:33:15 -07001445 notifyLocation = lastLocation; // use fine location
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001446 } else {
Victoria Lease09016ab2012-09-16 12:33:15 -07001447 notifyLocation = coarseLocation; // use coarse location if available
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001448 }
Victoria Lease09016ab2012-09-16 12:33:15 -07001449 if (notifyLocation != null) {
1450 Location lastLoc = r.mLastFixBroadcast;
1451 if ((lastLoc == null) || shouldBroadcastSafe(notifyLocation, lastLoc, r)) {
1452 if (lastLoc == null) {
1453 lastLoc = new Location(notifyLocation);
1454 r.mLastFixBroadcast = lastLoc;
1455 } else {
1456 lastLoc.set(notifyLocation);
1457 }
1458 if (!receiver.callLocationChangedLocked(notifyLocation)) {
1459 Slog.w(TAG, "RemoteException calling onLocationChanged on " + receiver);
1460 receiverDead = true;
1461 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001462 }
1463 }
1464
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001465 long prevStatusUpdateTime = r.mLastStatusBroadcast;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001466 if ((newStatusUpdateTime > prevStatusUpdateTime) &&
Victoria Lease09016ab2012-09-16 12:33:15 -07001467 (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001468
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001469 r.mLastStatusBroadcast = newStatusUpdateTime;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001470 if (!receiver.callStatusChangedLocked(provider, status, extras)) {
Mike Lockwood03ca2162010-04-01 08:10:09 -07001471 receiverDead = true;
Joe Onorato8a9b2202010-02-26 18:56:32 -08001472 Slog.w(TAG, "RemoteException calling onStatusChanged on " + receiver);
Mike Lockwood03ca2162010-04-01 08:10:09 -07001473 }
1474 }
1475
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001476 // track expired records
1477 if (r.mRequest.getNumUpdates() == 0 || r.mRequest.getExpireAt() < now) {
1478 if (deadUpdateRecords == null) {
1479 deadUpdateRecords = new ArrayList<UpdateRecord>();
1480 }
1481 deadUpdateRecords.add(r);
1482 }
1483 // track dead receivers
1484 if (receiverDead) {
Mike Lockwood03ca2162010-04-01 08:10:09 -07001485 if (deadReceivers == null) {
1486 deadReceivers = new ArrayList<Receiver>();
1487 }
1488 if (!deadReceivers.contains(receiver)) {
1489 deadReceivers.add(receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001490 }
1491 }
1492 }
Nick Pellye0fd6932012-07-11 10:26:13 -07001493
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001494 // remove dead records and receivers outside the loop
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001495 if (deadReceivers != null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001496 for (Receiver receiver : deadReceivers) {
1497 removeUpdatesLocked(receiver);
1498 }
1499 }
1500 if (deadUpdateRecords != null) {
1501 for (UpdateRecord r : deadUpdateRecords) {
1502 r.disposeLocked(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001503 }
1504 }
1505 }
1506
1507 private class LocationWorkerHandler extends Handler {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001508 @Override
1509 public void handleMessage(Message msg) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001510 switch (msg.what) {
1511 case MSG_LOCATION_CHANGED:
1512 handleLocationChanged((Location) msg.obj, msg.arg1 == 1);
1513 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001514 }
1515 }
1516 }
1517
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001518 private void handleLocationChanged(Location location, boolean passive) {
1519 String provider = location.getProvider();
Jeff Sharkey5e613312012-01-30 11:16:20 -08001520
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001521 if (!passive) {
1522 // notify passive provider of the new location
1523 mPassiveProvider.updateLocation(location);
1524 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001525
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001526 synchronized (mLock) {
1527 if (isAllowedBySettingsLocked(provider)) {
1528 handleLocationChangedLocked(location, passive);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001529 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001530 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001531 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001532
Mike Lockwoode97ae402010-09-29 15:23:46 -04001533 private final PackageMonitor mPackageMonitor = new PackageMonitor() {
1534 @Override
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001535 public void onPackageDisappeared(String packageName, int reason) {
1536 // remove all receivers associated with this package name
1537 synchronized (mLock) {
1538 ArrayList<Receiver> deadReceivers = null;
1539
1540 for (Receiver receiver : mReceivers.values()) {
1541 if (receiver.mPackageName.equals(packageName)) {
1542 if (deadReceivers == null) {
1543 deadReceivers = new ArrayList<Receiver>();
1544 }
1545 deadReceivers.add(receiver);
1546 }
1547 }
1548
1549 // perform removal outside of mReceivers loop
1550 if (deadReceivers != null) {
1551 for (Receiver receiver : deadReceivers) {
1552 removeUpdatesLocked(receiver);
1553 }
1554 }
1555 }
Nick Pellye0fd6932012-07-11 10:26:13 -07001556 }
Mike Lockwoode97ae402010-09-29 15:23:46 -04001557 };
1558
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001559 // Wake locks
1560
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001561 private void incrementPendingBroadcasts() {
1562 synchronized (mWakeLock) {
1563 if (mPendingBroadcasts++ == 0) {
1564 try {
1565 mWakeLock.acquire();
1566 log("Acquired wakelock");
1567 } catch (Exception e) {
1568 // This is to catch a runtime exception thrown when we try to release an
1569 // already released lock.
Joe Onorato8a9b2202010-02-26 18:56:32 -08001570 Slog.e(TAG, "exception in acquireWakeLock()", e);
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001571 }
1572 }
Mike Lockwood48f17512009-04-23 09:12:08 -07001573 }
1574 }
1575
1576 private void decrementPendingBroadcasts() {
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001577 synchronized (mWakeLock) {
Mike Lockwood48f17512009-04-23 09:12:08 -07001578 if (--mPendingBroadcasts == 0) {
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001579 try {
1580 // Release wake lock
1581 if (mWakeLock.isHeld()) {
1582 mWakeLock.release();
1583 log("Released wakelock");
1584 } else {
1585 log("Can't release wakelock again!");
1586 }
1587 } catch (Exception e) {
1588 // This is to catch a runtime exception thrown when we try to release an
1589 // already released lock.
Joe Onorato8a9b2202010-02-26 18:56:32 -08001590 Slog.e(TAG, "exception in releaseWakeLock()", e);
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001591 }
Mike Lockwood48f17512009-04-23 09:12:08 -07001592 }
1593 }
1594 }
1595
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001596 // Geocoder
1597
Nick Pellye0fd6932012-07-11 10:26:13 -07001598 @Override
Mike Lockwoode15735a2010-09-20 17:48:47 -04001599 public boolean geocoderIsPresent() {
Mark Vandevoorde01ac80b2010-05-21 15:43:26 -07001600 return mGeocodeProvider != null;
1601 }
1602
Nick Pellye0fd6932012-07-11 10:26:13 -07001603 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001604 public String getFromLocation(double latitude, double longitude, int maxResults,
Mike Lockwood34901402010-01-04 12:14:21 -05001605 GeocoderParams params, List<Address> addrs) {
Mike Lockwooda55c3212009-04-15 11:10:11 -04001606 if (mGeocodeProvider != null) {
Mike Lockwood628fd6d2010-01-25 22:46:13 -05001607 return mGeocodeProvider.getFromLocation(latitude, longitude, maxResults,
1608 params, addrs);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001609 }
Mike Lockwooda55c3212009-04-15 11:10:11 -04001610 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001611 }
1612
Mike Lockwooda55c3212009-04-15 11:10:11 -04001613
Nick Pellye0fd6932012-07-11 10:26:13 -07001614 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001615 public String getFromLocationName(String locationName,
Mike Lockwooda55c3212009-04-15 11:10:11 -04001616 double lowerLeftLatitude, double lowerLeftLongitude,
1617 double upperRightLatitude, double upperRightLongitude, int maxResults,
Mike Lockwood34901402010-01-04 12:14:21 -05001618 GeocoderParams params, List<Address> addrs) {
Mike Lockwooda55c3212009-04-15 11:10:11 -04001619
1620 if (mGeocodeProvider != null) {
Mike Lockwood628fd6d2010-01-25 22:46:13 -05001621 return mGeocodeProvider.getFromLocationName(locationName, lowerLeftLatitude,
1622 lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
1623 maxResults, params, addrs);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001624 }
Mike Lockwooda55c3212009-04-15 11:10:11 -04001625 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001626 }
1627
1628 // Mock Providers
1629
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001630 private void checkMockPermissionsSafe() {
1631 boolean allowMocks = Settings.Secure.getInt(mContext.getContentResolver(),
1632 Settings.Secure.ALLOW_MOCK_LOCATION, 0) == 1;
1633 if (!allowMocks) {
1634 throw new SecurityException("Requires ACCESS_MOCK_LOCATION secure setting");
1635 }
1636
1637 if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) !=
1638 PackageManager.PERMISSION_GRANTED) {
1639 throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission");
Nick Pellye0fd6932012-07-11 10:26:13 -07001640 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001641 }
1642
Nick Pellye0fd6932012-07-11 10:26:13 -07001643 @Override
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001644 public void addTestProvider(String name, ProviderProperties properties) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001645 checkMockPermissionsSafe();
1646
Mike Lockwooda4903f22010-02-17 06:42:23 -05001647 if (LocationManager.PASSIVE_PROVIDER.equals(name)) {
1648 throw new IllegalArgumentException("Cannot mock the passive location provider");
1649 }
1650
Mike Lockwood86328a92009-10-23 08:38:25 -04001651 long identity = Binder.clearCallingIdentity();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001652 synchronized (mLock) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001653 MockProvider provider = new MockProvider(name, this, properties);
Mike Lockwood7566c1d2009-08-25 10:05:18 -07001654 // remove the real provider if we are replacing GPS or network provider
1655 if (LocationManager.GPS_PROVIDER.equals(name)
Nick Pelly1332b532012-08-21 16:25:47 -07001656 || LocationManager.NETWORK_PROVIDER.equals(name)
1657 || LocationManager.FUSED_PROVIDER.equals(name)) {
Mike Lockwoodd03ff942010-02-09 08:46:14 -05001658 LocationProviderInterface p = mProvidersByName.get(name);
1659 if (p != null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001660 removeProviderLocked(p);
Mike Lockwood7566c1d2009-08-25 10:05:18 -07001661 }
1662 }
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001663 if (mProvidersByName.get(name) != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001664 throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
1665 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001666 addProviderLocked(provider);
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001667 mMockProviders.put(name, provider);
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001668 mLastLocation.put(name, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001669 updateProvidersLocked();
1670 }
Mike Lockwood86328a92009-10-23 08:38:25 -04001671 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001672 }
1673
Nick Pellye0fd6932012-07-11 10:26:13 -07001674 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001675 public void removeTestProvider(String provider) {
1676 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001677 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001678 MockProvider mockProvider = mMockProviders.get(provider);
1679 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001680 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1681 }
Mike Lockwood86328a92009-10-23 08:38:25 -04001682 long identity = Binder.clearCallingIdentity();
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001683 removeProviderLocked(mProvidersByName.get(provider));
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001684 mMockProviders.remove(mockProvider);
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001685
1686 // reinstate real provider if available
1687 LocationProviderInterface realProvider = mRealProviders.get(provider);
1688 if (realProvider != null) {
1689 addProviderLocked(realProvider);
Mike Lockwood7566c1d2009-08-25 10:05:18 -07001690 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001691 mLastLocation.put(provider, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001692 updateProvidersLocked();
Mike Lockwood86328a92009-10-23 08:38:25 -04001693 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001694 }
1695 }
1696
Nick Pellye0fd6932012-07-11 10:26:13 -07001697 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001698 public void setTestProviderLocation(String provider, Location loc) {
1699 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001700 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001701 MockProvider mockProvider = mMockProviders.get(provider);
1702 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001703 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1704 }
Mike Lockwood95427cd2009-05-07 13:27:54 -04001705 // clear calling identity so INSTALL_LOCATION_PROVIDER permission is not required
1706 long identity = Binder.clearCallingIdentity();
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001707 mockProvider.setLocation(loc);
Mike Lockwood95427cd2009-05-07 13:27:54 -04001708 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001709 }
1710 }
1711
Nick Pellye0fd6932012-07-11 10:26:13 -07001712 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001713 public void clearTestProviderLocation(String provider) {
1714 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001715 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001716 MockProvider mockProvider = mMockProviders.get(provider);
1717 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001718 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1719 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001720 mockProvider.clearLocation();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001721 }
1722 }
1723
Nick Pellye0fd6932012-07-11 10:26:13 -07001724 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001725 public void setTestProviderEnabled(String provider, boolean enabled) {
1726 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001727 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001728 MockProvider mockProvider = mMockProviders.get(provider);
1729 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001730 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1731 }
Mike Lockwood86328a92009-10-23 08:38:25 -04001732 long identity = Binder.clearCallingIdentity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001733 if (enabled) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001734 mockProvider.enable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001735 mEnabledProviders.add(provider);
1736 mDisabledProviders.remove(provider);
1737 } else {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001738 mockProvider.disable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001739 mEnabledProviders.remove(provider);
1740 mDisabledProviders.add(provider);
1741 }
1742 updateProvidersLocked();
Mike Lockwood86328a92009-10-23 08:38:25 -04001743 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001744 }
1745 }
1746
Nick Pellye0fd6932012-07-11 10:26:13 -07001747 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001748 public void clearTestProviderEnabled(String provider) {
1749 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001750 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001751 MockProvider mockProvider = mMockProviders.get(provider);
1752 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001753 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1754 }
Mike Lockwood86328a92009-10-23 08:38:25 -04001755 long identity = Binder.clearCallingIdentity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001756 mEnabledProviders.remove(provider);
1757 mDisabledProviders.remove(provider);
1758 updateProvidersLocked();
Mike Lockwood86328a92009-10-23 08:38:25 -04001759 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001760 }
1761 }
1762
Nick Pellye0fd6932012-07-11 10:26:13 -07001763 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001764 public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
1765 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001766 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001767 MockProvider mockProvider = mMockProviders.get(provider);
1768 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001769 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1770 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001771 mockProvider.setStatus(status, extras, updateTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001772 }
1773 }
1774
Nick Pellye0fd6932012-07-11 10:26:13 -07001775 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001776 public void clearTestProviderStatus(String provider) {
1777 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001778 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001779 MockProvider mockProvider = mMockProviders.get(provider);
1780 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001781 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1782 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001783 mockProvider.clearStatus();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001784 }
1785 }
1786
1787 private void log(String log) {
1788 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001789 Slog.d(TAG, log);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001790 }
1791 }
Nick Pellye0fd6932012-07-11 10:26:13 -07001792
1793 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001794 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1795 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1796 != PackageManager.PERMISSION_GRANTED) {
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001797 pw.println("Permission Denial: can't dump LocationManagerService from from pid="
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001798 + Binder.getCallingPid()
1799 + ", uid=" + Binder.getCallingUid());
1800 return;
1801 }
Nick Pellye0fd6932012-07-11 10:26:13 -07001802
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001803 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001804 pw.println("Current Location Manager state:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001805 pw.println(" Location Listeners:");
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001806 for (Receiver receiver : mReceivers.values()) {
1807 pw.println(" " + receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001808 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001809 pw.println(" Records by Provider:");
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001810 for (Map.Entry<String, ArrayList<UpdateRecord>> entry : mRecordsByProvider.entrySet()) {
1811 pw.println(" " + entry.getKey() + ":");
1812 for (UpdateRecord record : entry.getValue()) {
1813 pw.println(" " + record);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001814 }
1815 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001816 pw.println(" Last Known Locations:");
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001817 for (Map.Entry<String, Location> entry : mLastLocation.entrySet()) {
1818 String provider = entry.getKey();
1819 Location location = entry.getValue();
1820 pw.println(" " + provider + ": " + location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001821 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001822
Nick Pellye0fd6932012-07-11 10:26:13 -07001823 mGeofenceManager.dump(pw);
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001824
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001825 if (mEnabledProviders.size() > 0) {
1826 pw.println(" Enabled Providers:");
1827 for (String i : mEnabledProviders) {
1828 pw.println(" " + i);
1829 }
Nick Pellye0fd6932012-07-11 10:26:13 -07001830
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001831 }
1832 if (mDisabledProviders.size() > 0) {
1833 pw.println(" Disabled Providers:");
1834 for (String i : mDisabledProviders) {
1835 pw.println(" " + i);
1836 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001837 }
Nick Pelly4035f5a2012-08-17 14:43:49 -07001838 pw.append(" ");
1839 mBlacklist.dump(pw);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001840 if (mMockProviders.size() > 0) {
1841 pw.println(" Mock Providers:");
1842 for (Map.Entry<String, MockProvider> i : mMockProviders.entrySet()) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001843 i.getValue().dump(pw, " ");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001844 }
1845 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001846
Nick Pelly74fa7ea2012-08-13 19:36:38 -07001847 pw.append(" fudger: ");
1848 mLocationFudger.dump(fd, pw, args);
1849
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001850 if (args.length > 0 && "short".equals(args[0])) {
1851 return;
1852 }
Fred Fettinger3c8fbdf2010-01-04 15:38:13 -06001853 for (LocationProviderInterface provider: mProviders) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001854 pw.print(provider.getName() + " Internal State");
1855 if (provider instanceof LocationProviderProxy) {
1856 LocationProviderProxy proxy = (LocationProviderProxy) provider;
1857 pw.print(" (" + proxy.getConnectedPackageName() + ")");
Fred Fettinger3c8fbdf2010-01-04 15:38:13 -06001858 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001859 pw.println(":");
1860 provider.dump(fd, pw, args);
Fred Fettinger3c8fbdf2010-01-04 15:38:13 -06001861 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001862 }
1863 }
1864}