blob: e73d599297aa04333529dbee93e93998597c1b10 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080019import android.app.PendingIntent;
Victoria Lease38389b62012-09-30 11:44:22 -070020import android.content.BroadcastReceiver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080021import android.content.ContentResolver;
22import android.content.Context;
23import android.content.Intent;
Victoria Lease38389b62012-09-30 11:44:22 -070024import android.content.IntentFilter;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070025import android.content.pm.ApplicationInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026import android.content.pm.PackageManager;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070027import android.content.pm.PackageManager.NameNotFoundException;
Mike Lockwood628fd6d2010-01-25 22:46:13 -050028import android.content.res.Resources;
Brian Muramatsubb95cb92012-08-29 10:43:21 -070029import android.database.ContentObserver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import android.location.Address;
Mike Lockwood03ca2162010-04-01 08:10:09 -070031import android.location.Criteria;
Mike Lockwood34901402010-01-04 12:14:21 -050032import android.location.GeocoderParams;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070033import android.location.Geofence;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034import android.location.IGpsStatusListener;
Mike Lockwood15e3d0f2009-05-01 07:53:28 -040035import android.location.IGpsStatusProvider;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036import android.location.ILocationListener;
37import android.location.ILocationManager;
Danke Xie22d1f9f2009-08-18 18:28:45 -040038import android.location.INetInitiatedListener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039import android.location.Location;
40import android.location.LocationManager;
41import android.location.LocationProvider;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070042import android.location.LocationRequest;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043import android.os.Binder;
44import android.os.Bundle;
45import android.os.Handler;
46import android.os.IBinder;
Mike Lockwood3d12b512009-04-21 23:25:35 -070047import android.os.Looper;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048import android.os.Message;
49import android.os.PowerManager;
Mike Lockwoode932f7f2009-04-06 10:51:26 -070050import android.os.Process;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051import android.os.RemoteException;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070052import android.os.SystemClock;
Dianne Hackborn5ac72a22012-08-29 18:32:08 -070053import android.os.UserHandle;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070054import android.os.WorkSource;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055import android.provider.Settings;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056import android.util.Log;
Joe Onorato8a9b2202010-02-26 18:56:32 -080057import android.util.Slog;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058
Mike Lockwoode97ae402010-09-29 15:23:46 -040059import com.android.internal.content.PackageMonitor;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070060import com.android.internal.location.ProviderProperties;
61import com.android.internal.location.ProviderRequest;
Mike Lockwood43e33f22010-03-26 10:41:48 -040062import com.android.server.location.GeocoderProxy;
Nick Pellye0fd6932012-07-11 10:26:13 -070063import com.android.server.location.GeofenceManager;
Mike Lockwood43e33f22010-03-26 10:41:48 -040064import com.android.server.location.GpsLocationProvider;
Nick Pelly4035f5a2012-08-17 14:43:49 -070065import com.android.server.location.LocationBlacklist;
Nick Pelly74fa7ea2012-08-13 19:36:38 -070066import com.android.server.location.LocationFudger;
Mike Lockwood43e33f22010-03-26 10:41:48 -040067import com.android.server.location.LocationProviderInterface;
68import com.android.server.location.LocationProviderProxy;
69import com.android.server.location.MockProvider;
70import com.android.server.location.PassiveProvider;
71
72import java.io.FileDescriptor;
73import java.io.PrintWriter;
74import java.util.ArrayList;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070075import java.util.Arrays;
Mike Lockwood43e33f22010-03-26 10:41:48 -040076import java.util.HashMap;
77import java.util.HashSet;
78import java.util.List;
79import java.util.Map;
Mike Lockwood43e33f22010-03-26 10:41:48 -040080import java.util.Set;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081
82/**
83 * The service class that manages LocationProviders and issues location
84 * updates and alerts.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080085 */
Brian Muramatsubb95cb92012-08-29 10:43:21 -070086public class LocationManagerService extends ILocationManager.Stub implements Runnable {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080087 private static final String TAG = "LocationManagerService";
Nick Pelly6fa9ad42012-07-16 12:18:23 -070088 public static final boolean D = false;
89
90 private static final String WAKELOCK_KEY = TAG;
91 private static final String THREAD_NAME = TAG;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080092
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080093 private static final String ACCESS_FINE_LOCATION =
Nick Pelly6fa9ad42012-07-16 12:18:23 -070094 android.Manifest.permission.ACCESS_FINE_LOCATION;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080095 private static final String ACCESS_COARSE_LOCATION =
Nick Pelly6fa9ad42012-07-16 12:18:23 -070096 android.Manifest.permission.ACCESS_COARSE_LOCATION;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080097 private static final String ACCESS_MOCK_LOCATION =
Nick Pelly6fa9ad42012-07-16 12:18:23 -070098 android.Manifest.permission.ACCESS_MOCK_LOCATION;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080099 private static final String ACCESS_LOCATION_EXTRA_COMMANDS =
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700100 android.Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS;
Mike Lockwood275555c2009-05-01 11:30:34 -0400101 private static final String INSTALL_LOCATION_PROVIDER =
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700102 android.Manifest.permission.INSTALL_LOCATION_PROVIDER;
103
104 private static final String NETWORK_LOCATION_SERVICE_ACTION =
105 "com.android.location.service.v2.NetworkLocationProvider";
106 private static final String FUSED_LOCATION_SERVICE_ACTION =
107 "com.android.location.service.FusedLocationProvider";
108
109 private static final int MSG_LOCATION_CHANGED = 1;
110
Nick Pellyf1be6862012-05-15 10:53:42 -0700111 // Location Providers may sometimes deliver location updates
112 // slightly faster that requested - provide grace period so
113 // we don't unnecessarily filter events that are otherwise on
114 // time
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700115 private static final int MAX_PROVIDER_SCHEDULING_JITTER_MS = 100;
Nick Pellyf1be6862012-05-15 10:53:42 -0700116
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700117 private static final LocationRequest DEFAULT_LOCATION_REQUEST = new LocationRequest();
118
119 private final Context mContext;
120
121 // used internally for synchronization
122 private final Object mLock = new Object();
123
124 // --- fields below are final after init() ---
Nick Pelly74fa7ea2012-08-13 19:36:38 -0700125 private LocationFudger mLocationFudger;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700126 private GeofenceManager mGeofenceManager;
127 private PowerManager.WakeLock mWakeLock;
128 private PackageManager mPackageManager;
129 private GeocoderProxy mGeocodeProvider;
130 private IGpsStatusProvider mGpsStatusProvider;
131 private INetInitiatedListener mNetInitiatedListener;
132 private LocationWorkerHandler mLocationHandler;
Nick Pelly4035f5a2012-08-17 14:43:49 -0700133 private PassiveProvider mPassiveProvider; // track passive provider for special cases
134 private LocationBlacklist mBlacklist;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700135
136 // --- fields below are protected by mWakeLock ---
137 private int mPendingBroadcasts;
138
139 // --- fields below are protected by mLock ---
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800140 // Set of providers that are explicitly enabled
141 private final Set<String> mEnabledProviders = new HashSet<String>();
142
143 // Set of providers that are explicitly disabled
144 private final Set<String> mDisabledProviders = new HashSet<String>();
145
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700146 // Mock (test) providers
147 private final HashMap<String, MockProvider> mMockProviders =
148 new HashMap<String, MockProvider>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800149
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700150 // all receivers
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400151 private final HashMap<Object, Receiver> mReceivers = new HashMap<Object, Receiver>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800152
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700153 // currently installed providers (with mocks replacing real providers)
Mike Lockwoodd03ff942010-02-09 08:46:14 -0500154 private final ArrayList<LocationProviderInterface> mProviders =
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700155 new ArrayList<LocationProviderInterface>();
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400156
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700157 // real providers, saved here when mocked out
158 private final HashMap<String, LocationProviderInterface> mRealProviders =
159 new HashMap<String, LocationProviderInterface>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800160
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700161 // mapping from provider name to provider
162 private final HashMap<String, LocationProviderInterface> mProvidersByName =
163 new HashMap<String, LocationProviderInterface>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800164
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700165 // mapping from provider name to all its UpdateRecords
166 private final HashMap<String, ArrayList<UpdateRecord>> mRecordsByProvider =
167 new HashMap<String, ArrayList<UpdateRecord>>();
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700168
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700169 // mapping from provider name to last known location
170 private final HashMap<String, Location> mLastLocation = new HashMap<String, Location>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800171
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700172 // all providers that operate over proxy, for authorizing incoming location
173 private final ArrayList<LocationProviderProxy> mProxyProviders =
174 new ArrayList<LocationProviderProxy>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800175
Victoria Lease38389b62012-09-30 11:44:22 -0700176 // current active user on the device - other users are denied location data
177 private int mCurrentUserId = UserHandle.USER_OWNER;
178
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700179 public LocationManagerService(Context context) {
180 super();
181 mContext = context;
The Android Open Source Project4df24232009-03-05 14:34:35 -0800182
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700183 if (D) Log.d(TAG, "Constructed");
184
185 // most startup is deferred until systemReady()
186 }
187
188 public void systemReady() {
189 Thread thread = new Thread(null, this, THREAD_NAME);
190 thread.start();
191 }
192
193 @Override
194 public void run() {
195 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
196 Looper.prepare();
197 mLocationHandler = new LocationWorkerHandler();
198 init();
199 Looper.loop();
200 }
201
202 private void init() {
203 if (D) Log.d(TAG, "init()");
204
205 PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
206 mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
207 mPackageManager = mContext.getPackageManager();
208
Brian Muramatsubb95cb92012-08-29 10:43:21 -0700209 mBlacklist = new LocationBlacklist(mContext, mLocationHandler);
210 mBlacklist.init();
Victoria Leasedf9ec612012-09-11 15:16:25 -0700211 mLocationFudger = new LocationFudger(mContext, mLocationHandler);
Brian Muramatsubb95cb92012-08-29 10:43:21 -0700212
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700213 synchronized (mLock) {
214 loadProvidersLocked();
215 }
Brian Muramatsubb95cb92012-08-29 10:43:21 -0700216
Nick Pelly4035f5a2012-08-17 14:43:49 -0700217 mGeofenceManager = new GeofenceManager(mContext, mBlacklist);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700218
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700219 // listen for settings changes
Brian Muramatsubb95cb92012-08-29 10:43:21 -0700220 mContext.getContentResolver().registerContentObserver(
221 Settings.Secure.getUriFor(Settings.Secure.LOCATION_PROVIDERS_ALLOWED), true,
222 new ContentObserver(mLocationHandler) {
223 @Override
224 public void onChange(boolean selfChange) {
225 synchronized (mLock) {
226 updateProvidersLocked();
227 }
228 }
229 });
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700230 mPackageMonitor.register(mContext, Looper.myLooper(), true);
Brian Muramatsubb95cb92012-08-29 10:43:21 -0700231
Victoria Lease38389b62012-09-30 11:44:22 -0700232 // listen for user change
233 IntentFilter intentFilter = new IntentFilter();
234 intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
235
236 mContext.registerReceiverAsUser(new BroadcastReceiver() {
237 @Override
238 public void onReceive(Context context, Intent intent) {
239 String action = intent.getAction();
240 if (Intent.ACTION_USER_SWITCHED.equals(action)) {
241 switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
242 }
243 }
244 }, UserHandle.ALL, intentFilter, null, null);
245
Brian Muramatsubb95cb92012-08-29 10:43:21 -0700246 updateProvidersLocked();
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700247 }
248
249 private void loadProvidersLocked() {
Victoria Lease5c24fd02012-10-01 11:00:50 -0700250 // create a passive location provider, which is always enabled
251 PassiveProvider passiveProvider = new PassiveProvider(this);
252 addProviderLocked(passiveProvider);
253 mEnabledProviders.add(passiveProvider.getName());
254 mPassiveProvider = passiveProvider;
255
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700256 if (GpsLocationProvider.isSupported()) {
257 // Create a gps location provider
258 GpsLocationProvider gpsProvider = new GpsLocationProvider(mContext, this);
259 mGpsStatusProvider = gpsProvider.getGpsStatusProvider();
260 mNetInitiatedListener = gpsProvider.getNetInitiatedListener();
261 addProviderLocked(gpsProvider);
262 mRealProviders.put(LocationManager.GPS_PROVIDER, gpsProvider);
263 }
264
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700265 /*
266 Load package name(s) containing location provider support.
267 These packages can contain services implementing location providers:
268 Geocoder Provider, Network Location Provider, and
269 Fused Location Provider. They will each be searched for
270 service components implementing these providers.
271 The location framework also has support for installation
272 of new location providers at run-time. The new package does not
273 have to be explicitly listed here, however it must have a signature
274 that matches the signature of at least one package on this list.
275 */
276 Resources resources = mContext.getResources();
277 ArrayList<String> providerPackageNames = new ArrayList<String>();
278 String[] pkgs1 = resources.getStringArray(
279 com.android.internal.R.array.config_locationProviderPackageNames);
280 String[] pkgs2 = resources.getStringArray(
281 com.android.internal.R.array.config_overlay_locationProviderPackageNames);
282 if (D) Log.d(TAG, "built-in location providers: " + Arrays.toString(pkgs1));
283 if (D) Log.d(TAG, "overlay location providers: " + Arrays.toString(pkgs2));
284 if (pkgs1 != null) providerPackageNames.addAll(Arrays.asList(pkgs1));
285 if (pkgs2 != null) providerPackageNames.addAll(Arrays.asList(pkgs2));
286
287 // bind to network provider
288 LocationProviderProxy networkProvider = LocationProviderProxy.createAndBind(
289 mContext,
290 LocationManager.NETWORK_PROVIDER,
291 NETWORK_LOCATION_SERVICE_ACTION,
292 providerPackageNames, mLocationHandler);
293 if (networkProvider != null) {
294 mRealProviders.put(LocationManager.NETWORK_PROVIDER, networkProvider);
295 mProxyProviders.add(networkProvider);
296 addProviderLocked(networkProvider);
297 } else {
298 Slog.w(TAG, "no network location provider found");
299 }
300
301 // bind to fused provider
302 LocationProviderProxy fusedLocationProvider = LocationProviderProxy.createAndBind(
303 mContext,
304 LocationManager.FUSED_PROVIDER,
305 FUSED_LOCATION_SERVICE_ACTION,
306 providerPackageNames, mLocationHandler);
307 if (fusedLocationProvider != null) {
308 addProviderLocked(fusedLocationProvider);
309 mProxyProviders.add(fusedLocationProvider);
310 mEnabledProviders.add(fusedLocationProvider.getName());
311 } else {
312 Slog.e(TAG, "no fused location provider found",
313 new IllegalStateException("Location service needs a fused location provider"));
314 }
315
316 // bind to geocoder provider
317 mGeocodeProvider = GeocoderProxy.createAndBind(mContext, providerPackageNames);
318 if (mGeocodeProvider == null) {
319 Slog.e(TAG, "no geocoder provider found");
320 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700321 }
Mike Lockwood9637d472009-04-02 21:41:57 -0700322
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800323 /**
Victoria Lease38389b62012-09-30 11:44:22 -0700324 * Called when the device's active user changes.
325 * @param userId the new active user's UserId
326 */
327 private void switchUser(int userId) {
Victoria Lease83762d22012-10-03 13:51:17 -0700328 mBlacklist.switchUser(userId);
Victoria Lease38389b62012-09-30 11:44:22 -0700329 //Log.d("LocationManagerService", "switchUser(" + mCurrentUserId + " -> " + userId + ")"); // TODO: remove this
330 synchronized (mLock) {
331 // TODO: inform previous user's Receivers that they will no longer receive updates
332 mCurrentUserId = userId;
333 // TODO: inform new user's Receivers that they are back on the update train
334 }
335 }
336
337 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800338 * A wrapper class holding either an ILocationListener or a PendingIntent to receive
339 * location updates.
340 */
Mike Lockwood48f17512009-04-23 09:12:08 -0700341 private final class Receiver implements IBinder.DeathRecipient, PendingIntent.OnFinished {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700342 final int mUid; // uid of receiver
343 final int mPid; // pid of receiver
344 final String mPackageName; // package name of receiver
345 final String mPermission; // best permission that receiver has
346
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800347 final ILocationListener mListener;
348 final PendingIntent mPendingIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800349 final Object mKey;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700350
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400351 final HashMap<String,UpdateRecord> mUpdateRecords = new HashMap<String,UpdateRecord>();
Nick Pellyf1be6862012-05-15 10:53:42 -0700352
Mike Lockwood48f17512009-04-23 09:12:08 -0700353 int mPendingBroadcasts;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800354
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700355 Receiver(ILocationListener listener, PendingIntent intent, int pid, int uid,
356 String packageName) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800357 mListener = listener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800358 mPendingIntent = intent;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700359 if (listener != null) {
360 mKey = listener.asBinder();
361 } else {
362 mKey = intent;
363 }
364 mPermission = checkPermission();
365 mUid = uid;
366 mPid = pid;
367 mPackageName = packageName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800368 }
369
370 @Override
371 public boolean equals(Object otherObj) {
372 if (otherObj instanceof Receiver) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700373 return mKey.equals(((Receiver)otherObj).mKey);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800374 }
375 return false;
376 }
377
378 @Override
379 public int hashCode() {
380 return mKey.hashCode();
381 }
Mike Lockwood3681f262009-05-12 10:52:03 -0400382
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800383 @Override
384 public String toString() {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700385 StringBuilder s = new StringBuilder();
386 s.append("Reciever[");
387 s.append(Integer.toHexString(System.identityHashCode(this)));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800388 if (mListener != null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700389 s.append(" listener");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800390 } else {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700391 s.append(" intent");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800392 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700393 for (String p : mUpdateRecords.keySet()) {
394 s.append(" ").append(mUpdateRecords.get(p).toString());
395 }
396 s.append("]");
397 return s.toString();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800398 }
399
400 public boolean isListener() {
401 return mListener != null;
402 }
403
404 public boolean isPendingIntent() {
405 return mPendingIntent != null;
406 }
407
408 public ILocationListener getListener() {
409 if (mListener != null) {
410 return mListener;
411 }
412 throw new IllegalStateException("Request for non-existent listener");
413 }
414
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800415 public boolean callStatusChangedLocked(String provider, int status, Bundle extras) {
416 if (mListener != null) {
417 try {
Mike Lockwood48f17512009-04-23 09:12:08 -0700418 synchronized (this) {
419 // synchronize to ensure incrementPendingBroadcastsLocked()
420 // is called before decrementPendingBroadcasts()
421 mListener.onStatusChanged(provider, status, extras);
Nick Pellye0fd6932012-07-11 10:26:13 -0700422 // call this after broadcasting so we do not increment
423 // if we throw an exeption.
424 incrementPendingBroadcastsLocked();
Mike Lockwood48f17512009-04-23 09:12:08 -0700425 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800426 } catch (RemoteException e) {
427 return false;
428 }
429 } else {
430 Intent statusChanged = new Intent();
431 statusChanged.putExtras(extras);
432 statusChanged.putExtra(LocationManager.KEY_STATUS_CHANGED, status);
433 try {
Mike Lockwood48f17512009-04-23 09:12:08 -0700434 synchronized (this) {
435 // synchronize to ensure incrementPendingBroadcastsLocked()
436 // is called before decrementPendingBroadcasts()
Dianne Hackborn6c418d52011-06-29 14:05:33 -0700437 mPendingIntent.send(mContext, 0, statusChanged, this, mLocationHandler,
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700438 mPermission);
Mike Lockwood48f17512009-04-23 09:12:08 -0700439 // call this after broadcasting so we do not increment
440 // if we throw an exeption.
441 incrementPendingBroadcastsLocked();
442 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800443 } catch (PendingIntent.CanceledException e) {
444 return false;
445 }
446 }
447 return true;
448 }
449
450 public boolean callLocationChangedLocked(Location location) {
451 if (mListener != null) {
452 try {
Mike Lockwood48f17512009-04-23 09:12:08 -0700453 synchronized (this) {
454 // synchronize to ensure incrementPendingBroadcastsLocked()
455 // is called before decrementPendingBroadcasts()
456 mListener.onLocationChanged(location);
Nick Pellye0fd6932012-07-11 10:26:13 -0700457 // call this after broadcasting so we do not increment
458 // if we throw an exeption.
459 incrementPendingBroadcastsLocked();
Mike Lockwood48f17512009-04-23 09:12:08 -0700460 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800461 } catch (RemoteException e) {
462 return false;
463 }
464 } else {
465 Intent locationChanged = new Intent();
466 locationChanged.putExtra(LocationManager.KEY_LOCATION_CHANGED, location);
467 try {
Mike Lockwood48f17512009-04-23 09:12:08 -0700468 synchronized (this) {
469 // synchronize to ensure incrementPendingBroadcastsLocked()
470 // is called before decrementPendingBroadcasts()
Dianne Hackborn6c418d52011-06-29 14:05:33 -0700471 mPendingIntent.send(mContext, 0, locationChanged, this, mLocationHandler,
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700472 mPermission);
Mike Lockwood48f17512009-04-23 09:12:08 -0700473 // call this after broadcasting so we do not increment
474 // if we throw an exeption.
475 incrementPendingBroadcastsLocked();
476 }
477 } catch (PendingIntent.CanceledException e) {
478 return false;
479 }
480 }
481 return true;
482 }
483
484 public boolean callProviderEnabledLocked(String provider, boolean enabled) {
485 if (mListener != null) {
486 try {
487 synchronized (this) {
488 // synchronize to ensure incrementPendingBroadcastsLocked()
489 // is called before decrementPendingBroadcasts()
490 if (enabled) {
491 mListener.onProviderEnabled(provider);
492 } else {
493 mListener.onProviderDisabled(provider);
494 }
Nick Pellye0fd6932012-07-11 10:26:13 -0700495 // call this after broadcasting so we do not increment
496 // if we throw an exeption.
497 incrementPendingBroadcastsLocked();
Mike Lockwood48f17512009-04-23 09:12:08 -0700498 }
499 } catch (RemoteException e) {
500 return false;
501 }
502 } else {
503 Intent providerIntent = new Intent();
504 providerIntent.putExtra(LocationManager.KEY_PROVIDER_ENABLED, enabled);
505 try {
506 synchronized (this) {
507 // synchronize to ensure incrementPendingBroadcastsLocked()
508 // is called before decrementPendingBroadcasts()
Dianne Hackborn6c418d52011-06-29 14:05:33 -0700509 mPendingIntent.send(mContext, 0, providerIntent, this, mLocationHandler,
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700510 mPermission);
Mike Lockwood48f17512009-04-23 09:12:08 -0700511 // call this after broadcasting so we do not increment
512 // if we throw an exeption.
513 incrementPendingBroadcastsLocked();
514 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800515 } catch (PendingIntent.CanceledException e) {
516 return false;
517 }
518 }
519 return true;
520 }
521
Nick Pellyf1be6862012-05-15 10:53:42 -0700522 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800523 public void binderDied() {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700524 if (D) Log.d(TAG, "Location listener died");
525
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400526 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800527 removeUpdatesLocked(this);
528 }
Mike Lockwood48f17512009-04-23 09:12:08 -0700529 synchronized (this) {
530 if (mPendingBroadcasts > 0) {
531 LocationManagerService.this.decrementPendingBroadcasts();
532 mPendingBroadcasts = 0;
533 }
534 }
535 }
536
Nick Pellye0fd6932012-07-11 10:26:13 -0700537 @Override
Mike Lockwood48f17512009-04-23 09:12:08 -0700538 public void onSendFinished(PendingIntent pendingIntent, Intent intent,
539 int resultCode, String resultData, Bundle resultExtras) {
Mike Lockwood0528b9b2009-05-07 10:12:54 -0400540 synchronized (this) {
541 decrementPendingBroadcastsLocked();
Mike Lockwood48f17512009-04-23 09:12:08 -0700542 }
543 }
544
Mike Lockwood0528b9b2009-05-07 10:12:54 -0400545 // this must be called while synchronized by caller in a synchronized block
546 // containing the sending of the broadcaset
547 private void incrementPendingBroadcastsLocked() {
548 if (mPendingBroadcasts++ == 0) {
549 LocationManagerService.this.incrementPendingBroadcasts();
550 }
551 }
552
553 private void decrementPendingBroadcastsLocked() {
554 if (--mPendingBroadcasts == 0) {
555 LocationManagerService.this.decrementPendingBroadcasts();
Mike Lockwood48f17512009-04-23 09:12:08 -0700556 }
557 }
558 }
559
Nick Pellye0fd6932012-07-11 10:26:13 -0700560 @Override
Mike Lockwood48f17512009-04-23 09:12:08 -0700561 public void locationCallbackFinished(ILocationListener listener) {
Joshua Bartel080b61b2009-10-05 12:44:46 -0400562 //Do not use getReceiver here as that will add the ILocationListener to
563 //the receiver list if it is not found. If it is not found then the
564 //LocationListener was removed when it had a pending broadcast and should
565 //not be added back.
566 IBinder binder = listener.asBinder();
567 Receiver receiver = mReceivers.get(binder);
Mike Lockwood48f17512009-04-23 09:12:08 -0700568 if (receiver != null) {
Mike Lockwood0528b9b2009-05-07 10:12:54 -0400569 synchronized (receiver) {
570 // so wakelock calls will succeed
571 long identity = Binder.clearCallingIdentity();
572 receiver.decrementPendingBroadcastsLocked();
573 Binder.restoreCallingIdentity(identity);
574 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800575 }
576 }
577
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700578 private void addProviderLocked(LocationProviderInterface provider) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400579 mProviders.add(provider);
580 mProvidersByName.put(provider.getName(), provider);
581 }
582
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700583 private void removeProviderLocked(LocationProviderInterface provider) {
584 provider.disable();
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400585 mProviders.remove(provider);
586 mProvidersByName.remove(provider.getName());
587 }
588
Mike Lockwood3d12b512009-04-21 23:25:35 -0700589
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800590 private boolean isAllowedBySettingsLocked(String provider) {
591 if (mEnabledProviders.contains(provider)) {
592 return true;
593 }
594 if (mDisabledProviders.contains(provider)) {
595 return false;
596 }
597 // Use system settings
598 ContentResolver resolver = mContext.getContentResolver();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800599
Brad Larson8eb3ea62009-12-29 11:47:55 -0600600 return Settings.Secure.isLocationProviderEnabled(resolver, provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800601 }
602
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700603 /**
604 * Throw SecurityException if caller has neither COARSE or FINE.
605 * Otherwise, return the best permission.
606 */
607 private String checkPermission() {
608 if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) ==
609 PackageManager.PERMISSION_GRANTED) {
Dianne Hackborn6c418d52011-06-29 14:05:33 -0700610 return ACCESS_FINE_LOCATION;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700611 } else if (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION) ==
612 PackageManager.PERMISSION_GRANTED) {
613 return ACCESS_COARSE_LOCATION;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800614 }
Dianne Hackborn6c418d52011-06-29 14:05:33 -0700615
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700616 throw new SecurityException("Location requires either ACCESS_COARSE_LOCATION or" +
Victoria Lease4fab68b2012-09-13 13:20:59 -0700617 " ACCESS_FINE_LOCATION permission");
618 }
619
620 /**
621 * Throw SecurityException if caller lacks permission to use Geofences.
622 */
623 private void checkGeofencePermission() {
624 if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) !=
625 PackageManager.PERMISSION_GRANTED) {
626 throw new SecurityException("Geofence usage requires ACCESS_FINE_LOCATION permission");
627 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800628 }
629
Victoria Lease8dbb6342012-09-21 16:55:53 -0700630 private boolean isAllowedProviderSafe(String provider) {
631 if (LocationManager.GPS_PROVIDER.equals(provider) ||
632 LocationManager.PASSIVE_PROVIDER.equals(provider)) {
633 // gps and passive providers require FINE permission
634 return mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
635 == PackageManager.PERMISSION_GRANTED;
636 } else if (LocationManager.NETWORK_PROVIDER.equals(provider) ||
637 LocationManager.FUSED_PROVIDER.equals(provider)) {
638 // network and fused providers are ok with COARSE or FINE
639 return (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
640 == PackageManager.PERMISSION_GRANTED) ||
641 (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION)
642 == PackageManager.PERMISSION_GRANTED);
Laurent Tu941221c2012-10-04 14:21:52 -0700643 } else {
644 // mock providers
645 LocationProviderInterface lp = mMockProviders.get(provider);
646 if (lp != null) {
647 ProviderProperties properties = lp.getProperties();
648 if (properties != null) {
649 if (properties.mRequiresSatellite) {
650 // provider requiring satellites require FINE permission
651 return mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
652 == PackageManager.PERMISSION_GRANTED;
653 } else if (properties.mRequiresNetwork || properties.mRequiresCell) {
654 // provider requiring network and or cell require COARSE or FINE
655 return (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
656 == PackageManager.PERMISSION_GRANTED) ||
657 (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION)
658 == PackageManager.PERMISSION_GRANTED);
659 }
660 }
661 }
Victoria Lease8dbb6342012-09-21 16:55:53 -0700662 }
Laurent Tu941221c2012-10-04 14:21:52 -0700663
Victoria Lease8dbb6342012-09-21 16:55:53 -0700664 return false;
665 }
666
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700667 /**
668 * Returns all providers by name, including passive, but excluding
Laurent Tu0d21e212012-10-02 15:33:48 -0700669 * fused, also including ones that are not permitted to
670 * be accessed by the calling activity or are currently disabled.
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700671 */
Nick Pellye0fd6932012-07-11 10:26:13 -0700672 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800673 public List<String> getAllProviders() {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700674 ArrayList<String> out;
675 synchronized (mLock) {
676 out = new ArrayList<String>(mProviders.size());
677 for (LocationProviderInterface provider : mProviders) {
678 String name = provider.getName();
679 if (LocationManager.FUSED_PROVIDER.equals(name)) {
Mike Lockwood03ca2162010-04-01 08:10:09 -0700680 continue;
681 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800682 out.add(name);
683 }
684 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700685
686 if (D) Log.d(TAG, "getAllProviders()=" + out);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800687 return out;
688 }
689
Mike Lockwood03ca2162010-04-01 08:10:09 -0700690 /**
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700691 * Return all providers by name, that match criteria and are optionally
692 * enabled.
693 * Can return passive provider, but never returns fused provider.
Mike Lockwood03ca2162010-04-01 08:10:09 -0700694 */
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700695 @Override
696 public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700697 ArrayList<String> out;
Mike Lockwood03ca2162010-04-01 08:10:09 -0700698 synchronized (mLock) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700699 out = new ArrayList<String>(mProviders.size());
700 for (LocationProviderInterface provider : mProviders) {
701 String name = provider.getName();
702 if (LocationManager.FUSED_PROVIDER.equals(name)) {
703 continue;
704 }
Victoria Lease8dbb6342012-09-21 16:55:53 -0700705 if (isAllowedProviderSafe(name)) {
706 if (enabledOnly && !isAllowedBySettingsLocked(name)) {
707 continue;
708 }
709 if (criteria != null && !LocationProvider.propertiesMeetCriteria(
710 name, provider.getProperties(), criteria)) {
711 continue;
712 }
713 out.add(name);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700714 }
Mike Lockwood03ca2162010-04-01 08:10:09 -0700715 }
716 }
717
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700718 if (D) Log.d(TAG, "getProviders()=" + out);
719 return out;
Mike Lockwood03ca2162010-04-01 08:10:09 -0700720 }
721
722 /**
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700723 * Return the name of the best provider given a Criteria object.
724 * This method has been deprecated from the public API,
Victoria Lease8dbb6342012-09-21 16:55:53 -0700725 * and the whole LocationProvider (including #meetsCriteria)
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700726 * has been deprecated as well. So this method now uses
727 * some simplified logic.
Mike Lockwood03ca2162010-04-01 08:10:09 -0700728 */
Nick Pellye0fd6932012-07-11 10:26:13 -0700729 @Override
Mike Lockwood03ca2162010-04-01 08:10:09 -0700730 public String getBestProvider(Criteria criteria, boolean enabledOnly) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700731 String result = null;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700732
733 List<String> providers = getProviders(criteria, enabledOnly);
Victoria Lease8dbb6342012-09-21 16:55:53 -0700734 if (!providers.isEmpty()) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700735 result = pickBest(providers);
736 if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
737 return result;
738 }
739 providers = getProviders(null, enabledOnly);
Victoria Lease8dbb6342012-09-21 16:55:53 -0700740 if (!providers.isEmpty()) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700741 result = pickBest(providers);
742 if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
743 return result;
Mike Lockwood03ca2162010-04-01 08:10:09 -0700744 }
745
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700746 if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
Mike Lockwood03ca2162010-04-01 08:10:09 -0700747 return null;
748 }
749
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700750 private String pickBest(List<String> providers) {
Victoria Lease1925e292012-09-24 17:00:18 -0700751 if (providers.contains(LocationManager.GPS_PROVIDER)) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700752 return LocationManager.GPS_PROVIDER;
Victoria Lease1925e292012-09-24 17:00:18 -0700753 } else if (providers.contains(LocationManager.NETWORK_PROVIDER)) {
754 return LocationManager.NETWORK_PROVIDER;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700755 } else {
756 return providers.get(0);
757 }
758 }
759
Nick Pellye0fd6932012-07-11 10:26:13 -0700760 @Override
Mike Lockwood03ca2162010-04-01 08:10:09 -0700761 public boolean providerMeetsCriteria(String provider, Criteria criteria) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700762 checkPermission();
763
Mike Lockwood03ca2162010-04-01 08:10:09 -0700764 LocationProviderInterface p = mProvidersByName.get(provider);
765 if (p == null) {
766 throw new IllegalArgumentException("provider=" + provider);
767 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700768
769 boolean result = LocationProvider.propertiesMeetCriteria(
770 p.getName(), p.getProperties(), criteria);
771 if (D) Log.d(TAG, "providerMeetsCriteria(" + provider + ", " + criteria + ")=" + result);
772 return result;
Mike Lockwood03ca2162010-04-01 08:10:09 -0700773 }
774
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800775 private void updateProvidersLocked() {
Brad Fitzpatrick0c5a0402010-08-27 14:01:23 -0700776 boolean changesMade = false;
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400777 for (int i = mProviders.size() - 1; i >= 0; i--) {
Mike Lockwoodd03ff942010-02-09 08:46:14 -0500778 LocationProviderInterface p = mProviders.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800779 boolean isEnabled = p.isEnabled();
780 String name = p.getName();
781 boolean shouldBeEnabled = isAllowedBySettingsLocked(name);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800782 if (isEnabled && !shouldBeEnabled) {
783 updateProviderListenersLocked(name, false);
Brad Fitzpatrick0c5a0402010-08-27 14:01:23 -0700784 changesMade = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800785 } else if (!isEnabled && shouldBeEnabled) {
786 updateProviderListenersLocked(name, true);
Brad Fitzpatrick0c5a0402010-08-27 14:01:23 -0700787 changesMade = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800788 }
Brad Fitzpatrick0c5a0402010-08-27 14:01:23 -0700789 }
790 if (changesMade) {
Dianne Hackborn5ac72a22012-08-29 18:32:08 -0700791 mContext.sendBroadcastAsUser(new Intent(LocationManager.PROVIDERS_CHANGED_ACTION),
792 UserHandle.ALL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800793 }
794 }
795
796 private void updateProviderListenersLocked(String provider, boolean enabled) {
797 int listeners = 0;
798
Mike Lockwoodd03ff942010-02-09 08:46:14 -0500799 LocationProviderInterface p = mProvidersByName.get(provider);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700800 if (p == null) return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800801
802 ArrayList<Receiver> deadReceivers = null;
Nick Pellye0fd6932012-07-11 10:26:13 -0700803
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800804 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
805 if (records != null) {
806 final int N = records.size();
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700807 for (int i = 0; i < N; i++) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800808 UpdateRecord record = records.get(i);
809 // Sends a notification message to the receiver
Mike Lockwood48f17512009-04-23 09:12:08 -0700810 if (!record.mReceiver.callProviderEnabledLocked(provider, enabled)) {
811 if (deadReceivers == null) {
812 deadReceivers = new ArrayList<Receiver>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800813 }
Simon Schoar46866572009-06-10 21:12:10 +0200814 deadReceivers.add(record.mReceiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800815 }
816 listeners++;
817 }
818 }
819
820 if (deadReceivers != null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700821 for (int i = deadReceivers.size() - 1; i >= 0; i--) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800822 removeUpdatesLocked(deadReceivers.get(i));
823 }
824 }
Nick Pellye0fd6932012-07-11 10:26:13 -0700825
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800826 if (enabled) {
827 p.enable();
828 if (listeners > 0) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700829 applyRequirementsLocked(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800830 }
831 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800832 p.disable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800833 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800834 }
835
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700836 private void applyRequirementsLocked(String provider) {
837 LocationProviderInterface p = mProvidersByName.get(provider);
838 if (p == null) return;
839
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800840 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700841 WorkSource worksource = new WorkSource();
842 ProviderRequest providerRequest = new ProviderRequest();
843
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800844 if (records != null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700845 for (UpdateRecord record : records) {
846 LocationRequest locationRequest = record.mRequest;
847
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700848 providerRequest.locationRequests.add(locationRequest);
849 if (locationRequest.getInterval() < providerRequest.interval) {
850 providerRequest.reportLocation = true;
851 providerRequest.interval = locationRequest.getInterval();
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700852 }
853 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700854
855 if (providerRequest.reportLocation) {
856 // calculate who to blame for power
857 // This is somewhat arbitrary. We pick a threshold interval
858 // that is slightly higher that the minimum interval, and
859 // spread the blame across all applications with a request
860 // under that threshold.
861 long thresholdInterval = (providerRequest.interval + 1000) * 3 / 2;
862 for (UpdateRecord record : records) {
863 LocationRequest locationRequest = record.mRequest;
864 if (locationRequest.getInterval() <= thresholdInterval) {
865 worksource.add(record.mReceiver.mUid);
866 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700867 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800868 }
869 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700870
871 if (D) Log.d(TAG, "provider request: " + provider + " " + providerRequest);
872 p.setRequest(providerRequest, worksource);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800873 }
874
875 private class UpdateRecord {
876 final String mProvider;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700877 final LocationRequest mRequest;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800878 final Receiver mReceiver;
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400879 Location mLastFixBroadcast;
880 long mLastStatusBroadcast;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800881
882 /**
883 * Note: must be constructed with lock held.
884 */
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700885 UpdateRecord(String provider, LocationRequest request, Receiver receiver) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800886 mProvider = provider;
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700887 mRequest = request;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800888 mReceiver = receiver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800889
890 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
891 if (records == null) {
892 records = new ArrayList<UpdateRecord>();
893 mRecordsByProvider.put(provider, records);
894 }
895 if (!records.contains(this)) {
896 records.add(this);
897 }
898 }
899
900 /**
901 * Method to be called when a record will no longer be used. Calling this multiple times
902 * must have the same effect as calling it once.
903 */
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700904 void disposeLocked(boolean removeReceiver) {
905 // remove from mRecordsByProvider
906 ArrayList<UpdateRecord> globalRecords = mRecordsByProvider.get(this.mProvider);
907 if (globalRecords != null) {
908 globalRecords.remove(this);
909 }
910
911 if (!removeReceiver) return; // the caller will handle the rest
912
913 // remove from Receiver#mUpdateRecords
914 HashMap<String, UpdateRecord> receiverRecords = mReceiver.mUpdateRecords;
915 if (receiverRecords != null) {
916 receiverRecords.remove(this.mProvider);
917
918 // and also remove the Receiver if it has no more update records
919 if (removeReceiver && receiverRecords.size() == 0) {
920 removeUpdatesLocked(mReceiver);
921 }
Mike Lockwood3a76fd62009-09-01 07:26:56 -0400922 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800923 }
924
925 @Override
926 public String toString() {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700927 StringBuilder s = new StringBuilder();
928 s.append("UpdateRecord[");
929 s.append(mProvider);
930 s.append(' ').append(mReceiver.mPackageName).append('(');
931 s.append(mReceiver.mUid).append(')');
932 s.append(' ').append(mRequest);
933 s.append(']');
934 return s.toString();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800935 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800936 }
937
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700938 private Receiver getReceiver(ILocationListener listener, int pid, int uid, String packageName) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400939 IBinder binder = listener.asBinder();
940 Receiver receiver = mReceivers.get(binder);
941 if (receiver == null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700942 receiver = new Receiver(listener, null, pid, uid, packageName);
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400943 mReceivers.put(binder, receiver);
944
945 try {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700946 receiver.getListener().asBinder().linkToDeath(receiver, 0);
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400947 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800948 Slog.e(TAG, "linkToDeath failed:", e);
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400949 return null;
950 }
951 }
952 return receiver;
953 }
954
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700955 private Receiver getReceiver(PendingIntent intent, int pid, int uid, String packageName) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400956 Receiver receiver = mReceivers.get(intent);
957 if (receiver == null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700958 receiver = new Receiver(null, intent, pid, uid, packageName);
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400959 mReceivers.put(intent, receiver);
960 }
961 return receiver;
962 }
963
Victoria Lease09016ab2012-09-16 12:33:15 -0700964 private boolean isProviderAllowedByCoarsePermission(String provider) {
965 if (LocationManager.FUSED_PROVIDER.equals(provider)) {
966 return true;
967 }
968 if (LocationManager.PASSIVE_PROVIDER.equals(provider)) {
969 return true;
970 }
971 if (LocationManager.NETWORK_PROVIDER.equals(provider)) {
972 return true;
973 }
974 return false;
975 }
976
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700977 private String checkPermissionAndRequest(LocationRequest request) {
978 String perm = checkPermission();
979
980 if (ACCESS_COARSE_LOCATION.equals(perm)) {
Victoria Lease09016ab2012-09-16 12:33:15 -0700981 if (!isProviderAllowedByCoarsePermission(request.getProvider())) {
982 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
983 }
984 switch (request.getQuality()) {
985 case LocationRequest.ACCURACY_FINE:
986 request.setQuality(LocationRequest.ACCURACY_BLOCK);
987 break;
988 case LocationRequest.POWER_HIGH:
989 request.setQuality(LocationRequest.POWER_LOW);
990 break;
991 }
992 // throttle
993 if (request.getInterval() < LocationFudger.FASTEST_INTERVAL_MS) {
994 request.setInterval(LocationFudger.FASTEST_INTERVAL_MS);
995 }
996 if (request.getFastestInterval() < LocationFudger.FASTEST_INTERVAL_MS) {
997 request.setFastestInterval(LocationFudger.FASTEST_INTERVAL_MS);
998 }
Nick Pelly74fa7ea2012-08-13 19:36:38 -0700999 }
Nick Pelly4e31c4f2012-08-13 19:35:39 -07001000 // make getFastestInterval() the minimum of interval and fastest interval
1001 if (request.getFastestInterval() > request.getInterval()) {
1002 request.setFastestInterval(request.getInterval());
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001003 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001004 return perm;
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001005 }
1006
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001007 private void checkPackageName(String packageName) {
Nick Pellye0fd6932012-07-11 10:26:13 -07001008 if (packageName == null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001009 throw new SecurityException("invalid package name: " + packageName);
Nick Pellye0fd6932012-07-11 10:26:13 -07001010 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001011 int uid = Binder.getCallingUid();
Nick Pellye0fd6932012-07-11 10:26:13 -07001012 String[] packages = mPackageManager.getPackagesForUid(uid);
1013 if (packages == null) {
1014 throw new SecurityException("invalid UID " + uid);
1015 }
1016 for (String pkg : packages) {
1017 if (packageName.equals(pkg)) return;
1018 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001019 throw new SecurityException("invalid package name: " + packageName);
Nick Pellye0fd6932012-07-11 10:26:13 -07001020 }
1021
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001022 private void checkPendingIntent(PendingIntent intent) {
1023 if (intent == null) {
1024 throw new IllegalArgumentException("invalid pending intent: " + intent);
Dianne Hackborn6c418d52011-06-29 14:05:33 -07001025 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001026 }
1027
1028 private Receiver checkListenerOrIntent(ILocationListener listener, PendingIntent intent,
1029 int pid, int uid, String packageName) {
1030 if (intent == null && listener == null) {
1031 throw new IllegalArgumentException("need eiter listener or intent");
1032 } else if (intent != null && listener != null) {
1033 throw new IllegalArgumentException("cannot register both listener and intent");
1034 } else if (intent != null) {
1035 checkPendingIntent(intent);
1036 return getReceiver(intent, pid, uid, packageName);
1037 } else {
1038 return getReceiver(listener, pid, uid, packageName);
1039 }
Dianne Hackborn6c418d52011-06-29 14:05:33 -07001040 }
1041
Nick Pellye0fd6932012-07-11 10:26:13 -07001042 @Override
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001043 public void requestLocationUpdates(LocationRequest request, ILocationListener listener,
1044 PendingIntent intent, String packageName) {
1045 if (request == null) request = DEFAULT_LOCATION_REQUEST;
1046 checkPackageName(packageName);
1047 checkPermissionAndRequest(request);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001048
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001049 final int pid = Binder.getCallingPid();
1050 final int uid = Binder.getCallingUid();
1051 Receiver recevier = checkListenerOrIntent(listener, intent, pid, uid, packageName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001052
Nick Pelly2b7a0d02012-08-17 15:09:44 -07001053 // providers may use public location API's, need to clear identity
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001054 long identity = Binder.clearCallingIdentity();
1055 try {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001056 synchronized (mLock) {
1057 requestLocationUpdatesLocked(request, recevier, pid, uid, packageName);
Mike Lockwood2d4d1bf2010-10-18 17:06:26 -04001058 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001059 } finally {
1060 Binder.restoreCallingIdentity(identity);
1061 }
1062 }
1063
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001064 private void requestLocationUpdatesLocked(LocationRequest request, Receiver receiver,
1065 int pid, int uid, String packageName) {
1066 // Figure out the provider. Either its explicitly request (legacy use cases), or
1067 // use the fused provider
1068 if (request == null) request = DEFAULT_LOCATION_REQUEST;
1069 String name = request.getProvider();
Victoria Lease09016ab2012-09-16 12:33:15 -07001070 if (name == null) {
1071 throw new IllegalArgumentException("provider name must not be null");
1072 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001073 LocationProviderInterface provider = mProvidersByName.get(name);
1074 if (provider == null) {
1075 throw new IllegalArgumentException("provider doesn't exisit: " + provider);
1076 }
1077
1078 Log.i(TAG, "request " + Integer.toHexString(System.identityHashCode(receiver)) + " " +
1079 name + " " + request + " from " + packageName + "(" + uid + ")");
1080
1081 UpdateRecord record = new UpdateRecord(name, request, receiver);
1082 UpdateRecord oldRecord = receiver.mUpdateRecords.put(name, record);
1083 if (oldRecord != null) {
1084 oldRecord.disposeLocked(false);
1085 }
1086
1087 boolean isProviderEnabled = isAllowedBySettingsLocked(name);
1088 if (isProviderEnabled) {
1089 applyRequirementsLocked(name);
1090 } else {
1091 // Notify the listener that updates are currently disabled
1092 receiver.callProviderEnabledLocked(name, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001093 }
1094 }
1095
Nick Pellye0fd6932012-07-11 10:26:13 -07001096 @Override
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001097 public void removeUpdates(ILocationListener listener, PendingIntent intent,
1098 String packageName) {
1099 checkPackageName(packageName);
1100 checkPermission();
1101 final int pid = Binder.getCallingPid();
1102 final int uid = Binder.getCallingUid();
1103 Receiver receiver = checkListenerOrIntent(listener, intent, pid, uid, packageName);
1104
Nick Pelly2b7a0d02012-08-17 15:09:44 -07001105 // providers may use public location API's, need to clear identity
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001106 long identity = Binder.clearCallingIdentity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001107 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001108 synchronized (mLock) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001109 removeUpdatesLocked(receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001110 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001111 } finally {
1112 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001113 }
1114 }
1115
1116 private void removeUpdatesLocked(Receiver receiver) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001117 Log.i(TAG, "remove " + Integer.toHexString(System.identityHashCode(receiver)));
1118
1119 if (mReceivers.remove(receiver.mKey) != null && receiver.isListener()) {
1120 receiver.getListener().asBinder().unlinkToDeath(receiver, 0);
1121 synchronized (receiver) {
1122 if (receiver.mPendingBroadcasts > 0) {
1123 decrementPendingBroadcasts();
1124 receiver.mPendingBroadcasts = 0;
1125 }
1126 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001127 }
1128
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001129 // Record which providers were associated with this listener
1130 HashSet<String> providers = new HashSet<String>();
1131 HashMap<String, UpdateRecord> oldRecords = receiver.mUpdateRecords;
1132 if (oldRecords != null) {
1133 // Call dispose() on the obsolete update records.
1134 for (UpdateRecord record : oldRecords.values()) {
1135 record.disposeLocked(false);
1136 }
1137 // Accumulate providers
1138 providers.addAll(oldRecords.keySet());
1139 }
1140
1141 // update provider
1142 for (String provider : providers) {
1143 // If provider is already disabled, don't need to do anything
1144 if (!isAllowedBySettingsLocked(provider)) {
1145 continue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001146 }
1147
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001148 applyRequirementsLocked(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001149 }
1150 }
1151
Nick Pellye0fd6932012-07-11 10:26:13 -07001152 @Override
Nick Pelly4035f5a2012-08-17 14:43:49 -07001153 public Location getLastLocation(LocationRequest request, String packageName) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001154 if (D) Log.d(TAG, "getLastLocation: " + request);
1155 if (request == null) request = DEFAULT_LOCATION_REQUEST;
1156 String perm = checkPermissionAndRequest(request);
Nick Pelly4035f5a2012-08-17 14:43:49 -07001157 checkPackageName(packageName);
1158
1159 if (mBlacklist.isBlacklisted(packageName)) {
1160 if (D) Log.d(TAG, "not returning last loc for blacklisted app: " +
1161 packageName);
1162 return null;
1163 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001164
1165 synchronized (mLock) {
1166 // Figure out the provider. Either its explicitly request (deprecated API's),
1167 // or use the fused provider
1168 String name = request.getProvider();
1169 if (name == null) name = LocationManager.FUSED_PROVIDER;
1170 LocationProviderInterface provider = mProvidersByName.get(name);
1171 if (provider == null) return null;
1172
1173 if (!isAllowedBySettingsLocked(name)) return null;
1174
1175 Location location = mLastLocation.get(name);
Victoria Lease09016ab2012-09-16 12:33:15 -07001176 if (location == null) {
1177 return null;
1178 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001179 if (ACCESS_FINE_LOCATION.equals(perm)) {
1180 return location;
1181 } else {
Victoria Lease09016ab2012-09-16 12:33:15 -07001182 Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
1183 if (noGPSLocation != null) {
1184 return mLocationFudger.getOrCreate(noGPSLocation);
1185 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001186 }
1187 }
Victoria Lease09016ab2012-09-16 12:33:15 -07001188 return null;
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001189 }
1190
1191 @Override
1192 public void requestGeofence(LocationRequest request, Geofence geofence, PendingIntent intent,
1193 String packageName) {
1194 if (request == null) request = DEFAULT_LOCATION_REQUEST;
Victoria Lease4fab68b2012-09-13 13:20:59 -07001195 checkGeofencePermission();
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001196 checkPermissionAndRequest(request);
1197 checkPendingIntent(intent);
1198 checkPackageName(packageName);
1199
1200 if (D) Log.d(TAG, "requestGeofence: " + request + " " + geofence + " " + intent);
1201
Nick Pelly2b7a0d02012-08-17 15:09:44 -07001202 // geo-fence manager uses the public location API, need to clear identity
1203 int uid = Binder.getCallingUid();
1204 long identity = Binder.clearCallingIdentity();
1205 try {
1206 mGeofenceManager.addFence(request, geofence, intent, uid, packageName);
1207 } finally {
1208 Binder.restoreCallingIdentity(identity);
1209 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001210 }
1211
1212 @Override
1213 public void removeGeofence(Geofence geofence, PendingIntent intent, String packageName) {
Victoria Lease4fab68b2012-09-13 13:20:59 -07001214 checkGeofencePermission();
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001215 checkPendingIntent(intent);
1216 checkPackageName(packageName);
1217
1218 if (D) Log.d(TAG, "removeGeofence: " + geofence + " " + intent);
1219
Nick Pelly2b7a0d02012-08-17 15:09:44 -07001220 // geo-fence manager uses the public location API, need to clear identity
1221 long identity = Binder.clearCallingIdentity();
1222 try {
1223 mGeofenceManager.removeFence(geofence, intent);
1224 } finally {
1225 Binder.restoreCallingIdentity(identity);
1226 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001227 }
1228
1229
1230 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001231 public boolean addGpsStatusListener(IGpsStatusListener listener) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001232 if (mGpsStatusProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001233 return false;
1234 }
Mike Lockwoodb7e99222009-07-07 13:18:21 -04001235 if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) !=
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001236 PackageManager.PERMISSION_GRANTED) {
1237 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
1238 }
1239
1240 try {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001241 mGpsStatusProvider.addGpsStatusListener(listener);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001242 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001243 Slog.e(TAG, "mGpsStatusProvider.addGpsStatusListener failed", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001244 return false;
1245 }
1246 return true;
1247 }
1248
Nick Pellye0fd6932012-07-11 10:26:13 -07001249 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001250 public void removeGpsStatusListener(IGpsStatusListener listener) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001251 synchronized (mLock) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001252 try {
1253 mGpsStatusProvider.removeGpsStatusListener(listener);
1254 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001255 Slog.e(TAG, "mGpsStatusProvider.removeGpsStatusListener failed", e);
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001256 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001257 }
1258 }
1259
Nick Pellye0fd6932012-07-11 10:26:13 -07001260 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001261 public boolean sendExtraCommand(String provider, String command, Bundle extras) {
Mike Lockwoodc6cc8362009-08-17 13:16:08 -04001262 if (provider == null) {
1263 // throw NullPointerException to remain compatible with previous implementation
1264 throw new NullPointerException();
1265 }
1266
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001267 checkPermission();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001268 // and check for ACCESS_LOCATION_EXTRA_COMMANDS
Mike Lockwoodb7e99222009-07-07 13:18:21 -04001269 if ((mContext.checkCallingOrSelfPermission(ACCESS_LOCATION_EXTRA_COMMANDS)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001270 != PackageManager.PERMISSION_GRANTED)) {
1271 throw new SecurityException("Requires ACCESS_LOCATION_EXTRA_COMMANDS permission");
1272 }
1273
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001274 synchronized (mLock) {
Mike Lockwoodd03ff942010-02-09 08:46:14 -05001275 LocationProviderInterface p = mProvidersByName.get(provider);
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001276 if (p == null) return false;
Nick Pellye0fd6932012-07-11 10:26:13 -07001277
Mike Lockwoodd03ff942010-02-09 08:46:14 -05001278 return p.sendExtraCommand(command, extras);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001279 }
1280 }
1281
Nick Pellye0fd6932012-07-11 10:26:13 -07001282 @Override
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001283 public boolean sendNiResponse(int notifId, int userResponse) {
Mike Lockwood18ad9f62009-08-27 14:01:23 -07001284 if (Binder.getCallingUid() != Process.myUid()) {
1285 throw new SecurityException(
1286 "calling sendNiResponse from outside of the system is not allowed");
1287 }
Danke Xie22d1f9f2009-08-18 18:28:45 -04001288 try {
1289 return mNetInitiatedListener.sendNiResponse(notifId, userResponse);
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001290 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001291 Slog.e(TAG, "RemoteException in LocationManagerService.sendNiResponse");
Danke Xie22d1f9f2009-08-18 18:28:45 -04001292 return false;
1293 }
1294 }
1295
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001296 /**
Mike Lockwood628fd6d2010-01-25 22:46:13 -05001297 * @return null if the provider does not exist
Alexey Tarasovf2db9fb2009-09-01 02:37:07 +11001298 * @throws SecurityException if the provider is not allowed to be
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001299 * accessed by the caller
1300 */
Nick Pellye0fd6932012-07-11 10:26:13 -07001301 @Override
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001302 public ProviderProperties getProviderProperties(String provider) {
1303 checkPermission();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001304
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001305 LocationProviderInterface p;
1306 synchronized (mLock) {
1307 p = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001308 }
1309
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001310 if (p == null) return null;
1311 return p.getProperties();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001312 }
1313
Nick Pellye0fd6932012-07-11 10:26:13 -07001314 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001315 public boolean isProviderEnabled(String provider) {
Victoria Leasef429921e2012-10-04 08:01:19 -07001316 String perms = checkPermission();
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001317 if (LocationManager.FUSED_PROVIDER.equals(provider)) return false;
Victoria Leasef429921e2012-10-04 08:01:19 -07001318 if (ACCESS_COARSE_LOCATION.equals(perms) &&
1319 !isProviderAllowedByCoarsePermission(provider)) {
1320 throw new SecurityException("The \"" + provider +
1321 "\" provider requires ACCESS_FINE_LOCATION permission");
1322 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001323
1324 synchronized (mLock) {
1325 LocationProviderInterface p = mProvidersByName.get(provider);
1326 if (p == null) return false;
1327
1328 return isAllowedBySettingsLocked(provider);
1329 }
1330 }
1331
1332 private void checkCallerIsProvider() {
1333 if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER)
1334 == PackageManager.PERMISSION_GRANTED) {
1335 return;
1336 }
1337
1338 // Previously we only used the INSTALL_LOCATION_PROVIDER
1339 // check. But that is system or signature
1340 // protection level which is not flexible enough for
1341 // providers installed oustide the system image. So
1342 // also allow providers with a UID matching the
1343 // currently bound package name
1344
1345 int uid = Binder.getCallingUid();
1346
1347 if (mGeocodeProvider != null) {
1348 if (doesPackageHaveUid(uid, mGeocodeProvider.getConnectedPackageName())) return;
1349 }
1350 for (LocationProviderProxy proxy : mProxyProviders) {
1351 if (doesPackageHaveUid(uid, proxy.getConnectedPackageName())) return;
1352 }
1353 throw new SecurityException("need INSTALL_LOCATION_PROVIDER permission, " +
1354 "or UID of a currently bound location provider");
1355 }
1356
1357 private boolean doesPackageHaveUid(int uid, String packageName) {
1358 if (packageName == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001359 return false;
1360 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001361 try {
1362 ApplicationInfo appInfo = mPackageManager.getApplicationInfo(packageName, 0);
1363 if (appInfo.uid != uid) {
1364 return false;
1365 }
1366 } catch (NameNotFoundException e) {
1367 return false;
1368 }
1369 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001370 }
1371
Nick Pellye0fd6932012-07-11 10:26:13 -07001372 @Override
Mike Lockwooda4903f22010-02-17 06:42:23 -05001373 public void reportLocation(Location location, boolean passive) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001374 checkCallerIsProvider();
Mike Lockwood275555c2009-05-01 11:30:34 -04001375
Nick Pelly2eeeec22012-07-18 13:13:37 -07001376 if (!location.isComplete()) {
1377 Log.w(TAG, "Dropping incomplete location: " + location);
1378 return;
1379 }
1380
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001381 mLocationHandler.removeMessages(MSG_LOCATION_CHANGED, location);
1382 Message m = Message.obtain(mLocationHandler, MSG_LOCATION_CHANGED, location);
Mike Lockwooda4903f22010-02-17 06:42:23 -05001383 m.arg1 = (passive ? 1 : 0);
Mike Lockwood4e50b782009-04-03 08:24:43 -07001384 mLocationHandler.sendMessageAtFrontOfQueue(m);
1385 }
1386
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001387
1388 private static boolean shouldBroadcastSafe(Location loc, Location lastLoc, UpdateRecord record) {
1389 // Always broadcast the first update
1390 if (lastLoc == null) {
1391 return true;
1392 }
1393
Nick Pellyf1be6862012-05-15 10:53:42 -07001394 // Check whether sufficient time has passed
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001395 long minTime = record.mRequest.getFastestInterval();
Philip Milne41180122012-09-26 11:29:25 -07001396 long delta = (loc.getElapsedRealtimeNanos() - lastLoc.getElapsedRealtimeNanos()) / 1000000L;
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001397 if (delta < minTime - MAX_PROVIDER_SCHEDULING_JITTER_MS) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001398 return false;
1399 }
1400
1401 // Check whether sufficient distance has been traveled
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001402 double minDistance = record.mRequest.getSmallestDisplacement();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001403 if (minDistance > 0.0) {
1404 if (loc.distanceTo(lastLoc) <= minDistance) {
1405 return false;
1406 }
1407 }
1408
1409 return true;
1410 }
1411
Mike Lockwooda4903f22010-02-17 06:42:23 -05001412 private void handleLocationChangedLocked(Location location, boolean passive) {
Nick Pelly4e31c4f2012-08-13 19:35:39 -07001413 if (D) Log.d(TAG, "incoming location: " + location);
1414
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001415 long now = SystemClock.elapsedRealtime();
Mike Lockwooda4903f22010-02-17 06:42:23 -05001416 String provider = (passive ? LocationManager.PASSIVE_PROVIDER : location.getProvider());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001417
Laurent Tu60ec50a2012-10-04 17:00:10 -07001418 // Skip if the provider is unknown.
Mike Lockwoodd03ff942010-02-09 08:46:14 -05001419 LocationProviderInterface p = mProvidersByName.get(provider);
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001420 if (p == null) return;
1421
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001422 // Update last known locations
Victoria Lease09016ab2012-09-16 12:33:15 -07001423 Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
1424 Location lastNoGPSLocation = null;
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001425 Location lastLocation = mLastLocation.get(provider);
Mike Lockwood4e50b782009-04-03 08:24:43 -07001426 if (lastLocation == null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001427 lastLocation = new Location(provider);
1428 mLastLocation.put(provider, lastLocation);
Victoria Lease09016ab2012-09-16 12:33:15 -07001429 } else {
1430 lastNoGPSLocation = lastLocation.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
1431 if (noGPSLocation == null && lastNoGPSLocation != null) {
1432 // New location has no no-GPS location: adopt last no-GPS location. This is set
1433 // directly into location because we do not want to notify COARSE clients.
1434 location.setExtraLocation(Location.EXTRA_NO_GPS_LOCATION, lastNoGPSLocation);
1435 }
Mike Lockwood4e50b782009-04-03 08:24:43 -07001436 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001437 lastLocation.set(location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001438
Laurent Tu60ec50a2012-10-04 17:00:10 -07001439 // Skip if there are no UpdateRecords for this provider.
1440 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
1441 if (records == null || records.size() == 0) return;
1442
Victoria Lease09016ab2012-09-16 12:33:15 -07001443 // Fetch coarse location
1444 Location coarseLocation = null;
1445 if (noGPSLocation != null && !noGPSLocation.equals(lastNoGPSLocation)) {
1446 coarseLocation = mLocationFudger.getOrCreate(noGPSLocation);
1447 }
1448
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001449 // Fetch latest status update time
1450 long newStatusUpdateTime = p.getStatusUpdateTime();
1451
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001452 // Get latest status
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001453 Bundle extras = new Bundle();
1454 int status = p.getStatus(extras);
1455
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001456 ArrayList<Receiver> deadReceivers = null;
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001457 ArrayList<UpdateRecord> deadUpdateRecords = null;
Nick Pellye0fd6932012-07-11 10:26:13 -07001458
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001459 // Broadcast location or status to all listeners
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001460 for (UpdateRecord r : records) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001461 Receiver receiver = r.mReceiver;
Mike Lockwood03ca2162010-04-01 08:10:09 -07001462 boolean receiverDead = false;
Nick Pelly4035f5a2012-08-17 14:43:49 -07001463
1464 if (mBlacklist.isBlacklisted(receiver.mPackageName)) {
1465 if (D) Log.d(TAG, "skipping loc update for blacklisted app: " +
1466 receiver.mPackageName);
1467 continue;
1468 }
1469
Victoria Lease09016ab2012-09-16 12:33:15 -07001470 Location notifyLocation = null;
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001471 if (ACCESS_FINE_LOCATION.equals(receiver.mPermission)) {
Victoria Lease09016ab2012-09-16 12:33:15 -07001472 notifyLocation = lastLocation; // use fine location
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001473 } else {
Victoria Lease09016ab2012-09-16 12:33:15 -07001474 notifyLocation = coarseLocation; // use coarse location if available
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001475 }
Victoria Lease09016ab2012-09-16 12:33:15 -07001476 if (notifyLocation != null) {
1477 Location lastLoc = r.mLastFixBroadcast;
1478 if ((lastLoc == null) || shouldBroadcastSafe(notifyLocation, lastLoc, r)) {
1479 if (lastLoc == null) {
1480 lastLoc = new Location(notifyLocation);
1481 r.mLastFixBroadcast = lastLoc;
1482 } else {
1483 lastLoc.set(notifyLocation);
1484 }
1485 if (!receiver.callLocationChangedLocked(notifyLocation)) {
1486 Slog.w(TAG, "RemoteException calling onLocationChanged on " + receiver);
1487 receiverDead = true;
1488 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001489 }
1490 }
1491
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001492 long prevStatusUpdateTime = r.mLastStatusBroadcast;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001493 if ((newStatusUpdateTime > prevStatusUpdateTime) &&
Victoria Lease09016ab2012-09-16 12:33:15 -07001494 (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001495
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001496 r.mLastStatusBroadcast = newStatusUpdateTime;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001497 if (!receiver.callStatusChangedLocked(provider, status, extras)) {
Mike Lockwood03ca2162010-04-01 08:10:09 -07001498 receiverDead = true;
Joe Onorato8a9b2202010-02-26 18:56:32 -08001499 Slog.w(TAG, "RemoteException calling onStatusChanged on " + receiver);
Mike Lockwood03ca2162010-04-01 08:10:09 -07001500 }
1501 }
1502
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001503 // track expired records
1504 if (r.mRequest.getNumUpdates() == 0 || r.mRequest.getExpireAt() < now) {
1505 if (deadUpdateRecords == null) {
1506 deadUpdateRecords = new ArrayList<UpdateRecord>();
1507 }
1508 deadUpdateRecords.add(r);
1509 }
1510 // track dead receivers
1511 if (receiverDead) {
Mike Lockwood03ca2162010-04-01 08:10:09 -07001512 if (deadReceivers == null) {
1513 deadReceivers = new ArrayList<Receiver>();
1514 }
1515 if (!deadReceivers.contains(receiver)) {
1516 deadReceivers.add(receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001517 }
1518 }
1519 }
Nick Pellye0fd6932012-07-11 10:26:13 -07001520
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001521 // remove dead records and receivers outside the loop
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001522 if (deadReceivers != null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001523 for (Receiver receiver : deadReceivers) {
1524 removeUpdatesLocked(receiver);
1525 }
1526 }
1527 if (deadUpdateRecords != null) {
1528 for (UpdateRecord r : deadUpdateRecords) {
1529 r.disposeLocked(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001530 }
1531 }
1532 }
1533
1534 private class LocationWorkerHandler extends Handler {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001535 @Override
1536 public void handleMessage(Message msg) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001537 switch (msg.what) {
1538 case MSG_LOCATION_CHANGED:
1539 handleLocationChanged((Location) msg.obj, msg.arg1 == 1);
1540 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001541 }
1542 }
1543 }
1544
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001545 private void handleLocationChanged(Location location, boolean passive) {
1546 String provider = location.getProvider();
Jeff Sharkey5e613312012-01-30 11:16:20 -08001547
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001548 if (!passive) {
1549 // notify passive provider of the new location
1550 mPassiveProvider.updateLocation(location);
1551 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001552
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001553 synchronized (mLock) {
1554 if (isAllowedBySettingsLocked(provider)) {
1555 handleLocationChangedLocked(location, passive);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001556 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001557 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001558 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001559
Mike Lockwoode97ae402010-09-29 15:23:46 -04001560 private final PackageMonitor mPackageMonitor = new PackageMonitor() {
1561 @Override
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001562 public void onPackageDisappeared(String packageName, int reason) {
1563 // remove all receivers associated with this package name
1564 synchronized (mLock) {
1565 ArrayList<Receiver> deadReceivers = null;
1566
1567 for (Receiver receiver : mReceivers.values()) {
1568 if (receiver.mPackageName.equals(packageName)) {
1569 if (deadReceivers == null) {
1570 deadReceivers = new ArrayList<Receiver>();
1571 }
1572 deadReceivers.add(receiver);
1573 }
1574 }
1575
1576 // perform removal outside of mReceivers loop
1577 if (deadReceivers != null) {
1578 for (Receiver receiver : deadReceivers) {
1579 removeUpdatesLocked(receiver);
1580 }
1581 }
1582 }
Nick Pellye0fd6932012-07-11 10:26:13 -07001583 }
Mike Lockwoode97ae402010-09-29 15:23:46 -04001584 };
1585
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001586 // Wake locks
1587
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001588 private void incrementPendingBroadcasts() {
1589 synchronized (mWakeLock) {
1590 if (mPendingBroadcasts++ == 0) {
1591 try {
1592 mWakeLock.acquire();
1593 log("Acquired wakelock");
1594 } catch (Exception e) {
1595 // This is to catch a runtime exception thrown when we try to release an
1596 // already released lock.
Joe Onorato8a9b2202010-02-26 18:56:32 -08001597 Slog.e(TAG, "exception in acquireWakeLock()", e);
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001598 }
1599 }
Mike Lockwood48f17512009-04-23 09:12:08 -07001600 }
1601 }
1602
1603 private void decrementPendingBroadcasts() {
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001604 synchronized (mWakeLock) {
Mike Lockwood48f17512009-04-23 09:12:08 -07001605 if (--mPendingBroadcasts == 0) {
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001606 try {
1607 // Release wake lock
1608 if (mWakeLock.isHeld()) {
1609 mWakeLock.release();
1610 log("Released wakelock");
1611 } else {
1612 log("Can't release wakelock again!");
1613 }
1614 } catch (Exception e) {
1615 // This is to catch a runtime exception thrown when we try to release an
1616 // already released lock.
Joe Onorato8a9b2202010-02-26 18:56:32 -08001617 Slog.e(TAG, "exception in releaseWakeLock()", e);
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001618 }
Mike Lockwood48f17512009-04-23 09:12:08 -07001619 }
1620 }
1621 }
1622
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001623 // Geocoder
1624
Nick Pellye0fd6932012-07-11 10:26:13 -07001625 @Override
Mike Lockwoode15735a2010-09-20 17:48:47 -04001626 public boolean geocoderIsPresent() {
Mark Vandevoorde01ac80b2010-05-21 15:43:26 -07001627 return mGeocodeProvider != null;
1628 }
1629
Nick Pellye0fd6932012-07-11 10:26:13 -07001630 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001631 public String getFromLocation(double latitude, double longitude, int maxResults,
Mike Lockwood34901402010-01-04 12:14:21 -05001632 GeocoderParams params, List<Address> addrs) {
Mike Lockwooda55c3212009-04-15 11:10:11 -04001633 if (mGeocodeProvider != null) {
Mike Lockwood628fd6d2010-01-25 22:46:13 -05001634 return mGeocodeProvider.getFromLocation(latitude, longitude, maxResults,
1635 params, addrs);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001636 }
Mike Lockwooda55c3212009-04-15 11:10:11 -04001637 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001638 }
1639
Mike Lockwooda55c3212009-04-15 11:10:11 -04001640
Nick Pellye0fd6932012-07-11 10:26:13 -07001641 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001642 public String getFromLocationName(String locationName,
Mike Lockwooda55c3212009-04-15 11:10:11 -04001643 double lowerLeftLatitude, double lowerLeftLongitude,
1644 double upperRightLatitude, double upperRightLongitude, int maxResults,
Mike Lockwood34901402010-01-04 12:14:21 -05001645 GeocoderParams params, List<Address> addrs) {
Mike Lockwooda55c3212009-04-15 11:10:11 -04001646
1647 if (mGeocodeProvider != null) {
Mike Lockwood628fd6d2010-01-25 22:46:13 -05001648 return mGeocodeProvider.getFromLocationName(locationName, lowerLeftLatitude,
1649 lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
1650 maxResults, params, addrs);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001651 }
Mike Lockwooda55c3212009-04-15 11:10:11 -04001652 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001653 }
1654
1655 // Mock Providers
1656
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001657 private void checkMockPermissionsSafe() {
1658 boolean allowMocks = Settings.Secure.getInt(mContext.getContentResolver(),
1659 Settings.Secure.ALLOW_MOCK_LOCATION, 0) == 1;
1660 if (!allowMocks) {
1661 throw new SecurityException("Requires ACCESS_MOCK_LOCATION secure setting");
1662 }
1663
1664 if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) !=
1665 PackageManager.PERMISSION_GRANTED) {
1666 throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission");
Nick Pellye0fd6932012-07-11 10:26:13 -07001667 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001668 }
1669
Nick Pellye0fd6932012-07-11 10:26:13 -07001670 @Override
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001671 public void addTestProvider(String name, ProviderProperties properties) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001672 checkMockPermissionsSafe();
1673
Mike Lockwooda4903f22010-02-17 06:42:23 -05001674 if (LocationManager.PASSIVE_PROVIDER.equals(name)) {
1675 throw new IllegalArgumentException("Cannot mock the passive location provider");
1676 }
1677
Mike Lockwood86328a92009-10-23 08:38:25 -04001678 long identity = Binder.clearCallingIdentity();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001679 synchronized (mLock) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001680 MockProvider provider = new MockProvider(name, this, properties);
Mike Lockwood7566c1d2009-08-25 10:05:18 -07001681 // remove the real provider if we are replacing GPS or network provider
1682 if (LocationManager.GPS_PROVIDER.equals(name)
Nick Pelly1332b532012-08-21 16:25:47 -07001683 || LocationManager.NETWORK_PROVIDER.equals(name)
1684 || LocationManager.FUSED_PROVIDER.equals(name)) {
Mike Lockwoodd03ff942010-02-09 08:46:14 -05001685 LocationProviderInterface p = mProvidersByName.get(name);
1686 if (p != null) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001687 removeProviderLocked(p);
Mike Lockwood7566c1d2009-08-25 10:05:18 -07001688 }
1689 }
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001690 if (mProvidersByName.get(name) != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001691 throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
1692 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001693 addProviderLocked(provider);
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001694 mMockProviders.put(name, provider);
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001695 mLastLocation.put(name, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001696 updateProvidersLocked();
1697 }
Mike Lockwood86328a92009-10-23 08:38:25 -04001698 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001699 }
1700
Nick Pellye0fd6932012-07-11 10:26:13 -07001701 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001702 public void removeTestProvider(String provider) {
1703 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001704 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001705 MockProvider mockProvider = mMockProviders.get(provider);
1706 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001707 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1708 }
Mike Lockwood86328a92009-10-23 08:38:25 -04001709 long identity = Binder.clearCallingIdentity();
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001710 removeProviderLocked(mProvidersByName.get(provider));
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001711 mMockProviders.remove(mockProvider);
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001712
1713 // reinstate real provider if available
1714 LocationProviderInterface realProvider = mRealProviders.get(provider);
1715 if (realProvider != null) {
1716 addProviderLocked(realProvider);
Mike Lockwood7566c1d2009-08-25 10:05:18 -07001717 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001718 mLastLocation.put(provider, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001719 updateProvidersLocked();
Mike Lockwood86328a92009-10-23 08:38:25 -04001720 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001721 }
1722 }
1723
Nick Pellye0fd6932012-07-11 10:26:13 -07001724 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001725 public void setTestProviderLocation(String provider, Location loc) {
1726 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001727 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001728 MockProvider mockProvider = mMockProviders.get(provider);
1729 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001730 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1731 }
Mike Lockwood95427cd2009-05-07 13:27:54 -04001732 // clear calling identity so INSTALL_LOCATION_PROVIDER permission is not required
1733 long identity = Binder.clearCallingIdentity();
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001734 mockProvider.setLocation(loc);
Mike Lockwood95427cd2009-05-07 13:27:54 -04001735 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001736 }
1737 }
1738
Nick Pellye0fd6932012-07-11 10:26:13 -07001739 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001740 public void clearTestProviderLocation(String provider) {
1741 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001742 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001743 MockProvider mockProvider = mMockProviders.get(provider);
1744 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001745 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1746 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001747 mockProvider.clearLocation();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001748 }
1749 }
1750
Nick Pellye0fd6932012-07-11 10:26:13 -07001751 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001752 public void setTestProviderEnabled(String provider, boolean enabled) {
1753 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001754 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001755 MockProvider mockProvider = mMockProviders.get(provider);
1756 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001757 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1758 }
Mike Lockwood86328a92009-10-23 08:38:25 -04001759 long identity = Binder.clearCallingIdentity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001760 if (enabled) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001761 mockProvider.enable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001762 mEnabledProviders.add(provider);
1763 mDisabledProviders.remove(provider);
1764 } else {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001765 mockProvider.disable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001766 mEnabledProviders.remove(provider);
1767 mDisabledProviders.add(provider);
1768 }
1769 updateProvidersLocked();
Mike Lockwood86328a92009-10-23 08:38:25 -04001770 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001771 }
1772 }
1773
Nick Pellye0fd6932012-07-11 10:26:13 -07001774 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001775 public void clearTestProviderEnabled(String provider) {
1776 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001777 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001778 MockProvider mockProvider = mMockProviders.get(provider);
1779 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001780 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1781 }
Mike Lockwood86328a92009-10-23 08:38:25 -04001782 long identity = Binder.clearCallingIdentity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001783 mEnabledProviders.remove(provider);
1784 mDisabledProviders.remove(provider);
1785 updateProvidersLocked();
Mike Lockwood86328a92009-10-23 08:38:25 -04001786 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001787 }
1788 }
1789
Nick Pellye0fd6932012-07-11 10:26:13 -07001790 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001791 public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
1792 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001793 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001794 MockProvider mockProvider = mMockProviders.get(provider);
1795 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001796 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1797 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001798 mockProvider.setStatus(status, extras, updateTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001799 }
1800 }
1801
Nick Pellye0fd6932012-07-11 10:26:13 -07001802 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001803 public void clearTestProviderStatus(String provider) {
1804 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001805 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001806 MockProvider mockProvider = mMockProviders.get(provider);
1807 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001808 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1809 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001810 mockProvider.clearStatus();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001811 }
1812 }
1813
1814 private void log(String log) {
1815 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001816 Slog.d(TAG, log);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001817 }
1818 }
Nick Pellye0fd6932012-07-11 10:26:13 -07001819
1820 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001821 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1822 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1823 != PackageManager.PERMISSION_GRANTED) {
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001824 pw.println("Permission Denial: can't dump LocationManagerService from from pid="
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001825 + Binder.getCallingPid()
1826 + ", uid=" + Binder.getCallingUid());
1827 return;
1828 }
Nick Pellye0fd6932012-07-11 10:26:13 -07001829
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001830 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001831 pw.println("Current Location Manager state:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001832 pw.println(" Location Listeners:");
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001833 for (Receiver receiver : mReceivers.values()) {
1834 pw.println(" " + receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001835 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001836 pw.println(" Records by Provider:");
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001837 for (Map.Entry<String, ArrayList<UpdateRecord>> entry : mRecordsByProvider.entrySet()) {
1838 pw.println(" " + entry.getKey() + ":");
1839 for (UpdateRecord record : entry.getValue()) {
1840 pw.println(" " + record);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001841 }
1842 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001843 pw.println(" Last Known Locations:");
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001844 for (Map.Entry<String, Location> entry : mLastLocation.entrySet()) {
1845 String provider = entry.getKey();
1846 Location location = entry.getValue();
1847 pw.println(" " + provider + ": " + location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001848 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001849
Nick Pellye0fd6932012-07-11 10:26:13 -07001850 mGeofenceManager.dump(pw);
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001851
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001852 if (mEnabledProviders.size() > 0) {
1853 pw.println(" Enabled Providers:");
1854 for (String i : mEnabledProviders) {
1855 pw.println(" " + i);
1856 }
Nick Pellye0fd6932012-07-11 10:26:13 -07001857
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001858 }
1859 if (mDisabledProviders.size() > 0) {
1860 pw.println(" Disabled Providers:");
1861 for (String i : mDisabledProviders) {
1862 pw.println(" " + i);
1863 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001864 }
Nick Pelly4035f5a2012-08-17 14:43:49 -07001865 pw.append(" ");
1866 mBlacklist.dump(pw);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001867 if (mMockProviders.size() > 0) {
1868 pw.println(" Mock Providers:");
1869 for (Map.Entry<String, MockProvider> i : mMockProviders.entrySet()) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001870 i.getValue().dump(pw, " ");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001871 }
1872 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001873
Nick Pelly74fa7ea2012-08-13 19:36:38 -07001874 pw.append(" fudger: ");
1875 mLocationFudger.dump(fd, pw, args);
1876
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001877 if (args.length > 0 && "short".equals(args[0])) {
1878 return;
1879 }
Fred Fettinger3c8fbdf2010-01-04 15:38:13 -06001880 for (LocationProviderInterface provider: mProviders) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001881 pw.print(provider.getName() + " Internal State");
1882 if (provider instanceof LocationProviderProxy) {
1883 LocationProviderProxy proxy = (LocationProviderProxy) provider;
1884 pw.print(" (" + proxy.getConnectedPackageName() + ")");
Fred Fettinger3c8fbdf2010-01-04 15:38:13 -06001885 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001886 pw.println(":");
1887 provider.dump(fd, pw, args);
Fred Fettinger3c8fbdf2010-01-04 15:38:13 -06001888 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001889 }
1890 }
1891}