blob: dfe7ca9937f10b23b36413df17bf104ac9c54585 [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>();
Jeff Hamiltonc19efc22012-10-03 00:40:50 -0500278 String[] pkgs = resources.getStringArray(
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700279 com.android.internal.R.array.config_locationProviderPackageNames);
Jeff Hamiltonc19efc22012-10-03 00:40:50 -0500280 if (D) Log.d(TAG, "built-in location providers: " + Arrays.toString(pkgs));
281 if (pkgs != null) providerPackageNames.addAll(Arrays.asList(pkgs));
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700282
283 // bind to network provider
284 LocationProviderProxy networkProvider = LocationProviderProxy.createAndBind(
285 mContext,
286 LocationManager.NETWORK_PROVIDER,
287 NETWORK_LOCATION_SERVICE_ACTION,
288 providerPackageNames, mLocationHandler);
289 if (networkProvider != null) {
290 mRealProviders.put(LocationManager.NETWORK_PROVIDER, networkProvider);
291 mProxyProviders.add(networkProvider);
292 addProviderLocked(networkProvider);
293 } else {
294 Slog.w(TAG, "no network location provider found");
295 }
296
297 // bind to fused provider
298 LocationProviderProxy fusedLocationProvider = LocationProviderProxy.createAndBind(
299 mContext,
300 LocationManager.FUSED_PROVIDER,
301 FUSED_LOCATION_SERVICE_ACTION,
302 providerPackageNames, mLocationHandler);
303 if (fusedLocationProvider != null) {
304 addProviderLocked(fusedLocationProvider);
305 mProxyProviders.add(fusedLocationProvider);
306 mEnabledProviders.add(fusedLocationProvider.getName());
307 } else {
308 Slog.e(TAG, "no fused location provider found",
309 new IllegalStateException("Location service needs a fused location provider"));
310 }
311
312 // bind to geocoder provider
313 mGeocodeProvider = GeocoderProxy.createAndBind(mContext, providerPackageNames);
314 if (mGeocodeProvider == null) {
315 Slog.e(TAG, "no geocoder provider found");
316 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700317 }
Mike Lockwood9637d472009-04-02 21:41:57 -0700318
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800319 /**
Victoria Lease38389b62012-09-30 11:44:22 -0700320 * Called when the device's active user changes.
321 * @param userId the new active user's UserId
322 */
323 private void switchUser(int userId) {
Victoria Lease83762d22012-10-03 13:51:17 -0700324 mBlacklist.switchUser(userId);
Victoria Lease38389b62012-09-30 11:44:22 -0700325 //Log.d("LocationManagerService", "switchUser(" + mCurrentUserId + " -> " + userId + ")"); // TODO: remove this
326 synchronized (mLock) {
327 // TODO: inform previous user's Receivers that they will no longer receive updates
328 mCurrentUserId = userId;
329 // TODO: inform new user's Receivers that they are back on the update train
330 }
331 }
332
333 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800334 * A wrapper class holding either an ILocationListener or a PendingIntent to receive
335 * location updates.
336 */
Mike Lockwood48f17512009-04-23 09:12:08 -0700337 private final class Receiver implements IBinder.DeathRecipient, PendingIntent.OnFinished {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700338 final int mUid; // uid of receiver
339 final int mPid; // pid of receiver
340 final String mPackageName; // package name of receiver
341 final String mPermission; // best permission that receiver has
342
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800343 final ILocationListener mListener;
344 final PendingIntent mPendingIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800345 final Object mKey;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700346
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400347 final HashMap<String,UpdateRecord> mUpdateRecords = new HashMap<String,UpdateRecord>();
Nick Pellyf1be6862012-05-15 10:53:42 -0700348
Mike Lockwood48f17512009-04-23 09:12:08 -0700349 int mPendingBroadcasts;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800350
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700351 Receiver(ILocationListener listener, PendingIntent intent, int pid, int uid,
352 String packageName) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800353 mListener = listener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800354 mPendingIntent = intent;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700355 if (listener != null) {
356 mKey = listener.asBinder();
357 } else {
358 mKey = intent;
359 }
360 mPermission = checkPermission();
361 mUid = uid;
362 mPid = pid;
363 mPackageName = packageName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800364 }
365
366 @Override
367 public boolean equals(Object otherObj) {
368 if (otherObj instanceof Receiver) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700369 return mKey.equals(((Receiver)otherObj).mKey);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800370 }
371 return false;
372 }
373
374 @Override
375 public int hashCode() {
376 return mKey.hashCode();
377 }
Mike Lockwood3681f262009-05-12 10:52:03 -0400378
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800379 @Override
380 public String toString() {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700381 StringBuilder s = new StringBuilder();
382 s.append("Reciever[");
383 s.append(Integer.toHexString(System.identityHashCode(this)));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800384 if (mListener != null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700385 s.append(" listener");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800386 } else {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700387 s.append(" intent");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800388 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700389 for (String p : mUpdateRecords.keySet()) {
390 s.append(" ").append(mUpdateRecords.get(p).toString());
391 }
392 s.append("]");
393 return s.toString();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800394 }
395
396 public boolean isListener() {
397 return mListener != null;
398 }
399
400 public boolean isPendingIntent() {
401 return mPendingIntent != null;
402 }
403
404 public ILocationListener getListener() {
405 if (mListener != null) {
406 return mListener;
407 }
408 throw new IllegalStateException("Request for non-existent listener");
409 }
410
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800411 public boolean callStatusChangedLocked(String provider, int status, Bundle extras) {
412 if (mListener != null) {
413 try {
Mike Lockwood48f17512009-04-23 09:12:08 -0700414 synchronized (this) {
415 // synchronize to ensure incrementPendingBroadcastsLocked()
416 // is called before decrementPendingBroadcasts()
417 mListener.onStatusChanged(provider, status, extras);
Nick Pellye0fd6932012-07-11 10:26:13 -0700418 // call this after broadcasting so we do not increment
419 // if we throw an exeption.
420 incrementPendingBroadcastsLocked();
Mike Lockwood48f17512009-04-23 09:12:08 -0700421 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800422 } catch (RemoteException e) {
423 return false;
424 }
425 } else {
426 Intent statusChanged = new Intent();
427 statusChanged.putExtras(extras);
428 statusChanged.putExtra(LocationManager.KEY_STATUS_CHANGED, status);
429 try {
Mike Lockwood48f17512009-04-23 09:12:08 -0700430 synchronized (this) {
431 // synchronize to ensure incrementPendingBroadcastsLocked()
432 // is called before decrementPendingBroadcasts()
Dianne Hackborn6c418d52011-06-29 14:05:33 -0700433 mPendingIntent.send(mContext, 0, statusChanged, this, mLocationHandler,
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700434 mPermission);
Mike Lockwood48f17512009-04-23 09:12:08 -0700435 // call this after broadcasting so we do not increment
436 // if we throw an exeption.
437 incrementPendingBroadcastsLocked();
438 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800439 } catch (PendingIntent.CanceledException e) {
440 return false;
441 }
442 }
443 return true;
444 }
445
446 public boolean callLocationChangedLocked(Location location) {
447 if (mListener != null) {
448 try {
Mike Lockwood48f17512009-04-23 09:12:08 -0700449 synchronized (this) {
450 // synchronize to ensure incrementPendingBroadcastsLocked()
451 // is called before decrementPendingBroadcasts()
452 mListener.onLocationChanged(location);
Nick Pellye0fd6932012-07-11 10:26:13 -0700453 // call this after broadcasting so we do not increment
454 // if we throw an exeption.
455 incrementPendingBroadcastsLocked();
Mike Lockwood48f17512009-04-23 09:12:08 -0700456 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800457 } catch (RemoteException e) {
458 return false;
459 }
460 } else {
461 Intent locationChanged = new Intent();
462 locationChanged.putExtra(LocationManager.KEY_LOCATION_CHANGED, location);
463 try {
Mike Lockwood48f17512009-04-23 09:12:08 -0700464 synchronized (this) {
465 // synchronize to ensure incrementPendingBroadcastsLocked()
466 // is called before decrementPendingBroadcasts()
Dianne Hackborn6c418d52011-06-29 14:05:33 -0700467 mPendingIntent.send(mContext, 0, locationChanged, this, mLocationHandler,
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700468 mPermission);
Mike Lockwood48f17512009-04-23 09:12:08 -0700469 // call this after broadcasting so we do not increment
470 // if we throw an exeption.
471 incrementPendingBroadcastsLocked();
472 }
473 } catch (PendingIntent.CanceledException e) {
474 return false;
475 }
476 }
477 return true;
478 }
479
480 public boolean callProviderEnabledLocked(String provider, boolean enabled) {
481 if (mListener != null) {
482 try {
483 synchronized (this) {
484 // synchronize to ensure incrementPendingBroadcastsLocked()
485 // is called before decrementPendingBroadcasts()
486 if (enabled) {
487 mListener.onProviderEnabled(provider);
488 } else {
489 mListener.onProviderDisabled(provider);
490 }
Nick Pellye0fd6932012-07-11 10:26:13 -0700491 // call this after broadcasting so we do not increment
492 // if we throw an exeption.
493 incrementPendingBroadcastsLocked();
Mike Lockwood48f17512009-04-23 09:12:08 -0700494 }
495 } catch (RemoteException e) {
496 return false;
497 }
498 } else {
499 Intent providerIntent = new Intent();
500 providerIntent.putExtra(LocationManager.KEY_PROVIDER_ENABLED, enabled);
501 try {
502 synchronized (this) {
503 // synchronize to ensure incrementPendingBroadcastsLocked()
504 // is called before decrementPendingBroadcasts()
Dianne Hackborn6c418d52011-06-29 14:05:33 -0700505 mPendingIntent.send(mContext, 0, providerIntent, this, mLocationHandler,
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700506 mPermission);
Mike Lockwood48f17512009-04-23 09:12:08 -0700507 // call this after broadcasting so we do not increment
508 // if we throw an exeption.
509 incrementPendingBroadcastsLocked();
510 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800511 } catch (PendingIntent.CanceledException e) {
512 return false;
513 }
514 }
515 return true;
516 }
517
Nick Pellyf1be6862012-05-15 10:53:42 -0700518 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800519 public void binderDied() {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700520 if (D) Log.d(TAG, "Location listener died");
521
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400522 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800523 removeUpdatesLocked(this);
524 }
Mike Lockwood48f17512009-04-23 09:12:08 -0700525 synchronized (this) {
526 if (mPendingBroadcasts > 0) {
527 LocationManagerService.this.decrementPendingBroadcasts();
528 mPendingBroadcasts = 0;
529 }
530 }
531 }
532
Nick Pellye0fd6932012-07-11 10:26:13 -0700533 @Override
Mike Lockwood48f17512009-04-23 09:12:08 -0700534 public void onSendFinished(PendingIntent pendingIntent, Intent intent,
535 int resultCode, String resultData, Bundle resultExtras) {
Mike Lockwood0528b9b2009-05-07 10:12:54 -0400536 synchronized (this) {
537 decrementPendingBroadcastsLocked();
Mike Lockwood48f17512009-04-23 09:12:08 -0700538 }
539 }
540
Mike Lockwood0528b9b2009-05-07 10:12:54 -0400541 // this must be called while synchronized by caller in a synchronized block
542 // containing the sending of the broadcaset
543 private void incrementPendingBroadcastsLocked() {
544 if (mPendingBroadcasts++ == 0) {
545 LocationManagerService.this.incrementPendingBroadcasts();
546 }
547 }
548
549 private void decrementPendingBroadcastsLocked() {
550 if (--mPendingBroadcasts == 0) {
551 LocationManagerService.this.decrementPendingBroadcasts();
Mike Lockwood48f17512009-04-23 09:12:08 -0700552 }
553 }
554 }
555
Nick Pellye0fd6932012-07-11 10:26:13 -0700556 @Override
Mike Lockwood48f17512009-04-23 09:12:08 -0700557 public void locationCallbackFinished(ILocationListener listener) {
Joshua Bartel080b61b2009-10-05 12:44:46 -0400558 //Do not use getReceiver here as that will add the ILocationListener to
559 //the receiver list if it is not found. If it is not found then the
560 //LocationListener was removed when it had a pending broadcast and should
561 //not be added back.
562 IBinder binder = listener.asBinder();
563 Receiver receiver = mReceivers.get(binder);
Mike Lockwood48f17512009-04-23 09:12:08 -0700564 if (receiver != null) {
Mike Lockwood0528b9b2009-05-07 10:12:54 -0400565 synchronized (receiver) {
566 // so wakelock calls will succeed
567 long identity = Binder.clearCallingIdentity();
568 receiver.decrementPendingBroadcastsLocked();
569 Binder.restoreCallingIdentity(identity);
570 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800571 }
572 }
573
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700574 private void addProviderLocked(LocationProviderInterface provider) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400575 mProviders.add(provider);
576 mProvidersByName.put(provider.getName(), provider);
577 }
578
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700579 private void removeProviderLocked(LocationProviderInterface provider) {
580 provider.disable();
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400581 mProviders.remove(provider);
582 mProvidersByName.remove(provider.getName());
583 }
584
Mike Lockwood3d12b512009-04-21 23:25:35 -0700585
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800586 private boolean isAllowedBySettingsLocked(String provider) {
587 if (mEnabledProviders.contains(provider)) {
588 return true;
589 }
590 if (mDisabledProviders.contains(provider)) {
591 return false;
592 }
593 // Use system settings
594 ContentResolver resolver = mContext.getContentResolver();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800595
Brad Larson8eb3ea62009-12-29 11:47:55 -0600596 return Settings.Secure.isLocationProviderEnabled(resolver, provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800597 }
598
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700599 /**
600 * Throw SecurityException if caller has neither COARSE or FINE.
601 * Otherwise, return the best permission.
602 */
603 private String checkPermission() {
604 if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) ==
605 PackageManager.PERMISSION_GRANTED) {
Dianne Hackborn6c418d52011-06-29 14:05:33 -0700606 return ACCESS_FINE_LOCATION;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700607 } else if (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION) ==
608 PackageManager.PERMISSION_GRANTED) {
609 return ACCESS_COARSE_LOCATION;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800610 }
Dianne Hackborn6c418d52011-06-29 14:05:33 -0700611
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700612 throw new SecurityException("Location requires either ACCESS_COARSE_LOCATION or" +
Victoria Lease4fab68b2012-09-13 13:20:59 -0700613 " ACCESS_FINE_LOCATION permission");
614 }
615
616 /**
617 * Throw SecurityException if caller lacks permission to use Geofences.
618 */
619 private void checkGeofencePermission() {
620 if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) !=
621 PackageManager.PERMISSION_GRANTED) {
622 throw new SecurityException("Geofence usage requires ACCESS_FINE_LOCATION permission");
623 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800624 }
625
Victoria Lease8dbb6342012-09-21 16:55:53 -0700626 private boolean isAllowedProviderSafe(String provider) {
627 if (LocationManager.GPS_PROVIDER.equals(provider) ||
628 LocationManager.PASSIVE_PROVIDER.equals(provider)) {
629 // gps and passive providers require FINE permission
630 return mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
631 == PackageManager.PERMISSION_GRANTED;
632 } else if (LocationManager.NETWORK_PROVIDER.equals(provider) ||
633 LocationManager.FUSED_PROVIDER.equals(provider)) {
634 // network and fused providers are ok with COARSE or FINE
635 return (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
636 == PackageManager.PERMISSION_GRANTED) ||
637 (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION)
638 == PackageManager.PERMISSION_GRANTED);
Laurent Tu941221c2012-10-04 14:21:52 -0700639 } else {
640 // mock providers
641 LocationProviderInterface lp = mMockProviders.get(provider);
642 if (lp != null) {
643 ProviderProperties properties = lp.getProperties();
644 if (properties != null) {
645 if (properties.mRequiresSatellite) {
646 // provider requiring satellites require FINE permission
647 return mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
648 == PackageManager.PERMISSION_GRANTED;
649 } else if (properties.mRequiresNetwork || properties.mRequiresCell) {
650 // provider requiring network and or cell require COARSE or FINE
651 return (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
652 == PackageManager.PERMISSION_GRANTED) ||
653 (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION)
654 == PackageManager.PERMISSION_GRANTED);
655 }
656 }
657 }
Victoria Lease8dbb6342012-09-21 16:55:53 -0700658 }
Laurent Tu941221c2012-10-04 14:21:52 -0700659
Victoria Lease8dbb6342012-09-21 16:55:53 -0700660 return false;
661 }
662
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700663 /**
664 * Returns all providers by name, including passive, but excluding
Laurent Tu0d21e212012-10-02 15:33:48 -0700665 * fused, also including ones that are not permitted to
666 * be accessed by the calling activity or are currently disabled.
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700667 */
Nick Pellye0fd6932012-07-11 10:26:13 -0700668 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800669 public List<String> getAllProviders() {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700670 ArrayList<String> out;
671 synchronized (mLock) {
672 out = new ArrayList<String>(mProviders.size());
673 for (LocationProviderInterface provider : mProviders) {
674 String name = provider.getName();
675 if (LocationManager.FUSED_PROVIDER.equals(name)) {
Mike Lockwood03ca2162010-04-01 08:10:09 -0700676 continue;
677 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800678 out.add(name);
679 }
680 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700681
682 if (D) Log.d(TAG, "getAllProviders()=" + out);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800683 return out;
684 }
685
Mike Lockwood03ca2162010-04-01 08:10:09 -0700686 /**
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700687 * Return all providers by name, that match criteria and are optionally
688 * enabled.
689 * Can return passive provider, but never returns fused provider.
Mike Lockwood03ca2162010-04-01 08:10:09 -0700690 */
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700691 @Override
692 public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700693 ArrayList<String> out;
Mike Lockwood03ca2162010-04-01 08:10:09 -0700694 synchronized (mLock) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700695 out = new ArrayList<String>(mProviders.size());
696 for (LocationProviderInterface provider : mProviders) {
697 String name = provider.getName();
698 if (LocationManager.FUSED_PROVIDER.equals(name)) {
699 continue;
700 }
Victoria Lease8dbb6342012-09-21 16:55:53 -0700701 if (isAllowedProviderSafe(name)) {
702 if (enabledOnly && !isAllowedBySettingsLocked(name)) {
703 continue;
704 }
705 if (criteria != null && !LocationProvider.propertiesMeetCriteria(
706 name, provider.getProperties(), criteria)) {
707 continue;
708 }
709 out.add(name);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700710 }
Mike Lockwood03ca2162010-04-01 08:10:09 -0700711 }
712 }
713
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700714 if (D) Log.d(TAG, "getProviders()=" + out);
715 return out;
Mike Lockwood03ca2162010-04-01 08:10:09 -0700716 }
717
718 /**
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700719 * Return the name of the best provider given a Criteria object.
720 * This method has been deprecated from the public API,
Victoria Lease8dbb6342012-09-21 16:55:53 -0700721 * and the whole LocationProvider (including #meetsCriteria)
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700722 * has been deprecated as well. So this method now uses
723 * some simplified logic.
Mike Lockwood03ca2162010-04-01 08:10:09 -0700724 */
Nick Pellye0fd6932012-07-11 10:26:13 -0700725 @Override
Mike Lockwood03ca2162010-04-01 08:10:09 -0700726 public String getBestProvider(Criteria criteria, boolean enabledOnly) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700727 String result = null;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700728
729 List<String> providers = getProviders(criteria, enabledOnly);
Victoria Lease8dbb6342012-09-21 16:55:53 -0700730 if (!providers.isEmpty()) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700731 result = pickBest(providers);
732 if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
733 return result;
734 }
735 providers = getProviders(null, enabledOnly);
Victoria Lease8dbb6342012-09-21 16:55:53 -0700736 if (!providers.isEmpty()) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700737 result = pickBest(providers);
738 if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
739 return result;
Mike Lockwood03ca2162010-04-01 08:10:09 -0700740 }
741
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700742 if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
Mike Lockwood03ca2162010-04-01 08:10:09 -0700743 return null;
744 }
745
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700746 private String pickBest(List<String> providers) {
Victoria Lease1925e292012-09-24 17:00:18 -0700747 if (providers.contains(LocationManager.GPS_PROVIDER)) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700748 return LocationManager.GPS_PROVIDER;
Victoria Lease1925e292012-09-24 17:00:18 -0700749 } else if (providers.contains(LocationManager.NETWORK_PROVIDER)) {
750 return LocationManager.NETWORK_PROVIDER;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700751 } else {
752 return providers.get(0);
753 }
754 }
755
Nick Pellye0fd6932012-07-11 10:26:13 -0700756 @Override
Mike Lockwood03ca2162010-04-01 08:10:09 -0700757 public boolean providerMeetsCriteria(String provider, Criteria criteria) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700758 checkPermission();
759
Mike Lockwood03ca2162010-04-01 08:10:09 -0700760 LocationProviderInterface p = mProvidersByName.get(provider);
761 if (p == null) {
762 throw new IllegalArgumentException("provider=" + provider);
763 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700764
765 boolean result = LocationProvider.propertiesMeetCriteria(
766 p.getName(), p.getProperties(), criteria);
767 if (D) Log.d(TAG, "providerMeetsCriteria(" + provider + ", " + criteria + ")=" + result);
768 return result;
Mike Lockwood03ca2162010-04-01 08:10:09 -0700769 }
770
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800771 private void updateProvidersLocked() {
Brad Fitzpatrick0c5a0402010-08-27 14:01:23 -0700772 boolean changesMade = false;
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400773 for (int i = mProviders.size() - 1; i >= 0; i--) {
Mike Lockwoodd03ff942010-02-09 08:46:14 -0500774 LocationProviderInterface p = mProviders.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800775 boolean isEnabled = p.isEnabled();
776 String name = p.getName();
777 boolean shouldBeEnabled = isAllowedBySettingsLocked(name);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800778 if (isEnabled && !shouldBeEnabled) {
779 updateProviderListenersLocked(name, false);
Brad Fitzpatrick0c5a0402010-08-27 14:01:23 -0700780 changesMade = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800781 } else if (!isEnabled && shouldBeEnabled) {
782 updateProviderListenersLocked(name, true);
Brad Fitzpatrick0c5a0402010-08-27 14:01:23 -0700783 changesMade = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800784 }
Brad Fitzpatrick0c5a0402010-08-27 14:01:23 -0700785 }
786 if (changesMade) {
Dianne Hackborn5ac72a22012-08-29 18:32:08 -0700787 mContext.sendBroadcastAsUser(new Intent(LocationManager.PROVIDERS_CHANGED_ACTION),
788 UserHandle.ALL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800789 }
790 }
791
792 private void updateProviderListenersLocked(String provider, boolean enabled) {
793 int listeners = 0;
794
Mike Lockwoodd03ff942010-02-09 08:46:14 -0500795 LocationProviderInterface p = mProvidersByName.get(provider);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700796 if (p == null) return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800797
798 ArrayList<Receiver> deadReceivers = null;
Nick Pellye0fd6932012-07-11 10:26:13 -0700799
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800800 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
801 if (records != null) {
802 final int N = records.size();
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700803 for (int i = 0; i < N; i++) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800804 UpdateRecord record = records.get(i);
805 // Sends a notification message to the receiver
Mike Lockwood48f17512009-04-23 09:12:08 -0700806 if (!record.mReceiver.callProviderEnabledLocked(provider, enabled)) {
807 if (deadReceivers == null) {
808 deadReceivers = new ArrayList<Receiver>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800809 }
Simon Schoar46866572009-06-10 21:12:10 +0200810 deadReceivers.add(record.mReceiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800811 }
812 listeners++;
813 }
814 }
815
816 if (deadReceivers != null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700817 for (int i = deadReceivers.size() - 1; i >= 0; i--) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800818 removeUpdatesLocked(deadReceivers.get(i));
819 }
820 }
Nick Pellye0fd6932012-07-11 10:26:13 -0700821
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800822 if (enabled) {
823 p.enable();
824 if (listeners > 0) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700825 applyRequirementsLocked(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800826 }
827 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800828 p.disable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800829 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800830 }
831
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700832 private void applyRequirementsLocked(String provider) {
833 LocationProviderInterface p = mProvidersByName.get(provider);
834 if (p == null) return;
835
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800836 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700837 WorkSource worksource = new WorkSource();
838 ProviderRequest providerRequest = new ProviderRequest();
839
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800840 if (records != null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700841 for (UpdateRecord record : records) {
842 LocationRequest locationRequest = record.mRequest;
843
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700844 providerRequest.locationRequests.add(locationRequest);
845 if (locationRequest.getInterval() < providerRequest.interval) {
846 providerRequest.reportLocation = true;
847 providerRequest.interval = locationRequest.getInterval();
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700848 }
849 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700850
851 if (providerRequest.reportLocation) {
852 // calculate who to blame for power
853 // This is somewhat arbitrary. We pick a threshold interval
854 // that is slightly higher that the minimum interval, and
855 // spread the blame across all applications with a request
856 // under that threshold.
857 long thresholdInterval = (providerRequest.interval + 1000) * 3 / 2;
858 for (UpdateRecord record : records) {
859 LocationRequest locationRequest = record.mRequest;
860 if (locationRequest.getInterval() <= thresholdInterval) {
861 worksource.add(record.mReceiver.mUid);
862 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700863 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800864 }
865 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700866
867 if (D) Log.d(TAG, "provider request: " + provider + " " + providerRequest);
868 p.setRequest(providerRequest, worksource);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800869 }
870
871 private class UpdateRecord {
872 final String mProvider;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700873 final LocationRequest mRequest;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800874 final Receiver mReceiver;
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400875 Location mLastFixBroadcast;
876 long mLastStatusBroadcast;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800877
878 /**
879 * Note: must be constructed with lock held.
880 */
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700881 UpdateRecord(String provider, LocationRequest request, Receiver receiver) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800882 mProvider = provider;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700883 mRequest = request;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800884 mReceiver = receiver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800885
886 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
887 if (records == null) {
888 records = new ArrayList<UpdateRecord>();
889 mRecordsByProvider.put(provider, records);
890 }
891 if (!records.contains(this)) {
892 records.add(this);
893 }
894 }
895
896 /**
897 * Method to be called when a record will no longer be used. Calling this multiple times
898 * must have the same effect as calling it once.
899 */
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700900 void disposeLocked(boolean removeReceiver) {
901 // remove from mRecordsByProvider
902 ArrayList<UpdateRecord> globalRecords = mRecordsByProvider.get(this.mProvider);
903 if (globalRecords != null) {
904 globalRecords.remove(this);
905 }
906
907 if (!removeReceiver) return; // the caller will handle the rest
908
909 // remove from Receiver#mUpdateRecords
910 HashMap<String, UpdateRecord> receiverRecords = mReceiver.mUpdateRecords;
911 if (receiverRecords != null) {
912 receiverRecords.remove(this.mProvider);
913
914 // and also remove the Receiver if it has no more update records
915 if (removeReceiver && receiverRecords.size() == 0) {
916 removeUpdatesLocked(mReceiver);
917 }
Mike Lockwood3a76fd62009-09-01 07:26:56 -0400918 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800919 }
920
921 @Override
922 public String toString() {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700923 StringBuilder s = new StringBuilder();
924 s.append("UpdateRecord[");
925 s.append(mProvider);
926 s.append(' ').append(mReceiver.mPackageName).append('(');
927 s.append(mReceiver.mUid).append(')');
928 s.append(' ').append(mRequest);
929 s.append(']');
930 return s.toString();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800931 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800932 }
933
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700934 private Receiver getReceiver(ILocationListener listener, int pid, int uid, String packageName) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400935 IBinder binder = listener.asBinder();
936 Receiver receiver = mReceivers.get(binder);
937 if (receiver == null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700938 receiver = new Receiver(listener, null, pid, uid, packageName);
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400939 mReceivers.put(binder, receiver);
940
941 try {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700942 receiver.getListener().asBinder().linkToDeath(receiver, 0);
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400943 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800944 Slog.e(TAG, "linkToDeath failed:", e);
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400945 return null;
946 }
947 }
948 return receiver;
949 }
950
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700951 private Receiver getReceiver(PendingIntent intent, int pid, int uid, String packageName) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400952 Receiver receiver = mReceivers.get(intent);
953 if (receiver == null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700954 receiver = new Receiver(null, intent, pid, uid, packageName);
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400955 mReceivers.put(intent, receiver);
956 }
957 return receiver;
958 }
959
Victoria Lease09016ab2012-09-16 12:33:15 -0700960 private boolean isProviderAllowedByCoarsePermission(String provider) {
961 if (LocationManager.FUSED_PROVIDER.equals(provider)) {
962 return true;
963 }
964 if (LocationManager.PASSIVE_PROVIDER.equals(provider)) {
965 return true;
966 }
967 if (LocationManager.NETWORK_PROVIDER.equals(provider)) {
968 return true;
969 }
970 return false;
971 }
972
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700973 private String checkPermissionAndRequest(LocationRequest request) {
974 String perm = checkPermission();
975
976 if (ACCESS_COARSE_LOCATION.equals(perm)) {
Victoria Lease09016ab2012-09-16 12:33:15 -0700977 if (!isProviderAllowedByCoarsePermission(request.getProvider())) {
978 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
979 }
980 switch (request.getQuality()) {
981 case LocationRequest.ACCURACY_FINE:
982 request.setQuality(LocationRequest.ACCURACY_BLOCK);
983 break;
984 case LocationRequest.POWER_HIGH:
985 request.setQuality(LocationRequest.POWER_LOW);
986 break;
987 }
988 // throttle
989 if (request.getInterval() < LocationFudger.FASTEST_INTERVAL_MS) {
990 request.setInterval(LocationFudger.FASTEST_INTERVAL_MS);
991 }
992 if (request.getFastestInterval() < LocationFudger.FASTEST_INTERVAL_MS) {
993 request.setFastestInterval(LocationFudger.FASTEST_INTERVAL_MS);
994 }
Nick Pelly74fa7ea2012-08-13 19:36:38 -0700995 }
Nick Pelly4e31c4f2012-08-13 19:35:39 -0700996 // make getFastestInterval() the minimum of interval and fastest interval
997 if (request.getFastestInterval() > request.getInterval()) {
998 request.setFastestInterval(request.getInterval());
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400999 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001000 return perm;
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001001 }
1002
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001003 private void checkPackageName(String packageName) {
Nick Pellye0fd6932012-07-11 10:26:13 -07001004 if (packageName == null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001005 throw new SecurityException("invalid package name: " + packageName);
Nick Pellye0fd6932012-07-11 10:26:13 -07001006 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001007 int uid = Binder.getCallingUid();
Nick Pellye0fd6932012-07-11 10:26:13 -07001008 String[] packages = mPackageManager.getPackagesForUid(uid);
1009 if (packages == null) {
1010 throw new SecurityException("invalid UID " + uid);
1011 }
1012 for (String pkg : packages) {
1013 if (packageName.equals(pkg)) return;
1014 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001015 throw new SecurityException("invalid package name: " + packageName);
Nick Pellye0fd6932012-07-11 10:26:13 -07001016 }
1017
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001018 private void checkPendingIntent(PendingIntent intent) {
1019 if (intent == null) {
1020 throw new IllegalArgumentException("invalid pending intent: " + intent);
Dianne Hackborn6c418d52011-06-29 14:05:33 -07001021 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001022 }
1023
1024 private Receiver checkListenerOrIntent(ILocationListener listener, PendingIntent intent,
1025 int pid, int uid, String packageName) {
1026 if (intent == null && listener == null) {
1027 throw new IllegalArgumentException("need eiter listener or intent");
1028 } else if (intent != null && listener != null) {
1029 throw new IllegalArgumentException("cannot register both listener and intent");
1030 } else if (intent != null) {
1031 checkPendingIntent(intent);
1032 return getReceiver(intent, pid, uid, packageName);
1033 } else {
1034 return getReceiver(listener, pid, uid, packageName);
1035 }
Dianne Hackborn6c418d52011-06-29 14:05:33 -07001036 }
1037
Nick Pellye0fd6932012-07-11 10:26:13 -07001038 @Override
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001039 public void requestLocationUpdates(LocationRequest request, ILocationListener listener,
1040 PendingIntent intent, String packageName) {
1041 if (request == null) request = DEFAULT_LOCATION_REQUEST;
1042 checkPackageName(packageName);
1043 checkPermissionAndRequest(request);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001044
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001045 final int pid = Binder.getCallingPid();
1046 final int uid = Binder.getCallingUid();
1047 Receiver recevier = checkListenerOrIntent(listener, intent, pid, uid, packageName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001048
Nick Pelly2b7a0d02012-08-17 15:09:44 -07001049 // providers may use public location API's, need to clear identity
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001050 long identity = Binder.clearCallingIdentity();
1051 try {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001052 synchronized (mLock) {
1053 requestLocationUpdatesLocked(request, recevier, pid, uid, packageName);
Mike Lockwood2d4d1bf2010-10-18 17:06:26 -04001054 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001055 } finally {
1056 Binder.restoreCallingIdentity(identity);
1057 }
1058 }
1059
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001060 private void requestLocationUpdatesLocked(LocationRequest request, Receiver receiver,
1061 int pid, int uid, String packageName) {
1062 // Figure out the provider. Either its explicitly request (legacy use cases), or
1063 // use the fused provider
1064 if (request == null) request = DEFAULT_LOCATION_REQUEST;
1065 String name = request.getProvider();
Victoria Lease09016ab2012-09-16 12:33:15 -07001066 if (name == null) {
1067 throw new IllegalArgumentException("provider name must not be null");
1068 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001069 LocationProviderInterface provider = mProvidersByName.get(name);
1070 if (provider == null) {
1071 throw new IllegalArgumentException("provider doesn't exisit: " + provider);
1072 }
1073
1074 Log.i(TAG, "request " + Integer.toHexString(System.identityHashCode(receiver)) + " " +
1075 name + " " + request + " from " + packageName + "(" + uid + ")");
1076
1077 UpdateRecord record = new UpdateRecord(name, request, receiver);
1078 UpdateRecord oldRecord = receiver.mUpdateRecords.put(name, record);
1079 if (oldRecord != null) {
1080 oldRecord.disposeLocked(false);
1081 }
1082
1083 boolean isProviderEnabled = isAllowedBySettingsLocked(name);
1084 if (isProviderEnabled) {
1085 applyRequirementsLocked(name);
1086 } else {
1087 // Notify the listener that updates are currently disabled
1088 receiver.callProviderEnabledLocked(name, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001089 }
1090 }
1091
Nick Pellye0fd6932012-07-11 10:26:13 -07001092 @Override
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001093 public void removeUpdates(ILocationListener listener, PendingIntent intent,
1094 String packageName) {
1095 checkPackageName(packageName);
1096 checkPermission();
1097 final int pid = Binder.getCallingPid();
1098 final int uid = Binder.getCallingUid();
1099 Receiver receiver = checkListenerOrIntent(listener, intent, pid, uid, packageName);
1100
Nick Pelly2b7a0d02012-08-17 15:09:44 -07001101 // providers may use public location API's, need to clear identity
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001102 long identity = Binder.clearCallingIdentity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001103 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001104 synchronized (mLock) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001105 removeUpdatesLocked(receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001106 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001107 } finally {
1108 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001109 }
1110 }
1111
1112 private void removeUpdatesLocked(Receiver receiver) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001113 Log.i(TAG, "remove " + Integer.toHexString(System.identityHashCode(receiver)));
1114
1115 if (mReceivers.remove(receiver.mKey) != null && receiver.isListener()) {
1116 receiver.getListener().asBinder().unlinkToDeath(receiver, 0);
1117 synchronized (receiver) {
1118 if (receiver.mPendingBroadcasts > 0) {
1119 decrementPendingBroadcasts();
1120 receiver.mPendingBroadcasts = 0;
1121 }
1122 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001123 }
1124
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001125 // Record which providers were associated with this listener
1126 HashSet<String> providers = new HashSet<String>();
1127 HashMap<String, UpdateRecord> oldRecords = receiver.mUpdateRecords;
1128 if (oldRecords != null) {
1129 // Call dispose() on the obsolete update records.
1130 for (UpdateRecord record : oldRecords.values()) {
1131 record.disposeLocked(false);
1132 }
1133 // Accumulate providers
1134 providers.addAll(oldRecords.keySet());
1135 }
1136
1137 // update provider
1138 for (String provider : providers) {
1139 // If provider is already disabled, don't need to do anything
1140 if (!isAllowedBySettingsLocked(provider)) {
1141 continue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001142 }
1143
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001144 applyRequirementsLocked(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001145 }
1146 }
1147
Nick Pellye0fd6932012-07-11 10:26:13 -07001148 @Override
Nick Pelly4035f5a2012-08-17 14:43:49 -07001149 public Location getLastLocation(LocationRequest request, String packageName) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001150 if (D) Log.d(TAG, "getLastLocation: " + request);
1151 if (request == null) request = DEFAULT_LOCATION_REQUEST;
1152 String perm = checkPermissionAndRequest(request);
Nick Pelly4035f5a2012-08-17 14:43:49 -07001153 checkPackageName(packageName);
1154
1155 if (mBlacklist.isBlacklisted(packageName)) {
1156 if (D) Log.d(TAG, "not returning last loc for blacklisted app: " +
1157 packageName);
1158 return null;
1159 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001160
1161 synchronized (mLock) {
1162 // Figure out the provider. Either its explicitly request (deprecated API's),
1163 // or use the fused provider
1164 String name = request.getProvider();
1165 if (name == null) name = LocationManager.FUSED_PROVIDER;
1166 LocationProviderInterface provider = mProvidersByName.get(name);
1167 if (provider == null) return null;
1168
1169 if (!isAllowedBySettingsLocked(name)) return null;
1170
1171 Location location = mLastLocation.get(name);
Victoria Lease09016ab2012-09-16 12:33:15 -07001172 if (location == null) {
1173 return null;
1174 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001175 if (ACCESS_FINE_LOCATION.equals(perm)) {
1176 return location;
1177 } else {
Victoria Lease09016ab2012-09-16 12:33:15 -07001178 Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
1179 if (noGPSLocation != null) {
1180 return mLocationFudger.getOrCreate(noGPSLocation);
1181 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001182 }
1183 }
Victoria Lease09016ab2012-09-16 12:33:15 -07001184 return null;
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001185 }
1186
1187 @Override
1188 public void requestGeofence(LocationRequest request, Geofence geofence, PendingIntent intent,
1189 String packageName) {
1190 if (request == null) request = DEFAULT_LOCATION_REQUEST;
Victoria Lease4fab68b2012-09-13 13:20:59 -07001191 checkGeofencePermission();
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001192 checkPermissionAndRequest(request);
1193 checkPendingIntent(intent);
1194 checkPackageName(packageName);
1195
1196 if (D) Log.d(TAG, "requestGeofence: " + request + " " + geofence + " " + intent);
1197
Nick Pelly2b7a0d02012-08-17 15:09:44 -07001198 // geo-fence manager uses the public location API, need to clear identity
1199 int uid = Binder.getCallingUid();
1200 long identity = Binder.clearCallingIdentity();
1201 try {
1202 mGeofenceManager.addFence(request, geofence, intent, uid, packageName);
1203 } finally {
1204 Binder.restoreCallingIdentity(identity);
1205 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001206 }
1207
1208 @Override
1209 public void removeGeofence(Geofence geofence, PendingIntent intent, String packageName) {
Victoria Lease4fab68b2012-09-13 13:20:59 -07001210 checkGeofencePermission();
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001211 checkPendingIntent(intent);
1212 checkPackageName(packageName);
1213
1214 if (D) Log.d(TAG, "removeGeofence: " + geofence + " " + intent);
1215
Nick Pelly2b7a0d02012-08-17 15:09:44 -07001216 // geo-fence manager uses the public location API, need to clear identity
1217 long identity = Binder.clearCallingIdentity();
1218 try {
1219 mGeofenceManager.removeFence(geofence, intent);
1220 } finally {
1221 Binder.restoreCallingIdentity(identity);
1222 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001223 }
1224
1225
1226 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001227 public boolean addGpsStatusListener(IGpsStatusListener listener) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001228 if (mGpsStatusProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001229 return false;
1230 }
Mike Lockwoodb7e99222009-07-07 13:18:21 -04001231 if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) !=
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001232 PackageManager.PERMISSION_GRANTED) {
1233 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
1234 }
1235
1236 try {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001237 mGpsStatusProvider.addGpsStatusListener(listener);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001238 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001239 Slog.e(TAG, "mGpsStatusProvider.addGpsStatusListener failed", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001240 return false;
1241 }
1242 return true;
1243 }
1244
Nick Pellye0fd6932012-07-11 10:26:13 -07001245 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001246 public void removeGpsStatusListener(IGpsStatusListener listener) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001247 synchronized (mLock) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001248 try {
1249 mGpsStatusProvider.removeGpsStatusListener(listener);
1250 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001251 Slog.e(TAG, "mGpsStatusProvider.removeGpsStatusListener failed", e);
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001252 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001253 }
1254 }
1255
Nick Pellye0fd6932012-07-11 10:26:13 -07001256 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001257 public boolean sendExtraCommand(String provider, String command, Bundle extras) {
Mike Lockwoodc6cc8362009-08-17 13:16:08 -04001258 if (provider == null) {
1259 // throw NullPointerException to remain compatible with previous implementation
1260 throw new NullPointerException();
1261 }
1262
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001263 checkPermission();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001264 // and check for ACCESS_LOCATION_EXTRA_COMMANDS
Mike Lockwoodb7e99222009-07-07 13:18:21 -04001265 if ((mContext.checkCallingOrSelfPermission(ACCESS_LOCATION_EXTRA_COMMANDS)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001266 != PackageManager.PERMISSION_GRANTED)) {
1267 throw new SecurityException("Requires ACCESS_LOCATION_EXTRA_COMMANDS permission");
1268 }
1269
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001270 synchronized (mLock) {
Mike Lockwoodd03ff942010-02-09 08:46:14 -05001271 LocationProviderInterface p = mProvidersByName.get(provider);
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001272 if (p == null) return false;
Nick Pellye0fd6932012-07-11 10:26:13 -07001273
Mike Lockwoodd03ff942010-02-09 08:46:14 -05001274 return p.sendExtraCommand(command, extras);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001275 }
1276 }
1277
Nick Pellye0fd6932012-07-11 10:26:13 -07001278 @Override
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001279 public boolean sendNiResponse(int notifId, int userResponse) {
Mike Lockwood18ad9f62009-08-27 14:01:23 -07001280 if (Binder.getCallingUid() != Process.myUid()) {
1281 throw new SecurityException(
1282 "calling sendNiResponse from outside of the system is not allowed");
1283 }
Danke Xie22d1f9f2009-08-18 18:28:45 -04001284 try {
1285 return mNetInitiatedListener.sendNiResponse(notifId, userResponse);
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001286 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001287 Slog.e(TAG, "RemoteException in LocationManagerService.sendNiResponse");
Danke Xie22d1f9f2009-08-18 18:28:45 -04001288 return false;
1289 }
1290 }
1291
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001292 /**
Mike Lockwood628fd6d2010-01-25 22:46:13 -05001293 * @return null if the provider does not exist
Alexey Tarasovf2db9fb2009-09-01 02:37:07 +11001294 * @throws SecurityException if the provider is not allowed to be
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001295 * accessed by the caller
1296 */
Nick Pellye0fd6932012-07-11 10:26:13 -07001297 @Override
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001298 public ProviderProperties getProviderProperties(String provider) {
1299 checkPermission();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001300
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001301 LocationProviderInterface p;
1302 synchronized (mLock) {
1303 p = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001304 }
1305
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001306 if (p == null) return null;
1307 return p.getProperties();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001308 }
1309
Nick Pellye0fd6932012-07-11 10:26:13 -07001310 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001311 public boolean isProviderEnabled(String provider) {
Victoria Leasef429921e2012-10-04 08:01:19 -07001312 String perms = checkPermission();
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001313 if (LocationManager.FUSED_PROVIDER.equals(provider)) return false;
Victoria Leasef429921e2012-10-04 08:01:19 -07001314 if (ACCESS_COARSE_LOCATION.equals(perms) &&
1315 !isProviderAllowedByCoarsePermission(provider)) {
1316 throw new SecurityException("The \"" + provider +
1317 "\" provider requires ACCESS_FINE_LOCATION permission");
1318 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001319
1320 synchronized (mLock) {
1321 LocationProviderInterface p = mProvidersByName.get(provider);
1322 if (p == null) return false;
1323
1324 return isAllowedBySettingsLocked(provider);
1325 }
1326 }
1327
1328 private void checkCallerIsProvider() {
1329 if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER)
1330 == PackageManager.PERMISSION_GRANTED) {
1331 return;
1332 }
1333
1334 // Previously we only used the INSTALL_LOCATION_PROVIDER
1335 // check. But that is system or signature
1336 // protection level which is not flexible enough for
1337 // providers installed oustide the system image. So
1338 // also allow providers with a UID matching the
1339 // currently bound package name
1340
1341 int uid = Binder.getCallingUid();
1342
1343 if (mGeocodeProvider != null) {
1344 if (doesPackageHaveUid(uid, mGeocodeProvider.getConnectedPackageName())) return;
1345 }
1346 for (LocationProviderProxy proxy : mProxyProviders) {
1347 if (doesPackageHaveUid(uid, proxy.getConnectedPackageName())) return;
1348 }
1349 throw new SecurityException("need INSTALL_LOCATION_PROVIDER permission, " +
1350 "or UID of a currently bound location provider");
1351 }
1352
1353 private boolean doesPackageHaveUid(int uid, String packageName) {
1354 if (packageName == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001355 return false;
1356 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001357 try {
1358 ApplicationInfo appInfo = mPackageManager.getApplicationInfo(packageName, 0);
1359 if (appInfo.uid != uid) {
1360 return false;
1361 }
1362 } catch (NameNotFoundException e) {
1363 return false;
1364 }
1365 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001366 }
1367
Nick Pellye0fd6932012-07-11 10:26:13 -07001368 @Override
Mike Lockwooda4903f252010-02-17 06:42:23 -05001369 public void reportLocation(Location location, boolean passive) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001370 checkCallerIsProvider();
Mike Lockwood275555c2009-05-01 11:30:34 -04001371
Nick Pelly2eeeec22012-07-18 13:13:37 -07001372 if (!location.isComplete()) {
1373 Log.w(TAG, "Dropping incomplete location: " + location);
1374 return;
1375 }
1376
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001377 mLocationHandler.removeMessages(MSG_LOCATION_CHANGED, location);
1378 Message m = Message.obtain(mLocationHandler, MSG_LOCATION_CHANGED, location);
Mike Lockwooda4903f252010-02-17 06:42:23 -05001379 m.arg1 = (passive ? 1 : 0);
Mike Lockwood4e50b782009-04-03 08:24:43 -07001380 mLocationHandler.sendMessageAtFrontOfQueue(m);
1381 }
1382
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001383
1384 private static boolean shouldBroadcastSafe(Location loc, Location lastLoc, UpdateRecord record) {
1385 // Always broadcast the first update
1386 if (lastLoc == null) {
1387 return true;
1388 }
1389
Nick Pellyf1be6862012-05-15 10:53:42 -07001390 // Check whether sufficient time has passed
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001391 long minTime = record.mRequest.getFastestInterval();
Philip Milne41180122012-09-26 11:29:25 -07001392 long delta = (loc.getElapsedRealtimeNanos() - lastLoc.getElapsedRealtimeNanos()) / 1000000L;
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001393 if (delta < minTime - MAX_PROVIDER_SCHEDULING_JITTER_MS) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001394 return false;
1395 }
1396
1397 // Check whether sufficient distance has been traveled
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001398 double minDistance = record.mRequest.getSmallestDisplacement();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001399 if (minDistance > 0.0) {
1400 if (loc.distanceTo(lastLoc) <= minDistance) {
1401 return false;
1402 }
1403 }
1404
1405 return true;
1406 }
1407
Mike Lockwooda4903f252010-02-17 06:42:23 -05001408 private void handleLocationChangedLocked(Location location, boolean passive) {
Nick Pelly4e31c4f2012-08-13 19:35:39 -07001409 if (D) Log.d(TAG, "incoming location: " + location);
1410
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001411 long now = SystemClock.elapsedRealtime();
Mike Lockwooda4903f252010-02-17 06:42:23 -05001412 String provider = (passive ? LocationManager.PASSIVE_PROVIDER : location.getProvider());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001413 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001414 if (records == null || records.size() == 0) return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001415
Mike Lockwoodd03ff942010-02-09 08:46:14 -05001416 LocationProviderInterface p = mProvidersByName.get(provider);
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001417 if (p == null) return;
1418
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001419 // Update last known locations
Victoria Lease09016ab2012-09-16 12:33:15 -07001420 Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
1421 Location lastNoGPSLocation = null;
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001422 Location lastLocation = mLastLocation.get(provider);
Mike Lockwood4e50b782009-04-03 08:24:43 -07001423 if (lastLocation == null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001424 lastLocation = new Location(provider);
1425 mLastLocation.put(provider, lastLocation);
Victoria Lease09016ab2012-09-16 12:33:15 -07001426 } else {
1427 lastNoGPSLocation = lastLocation.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
1428 if (noGPSLocation == null && lastNoGPSLocation != null) {
1429 // New location has no no-GPS location: adopt last no-GPS location. This is set
1430 // directly into location because we do not want to notify COARSE clients.
1431 location.setExtraLocation(Location.EXTRA_NO_GPS_LOCATION, lastNoGPSLocation);
1432 }
Mike Lockwood4e50b782009-04-03 08:24:43 -07001433 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001434 lastLocation.set(location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001435
Victoria Lease09016ab2012-09-16 12:33:15 -07001436 // Fetch coarse location
1437 Location coarseLocation = null;
1438 if (noGPSLocation != null && !noGPSLocation.equals(lastNoGPSLocation)) {
1439 coarseLocation = mLocationFudger.getOrCreate(noGPSLocation);
1440 }
1441
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001442 // Fetch latest status update time
1443 long newStatusUpdateTime = p.getStatusUpdateTime();
1444
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001445 // Get latest status
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001446 Bundle extras = new Bundle();
1447 int status = p.getStatus(extras);
1448
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001449 ArrayList<Receiver> deadReceivers = null;
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001450 ArrayList<UpdateRecord> deadUpdateRecords = null;
Nick Pellye0fd6932012-07-11 10:26:13 -07001451
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001452 // Broadcast location or status to all listeners
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001453 for (UpdateRecord r : records) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001454 Receiver receiver = r.mReceiver;
Mike Lockwood03ca2162010-04-01 08:10:09 -07001455 boolean receiverDead = false;
Nick Pelly4035f5a2012-08-17 14:43:49 -07001456
1457 if (mBlacklist.isBlacklisted(receiver.mPackageName)) {
1458 if (D) Log.d(TAG, "skipping loc update for blacklisted app: " +
1459 receiver.mPackageName);
1460 continue;
1461 }
1462
Victoria Lease09016ab2012-09-16 12:33:15 -07001463 Location notifyLocation = null;
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001464 if (ACCESS_FINE_LOCATION.equals(receiver.mPermission)) {
Victoria Lease09016ab2012-09-16 12:33:15 -07001465 notifyLocation = lastLocation; // use fine location
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001466 } else {
Victoria Lease09016ab2012-09-16 12:33:15 -07001467 notifyLocation = coarseLocation; // use coarse location if available
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001468 }
Victoria Lease09016ab2012-09-16 12:33:15 -07001469 if (notifyLocation != null) {
1470 Location lastLoc = r.mLastFixBroadcast;
1471 if ((lastLoc == null) || shouldBroadcastSafe(notifyLocation, lastLoc, r)) {
1472 if (lastLoc == null) {
1473 lastLoc = new Location(notifyLocation);
1474 r.mLastFixBroadcast = lastLoc;
1475 } else {
1476 lastLoc.set(notifyLocation);
1477 }
1478 if (!receiver.callLocationChangedLocked(notifyLocation)) {
1479 Slog.w(TAG, "RemoteException calling onLocationChanged on " + receiver);
1480 receiverDead = true;
1481 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001482 }
1483 }
1484
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001485 long prevStatusUpdateTime = r.mLastStatusBroadcast;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001486 if ((newStatusUpdateTime > prevStatusUpdateTime) &&
Victoria Lease09016ab2012-09-16 12:33:15 -07001487 (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001488
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001489 r.mLastStatusBroadcast = newStatusUpdateTime;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001490 if (!receiver.callStatusChangedLocked(provider, status, extras)) {
Mike Lockwood03ca2162010-04-01 08:10:09 -07001491 receiverDead = true;
Joe Onorato8a9b2202010-02-26 18:56:32 -08001492 Slog.w(TAG, "RemoteException calling onStatusChanged on " + receiver);
Mike Lockwood03ca2162010-04-01 08:10:09 -07001493 }
1494 }
1495
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001496 // track expired records
1497 if (r.mRequest.getNumUpdates() == 0 || r.mRequest.getExpireAt() < now) {
1498 if (deadUpdateRecords == null) {
1499 deadUpdateRecords = new ArrayList<UpdateRecord>();
1500 }
1501 deadUpdateRecords.add(r);
1502 }
1503 // track dead receivers
1504 if (receiverDead) {
Mike Lockwood03ca2162010-04-01 08:10:09 -07001505 if (deadReceivers == null) {
1506 deadReceivers = new ArrayList<Receiver>();
1507 }
1508 if (!deadReceivers.contains(receiver)) {
1509 deadReceivers.add(receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001510 }
1511 }
1512 }
Nick Pellye0fd6932012-07-11 10:26:13 -07001513
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001514 // remove dead records and receivers outside the loop
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001515 if (deadReceivers != null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001516 for (Receiver receiver : deadReceivers) {
1517 removeUpdatesLocked(receiver);
1518 }
1519 }
1520 if (deadUpdateRecords != null) {
1521 for (UpdateRecord r : deadUpdateRecords) {
1522 r.disposeLocked(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001523 }
1524 }
1525 }
1526
1527 private class LocationWorkerHandler extends Handler {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001528 @Override
1529 public void handleMessage(Message msg) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001530 switch (msg.what) {
1531 case MSG_LOCATION_CHANGED:
1532 handleLocationChanged((Location) msg.obj, msg.arg1 == 1);
1533 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001534 }
1535 }
1536 }
1537
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001538 private void handleLocationChanged(Location location, boolean passive) {
1539 String provider = location.getProvider();
Jeff Sharkey5e613312012-01-30 11:16:20 -08001540
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001541 if (!passive) {
1542 // notify passive provider of the new location
1543 mPassiveProvider.updateLocation(location);
1544 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001545
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001546 synchronized (mLock) {
1547 if (isAllowedBySettingsLocked(provider)) {
1548 handleLocationChangedLocked(location, passive);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001549 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001550 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001551 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001552
Mike Lockwoode97ae402010-09-29 15:23:46 -04001553 private final PackageMonitor mPackageMonitor = new PackageMonitor() {
1554 @Override
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001555 public void onPackageDisappeared(String packageName, int reason) {
1556 // remove all receivers associated with this package name
1557 synchronized (mLock) {
1558 ArrayList<Receiver> deadReceivers = null;
1559
1560 for (Receiver receiver : mReceivers.values()) {
1561 if (receiver.mPackageName.equals(packageName)) {
1562 if (deadReceivers == null) {
1563 deadReceivers = new ArrayList<Receiver>();
1564 }
1565 deadReceivers.add(receiver);
1566 }
1567 }
1568
1569 // perform removal outside of mReceivers loop
1570 if (deadReceivers != null) {
1571 for (Receiver receiver : deadReceivers) {
1572 removeUpdatesLocked(receiver);
1573 }
1574 }
1575 }
Nick Pellye0fd6932012-07-11 10:26:13 -07001576 }
Mike Lockwoode97ae402010-09-29 15:23:46 -04001577 };
1578
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001579 // Wake locks
1580
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001581 private void incrementPendingBroadcasts() {
1582 synchronized (mWakeLock) {
1583 if (mPendingBroadcasts++ == 0) {
1584 try {
1585 mWakeLock.acquire();
1586 log("Acquired wakelock");
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 acquireWakeLock()", e);
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001591 }
1592 }
Mike Lockwood48f17512009-04-23 09:12:08 -07001593 }
1594 }
1595
1596 private void decrementPendingBroadcasts() {
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001597 synchronized (mWakeLock) {
Mike Lockwood48f17512009-04-23 09:12:08 -07001598 if (--mPendingBroadcasts == 0) {
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001599 try {
1600 // Release wake lock
1601 if (mWakeLock.isHeld()) {
1602 mWakeLock.release();
1603 log("Released wakelock");
1604 } else {
1605 log("Can't release wakelock again!");
1606 }
1607 } catch (Exception e) {
1608 // This is to catch a runtime exception thrown when we try to release an
1609 // already released lock.
Joe Onorato8a9b2202010-02-26 18:56:32 -08001610 Slog.e(TAG, "exception in releaseWakeLock()", e);
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001611 }
Mike Lockwood48f17512009-04-23 09:12:08 -07001612 }
1613 }
1614 }
1615
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001616 // Geocoder
1617
Nick Pellye0fd6932012-07-11 10:26:13 -07001618 @Override
Mike Lockwoode15735a2010-09-20 17:48:47 -04001619 public boolean geocoderIsPresent() {
Mark Vandevoorde01ac80b2010-05-21 15:43:26 -07001620 return mGeocodeProvider != null;
1621 }
1622
Nick Pellye0fd6932012-07-11 10:26:13 -07001623 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001624 public String getFromLocation(double latitude, double longitude, int maxResults,
Mike Lockwood34901402010-01-04 12:14:21 -05001625 GeocoderParams params, List<Address> addrs) {
Mike Lockwooda55c3212009-04-15 11:10:11 -04001626 if (mGeocodeProvider != null) {
Mike Lockwood628fd6d2010-01-25 22:46:13 -05001627 return mGeocodeProvider.getFromLocation(latitude, longitude, maxResults,
1628 params, addrs);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001629 }
Mike Lockwooda55c3212009-04-15 11:10:11 -04001630 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001631 }
1632
Mike Lockwooda55c3212009-04-15 11:10:11 -04001633
Nick Pellye0fd6932012-07-11 10:26:13 -07001634 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001635 public String getFromLocationName(String locationName,
Mike Lockwooda55c3212009-04-15 11:10:11 -04001636 double lowerLeftLatitude, double lowerLeftLongitude,
1637 double upperRightLatitude, double upperRightLongitude, int maxResults,
Mike Lockwood34901402010-01-04 12:14:21 -05001638 GeocoderParams params, List<Address> addrs) {
Mike Lockwooda55c3212009-04-15 11:10:11 -04001639
1640 if (mGeocodeProvider != null) {
Mike Lockwood628fd6d2010-01-25 22:46:13 -05001641 return mGeocodeProvider.getFromLocationName(locationName, lowerLeftLatitude,
1642 lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
1643 maxResults, params, addrs);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001644 }
Mike Lockwooda55c3212009-04-15 11:10:11 -04001645 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001646 }
1647
1648 // Mock Providers
1649
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001650 private void checkMockPermissionsSafe() {
1651 boolean allowMocks = Settings.Secure.getInt(mContext.getContentResolver(),
1652 Settings.Secure.ALLOW_MOCK_LOCATION, 0) == 1;
1653 if (!allowMocks) {
1654 throw new SecurityException("Requires ACCESS_MOCK_LOCATION secure setting");
1655 }
1656
1657 if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) !=
1658 PackageManager.PERMISSION_GRANTED) {
1659 throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission");
Nick Pellye0fd6932012-07-11 10:26:13 -07001660 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001661 }
1662
Nick Pellye0fd6932012-07-11 10:26:13 -07001663 @Override
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001664 public void addTestProvider(String name, ProviderProperties properties) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001665 checkMockPermissionsSafe();
1666
Mike Lockwooda4903f252010-02-17 06:42:23 -05001667 if (LocationManager.PASSIVE_PROVIDER.equals(name)) {
1668 throw new IllegalArgumentException("Cannot mock the passive location provider");
1669 }
1670
Mike Lockwood86328a92009-10-23 08:38:25 -04001671 long identity = Binder.clearCallingIdentity();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001672 synchronized (mLock) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001673 MockProvider provider = new MockProvider(name, this, properties);
Mike Lockwood7566c1d2009-08-25 10:05:18 -07001674 // remove the real provider if we are replacing GPS or network provider
1675 if (LocationManager.GPS_PROVIDER.equals(name)
Nick Pelly1332b532012-08-21 16:25:47 -07001676 || LocationManager.NETWORK_PROVIDER.equals(name)
1677 || LocationManager.FUSED_PROVIDER.equals(name)) {
Mike Lockwoodd03ff942010-02-09 08:46:14 -05001678 LocationProviderInterface p = mProvidersByName.get(name);
1679 if (p != null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001680 removeProviderLocked(p);
Mike Lockwood7566c1d2009-08-25 10:05:18 -07001681 }
1682 }
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001683 if (mProvidersByName.get(name) != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001684 throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
1685 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001686 addProviderLocked(provider);
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001687 mMockProviders.put(name, provider);
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001688 mLastLocation.put(name, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001689 updateProvidersLocked();
1690 }
Mike Lockwood86328a92009-10-23 08:38:25 -04001691 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001692 }
1693
Nick Pellye0fd6932012-07-11 10:26:13 -07001694 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001695 public void removeTestProvider(String provider) {
1696 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001697 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001698 MockProvider mockProvider = mMockProviders.get(provider);
1699 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001700 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1701 }
Mike Lockwood86328a92009-10-23 08:38:25 -04001702 long identity = Binder.clearCallingIdentity();
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001703 removeProviderLocked(mProvidersByName.get(provider));
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001704 mMockProviders.remove(mockProvider);
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001705
1706 // reinstate real provider if available
1707 LocationProviderInterface realProvider = mRealProviders.get(provider);
1708 if (realProvider != null) {
1709 addProviderLocked(realProvider);
Mike Lockwood7566c1d2009-08-25 10:05:18 -07001710 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001711 mLastLocation.put(provider, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001712 updateProvidersLocked();
Mike Lockwood86328a92009-10-23 08:38:25 -04001713 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001714 }
1715 }
1716
Nick Pellye0fd6932012-07-11 10:26:13 -07001717 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001718 public void setTestProviderLocation(String provider, Location loc) {
1719 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001720 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001721 MockProvider mockProvider = mMockProviders.get(provider);
1722 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001723 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1724 }
Mike Lockwood95427cd2009-05-07 13:27:54 -04001725 // clear calling identity so INSTALL_LOCATION_PROVIDER permission is not required
1726 long identity = Binder.clearCallingIdentity();
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001727 mockProvider.setLocation(loc);
Mike Lockwood95427cd2009-05-07 13:27:54 -04001728 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001729 }
1730 }
1731
Nick Pellye0fd6932012-07-11 10:26:13 -07001732 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001733 public void clearTestProviderLocation(String provider) {
1734 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001735 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001736 MockProvider mockProvider = mMockProviders.get(provider);
1737 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001738 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1739 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001740 mockProvider.clearLocation();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001741 }
1742 }
1743
Nick Pellye0fd6932012-07-11 10:26:13 -07001744 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001745 public void setTestProviderEnabled(String provider, boolean enabled) {
1746 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001747 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001748 MockProvider mockProvider = mMockProviders.get(provider);
1749 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001750 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1751 }
Mike Lockwood86328a92009-10-23 08:38:25 -04001752 long identity = Binder.clearCallingIdentity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001753 if (enabled) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001754 mockProvider.enable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001755 mEnabledProviders.add(provider);
1756 mDisabledProviders.remove(provider);
1757 } else {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001758 mockProvider.disable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001759 mEnabledProviders.remove(provider);
1760 mDisabledProviders.add(provider);
1761 }
1762 updateProvidersLocked();
Mike Lockwood86328a92009-10-23 08:38:25 -04001763 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001764 }
1765 }
1766
Nick Pellye0fd6932012-07-11 10:26:13 -07001767 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001768 public void clearTestProviderEnabled(String provider) {
1769 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001770 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001771 MockProvider mockProvider = mMockProviders.get(provider);
1772 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001773 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1774 }
Mike Lockwood86328a92009-10-23 08:38:25 -04001775 long identity = Binder.clearCallingIdentity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001776 mEnabledProviders.remove(provider);
1777 mDisabledProviders.remove(provider);
1778 updateProvidersLocked();
Mike Lockwood86328a92009-10-23 08:38:25 -04001779 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001780 }
1781 }
1782
Nick Pellye0fd6932012-07-11 10:26:13 -07001783 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001784 public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
1785 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001786 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001787 MockProvider mockProvider = mMockProviders.get(provider);
1788 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001789 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1790 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001791 mockProvider.setStatus(status, extras, updateTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001792 }
1793 }
1794
Nick Pellye0fd6932012-07-11 10:26:13 -07001795 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001796 public void clearTestProviderStatus(String provider) {
1797 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001798 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001799 MockProvider mockProvider = mMockProviders.get(provider);
1800 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001801 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1802 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001803 mockProvider.clearStatus();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001804 }
1805 }
1806
1807 private void log(String log) {
1808 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001809 Slog.d(TAG, log);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001810 }
1811 }
Nick Pellye0fd6932012-07-11 10:26:13 -07001812
1813 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001814 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1815 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1816 != PackageManager.PERMISSION_GRANTED) {
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001817 pw.println("Permission Denial: can't dump LocationManagerService from from pid="
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001818 + Binder.getCallingPid()
1819 + ", uid=" + Binder.getCallingUid());
1820 return;
1821 }
Nick Pellye0fd6932012-07-11 10:26:13 -07001822
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001823 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001824 pw.println("Current Location Manager state:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001825 pw.println(" Location Listeners:");
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001826 for (Receiver receiver : mReceivers.values()) {
1827 pw.println(" " + receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001828 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001829 pw.println(" Records by Provider:");
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001830 for (Map.Entry<String, ArrayList<UpdateRecord>> entry : mRecordsByProvider.entrySet()) {
1831 pw.println(" " + entry.getKey() + ":");
1832 for (UpdateRecord record : entry.getValue()) {
1833 pw.println(" " + record);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001834 }
1835 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001836 pw.println(" Last Known Locations:");
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001837 for (Map.Entry<String, Location> entry : mLastLocation.entrySet()) {
1838 String provider = entry.getKey();
1839 Location location = entry.getValue();
1840 pw.println(" " + provider + ": " + location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001841 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001842
Nick Pellye0fd6932012-07-11 10:26:13 -07001843 mGeofenceManager.dump(pw);
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001844
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001845 if (mEnabledProviders.size() > 0) {
1846 pw.println(" Enabled Providers:");
1847 for (String i : mEnabledProviders) {
1848 pw.println(" " + i);
1849 }
Nick Pellye0fd6932012-07-11 10:26:13 -07001850
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001851 }
1852 if (mDisabledProviders.size() > 0) {
1853 pw.println(" Disabled Providers:");
1854 for (String i : mDisabledProviders) {
1855 pw.println(" " + i);
1856 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001857 }
Nick Pelly4035f5a2012-08-17 14:43:49 -07001858 pw.append(" ");
1859 mBlacklist.dump(pw);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001860 if (mMockProviders.size() > 0) {
1861 pw.println(" Mock Providers:");
1862 for (Map.Entry<String, MockProvider> i : mMockProviders.entrySet()) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001863 i.getValue().dump(pw, " ");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001864 }
1865 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001866
Nick Pelly74fa7ea2012-08-13 19:36:38 -07001867 pw.append(" fudger: ");
1868 mLocationFudger.dump(fd, pw, args);
1869
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001870 if (args.length > 0 && "short".equals(args[0])) {
1871 return;
1872 }
Fred Fettinger3c8fbdf2010-01-04 15:38:13 -06001873 for (LocationProviderInterface provider: mProviders) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001874 pw.print(provider.getName() + " Internal State");
1875 if (provider instanceof LocationProviderProxy) {
1876 LocationProviderProxy proxy = (LocationProviderProxy) provider;
1877 pw.print(" (" + proxy.getConnectedPackageName() + ")");
Fred Fettinger3c8fbdf2010-01-04 15:38:13 -06001878 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001879 pw.println(":");
1880 provider.dump(fd, pw, args);
Fred Fettinger3c8fbdf2010-01-04 15:38:13 -06001881 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001882 }
1883 }
1884}