summaryrefslogtreecommitdiffstats
path: root/services/java/com/android/server/LocationManagerService.java
diff options
context:
space:
mode:
Diffstat (limited to 'services/java/com/android/server/LocationManagerService.java')
-rw-r--r--services/java/com/android/server/LocationManagerService.java2416
1 files changed, 0 insertions, 2416 deletions
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
deleted file mode 100644
index eebd1c5..0000000
--- a/services/java/com/android/server/LocationManagerService.java
+++ /dev/null
@@ -1,2416 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server;
-
-import android.app.AppOpsManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ResolveInfo;
-import android.content.pm.Signature;
-import android.content.res.Resources;
-import android.database.ContentObserver;
-import android.location.Address;
-import android.location.Criteria;
-import android.location.GeocoderParams;
-import android.location.Geofence;
-import android.location.IGpsStatusListener;
-import android.location.IGpsStatusProvider;
-import android.location.ILocationListener;
-import android.location.ILocationManager;
-import android.location.INetInitiatedListener;
-import android.location.Location;
-import android.location.LocationManager;
-import android.location.LocationProvider;
-import android.location.LocationRequest;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.PowerManager;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.os.WorkSource;
-import android.provider.Settings;
-import android.util.Log;
-import android.util.Slog;
-import com.android.internal.content.PackageMonitor;
-import com.android.internal.location.ProviderProperties;
-import com.android.internal.location.ProviderRequest;
-import com.android.internal.os.BackgroundThread;
-import com.android.server.location.FlpHardwareProvider;
-import com.android.server.location.FusedProxy;
-import com.android.server.location.GeocoderProxy;
-import com.android.server.location.GeofenceProxy;
-import com.android.server.location.GeofenceManager;
-import com.android.server.location.GpsLocationProvider;
-import com.android.server.location.LocationBlacklist;
-import com.android.server.location.LocationFudger;
-import com.android.server.location.LocationProviderInterface;
-import com.android.server.location.LocationProviderProxy;
-import com.android.server.location.MockProvider;
-import com.android.server.location.PassiveProvider;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * The service class that manages LocationProviders and issues location
- * updates and alerts.
- */
-public class LocationManagerService extends ILocationManager.Stub {
- private static final String TAG = "LocationManagerService";
- public static final boolean D = Log.isLoggable(TAG, Log.DEBUG);
-
- private static final String WAKELOCK_KEY = TAG;
-
- // Location resolution level: no location data whatsoever
- private static final int RESOLUTION_LEVEL_NONE = 0;
- // Location resolution level: coarse location data only
- private static final int RESOLUTION_LEVEL_COARSE = 1;
- // Location resolution level: fine location data
- private static final int RESOLUTION_LEVEL_FINE = 2;
-
- private static final String ACCESS_MOCK_LOCATION =
- android.Manifest.permission.ACCESS_MOCK_LOCATION;
- private static final String ACCESS_LOCATION_EXTRA_COMMANDS =
- android.Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS;
- private static final String INSTALL_LOCATION_PROVIDER =
- android.Manifest.permission.INSTALL_LOCATION_PROVIDER;
-
- private static final String NETWORK_LOCATION_SERVICE_ACTION =
- "com.android.location.service.v3.NetworkLocationProvider";
- private static final String FUSED_LOCATION_SERVICE_ACTION =
- "com.android.location.service.FusedLocationProvider";
-
- private static final int MSG_LOCATION_CHANGED = 1;
-
- private static final long NANOS_PER_MILLI = 1000000L;
-
- // The maximum interval a location request can have and still be considered "high power".
- private static final long HIGH_POWER_INTERVAL_MS = 5 * 60 * 1000;
-
- // Location Providers may sometimes deliver location updates
- // slightly faster that requested - provide grace period so
- // we don't unnecessarily filter events that are otherwise on
- // time
- private static final int MAX_PROVIDER_SCHEDULING_JITTER_MS = 100;
-
- private static final LocationRequest DEFAULT_LOCATION_REQUEST = new LocationRequest();
-
- private final Context mContext;
- private final AppOpsManager mAppOps;
-
- // used internally for synchronization
- private final Object mLock = new Object();
-
- // --- fields below are final after systemReady() ---
- private LocationFudger mLocationFudger;
- private GeofenceManager mGeofenceManager;
- private PackageManager mPackageManager;
- private PowerManager mPowerManager;
- private GeocoderProxy mGeocodeProvider;
- private IGpsStatusProvider mGpsStatusProvider;
- private INetInitiatedListener mNetInitiatedListener;
- private LocationWorkerHandler mLocationHandler;
- private PassiveProvider mPassiveProvider; // track passive provider for special cases
- private LocationBlacklist mBlacklist;
-
- // --- fields below are protected by mLock ---
- // Set of providers that are explicitly enabled
- private final Set<String> mEnabledProviders = new HashSet<String>();
-
- // Set of providers that are explicitly disabled
- private final Set<String> mDisabledProviders = new HashSet<String>();
-
- // Mock (test) providers
- private final HashMap<String, MockProvider> mMockProviders =
- new HashMap<String, MockProvider>();
-
- // all receivers
- private final HashMap<Object, Receiver> mReceivers = new HashMap<Object, Receiver>();
-
- // currently installed providers (with mocks replacing real providers)
- private final ArrayList<LocationProviderInterface> mProviders =
- new ArrayList<LocationProviderInterface>();
-
- // real providers, saved here when mocked out
- private final HashMap<String, LocationProviderInterface> mRealProviders =
- new HashMap<String, LocationProviderInterface>();
-
- // mapping from provider name to provider
- private final HashMap<String, LocationProviderInterface> mProvidersByName =
- new HashMap<String, LocationProviderInterface>();
-
- // mapping from provider name to all its UpdateRecords
- private final HashMap<String, ArrayList<UpdateRecord>> mRecordsByProvider =
- new HashMap<String, ArrayList<UpdateRecord>>();
-
- // mapping from provider name to last known location
- private final HashMap<String, Location> mLastLocation = new HashMap<String, Location>();
-
- // same as mLastLocation, but is not updated faster than LocationFudger.FASTEST_INTERVAL_MS.
- // locations stored here are not fudged for coarse permissions.
- private final HashMap<String, Location> mLastLocationCoarseInterval =
- new HashMap<String, Location>();
-
- // all providers that operate over proxy, for authorizing incoming location
- private final ArrayList<LocationProviderProxy> mProxyProviders =
- new ArrayList<LocationProviderProxy>();
-
- // current active user on the device - other users are denied location data
- private int mCurrentUserId = UserHandle.USER_OWNER;
-
- public LocationManagerService(Context context) {
- super();
- mContext = context;
- mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
-
- if (D) Log.d(TAG, "Constructed");
-
- // most startup is deferred until systemReady()
- }
-
- public void systemRunning() {
- synchronized (mLock) {
- if (D) Log.d(TAG, "systemReady()");
-
- // fetch package manager
- mPackageManager = mContext.getPackageManager();
-
- // fetch power manager
- mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
-
- // prepare worker thread
- mLocationHandler = new LocationWorkerHandler(BackgroundThread.get().getLooper());
-
- // prepare mLocationHandler's dependents
- mLocationFudger = new LocationFudger(mContext, mLocationHandler);
- mBlacklist = new LocationBlacklist(mContext, mLocationHandler);
- mBlacklist.init();
- mGeofenceManager = new GeofenceManager(mContext, mBlacklist);
-
- // Monitor for app ops mode changes.
- AppOpsManager.OnOpChangedListener callback
- = new AppOpsManager.OnOpChangedInternalListener() {
- public void onOpChanged(int op, String packageName) {
- synchronized (mLock) {
- for (Receiver receiver : mReceivers.values()) {
- receiver.updateMonitoring(true);
- }
- applyAllProviderRequirementsLocked();
- }
- }
- };
- mAppOps.startWatchingMode(AppOpsManager.OP_COARSE_LOCATION, null, callback);
-
- // prepare providers
- loadProvidersLocked();
- updateProvidersLocked();
- }
-
- // listen for settings changes
- mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.LOCATION_PROVIDERS_ALLOWED), true,
- new ContentObserver(mLocationHandler) {
- @Override
- public void onChange(boolean selfChange) {
- synchronized (mLock) {
- updateProvidersLocked();
- }
- }
- }, UserHandle.USER_ALL);
- mPackageMonitor.register(mContext, mLocationHandler.getLooper(), true);
-
- // listen for user change
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
-
- mContext.registerReceiverAsUser(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (Intent.ACTION_USER_SWITCHED.equals(action)) {
- switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
- }
- }
- }, UserHandle.ALL, intentFilter, null, mLocationHandler);
- }
-
- private void ensureFallbackFusedProviderPresentLocked(ArrayList<String> pkgs) {
- PackageManager pm = mContext.getPackageManager();
- String systemPackageName = mContext.getPackageName();
- ArrayList<HashSet<Signature>> sigSets = ServiceWatcher.getSignatureSets(mContext, pkgs);
-
- List<ResolveInfo> rInfos = pm.queryIntentServicesAsUser(
- new Intent(FUSED_LOCATION_SERVICE_ACTION),
- PackageManager.GET_META_DATA, mCurrentUserId);
- for (ResolveInfo rInfo : rInfos) {
- String packageName = rInfo.serviceInfo.packageName;
-
- // Check that the signature is in the list of supported sigs. If it's not in
- // this list the standard provider binding logic won't bind to it.
- try {
- PackageInfo pInfo;
- pInfo = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
- if (!ServiceWatcher.isSignatureMatch(pInfo.signatures, sigSets)) {
- Log.w(TAG, packageName + " resolves service " + FUSED_LOCATION_SERVICE_ACTION +
- ", but has wrong signature, ignoring");
- continue;
- }
- } catch (NameNotFoundException e) {
- Log.e(TAG, "missing package: " + packageName);
- continue;
- }
-
- // Get the version info
- if (rInfo.serviceInfo.metaData == null) {
- Log.w(TAG, "Found fused provider without metadata: " + packageName);
- continue;
- }
-
- int version = rInfo.serviceInfo.metaData.getInt(
- ServiceWatcher.EXTRA_SERVICE_VERSION, -1);
- if (version == 0) {
- // This should be the fallback fused location provider.
-
- // Make sure it's in the system partition.
- if ((rInfo.serviceInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
- if (D) Log.d(TAG, "Fallback candidate not in /system: " + packageName);
- continue;
- }
-
- // Check that the fallback is signed the same as the OS
- // as a proxy for coreApp="true"
- if (pm.checkSignatures(systemPackageName, packageName)
- != PackageManager.SIGNATURE_MATCH) {
- if (D) Log.d(TAG, "Fallback candidate not signed the same as system: "
- + packageName);
- continue;
- }
-
- // Found a valid fallback.
- if (D) Log.d(TAG, "Found fallback provider: " + packageName);
- return;
- } else {
- if (D) Log.d(TAG, "Fallback candidate not version 0: " + packageName);
- }
- }
-
- throw new IllegalStateException("Unable to find a fused location provider that is in the "
- + "system partition with version 0 and signed with the platform certificate. "
- + "Such a package is needed to provide a default fused location provider in the "
- + "event that no other fused location provider has been installed or is currently "
- + "available. For example, coreOnly boot mode when decrypting the data "
- + "partition. The fallback must also be marked coreApp=\"true\" in the manifest");
- }
-
- private void loadProvidersLocked() {
- // create a passive location provider, which is always enabled
- PassiveProvider passiveProvider = new PassiveProvider(this);
- addProviderLocked(passiveProvider);
- mEnabledProviders.add(passiveProvider.getName());
- mPassiveProvider = passiveProvider;
- // Create a gps location provider
- GpsLocationProvider gpsProvider = new GpsLocationProvider(mContext, this,
- mLocationHandler.getLooper());
-
- if (GpsLocationProvider.isSupported()) {
- mGpsStatusProvider = gpsProvider.getGpsStatusProvider();
- mNetInitiatedListener = gpsProvider.getNetInitiatedListener();
- addProviderLocked(gpsProvider);
- mRealProviders.put(LocationManager.GPS_PROVIDER, gpsProvider);
- }
-
- /*
- Load package name(s) containing location provider support.
- These packages can contain services implementing location providers:
- Geocoder Provider, Network Location Provider, and
- Fused Location Provider. They will each be searched for
- service components implementing these providers.
- The location framework also has support for installation
- of new location providers at run-time. The new package does not
- have to be explicitly listed here, however it must have a signature
- that matches the signature of at least one package on this list.
- */
- Resources resources = mContext.getResources();
- ArrayList<String> providerPackageNames = new ArrayList<String>();
- String[] pkgs = resources.getStringArray(
- com.android.internal.R.array.config_locationProviderPackageNames);
- if (D) Log.d(TAG, "certificates for location providers pulled from: " +
- Arrays.toString(pkgs));
- if (pkgs != null) providerPackageNames.addAll(Arrays.asList(pkgs));
-
- ensureFallbackFusedProviderPresentLocked(providerPackageNames);
-
- // bind to network provider
- LocationProviderProxy networkProvider = LocationProviderProxy.createAndBind(
- mContext,
- LocationManager.NETWORK_PROVIDER,
- NETWORK_LOCATION_SERVICE_ACTION,
- com.android.internal.R.bool.config_enableNetworkLocationOverlay,
- com.android.internal.R.string.config_networkLocationProviderPackageName,
- com.android.internal.R.array.config_locationProviderPackageNames,
- mLocationHandler);
- if (networkProvider != null) {
- mRealProviders.put(LocationManager.NETWORK_PROVIDER, networkProvider);
- mProxyProviders.add(networkProvider);
- addProviderLocked(networkProvider);
- } else {
- Slog.w(TAG, "no network location provider found");
- }
-
- // bind to fused provider
- LocationProviderProxy fusedLocationProvider = LocationProviderProxy.createAndBind(
- mContext,
- LocationManager.FUSED_PROVIDER,
- FUSED_LOCATION_SERVICE_ACTION,
- com.android.internal.R.bool.config_enableFusedLocationOverlay,
- com.android.internal.R.string.config_fusedLocationProviderPackageName,
- com.android.internal.R.array.config_locationProviderPackageNames,
- mLocationHandler);
- if (fusedLocationProvider != null) {
- addProviderLocked(fusedLocationProvider);
- mProxyProviders.add(fusedLocationProvider);
- mEnabledProviders.add(fusedLocationProvider.getName());
- mRealProviders.put(LocationManager.FUSED_PROVIDER, fusedLocationProvider);
- } else {
- Slog.e(TAG, "no fused location provider found",
- new IllegalStateException("Location service needs a fused location provider"));
- }
-
- // bind to geocoder provider
- mGeocodeProvider = GeocoderProxy.createAndBind(mContext,
- com.android.internal.R.bool.config_enableGeocoderOverlay,
- com.android.internal.R.string.config_geocoderProviderPackageName,
- com.android.internal.R.array.config_locationProviderPackageNames,
- mLocationHandler);
- if (mGeocodeProvider == null) {
- Slog.e(TAG, "no geocoder provider found");
- }
-
- // bind to fused provider
- FlpHardwareProvider flpHardwareProvider = FlpHardwareProvider.getInstance(mContext);
- FusedProxy fusedProxy = FusedProxy.createAndBind(
- mContext,
- mLocationHandler,
- flpHardwareProvider.getLocationHardware(),
- com.android.internal.R.bool.config_enableFusedLocationOverlay,
- com.android.internal.R.string.config_fusedLocationProviderPackageName,
- com.android.internal.R.array.config_locationProviderPackageNames);
- if(fusedProxy == null) {
- Slog.e(TAG, "No FusedProvider found.");
- }
-
- // bind to geofence provider
- GeofenceProxy provider = GeofenceProxy.createAndBind(mContext,
- com.android.internal.R.bool.config_enableGeofenceOverlay,
- com.android.internal.R.string.config_geofenceProviderPackageName,
- com.android.internal.R.array.config_locationProviderPackageNames,
- mLocationHandler,
- gpsProvider.getGpsGeofenceProxy(),
- flpHardwareProvider.getGeofenceHardware());
- if (provider == null) {
- Slog.e(TAG, "no geofence provider found");
- }
- }
-
- /**
- * Called when the device's active user changes.
- * @param userId the new active user's UserId
- */
- private void switchUser(int userId) {
- mBlacklist.switchUser(userId);
- mLocationHandler.removeMessages(MSG_LOCATION_CHANGED);
- synchronized (mLock) {
- mLastLocation.clear();
- mLastLocationCoarseInterval.clear();
- for (LocationProviderInterface p : mProviders) {
- updateProviderListenersLocked(p.getName(), false, mCurrentUserId);
- }
- mCurrentUserId = userId;
- updateProvidersLocked();
- }
- }
-
- /**
- * A wrapper class holding either an ILocationListener or a PendingIntent to receive
- * location updates.
- */
- private final class Receiver implements IBinder.DeathRecipient, PendingIntent.OnFinished {
- final int mUid; // uid of receiver
- final int mPid; // pid of receiver
- final String mPackageName; // package name of receiver
- final int mAllowedResolutionLevel; // resolution level allowed to receiver
-
- final ILocationListener mListener;
- final PendingIntent mPendingIntent;
- final WorkSource mWorkSource; // WorkSource for battery blame, or null to assign to caller.
- final boolean mHideFromAppOps; // True if AppOps should not monitor this receiver.
- final Object mKey;
-
- final HashMap<String,UpdateRecord> mUpdateRecords = new HashMap<String,UpdateRecord>();
-
- // True if app ops has started monitoring this receiver for locations.
- boolean mOpMonitoring;
- // True if app ops has started monitoring this receiver for high power (gps) locations.
- boolean mOpHighPowerMonitoring;
- int mPendingBroadcasts;
- PowerManager.WakeLock mWakeLock;
-
- Receiver(ILocationListener listener, PendingIntent intent, int pid, int uid,
- String packageName, WorkSource workSource, boolean hideFromAppOps) {
- mListener = listener;
- mPendingIntent = intent;
- if (listener != null) {
- mKey = listener.asBinder();
- } else {
- mKey = intent;
- }
- mAllowedResolutionLevel = getAllowedResolutionLevel(pid, uid);
- mUid = uid;
- mPid = pid;
- mPackageName = packageName;
- if (workSource != null && workSource.size() <= 0) {
- workSource = null;
- }
- mWorkSource = workSource;
- mHideFromAppOps = hideFromAppOps;
-
- updateMonitoring(true);
-
- // construct/configure wakelock
- mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
- if (workSource == null) {
- workSource = new WorkSource(mUid, mPackageName);
- }
- mWakeLock.setWorkSource(workSource);
- }
-
- @Override
- public boolean equals(Object otherObj) {
- if (otherObj instanceof Receiver) {
- return mKey.equals(((Receiver)otherObj).mKey);
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return mKey.hashCode();
- }
-
- @Override
- public String toString() {
- StringBuilder s = new StringBuilder();
- s.append("Reciever[");
- s.append(Integer.toHexString(System.identityHashCode(this)));
- if (mListener != null) {
- s.append(" listener");
- } else {
- s.append(" intent");
- }
- for (String p : mUpdateRecords.keySet()) {
- s.append(" ").append(mUpdateRecords.get(p).toString());
- }
- s.append("]");
- return s.toString();
- }
-
- /**
- * Update AppOp monitoring for this receiver.
- *
- * @param allow If true receiver is currently active, if false it's been removed.
- */
- public void updateMonitoring(boolean allow) {
- if (mHideFromAppOps) {
- return;
- }
-
- boolean requestingLocation = false;
- boolean requestingHighPowerLocation = false;
- if (allow) {
- // See if receiver has any enabled update records. Also note if any update records
- // are high power (has a high power provider with an interval under a threshold).
- for (UpdateRecord updateRecord : mUpdateRecords.values()) {
- if (isAllowedByCurrentUserSettingsLocked(updateRecord.mProvider)) {
- requestingLocation = true;
- LocationProviderInterface locationProvider
- = mProvidersByName.get(updateRecord.mProvider);
- ProviderProperties properties = locationProvider != null
- ? locationProvider.getProperties() : null;
- if (properties != null
- && properties.mPowerRequirement == Criteria.POWER_HIGH
- && updateRecord.mRequest.getInterval() < HIGH_POWER_INTERVAL_MS) {
- requestingHighPowerLocation = true;
- break;
- }
- }
- }
- }
-
- // First update monitoring of any location request (including high power).
- mOpMonitoring = updateMonitoring(
- requestingLocation,
- mOpMonitoring,
- AppOpsManager.OP_MONITOR_LOCATION);
-
- // Now update monitoring of high power requests only.
- boolean wasHighPowerMonitoring = mOpHighPowerMonitoring;
- mOpHighPowerMonitoring = updateMonitoring(
- requestingHighPowerLocation,
- mOpHighPowerMonitoring,
- AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION);
- if (mOpHighPowerMonitoring != wasHighPowerMonitoring) {
- // Send an intent to notify that a high power request has been added/removed.
- Intent intent = new Intent(LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION);
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
- }
- }
-
- /**
- * Update AppOps monitoring for a single location request and op type.
- *
- * @param allowMonitoring True if monitoring is allowed for this request/op.
- * @param currentlyMonitoring True if AppOps is currently monitoring this request/op.
- * @param op AppOps code for the op to update.
- * @return True if monitoring is on for this request/op after updating.
- */
- private boolean updateMonitoring(boolean allowMonitoring, boolean currentlyMonitoring,
- int op) {
- if (!currentlyMonitoring) {
- if (allowMonitoring) {
- return mAppOps.startOpNoThrow(op, mUid, mPackageName)
- == AppOpsManager.MODE_ALLOWED;
- }
- } else {
- if (!allowMonitoring || mAppOps.checkOpNoThrow(op, mUid, mPackageName)
- != AppOpsManager.MODE_ALLOWED) {
- mAppOps.finishOp(op, mUid, mPackageName);
- return false;
- }
- }
-
- return currentlyMonitoring;
- }
-
- public boolean isListener() {
- return mListener != null;
- }
-
- public boolean isPendingIntent() {
- return mPendingIntent != null;
- }
-
- public ILocationListener getListener() {
- if (mListener != null) {
- return mListener;
- }
- throw new IllegalStateException("Request for non-existent listener");
- }
-
- public boolean callStatusChangedLocked(String provider, int status, Bundle extras) {
- if (mListener != null) {
- try {
- synchronized (this) {
- // synchronize to ensure incrementPendingBroadcastsLocked()
- // is called before decrementPendingBroadcasts()
- mListener.onStatusChanged(provider, status, extras);
- // call this after broadcasting so we do not increment
- // if we throw an exeption.
- incrementPendingBroadcastsLocked();
- }
- } catch (RemoteException e) {
- return false;
- }
- } else {
- Intent statusChanged = new Intent();
- statusChanged.putExtras(new Bundle(extras));
- statusChanged.putExtra(LocationManager.KEY_STATUS_CHANGED, status);
- try {
- synchronized (this) {
- // synchronize to ensure incrementPendingBroadcastsLocked()
- // is called before decrementPendingBroadcasts()
- mPendingIntent.send(mContext, 0, statusChanged, this, mLocationHandler,
- getResolutionPermission(mAllowedResolutionLevel));
- // call this after broadcasting so we do not increment
- // if we throw an exeption.
- incrementPendingBroadcastsLocked();
- }
- } catch (PendingIntent.CanceledException e) {
- return false;
- }
- }
- return true;
- }
-
- public boolean callLocationChangedLocked(Location location) {
- if (mListener != null) {
- try {
- synchronized (this) {
- // synchronize to ensure incrementPendingBroadcastsLocked()
- // is called before decrementPendingBroadcasts()
- mListener.onLocationChanged(new Location(location));
- // call this after broadcasting so we do not increment
- // if we throw an exeption.
- incrementPendingBroadcastsLocked();
- }
- } catch (RemoteException e) {
- return false;
- }
- } else {
- Intent locationChanged = new Intent();
- locationChanged.putExtra(LocationManager.KEY_LOCATION_CHANGED, new Location(location));
- try {
- synchronized (this) {
- // synchronize to ensure incrementPendingBroadcastsLocked()
- // is called before decrementPendingBroadcasts()
- mPendingIntent.send(mContext, 0, locationChanged, this, mLocationHandler,
- getResolutionPermission(mAllowedResolutionLevel));
- // call this after broadcasting so we do not increment
- // if we throw an exeption.
- incrementPendingBroadcastsLocked();
- }
- } catch (PendingIntent.CanceledException e) {
- return false;
- }
- }
- return true;
- }
-
- public boolean callProviderEnabledLocked(String provider, boolean enabled) {
- // First update AppOp monitoring.
- // An app may get/lose location access as providers are enabled/disabled.
- updateMonitoring(true);
-
- if (mListener != null) {
- try {
- synchronized (this) {
- // synchronize to ensure incrementPendingBroadcastsLocked()
- // is called before decrementPendingBroadcasts()
- if (enabled) {
- mListener.onProviderEnabled(provider);
- } else {
- mListener.onProviderDisabled(provider);
- }
- // call this after broadcasting so we do not increment
- // if we throw an exeption.
- incrementPendingBroadcastsLocked();
- }
- } catch (RemoteException e) {
- return false;
- }
- } else {
- Intent providerIntent = new Intent();
- providerIntent.putExtra(LocationManager.KEY_PROVIDER_ENABLED, enabled);
- try {
- synchronized (this) {
- // synchronize to ensure incrementPendingBroadcastsLocked()
- // is called before decrementPendingBroadcasts()
- mPendingIntent.send(mContext, 0, providerIntent, this, mLocationHandler,
- getResolutionPermission(mAllowedResolutionLevel));
- // call this after broadcasting so we do not increment
- // if we throw an exeption.
- incrementPendingBroadcastsLocked();
- }
- } catch (PendingIntent.CanceledException e) {
- return false;
- }
- }
- return true;
- }
-
- @Override
- public void binderDied() {
- if (D) Log.d(TAG, "Location listener died");
-
- synchronized (mLock) {
- removeUpdatesLocked(this);
- }
- synchronized (this) {
- clearPendingBroadcastsLocked();
- }
- }
-
- @Override
- public void onSendFinished(PendingIntent pendingIntent, Intent intent,
- int resultCode, String resultData, Bundle resultExtras) {
- synchronized (this) {
- decrementPendingBroadcastsLocked();
- }
- }
-
- // this must be called while synchronized by caller in a synchronized block
- // containing the sending of the broadcaset
- private void incrementPendingBroadcastsLocked() {
- if (mPendingBroadcasts++ == 0) {
- mWakeLock.acquire();
- }
- }
-
- private void decrementPendingBroadcastsLocked() {
- if (--mPendingBroadcasts == 0) {
- if (mWakeLock.isHeld()) {
- mWakeLock.release();
- }
- }
- }
-
- public void clearPendingBroadcastsLocked() {
- if (mPendingBroadcasts > 0) {
- mPendingBroadcasts = 0;
- if (mWakeLock.isHeld()) {
- mWakeLock.release();
- }
- }
- }
- }
-
- @Override
- public void locationCallbackFinished(ILocationListener listener) {
- //Do not use getReceiverLocked here as that will add the ILocationListener to
- //the receiver list if it is not found. If it is not found then the
- //LocationListener was removed when it had a pending broadcast and should
- //not be added back.
- synchronized (mLock) {
- IBinder binder = listener.asBinder();
- Receiver receiver = mReceivers.get(binder);
- if (receiver != null) {
- synchronized (receiver) {
- // so wakelock calls will succeed
- long identity = Binder.clearCallingIdentity();
- receiver.decrementPendingBroadcastsLocked();
- Binder.restoreCallingIdentity(identity);
- }
- }
- }
- }
-
- private void addProviderLocked(LocationProviderInterface provider) {
- mProviders.add(provider);
- mProvidersByName.put(provider.getName(), provider);
- }
-
- private void removeProviderLocked(LocationProviderInterface provider) {
- provider.disable();
- mProviders.remove(provider);
- mProvidersByName.remove(provider.getName());
- }
-
- /**
- * Returns "true" if access to the specified location provider is allowed by the current
- * user's settings. Access to all location providers is forbidden to non-location-provider
- * processes belonging to background users.
- *
- * @param provider the name of the location provider
- * @return
- */
- private boolean isAllowedByCurrentUserSettingsLocked(String provider) {
- if (mEnabledProviders.contains(provider)) {
- return true;
- }
- if (mDisabledProviders.contains(provider)) {
- return false;
- }
- // Use system settings
- ContentResolver resolver = mContext.getContentResolver();
-
- return Settings.Secure.isLocationProviderEnabledForUser(resolver, provider, mCurrentUserId);
- }
-
- /**
- * Returns "true" if access to the specified location provider is allowed by the specified
- * user's settings. Access to all location providers is forbidden to non-location-provider
- * processes belonging to background users.
- *
- * @param provider the name of the location provider
- * @param uid the requestor's UID
- * @return
- */
- private boolean isAllowedByUserSettingsLocked(String provider, int uid) {
- if (UserHandle.getUserId(uid) != mCurrentUserId && !isUidALocationProvider(uid)) {
- return false;
- }
- return isAllowedByCurrentUserSettingsLocked(provider);
- }
-
- /**
- * Returns the permission string associated with the specified resolution level.
- *
- * @param resolutionLevel the resolution level
- * @return the permission string
- */
- private String getResolutionPermission(int resolutionLevel) {
- switch (resolutionLevel) {
- case RESOLUTION_LEVEL_FINE:
- return android.Manifest.permission.ACCESS_FINE_LOCATION;
- case RESOLUTION_LEVEL_COARSE:
- return android.Manifest.permission.ACCESS_COARSE_LOCATION;
- default:
- return null;
- }
- }
-
- /**
- * Returns the resolution level allowed to the given PID/UID pair.
- *
- * @param pid the PID
- * @param uid the UID
- * @return resolution level allowed to the pid/uid pair
- */
- private int getAllowedResolutionLevel(int pid, int uid) {
- if (mContext.checkPermission(android.Manifest.permission.ACCESS_FINE_LOCATION,
- pid, uid) == PackageManager.PERMISSION_GRANTED) {
- return RESOLUTION_LEVEL_FINE;
- } else if (mContext.checkPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION,
- pid, uid) == PackageManager.PERMISSION_GRANTED) {
- return RESOLUTION_LEVEL_COARSE;
- } else {
- return RESOLUTION_LEVEL_NONE;
- }
- }
-
- /**
- * Returns the resolution level allowed to the caller
- *
- * @return resolution level allowed to caller
- */
- private int getCallerAllowedResolutionLevel() {
- return getAllowedResolutionLevel(Binder.getCallingPid(), Binder.getCallingUid());
- }
-
- /**
- * Throw SecurityException if specified resolution level is insufficient to use geofences.
- *
- * @param allowedResolutionLevel resolution level allowed to caller
- */
- private void checkResolutionLevelIsSufficientForGeofenceUse(int allowedResolutionLevel) {
- if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
- throw new SecurityException("Geofence usage requires ACCESS_FINE_LOCATION permission");
- }
- }
-
- /**
- * Return the minimum resolution level required to use the specified location provider.
- *
- * @param provider the name of the location provider
- * @return minimum resolution level required for provider
- */
- private int getMinimumResolutionLevelForProviderUse(String provider) {
- if (LocationManager.GPS_PROVIDER.equals(provider) ||
- LocationManager.PASSIVE_PROVIDER.equals(provider)) {
- // gps and passive providers require FINE permission
- return RESOLUTION_LEVEL_FINE;
- } else if (LocationManager.NETWORK_PROVIDER.equals(provider) ||
- LocationManager.FUSED_PROVIDER.equals(provider)) {
- // network and fused providers are ok with COARSE or FINE
- return RESOLUTION_LEVEL_COARSE;
- } else {
- // mock providers
- LocationProviderInterface lp = mMockProviders.get(provider);
- if (lp != null) {
- ProviderProperties properties = lp.getProperties();
- if (properties != null) {
- if (properties.mRequiresSatellite) {
- // provider requiring satellites require FINE permission
- return RESOLUTION_LEVEL_FINE;
- } else if (properties.mRequiresNetwork || properties.mRequiresCell) {
- // provider requiring network and or cell require COARSE or FINE
- return RESOLUTION_LEVEL_COARSE;
- }
- }
- }
- }
- return RESOLUTION_LEVEL_FINE; // if in doubt, require FINE
- }
-
- /**
- * Throw SecurityException if specified resolution level is insufficient to use the named
- * location provider.
- *
- * @param allowedResolutionLevel resolution level allowed to caller
- * @param providerName the name of the location provider
- */
- private void checkResolutionLevelIsSufficientForProviderUse(int allowedResolutionLevel,
- String providerName) {
- int requiredResolutionLevel = getMinimumResolutionLevelForProviderUse(providerName);
- if (allowedResolutionLevel < requiredResolutionLevel) {
- switch (requiredResolutionLevel) {
- case RESOLUTION_LEVEL_FINE:
- throw new SecurityException("\"" + providerName + "\" location provider " +
- "requires ACCESS_FINE_LOCATION permission.");
- case RESOLUTION_LEVEL_COARSE:
- throw new SecurityException("\"" + providerName + "\" location provider " +
- "requires ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission.");
- default:
- throw new SecurityException("Insufficient permission for \"" + providerName +
- "\" location provider.");
- }
- }
- }
-
- /**
- * Throw SecurityException if WorkSource use is not allowed (i.e. can't blame other packages
- * for battery).
- */
- private void checkDeviceStatsAllowed() {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.UPDATE_DEVICE_STATS, null);
- }
-
- private void checkUpdateAppOpsAllowed() {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.UPDATE_APP_OPS_STATS, null);
- }
-
- public static int resolutionLevelToOp(int allowedResolutionLevel) {
- if (allowedResolutionLevel != RESOLUTION_LEVEL_NONE) {
- if (allowedResolutionLevel == RESOLUTION_LEVEL_COARSE) {
- return AppOpsManager.OP_COARSE_LOCATION;
- } else {
- return AppOpsManager.OP_FINE_LOCATION;
- }
- }
- return -1;
- }
-
- boolean reportLocationAccessNoThrow(int uid, String packageName, int allowedResolutionLevel) {
- int op = resolutionLevelToOp(allowedResolutionLevel);
- if (op >= 0) {
- if (mAppOps.noteOpNoThrow(op, uid, packageName) != AppOpsManager.MODE_ALLOWED) {
- return false;
- }
- }
- return true;
- }
-
- boolean checkLocationAccess(int uid, String packageName, int allowedResolutionLevel) {
- int op = resolutionLevelToOp(allowedResolutionLevel);
- if (op >= 0) {
- if (mAppOps.checkOp(op, uid, packageName) != AppOpsManager.MODE_ALLOWED) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Returns all providers by name, including passive, but excluding
- * fused, also including ones that are not permitted to
- * be accessed by the calling activity or are currently disabled.
- */
- @Override
- public List<String> getAllProviders() {
- ArrayList<String> out;
- synchronized (mLock) {
- out = new ArrayList<String>(mProviders.size());
- for (LocationProviderInterface provider : mProviders) {
- String name = provider.getName();
- if (LocationManager.FUSED_PROVIDER.equals(name)) {
- continue;
- }
- out.add(name);
- }
- }
-
- if (D) Log.d(TAG, "getAllProviders()=" + out);
- return out;
- }
-
- /**
- * Return all providers by name, that match criteria and are optionally
- * enabled.
- * Can return passive provider, but never returns fused provider.
- */
- @Override
- public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
- int allowedResolutionLevel = getCallerAllowedResolutionLevel();
- ArrayList<String> out;
- int uid = Binder.getCallingUid();;
- long identity = Binder.clearCallingIdentity();
- try {
- synchronized (mLock) {
- out = new ArrayList<String>(mProviders.size());
- for (LocationProviderInterface provider : mProviders) {
- String name = provider.getName();
- if (LocationManager.FUSED_PROVIDER.equals(name)) {
- continue;
- }
- if (allowedResolutionLevel >= getMinimumResolutionLevelForProviderUse(name)) {
- if (enabledOnly && !isAllowedByUserSettingsLocked(name, uid)) {
- continue;
- }
- if (criteria != null && !LocationProvider.propertiesMeetCriteria(
- name, provider.getProperties(), criteria)) {
- continue;
- }
- out.add(name);
- }
- }
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
-
- if (D) Log.d(TAG, "getProviders()=" + out);
- return out;
- }
-
- /**
- * Return the name of the best provider given a Criteria object.
- * This method has been deprecated from the public API,
- * and the whole LocationProvider (including #meetsCriteria)
- * has been deprecated as well. So this method now uses
- * some simplified logic.
- */
- @Override
- public String getBestProvider(Criteria criteria, boolean enabledOnly) {
- String result = null;
-
- List<String> providers = getProviders(criteria, enabledOnly);
- if (!providers.isEmpty()) {
- result = pickBest(providers);
- if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
- return result;
- }
- providers = getProviders(null, enabledOnly);
- if (!providers.isEmpty()) {
- result = pickBest(providers);
- if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
- return result;
- }
-
- if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
- return null;
- }
-
- private String pickBest(List<String> providers) {
- if (providers.contains(LocationManager.GPS_PROVIDER)) {
- return LocationManager.GPS_PROVIDER;
- } else if (providers.contains(LocationManager.NETWORK_PROVIDER)) {
- return LocationManager.NETWORK_PROVIDER;
- } else {
- return providers.get(0);
- }
- }
-
- @Override
- public boolean providerMeetsCriteria(String provider, Criteria criteria) {
- LocationProviderInterface p = mProvidersByName.get(provider);
- if (p == null) {
- throw new IllegalArgumentException("provider=" + provider);
- }
-
- boolean result = LocationProvider.propertiesMeetCriteria(
- p.getName(), p.getProperties(), criteria);
- if (D) Log.d(TAG, "providerMeetsCriteria(" + provider + ", " + criteria + ")=" + result);
- return result;
- }
-
- private void updateProvidersLocked() {
- boolean changesMade = false;
- for (int i = mProviders.size() - 1; i >= 0; i--) {
- LocationProviderInterface p = mProviders.get(i);
- boolean isEnabled = p.isEnabled();
- String name = p.getName();
- boolean shouldBeEnabled = isAllowedByCurrentUserSettingsLocked(name);
- if (isEnabled && !shouldBeEnabled) {
- updateProviderListenersLocked(name, false, mCurrentUserId);
- // If any provider has been disabled, clear all last locations for all providers.
- // This is to be on the safe side in case a provider has location derived from
- // this disabled provider.
- mLastLocation.clear();
- mLastLocationCoarseInterval.clear();
- changesMade = true;
- } else if (!isEnabled && shouldBeEnabled) {
- updateProviderListenersLocked(name, true, mCurrentUserId);
- changesMade = true;
- }
- }
- if (changesMade) {
- mContext.sendBroadcastAsUser(new Intent(LocationManager.PROVIDERS_CHANGED_ACTION),
- UserHandle.ALL);
- mContext.sendBroadcastAsUser(new Intent(LocationManager.MODE_CHANGED_ACTION),
- UserHandle.ALL);
- }
- }
-
- private void updateProviderListenersLocked(String provider, boolean enabled, int userId) {
- int listeners = 0;
-
- LocationProviderInterface p = mProvidersByName.get(provider);
- if (p == null) return;
-
- ArrayList<Receiver> deadReceivers = null;
-
- ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
- if (records != null) {
- final int N = records.size();
- for (int i = 0; i < N; i++) {
- UpdateRecord record = records.get(i);
- if (UserHandle.getUserId(record.mReceiver.mUid) == userId) {
- // Sends a notification message to the receiver
- if (!record.mReceiver.callProviderEnabledLocked(provider, enabled)) {
- if (deadReceivers == null) {
- deadReceivers = new ArrayList<Receiver>();
- }
- deadReceivers.add(record.mReceiver);
- }
- listeners++;
- }
- }
- }
-
- if (deadReceivers != null) {
- for (int i = deadReceivers.size() - 1; i >= 0; i--) {
- removeUpdatesLocked(deadReceivers.get(i));
- }
- }
-
- if (enabled) {
- p.enable();
- if (listeners > 0) {
- applyRequirementsLocked(provider);
- }
- } else {
- p.disable();
- }
- }
-
- private void applyRequirementsLocked(String provider) {
- LocationProviderInterface p = mProvidersByName.get(provider);
- if (p == null) return;
-
- ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
- WorkSource worksource = new WorkSource();
- ProviderRequest providerRequest = new ProviderRequest();
-
- if (records != null) {
- for (UpdateRecord record : records) {
- if (UserHandle.getUserId(record.mReceiver.mUid) == mCurrentUserId) {
- if (checkLocationAccess(record.mReceiver.mUid, record.mReceiver.mPackageName,
- record.mReceiver.mAllowedResolutionLevel)) {
- LocationRequest locationRequest = record.mRequest;
- providerRequest.locationRequests.add(locationRequest);
- if (locationRequest.getInterval() < providerRequest.interval) {
- providerRequest.reportLocation = true;
- providerRequest.interval = locationRequest.getInterval();
- }
- }
- }
- }
-
- if (providerRequest.reportLocation) {
- // calculate who to blame for power
- // This is somewhat arbitrary. We pick a threshold interval
- // that is slightly higher that the minimum interval, and
- // spread the blame across all applications with a request
- // under that threshold.
- long thresholdInterval = (providerRequest.interval + 1000) * 3 / 2;
- for (UpdateRecord record : records) {
- if (UserHandle.getUserId(record.mReceiver.mUid) == mCurrentUserId) {
- LocationRequest locationRequest = record.mRequest;
- if (locationRequest.getInterval() <= thresholdInterval) {
- if (record.mReceiver.mWorkSource != null
- && record.mReceiver.mWorkSource.size() > 0
- && record.mReceiver.mWorkSource.getName(0) != null) {
- // Assign blame to another work source.
- // Can only assign blame if the WorkSource contains names.
- worksource.add(record.mReceiver.mWorkSource);
- } else {
- // Assign blame to caller.
- worksource.add(
- record.mReceiver.mUid,
- record.mReceiver.mPackageName);
- }
- }
- }
- }
- }
- }
-
- if (D) Log.d(TAG, "provider request: " + provider + " " + providerRequest);
- p.setRequest(providerRequest, worksource);
- }
-
- private class UpdateRecord {
- final String mProvider;
- final LocationRequest mRequest;
- final Receiver mReceiver;
- Location mLastFixBroadcast;
- long mLastStatusBroadcast;
-
- /**
- * Note: must be constructed with lock held.
- */
- UpdateRecord(String provider, LocationRequest request, Receiver receiver) {
- mProvider = provider;
- mRequest = request;
- mReceiver = receiver;
-
- ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
- if (records == null) {
- records = new ArrayList<UpdateRecord>();
- mRecordsByProvider.put(provider, records);
- }
- if (!records.contains(this)) {
- records.add(this);
- }
- }
-
- /**
- * Method to be called when a record will no longer be used. Calling this multiple times
- * must have the same effect as calling it once.
- */
- void disposeLocked(boolean removeReceiver) {
- // remove from mRecordsByProvider
- ArrayList<UpdateRecord> globalRecords = mRecordsByProvider.get(this.mProvider);
- if (globalRecords != null) {
- globalRecords.remove(this);
- }
-
- if (!removeReceiver) return; // the caller will handle the rest
-
- // remove from Receiver#mUpdateRecords
- HashMap<String, UpdateRecord> receiverRecords = mReceiver.mUpdateRecords;
- if (receiverRecords != null) {
- receiverRecords.remove(this.mProvider);
-
- // and also remove the Receiver if it has no more update records
- if (removeReceiver && receiverRecords.size() == 0) {
- removeUpdatesLocked(mReceiver);
- }
- }
- }
-
- @Override
- public String toString() {
- StringBuilder s = new StringBuilder();
- s.append("UpdateRecord[");
- s.append(mProvider);
- s.append(' ').append(mReceiver.mPackageName).append('(');
- s.append(mReceiver.mUid).append(')');
- s.append(' ').append(mRequest);
- s.append(']');
- return s.toString();
- }
- }
-
- private Receiver getReceiverLocked(ILocationListener listener, int pid, int uid,
- String packageName, WorkSource workSource, boolean hideFromAppOps) {
- IBinder binder = listener.asBinder();
- Receiver receiver = mReceivers.get(binder);
- if (receiver == null) {
- receiver = new Receiver(listener, null, pid, uid, packageName, workSource,
- hideFromAppOps);
- mReceivers.put(binder, receiver);
-
- try {
- receiver.getListener().asBinder().linkToDeath(receiver, 0);
- } catch (RemoteException e) {
- Slog.e(TAG, "linkToDeath failed:", e);
- return null;
- }
- }
- return receiver;
- }
-
- private Receiver getReceiverLocked(PendingIntent intent, int pid, int uid, String packageName,
- WorkSource workSource, boolean hideFromAppOps) {
- Receiver receiver = mReceivers.get(intent);
- if (receiver == null) {
- receiver = new Receiver(null, intent, pid, uid, packageName, workSource,
- hideFromAppOps);
- mReceivers.put(intent, receiver);
- }
- return receiver;
- }
-
- /**
- * Creates a LocationRequest based upon the supplied LocationRequest that to meets resolution
- * and consistency requirements.
- *
- * @param request the LocationRequest from which to create a sanitized version
- * @return a version of request that meets the given resolution and consistency requirements
- * @hide
- */
- private LocationRequest createSanitizedRequest(LocationRequest request, int resolutionLevel) {
- LocationRequest sanitizedRequest = new LocationRequest(request);
- if (resolutionLevel < RESOLUTION_LEVEL_FINE) {
- switch (sanitizedRequest.getQuality()) {
- case LocationRequest.ACCURACY_FINE:
- sanitizedRequest.setQuality(LocationRequest.ACCURACY_BLOCK);
- break;
- case LocationRequest.POWER_HIGH:
- sanitizedRequest.setQuality(LocationRequest.POWER_LOW);
- break;
- }
- // throttle
- if (sanitizedRequest.getInterval() < LocationFudger.FASTEST_INTERVAL_MS) {
- sanitizedRequest.setInterval(LocationFudger.FASTEST_INTERVAL_MS);
- }
- if (sanitizedRequest.getFastestInterval() < LocationFudger.FASTEST_INTERVAL_MS) {
- sanitizedRequest.setFastestInterval(LocationFudger.FASTEST_INTERVAL_MS);
- }
- }
- // make getFastestInterval() the minimum of interval and fastest interval
- if (sanitizedRequest.getFastestInterval() > sanitizedRequest.getInterval()) {
- request.setFastestInterval(request.getInterval());
- }
- return sanitizedRequest;
- }
-
- private void checkPackageName(String packageName) {
- if (packageName == null) {
- throw new SecurityException("invalid package name: " + packageName);
- }
- int uid = Binder.getCallingUid();
- String[] packages = mPackageManager.getPackagesForUid(uid);
- if (packages == null) {
- throw new SecurityException("invalid UID " + uid);
- }
- for (String pkg : packages) {
- if (packageName.equals(pkg)) return;
- }
- throw new SecurityException("invalid package name: " + packageName);
- }
-
- private void checkPendingIntent(PendingIntent intent) {
- if (intent == null) {
- throw new IllegalArgumentException("invalid pending intent: " + intent);
- }
- }
-
- private Receiver checkListenerOrIntentLocked(ILocationListener listener, PendingIntent intent,
- int pid, int uid, String packageName, WorkSource workSource, boolean hideFromAppOps) {
- if (intent == null && listener == null) {
- throw new IllegalArgumentException("need either listener or intent");
- } else if (intent != null && listener != null) {
- throw new IllegalArgumentException("cannot register both listener and intent");
- } else if (intent != null) {
- checkPendingIntent(intent);
- return getReceiverLocked(intent, pid, uid, packageName, workSource, hideFromAppOps);
- } else {
- return getReceiverLocked(listener, pid, uid, packageName, workSource, hideFromAppOps);
- }
- }
-
- @Override
- public void requestLocationUpdates(LocationRequest request, ILocationListener listener,
- PendingIntent intent, String packageName) {
- if (request == null) request = DEFAULT_LOCATION_REQUEST;
- checkPackageName(packageName);
- int allowedResolutionLevel = getCallerAllowedResolutionLevel();
- checkResolutionLevelIsSufficientForProviderUse(allowedResolutionLevel,
- request.getProvider());
- WorkSource workSource = request.getWorkSource();
- if (workSource != null && workSource.size() > 0) {
- checkDeviceStatsAllowed();
- }
- boolean hideFromAppOps = request.getHideFromAppOps();
- if (hideFromAppOps) {
- checkUpdateAppOpsAllowed();
- }
- LocationRequest sanitizedRequest = createSanitizedRequest(request, allowedResolutionLevel);
-
- final int pid = Binder.getCallingPid();
- final int uid = Binder.getCallingUid();
- // providers may use public location API's, need to clear identity
- long identity = Binder.clearCallingIdentity();
- try {
- // We don't check for MODE_IGNORED here; we will do that when we go to deliver
- // a location.
- checkLocationAccess(uid, packageName, allowedResolutionLevel);
-
- synchronized (mLock) {
- Receiver recevier = checkListenerOrIntentLocked(listener, intent, pid, uid,
- packageName, workSource, hideFromAppOps);
- requestLocationUpdatesLocked(sanitizedRequest, recevier, pid, uid, packageName);
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- private void requestLocationUpdatesLocked(LocationRequest request, Receiver receiver,
- int pid, int uid, String packageName) {
- // Figure out the provider. Either its explicitly request (legacy use cases), or
- // use the fused provider
- if (request == null) request = DEFAULT_LOCATION_REQUEST;
- String name = request.getProvider();
- if (name == null) {
- throw new IllegalArgumentException("provider name must not be null");
- }
-
- if (D) Log.d(TAG, "request " + Integer.toHexString(System.identityHashCode(receiver))
- + " " + name + " " + request + " from " + packageName + "(" + uid + ")");
- LocationProviderInterface provider = mProvidersByName.get(name);
- if (provider == null) {
- throw new IllegalArgumentException("provider doesn't exist: " + name);
- }
-
- UpdateRecord record = new UpdateRecord(name, request, receiver);
- UpdateRecord oldRecord = receiver.mUpdateRecords.put(name, record);
- if (oldRecord != null) {
- oldRecord.disposeLocked(false);
- }
-
- boolean isProviderEnabled = isAllowedByUserSettingsLocked(name, uid);
- if (isProviderEnabled) {
- applyRequirementsLocked(name);
- } else {
- // Notify the listener that updates are currently disabled
- receiver.callProviderEnabledLocked(name, false);
- }
- // Update the monitoring here just in case multiple location requests were added to the
- // same receiver (this request may be high power and the initial might not have been).
- receiver.updateMonitoring(true);
- }
-
- @Override
- public void removeUpdates(ILocationListener listener, PendingIntent intent,
- String packageName) {
- checkPackageName(packageName);
-
- final int pid = Binder.getCallingPid();
- final int uid = Binder.getCallingUid();
-
- synchronized (mLock) {
- WorkSource workSource = null;
- boolean hideFromAppOps = false;
- Receiver receiver = checkListenerOrIntentLocked(listener, intent, pid, uid,
- packageName, workSource, hideFromAppOps);
-
- // providers may use public location API's, need to clear identity
- long identity = Binder.clearCallingIdentity();
- try {
- removeUpdatesLocked(receiver);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
- }
-
- private void removeUpdatesLocked(Receiver receiver) {
- if (D) Log.i(TAG, "remove " + Integer.toHexString(System.identityHashCode(receiver)));
-
- if (mReceivers.remove(receiver.mKey) != null && receiver.isListener()) {
- receiver.getListener().asBinder().unlinkToDeath(receiver, 0);
- synchronized (receiver) {
- receiver.clearPendingBroadcastsLocked();
- }
- }
-
- receiver.updateMonitoring(false);
-
- // Record which providers were associated with this listener
- HashSet<String> providers = new HashSet<String>();
- HashMap<String, UpdateRecord> oldRecords = receiver.mUpdateRecords;
- if (oldRecords != null) {
- // Call dispose() on the obsolete update records.
- for (UpdateRecord record : oldRecords.values()) {
- record.disposeLocked(false);
- }
- // Accumulate providers
- providers.addAll(oldRecords.keySet());
- }
-
- // update provider
- for (String provider : providers) {
- // If provider is already disabled, don't need to do anything
- if (!isAllowedByCurrentUserSettingsLocked(provider)) {
- continue;
- }
-
- applyRequirementsLocked(provider);
- }
- }
-
- private void applyAllProviderRequirementsLocked() {
- for (LocationProviderInterface p : mProviders) {
- // If provider is already disabled, don't need to do anything
- if (!isAllowedByCurrentUserSettingsLocked(p.getName())) {
- continue;
- }
-
- applyRequirementsLocked(p.getName());
- }
- }
-
- @Override
- public Location getLastLocation(LocationRequest request, String packageName) {
- if (D) Log.d(TAG, "getLastLocation: " + request);
- if (request == null) request = DEFAULT_LOCATION_REQUEST;
- int allowedResolutionLevel = getCallerAllowedResolutionLevel();
- checkPackageName(packageName);
- checkResolutionLevelIsSufficientForProviderUse(allowedResolutionLevel,
- request.getProvider());
- // no need to sanitize this request, as only the provider name is used
-
- final int uid = Binder.getCallingUid();
- final long identity = Binder.clearCallingIdentity();
- try {
- if (mBlacklist.isBlacklisted(packageName)) {
- if (D) Log.d(TAG, "not returning last loc for blacklisted app: " +
- packageName);
- return null;
- }
-
- if (!reportLocationAccessNoThrow(uid, packageName, allowedResolutionLevel)) {
- if (D) Log.d(TAG, "not returning last loc for no op app: " +
- packageName);
- return null;
- }
-
- synchronized (mLock) {
- // Figure out the provider. Either its explicitly request (deprecated API's),
- // or use the fused provider
- String name = request.getProvider();
- if (name == null) name = LocationManager.FUSED_PROVIDER;
- LocationProviderInterface provider = mProvidersByName.get(name);
- if (provider == null) return null;
-
- if (!isAllowedByUserSettingsLocked(name, uid)) return null;
-
- Location location;
- if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
- // Make sure that an app with coarse permissions can't get frequent location
- // updates by calling LocationManager.getLastKnownLocation repeatedly.
- location = mLastLocationCoarseInterval.get(name);
- } else {
- location = mLastLocation.get(name);
- }
- if (location == null) {
- return null;
- }
- if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
- Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
- if (noGPSLocation != null) {
- return new Location(mLocationFudger.getOrCreate(noGPSLocation));
- }
- } else {
- return new Location(location);
- }
- }
- return null;
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- @Override
- public void requestGeofence(LocationRequest request, Geofence geofence, PendingIntent intent,
- String packageName) {
- if (request == null) request = DEFAULT_LOCATION_REQUEST;
- int allowedResolutionLevel = getCallerAllowedResolutionLevel();
- checkResolutionLevelIsSufficientForGeofenceUse(allowedResolutionLevel);
- checkPendingIntent(intent);
- checkPackageName(packageName);
- checkResolutionLevelIsSufficientForProviderUse(allowedResolutionLevel,
- request.getProvider());
- LocationRequest sanitizedRequest = createSanitizedRequest(request, allowedResolutionLevel);
-
- if (D) Log.d(TAG, "requestGeofence: " + sanitizedRequest + " " + geofence + " " + intent);
-
- // geo-fence manager uses the public location API, need to clear identity
- int uid = Binder.getCallingUid();
- if (UserHandle.getUserId(uid) != UserHandle.USER_OWNER) {
- // temporary measure until geofences work for secondary users
- Log.w(TAG, "proximity alerts are currently available only to the primary user");
- return;
- }
- long identity = Binder.clearCallingIdentity();
- try {
- mGeofenceManager.addFence(sanitizedRequest, geofence, intent, allowedResolutionLevel,
- uid, packageName);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- @Override
- public void removeGeofence(Geofence geofence, PendingIntent intent, String packageName) {
- checkResolutionLevelIsSufficientForGeofenceUse(getCallerAllowedResolutionLevel());
- checkPendingIntent(intent);
- checkPackageName(packageName);
-
- if (D) Log.d(TAG, "removeGeofence: " + geofence + " " + intent);
-
- // geo-fence manager uses the public location API, need to clear identity
- long identity = Binder.clearCallingIdentity();
- try {
- mGeofenceManager.removeFence(geofence, intent);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
-
- @Override
- public boolean addGpsStatusListener(IGpsStatusListener listener, String packageName) {
- if (mGpsStatusProvider == null) {
- return false;
- }
- int allowedResolutionLevel = getCallerAllowedResolutionLevel();
- checkResolutionLevelIsSufficientForProviderUse(allowedResolutionLevel,
- LocationManager.GPS_PROVIDER);
-
- final int uid = Binder.getCallingUid();
- final long ident = Binder.clearCallingIdentity();
- try {
- if (!checkLocationAccess(uid, packageName, allowedResolutionLevel)) {
- return false;
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
-
- try {
- mGpsStatusProvider.addGpsStatusListener(listener);
- } catch (RemoteException e) {
- Slog.e(TAG, "mGpsStatusProvider.addGpsStatusListener failed", e);
- return false;
- }
- return true;
- }
-
- @Override
- public void removeGpsStatusListener(IGpsStatusListener listener) {
- synchronized (mLock) {
- try {
- mGpsStatusProvider.removeGpsStatusListener(listener);
- } catch (Exception e) {
- Slog.e(TAG, "mGpsStatusProvider.removeGpsStatusListener failed", e);
- }
- }
- }
-
- @Override
- public boolean sendExtraCommand(String provider, String command, Bundle extras) {
- if (provider == null) {
- // throw NullPointerException to remain compatible with previous implementation
- throw new NullPointerException();
- }
- checkResolutionLevelIsSufficientForProviderUse(getCallerAllowedResolutionLevel(),
- provider);
-
- // and check for ACCESS_LOCATION_EXTRA_COMMANDS
- if ((mContext.checkCallingOrSelfPermission(ACCESS_LOCATION_EXTRA_COMMANDS)
- != PackageManager.PERMISSION_GRANTED)) {
- throw new SecurityException("Requires ACCESS_LOCATION_EXTRA_COMMANDS permission");
- }
-
- synchronized (mLock) {
- LocationProviderInterface p = mProvidersByName.get(provider);
- if (p == null) return false;
-
- return p.sendExtraCommand(command, extras);
- }
- }
-
- @Override
- public boolean sendNiResponse(int notifId, int userResponse) {
- if (Binder.getCallingUid() != Process.myUid()) {
- throw new SecurityException(
- "calling sendNiResponse from outside of the system is not allowed");
- }
- try {
- return mNetInitiatedListener.sendNiResponse(notifId, userResponse);
- } catch (RemoteException e) {
- Slog.e(TAG, "RemoteException in LocationManagerService.sendNiResponse");
- return false;
- }
- }
-
- /**
- * @return null if the provider does not exist
- * @throws SecurityException if the provider is not allowed to be
- * accessed by the caller
- */
- @Override
- public ProviderProperties getProviderProperties(String provider) {
- if (mProvidersByName.get(provider) == null) {
- return null;
- }
-
- checkResolutionLevelIsSufficientForProviderUse(getCallerAllowedResolutionLevel(),
- provider);
-
- LocationProviderInterface p;
- synchronized (mLock) {
- p = mProvidersByName.get(provider);
- }
-
- if (p == null) return null;
- return p.getProperties();
- }
-
- @Override
- public boolean isProviderEnabled(String provider) {
- // TODO: remove this check in next release, see b/10696351
- checkResolutionLevelIsSufficientForProviderUse(getCallerAllowedResolutionLevel(),
- provider);
-
- // Fused provider is accessed indirectly via criteria rather than the provider-based APIs,
- // so we discourage its use
- if (LocationManager.FUSED_PROVIDER.equals(provider)) return false;
-
- int uid = Binder.getCallingUid();
- long identity = Binder.clearCallingIdentity();
- try {
- synchronized (mLock) {
- LocationProviderInterface p = mProvidersByName.get(provider);
- if (p == null) return false;
-
- return isAllowedByUserSettingsLocked(provider, uid);
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- /**
- * Returns "true" if the UID belongs to a bound location provider.
- *
- * @param uid the uid
- * @return true if uid belongs to a bound location provider
- */
- private boolean isUidALocationProvider(int uid) {
- if (uid == Process.SYSTEM_UID) {
- return true;
- }
- if (mGeocodeProvider != null) {
- if (doesPackageHaveUid(uid, mGeocodeProvider.getConnectedPackageName())) return true;
- }
- for (LocationProviderProxy proxy : mProxyProviders) {
- if (doesPackageHaveUid(uid, proxy.getConnectedPackageName())) return true;
- }
- return false;
- }
-
- private void checkCallerIsProvider() {
- if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER)
- == PackageManager.PERMISSION_GRANTED) {
- return;
- }
-
- // Previously we only used the INSTALL_LOCATION_PROVIDER
- // check. But that is system or signature
- // protection level which is not flexible enough for
- // providers installed oustide the system image. So
- // also allow providers with a UID matching the
- // currently bound package name
-
- if (isUidALocationProvider(Binder.getCallingUid())) {
- return;
- }
-
- throw new SecurityException("need INSTALL_LOCATION_PROVIDER permission, " +
- "or UID of a currently bound location provider");
- }
-
- private boolean doesPackageHaveUid(int uid, String packageName) {
- if (packageName == null) {
- return false;
- }
- try {
- ApplicationInfo appInfo = mPackageManager.getApplicationInfo(packageName, 0);
- if (appInfo.uid != uid) {
- return false;
- }
- } catch (NameNotFoundException e) {
- return false;
- }
- return true;
- }
-
- @Override
- public void reportLocation(Location location, boolean passive) {
- checkCallerIsProvider();
-
- if (!location.isComplete()) {
- Log.w(TAG, "Dropping incomplete location: " + location);
- return;
- }
-
- mLocationHandler.removeMessages(MSG_LOCATION_CHANGED, location);
- Message m = Message.obtain(mLocationHandler, MSG_LOCATION_CHANGED, location);
- m.arg1 = (passive ? 1 : 0);
- mLocationHandler.sendMessageAtFrontOfQueue(m);
- }
-
-
- private static boolean shouldBroadcastSafe(
- Location loc, Location lastLoc, UpdateRecord record, long now) {
- // Always broadcast the first update
- if (lastLoc == null) {
- return true;
- }
-
- // Check whether sufficient time has passed
- long minTime = record.mRequest.getFastestInterval();
- long delta = (loc.getElapsedRealtimeNanos() - lastLoc.getElapsedRealtimeNanos())
- / NANOS_PER_MILLI;
- if (delta < minTime - MAX_PROVIDER_SCHEDULING_JITTER_MS) {
- return false;
- }
-
- // Check whether sufficient distance has been traveled
- double minDistance = record.mRequest.getSmallestDisplacement();
- if (minDistance > 0.0) {
- if (loc.distanceTo(lastLoc) <= minDistance) {
- return false;
- }
- }
-
- // Check whether sufficient number of udpates is left
- if (record.mRequest.getNumUpdates() <= 0) {
- return false;
- }
-
- // Check whether the expiry date has passed
- if (record.mRequest.getExpireAt() < now) {
- return false;
- }
-
- return true;
- }
-
- private void handleLocationChangedLocked(Location location, boolean passive) {
- if (D) Log.d(TAG, "incoming location: " + location);
-
- long now = SystemClock.elapsedRealtime();
- String provider = (passive ? LocationManager.PASSIVE_PROVIDER : location.getProvider());
-
- // Skip if the provider is unknown.
- LocationProviderInterface p = mProvidersByName.get(provider);
- if (p == null) return;
-
- // Update last known locations
- Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
- Location lastNoGPSLocation = null;
- Location lastLocation = mLastLocation.get(provider);
- if (lastLocation == null) {
- lastLocation = new Location(provider);
- mLastLocation.put(provider, lastLocation);
- } else {
- lastNoGPSLocation = lastLocation.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
- if (noGPSLocation == null && lastNoGPSLocation != null) {
- // New location has no no-GPS location: adopt last no-GPS location. This is set
- // directly into location because we do not want to notify COARSE clients.
- location.setExtraLocation(Location.EXTRA_NO_GPS_LOCATION, lastNoGPSLocation);
- }
- }
- lastLocation.set(location);
-
- // Update last known coarse interval location if enough time has passed.
- Location lastLocationCoarseInterval = mLastLocationCoarseInterval.get(provider);
- if (lastLocationCoarseInterval == null) {
- lastLocationCoarseInterval = new Location(location);
- mLastLocationCoarseInterval.put(provider, lastLocationCoarseInterval);
- }
- long timeDiffNanos = location.getElapsedRealtimeNanos()
- - lastLocationCoarseInterval.getElapsedRealtimeNanos();
- if (timeDiffNanos > LocationFudger.FASTEST_INTERVAL_MS * NANOS_PER_MILLI) {
- lastLocationCoarseInterval.set(location);
- }
- // Don't ever return a coarse location that is more recent than the allowed update
- // interval (i.e. don't allow an app to keep registering and unregistering for
- // location updates to overcome the minimum interval).
- noGPSLocation =
- lastLocationCoarseInterval.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
-
- // Skip if there are no UpdateRecords for this provider.
- ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
- if (records == null || records.size() == 0) return;
-
- // Fetch coarse location
- Location coarseLocation = null;
- if (noGPSLocation != null) {
- coarseLocation = mLocationFudger.getOrCreate(noGPSLocation);
- }
-
- // Fetch latest status update time
- long newStatusUpdateTime = p.getStatusUpdateTime();
-
- // Get latest status
- Bundle extras = new Bundle();
- int status = p.getStatus(extras);
-
- ArrayList<Receiver> deadReceivers = null;
- ArrayList<UpdateRecord> deadUpdateRecords = null;
-
- // Broadcast location or status to all listeners
- for (UpdateRecord r : records) {
- Receiver receiver = r.mReceiver;
- boolean receiverDead = false;
-
- int receiverUserId = UserHandle.getUserId(receiver.mUid);
- if (receiverUserId != mCurrentUserId && !isUidALocationProvider(receiver.mUid)) {
- if (D) {
- Log.d(TAG, "skipping loc update for background user " + receiverUserId +
- " (current user: " + mCurrentUserId + ", app: " +
- receiver.mPackageName + ")");
- }
- continue;
- }
-
- if (mBlacklist.isBlacklisted(receiver.mPackageName)) {
- if (D) Log.d(TAG, "skipping loc update for blacklisted app: " +
- receiver.mPackageName);
- continue;
- }
-
- if (!reportLocationAccessNoThrow(receiver.mUid, receiver.mPackageName,
- receiver.mAllowedResolutionLevel)) {
- if (D) Log.d(TAG, "skipping loc update for no op app: " +
- receiver.mPackageName);
- continue;
- }
-
- Location notifyLocation = null;
- if (receiver.mAllowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
- notifyLocation = coarseLocation; // use coarse location
- } else {
- notifyLocation = lastLocation; // use fine location
- }
- if (notifyLocation != null) {
- Location lastLoc = r.mLastFixBroadcast;
- if ((lastLoc == null) || shouldBroadcastSafe(notifyLocation, lastLoc, r, now)) {
- if (lastLoc == null) {
- lastLoc = new Location(notifyLocation);
- r.mLastFixBroadcast = lastLoc;
- } else {
- lastLoc.set(notifyLocation);
- }
- if (!receiver.callLocationChangedLocked(notifyLocation)) {
- Slog.w(TAG, "RemoteException calling onLocationChanged on " + receiver);
- receiverDead = true;
- }
- r.mRequest.decrementNumUpdates();
- }
- }
-
- long prevStatusUpdateTime = r.mLastStatusBroadcast;
- if ((newStatusUpdateTime > prevStatusUpdateTime) &&
- (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) {
-
- r.mLastStatusBroadcast = newStatusUpdateTime;
- if (!receiver.callStatusChangedLocked(provider, status, extras)) {
- receiverDead = true;
- Slog.w(TAG, "RemoteException calling onStatusChanged on " + receiver);
- }
- }
-
- // track expired records
- if (r.mRequest.getNumUpdates() <= 0 || r.mRequest.getExpireAt() < now) {
- if (deadUpdateRecords == null) {
- deadUpdateRecords = new ArrayList<UpdateRecord>();
- }
- deadUpdateRecords.add(r);
- }
- // track dead receivers
- if (receiverDead) {
- if (deadReceivers == null) {
- deadReceivers = new ArrayList<Receiver>();
- }
- if (!deadReceivers.contains(receiver)) {
- deadReceivers.add(receiver);
- }
- }
- }
-
- // remove dead records and receivers outside the loop
- if (deadReceivers != null) {
- for (Receiver receiver : deadReceivers) {
- removeUpdatesLocked(receiver);
- }
- }
- if (deadUpdateRecords != null) {
- for (UpdateRecord r : deadUpdateRecords) {
- r.disposeLocked(true);
- }
- applyRequirementsLocked(provider);
- }
- }
-
- private class LocationWorkerHandler extends Handler {
- public LocationWorkerHandler(Looper looper) {
- super(looper, null, true);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_LOCATION_CHANGED:
- handleLocationChanged((Location) msg.obj, msg.arg1 == 1);
- break;
- }
- }
- }
-
- private boolean isMockProvider(String provider) {
- synchronized (mLock) {
- return mMockProviders.containsKey(provider);
- }
- }
-
- private void handleLocationChanged(Location location, boolean passive) {
- // create a working copy of the incoming Location so that the service can modify it without
- // disturbing the caller's copy
- Location myLocation = new Location(location);
- String provider = myLocation.getProvider();
-
- // set "isFromMockProvider" bit if location came from a mock provider. we do not clear this
- // bit if location did not come from a mock provider because passive/fused providers can
- // forward locations from mock providers, and should not grant them legitimacy in doing so.
- if (!myLocation.isFromMockProvider() && isMockProvider(provider)) {
- myLocation.setIsFromMockProvider(true);
- }
-
- synchronized (mLock) {
- if (isAllowedByCurrentUserSettingsLocked(provider)) {
- if (!passive) {
- // notify passive provider of the new location
- mPassiveProvider.updateLocation(myLocation);
- }
- handleLocationChangedLocked(myLocation, passive);
- }
- }
- }
-
- private final PackageMonitor mPackageMonitor = new PackageMonitor() {
- @Override
- public void onPackageDisappeared(String packageName, int reason) {
- // remove all receivers associated with this package name
- synchronized (mLock) {
- ArrayList<Receiver> deadReceivers = null;
-
- for (Receiver receiver : mReceivers.values()) {
- if (receiver.mPackageName.equals(packageName)) {
- if (deadReceivers == null) {
- deadReceivers = new ArrayList<Receiver>();
- }
- deadReceivers.add(receiver);
- }
- }
-
- // perform removal outside of mReceivers loop
- if (deadReceivers != null) {
- for (Receiver receiver : deadReceivers) {
- removeUpdatesLocked(receiver);
- }
- }
- }
- }
- };
-
- // Geocoder
-
- @Override
- public boolean geocoderIsPresent() {
- return mGeocodeProvider != null;
- }
-
- @Override
- public String getFromLocation(double latitude, double longitude, int maxResults,
- GeocoderParams params, List<Address> addrs) {
- if (mGeocodeProvider != null) {
- return mGeocodeProvider.getFromLocation(latitude, longitude, maxResults,
- params, addrs);
- }
- return null;
- }
-
-
- @Override
- public String getFromLocationName(String locationName,
- double lowerLeftLatitude, double lowerLeftLongitude,
- double upperRightLatitude, double upperRightLongitude, int maxResults,
- GeocoderParams params, List<Address> addrs) {
-
- if (mGeocodeProvider != null) {
- return mGeocodeProvider.getFromLocationName(locationName, lowerLeftLatitude,
- lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
- maxResults, params, addrs);
- }
- return null;
- }
-
- // Mock Providers
-
- private void checkMockPermissionsSafe() {
- boolean allowMocks = Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.ALLOW_MOCK_LOCATION, 0) == 1;
- if (!allowMocks) {
- throw new SecurityException("Requires ACCESS_MOCK_LOCATION secure setting");
- }
-
- if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) !=
- PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission");
- }
- }
-
- @Override
- public void addTestProvider(String name, ProviderProperties properties) {
- checkMockPermissionsSafe();
-
- if (LocationManager.PASSIVE_PROVIDER.equals(name)) {
- throw new IllegalArgumentException("Cannot mock the passive location provider");
- }
-
- long identity = Binder.clearCallingIdentity();
- synchronized (mLock) {
- MockProvider provider = new MockProvider(name, this, properties);
- // remove the real provider if we are replacing GPS or network provider
- if (LocationManager.GPS_PROVIDER.equals(name)
- || LocationManager.NETWORK_PROVIDER.equals(name)
- || LocationManager.FUSED_PROVIDER.equals(name)) {
- LocationProviderInterface p = mProvidersByName.get(name);
- if (p != null) {
- removeProviderLocked(p);
- }
- }
- if (mProvidersByName.get(name) != null) {
- throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
- }
- addProviderLocked(provider);
- mMockProviders.put(name, provider);
- mLastLocation.put(name, null);
- mLastLocationCoarseInterval.put(name, null);
- updateProvidersLocked();
- }
- Binder.restoreCallingIdentity(identity);
- }
-
- @Override
- public void removeTestProvider(String provider) {
- checkMockPermissionsSafe();
- synchronized (mLock) {
- MockProvider mockProvider = mMockProviders.remove(provider);
- if (mockProvider == null) {
- throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
- }
- long identity = Binder.clearCallingIdentity();
- removeProviderLocked(mProvidersByName.get(provider));
-
- // reinstate real provider if available
- LocationProviderInterface realProvider = mRealProviders.get(provider);
- if (realProvider != null) {
- addProviderLocked(realProvider);
- }
- mLastLocation.put(provider, null);
- mLastLocationCoarseInterval.put(provider, null);
- updateProvidersLocked();
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- @Override
- public void setTestProviderLocation(String provider, Location loc) {
- checkMockPermissionsSafe();
- synchronized (mLock) {
- MockProvider mockProvider = mMockProviders.get(provider);
- if (mockProvider == null) {
- throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
- }
- // clear calling identity so INSTALL_LOCATION_PROVIDER permission is not required
- long identity = Binder.clearCallingIdentity();
- mockProvider.setLocation(loc);
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- @Override
- public void clearTestProviderLocation(String provider) {
- checkMockPermissionsSafe();
- synchronized (mLock) {
- MockProvider mockProvider = mMockProviders.get(provider);
- if (mockProvider == null) {
- throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
- }
- mockProvider.clearLocation();
- }
- }
-
- @Override
- public void setTestProviderEnabled(String provider, boolean enabled) {
- checkMockPermissionsSafe();
- synchronized (mLock) {
- MockProvider mockProvider = mMockProviders.get(provider);
- if (mockProvider == null) {
- throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
- }
- long identity = Binder.clearCallingIdentity();
- if (enabled) {
- mockProvider.enable();
- mEnabledProviders.add(provider);
- mDisabledProviders.remove(provider);
- } else {
- mockProvider.disable();
- mEnabledProviders.remove(provider);
- mDisabledProviders.add(provider);
- }
- updateProvidersLocked();
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- @Override
- public void clearTestProviderEnabled(String provider) {
- checkMockPermissionsSafe();
- synchronized (mLock) {
- MockProvider mockProvider = mMockProviders.get(provider);
- if (mockProvider == null) {
- throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
- }
- long identity = Binder.clearCallingIdentity();
- mEnabledProviders.remove(provider);
- mDisabledProviders.remove(provider);
- updateProvidersLocked();
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- @Override
- public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
- checkMockPermissionsSafe();
- synchronized (mLock) {
- MockProvider mockProvider = mMockProviders.get(provider);
- if (mockProvider == null) {
- throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
- }
- mockProvider.setStatus(status, extras, updateTime);
- }
- }
-
- @Override
- public void clearTestProviderStatus(String provider) {
- checkMockPermissionsSafe();
- synchronized (mLock) {
- MockProvider mockProvider = mMockProviders.get(provider);
- if (mockProvider == null) {
- throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
- }
- mockProvider.clearStatus();
- }
- }
-
- private void log(String log) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Slog.d(TAG, log);
- }
- }
-
- @Override
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
- != PackageManager.PERMISSION_GRANTED) {
- pw.println("Permission Denial: can't dump LocationManagerService from from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid());
- return;
- }
-
- synchronized (mLock) {
- pw.println("Current Location Manager state:");
- pw.println(" Location Listeners:");
- for (Receiver receiver : mReceivers.values()) {
- pw.println(" " + receiver);
- }
- pw.println(" Records by Provider:");
- for (Map.Entry<String, ArrayList<UpdateRecord>> entry : mRecordsByProvider.entrySet()) {
- pw.println(" " + entry.getKey() + ":");
- for (UpdateRecord record : entry.getValue()) {
- pw.println(" " + record);
- }
- }
- pw.println(" Last Known Locations:");
- for (Map.Entry<String, Location> entry : mLastLocation.entrySet()) {
- String provider = entry.getKey();
- Location location = entry.getValue();
- pw.println(" " + provider + ": " + location);
- }
-
- pw.println(" Last Known Locations Coarse Intervals:");
- for (Map.Entry<String, Location> entry : mLastLocationCoarseInterval.entrySet()) {
- String provider = entry.getKey();
- Location location = entry.getValue();
- pw.println(" " + provider + ": " + location);
- }
-
- mGeofenceManager.dump(pw);
-
- if (mEnabledProviders.size() > 0) {
- pw.println(" Enabled Providers:");
- for (String i : mEnabledProviders) {
- pw.println(" " + i);
- }
-
- }
- if (mDisabledProviders.size() > 0) {
- pw.println(" Disabled Providers:");
- for (String i : mDisabledProviders) {
- pw.println(" " + i);
- }
- }
- pw.append(" ");
- mBlacklist.dump(pw);
- if (mMockProviders.size() > 0) {
- pw.println(" Mock Providers:");
- for (Map.Entry<String, MockProvider> i : mMockProviders.entrySet()) {
- i.getValue().dump(pw, " ");
- }
- }
-
- pw.append(" fudger: ");
- mLocationFudger.dump(fd, pw, args);
-
- if (args.length > 0 && "short".equals(args[0])) {
- return;
- }
- for (LocationProviderInterface provider: mProviders) {
- pw.print(provider.getName() + " Internal State");
- if (provider instanceof LocationProviderProxy) {
- LocationProviderProxy proxy = (LocationProviderProxy) provider;
- pw.print(" (" + proxy.getConnectedPackageName() + ")");
- }
- pw.println(":");
- provider.dump(fd, pw, args);
- }
- }
- }
-}