blob: e6210b2a8cc96ef80ef035140a44ccf463144734 [file] [log] [blame]
/*
* Copyright (C) 2020 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.location;
import static android.Manifest.permission.ACCESS_FINE_LOCATION;
import static android.app.compat.CompatChanges.isChangeEnabled;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.location.LocationManager.BLOCK_PENDING_INTENT_SYSTEM_API_USAGE;
import static android.location.LocationManager.FUSED_PROVIDER;
import static android.location.LocationManager.GPS_PROVIDER;
import static android.location.LocationManager.NETWORK_PROVIDER;
import static android.location.LocationRequest.LOW_POWER_EXCEPTIONS;
import static android.location.provider.LocationProviderBase.ACTION_FUSED_PROVIDER;
import static android.location.provider.LocationProviderBase.ACTION_NETWORK_PROVIDER;
import static com.android.server.location.LocationPermissions.PERMISSION_COARSE;
import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
import static com.android.server.location.eventlog.LocationEventLog.EVENT_LOG;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
import android.Manifest;
import android.Manifest.permission;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.PendingIntent;
import android.app.compat.CompatChanges;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.Criteria;
import android.location.GeocoderParams;
import android.location.Geofence;
import android.location.GnssAntennaInfo;
import android.location.GnssCapabilities;
import android.location.GnssMeasurementCorrections;
import android.location.GnssMeasurementRequest;
import android.location.IGeocodeListener;
import android.location.IGnssAntennaInfoListener;
import android.location.IGnssMeasurementsListener;
import android.location.IGnssNavigationMessageListener;
import android.location.IGnssNmeaListener;
import android.location.IGnssStatusListener;
import android.location.ILocationCallback;
import android.location.ILocationListener;
import android.location.ILocationManager;
import android.location.LastLocationRequest;
import android.location.Location;
import android.location.LocationManager;
import android.location.LocationManagerInternal;
import android.location.LocationManagerInternal.LocationPackageTagsListener;
import android.location.LocationProvider;
import android.location.LocationRequest;
import android.location.LocationTime;
import android.location.provider.IProviderRequestListener;
import android.location.provider.ProviderProperties;
import android.location.util.identity.CallerIdentity;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.ICancellationSignal;
import android.os.PackageTagsList;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.WorkSource;
import android.os.WorkSource.WorkChain;
import android.provider.Settings;
import android.stats.location.LocationStatsEnums;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.location.eventlog.LocationEventLog;
import com.android.server.location.geofence.GeofenceManager;
import com.android.server.location.geofence.GeofenceProxy;
import com.android.server.location.gnss.GnssConfiguration;
import com.android.server.location.gnss.GnssManagerService;
import com.android.server.location.gnss.hal.GnssNative;
import com.android.server.location.injector.AlarmHelper;
import com.android.server.location.injector.AppForegroundHelper;
import com.android.server.location.injector.AppOpsHelper;
import com.android.server.location.injector.DeviceIdleHelper;
import com.android.server.location.injector.DeviceStationaryHelper;
import com.android.server.location.injector.EmergencyHelper;
import com.android.server.location.injector.Injector;
import com.android.server.location.injector.LocationAttributionHelper;
import com.android.server.location.injector.LocationPermissionsHelper;
import com.android.server.location.injector.LocationPowerSaveModeHelper;
import com.android.server.location.injector.LocationUsageLogger;
import com.android.server.location.injector.ScreenInteractiveHelper;
import com.android.server.location.injector.SettingsHelper;
import com.android.server.location.injector.SystemAlarmHelper;
import com.android.server.location.injector.SystemAppForegroundHelper;
import com.android.server.location.injector.SystemAppOpsHelper;
import com.android.server.location.injector.SystemDeviceIdleHelper;
import com.android.server.location.injector.SystemDeviceStationaryHelper;
import com.android.server.location.injector.SystemEmergencyHelper;
import com.android.server.location.injector.SystemLocationPermissionsHelper;
import com.android.server.location.injector.SystemLocationPowerSaveModeHelper;
import com.android.server.location.injector.SystemScreenInteractiveHelper;
import com.android.server.location.injector.SystemSettingsHelper;
import com.android.server.location.injector.SystemUserInfoHelper;
import com.android.server.location.injector.UserInfoHelper;
import com.android.server.location.provider.AbstractLocationProvider;
import com.android.server.location.provider.LocationProviderManager;
import com.android.server.location.provider.MockLocationProvider;
import com.android.server.location.provider.PassiveLocationProvider;
import com.android.server.location.provider.PassiveLocationProviderManager;
import com.android.server.location.provider.StationaryThrottlingLocationProvider;
import com.android.server.location.provider.proxy.ProxyLocationProvider;
import com.android.server.location.settings.LocationSettings;
import com.android.server.location.settings.LocationUserSettings;
import com.android.server.pm.permission.LegacyPermissionManagerInternal;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* The service class that manages LocationProviders and issues location
* updates and alerts.
*/
public class LocationManagerService extends ILocationManager.Stub implements
LocationProviderManager.StateChangedListener {
/**
* Controls lifecycle of LocationManagerService.
*/
public static class Lifecycle extends SystemService {
private final LifecycleUserInfoHelper mUserInfoHelper;
private final SystemInjector mSystemInjector;
private final LocationManagerService mService;
public Lifecycle(Context context) {
super(context);
mUserInfoHelper = new LifecycleUserInfoHelper(context);
mSystemInjector = new SystemInjector(context, mUserInfoHelper);
mService = new LocationManagerService(context, mSystemInjector);
}
@Override
public void onStart() {
publishBinderService(Context.LOCATION_SERVICE, mService);
// client caching behavior is only enabled after seeing the first invalidate
LocationManager.invalidateLocalLocationEnabledCaches();
// disable caching for our own process
LocationManager.disableLocalLocationEnabledCaches();
}
@Override
public void onBootPhase(int phase) {
if (phase == PHASE_SYSTEM_SERVICES_READY) {
// the location service must be functioning after this boot phase
mSystemInjector.onSystemReady();
mService.onSystemReady();
} else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
// some providers rely on third party code, so we wait to initialize
// providers until third party code is allowed to run
mService.onSystemThirdPartyAppsCanStart();
}
}
@Override
public void onUserStarting(TargetUser user) {
mUserInfoHelper.onUserStarted(user.getUserIdentifier());
}
@Override
public void onUserSwitching(TargetUser from, TargetUser to) {
mUserInfoHelper.onCurrentUserChanged(from.getUserIdentifier(),
to.getUserIdentifier());
}
@Override
public void onUserStopped(TargetUser user) {
mUserInfoHelper.onUserStopped(user.getUserIdentifier());
}
private static class LifecycleUserInfoHelper extends SystemUserInfoHelper {
LifecycleUserInfoHelper(Context context) {
super(context);
}
void onUserStarted(int userId) {
dispatchOnUserStarted(userId);
}
void onUserStopped(int userId) {
dispatchOnUserStopped(userId);
}
void onCurrentUserChanged(int fromUserId, int toUserId) {
dispatchOnCurrentUserChanged(fromUserId, toUserId);
}
}
}
public static final String TAG = "LocationManagerService";
public static final boolean D = Log.isLoggable(TAG, Log.DEBUG);
private static final String ATTRIBUTION_TAG = "LocationService";
final Object mLock = new Object();
private final Context mContext;
private final Injector mInjector;
private final LocalService mLocalService;
private final GeofenceManager mGeofenceManager;
private volatile @Nullable GnssManagerService mGnssManagerService = null;
private GeocoderProxy mGeocodeProvider;
private final Object mDeprecatedGnssBatchingLock = new Object();
@GuardedBy("mDeprecatedGnssBatchingLock")
private @Nullable ILocationListener mDeprecatedGnssBatchingListener;
@GuardedBy("mLock")
private String mExtraLocationControllerPackage;
@GuardedBy("mLock")
private boolean mExtraLocationControllerPackageEnabled;
// location provider managers
private final PassiveLocationProviderManager mPassiveManager;
// @GuardedBy("mProviderManagers")
// hold lock for writes, no lock necessary for simple reads
final CopyOnWriteArrayList<LocationProviderManager> mProviderManagers =
new CopyOnWriteArrayList<>();
@GuardedBy("mLock")
@Nullable LocationPackageTagsListener mLocationTagsChangedListener;
LocationManagerService(Context context, Injector injector) {
mContext = context.createAttributionContext(ATTRIBUTION_TAG);
mInjector = injector;
mLocalService = new LocalService();
LocalServices.addService(LocationManagerInternal.class, mLocalService);
mGeofenceManager = new GeofenceManager(mContext, injector);
mInjector.getLocationSettings().registerLocationUserSettingsListener(
this::onLocationUserSettingsChanged);
mInjector.getSettingsHelper().addOnLocationEnabledChangedListener(
this::onLocationModeChanged);
mInjector.getSettingsHelper().addIgnoreSettingsAllowlistChangedListener(
() -> refreshAppOpsRestrictions(UserHandle.USER_ALL));
mInjector.getUserInfoHelper().addListener((userId, change) -> {
if (change == UserInfoHelper.UserListener.USER_STARTED) {
refreshAppOpsRestrictions(userId);
}
});
// set up passive provider first since it will be required for all other location providers,
// which are loaded later once the system is ready.
mPassiveManager = new PassiveLocationProviderManager(mContext, injector);
addLocationProviderManager(mPassiveManager, new PassiveLocationProvider(mContext));
// TODO: load the gps provider here as well, which will require refactoring
// Let the package manager query which are the default location
// providers as they get certain permissions granted by default.
LegacyPermissionManagerInternal permissionManagerInternal = LocalServices.getService(
LegacyPermissionManagerInternal.class);
permissionManagerInternal.setLocationPackagesProvider(
userId -> mContext.getResources().getStringArray(
com.android.internal.R.array.config_locationProviderPackageNames));
permissionManagerInternal.setLocationExtraPackagesProvider(
userId -> mContext.getResources().getStringArray(
com.android.internal.R.array.config_locationExtraPackageNames));
}
@Nullable
LocationProviderManager getLocationProviderManager(String providerName) {
if (providerName == null) {
return null;
}
for (LocationProviderManager manager : mProviderManagers) {
if (providerName.equals(manager.getName())) {
return manager;
}
}
return null;
}
private LocationProviderManager getOrAddLocationProviderManager(String providerName) {
synchronized (mProviderManagers) {
for (LocationProviderManager manager : mProviderManagers) {
if (providerName.equals(manager.getName())) {
return manager;
}
}
LocationProviderManager manager = new LocationProviderManager(mContext, mInjector,
providerName, mPassiveManager);
addLocationProviderManager(manager, null);
return manager;
}
}
private void addLocationProviderManager(LocationProviderManager manager,
@Nullable AbstractLocationProvider realProvider) {
synchronized (mProviderManagers) {
Preconditions.checkState(getLocationProviderManager(manager.getName()) == null);
manager.startManager(this);
if (realProvider != null) {
// custom logic wrapping all non-passive providers
if (manager != mPassiveManager) {
boolean enableStationaryThrottling = Settings.Global.getInt(
mContext.getContentResolver(),
Settings.Global.LOCATION_ENABLE_STATIONARY_THROTTLE, 1) != 0;
if (enableStationaryThrottling) {
realProvider = new StationaryThrottlingLocationProvider(manager.getName(),
mInjector, realProvider);
}
}
manager.setRealProvider(realProvider);
}
mProviderManagers.add(manager);
}
}
private void removeLocationProviderManager(LocationProviderManager manager) {
synchronized (mProviderManagers) {
boolean removed = mProviderManagers.remove(manager);
Preconditions.checkArgument(removed);
manager.setMockProvider(null);
manager.setRealProvider(null);
manager.stopManager();
}
}
void onSystemReady() {
if (Build.IS_DEBUGGABLE) {
// on debug builds, watch for location noteOps while location is off. there are some
// scenarios (emergency location) where this is expected, but generally this should
// rarely occur, and may indicate bugs. dump occurrences to logs for further evaluation
AppOpsManager appOps = Objects.requireNonNull(
mContext.getSystemService(AppOpsManager.class));
appOps.startWatchingNoted(
new int[]{AppOpsManager.OP_FINE_LOCATION, AppOpsManager.OP_COARSE_LOCATION},
(code, uid, packageName, attributionTag, flags, result) -> {
if (!isLocationEnabledForUser(UserHandle.getUserId(uid))) {
Log.w(TAG, "location noteOp with location off - "
+ CallerIdentity.forTest(uid, 0, packageName, attributionTag));
}
});
}
}
void onSystemThirdPartyAppsCanStart() {
// network provider should always be initialized before the gps provider since the gps
// provider has unfortunate hard dependencies on the network provider
ProxyLocationProvider networkProvider = ProxyLocationProvider.create(
mContext,
NETWORK_PROVIDER,
ACTION_NETWORK_PROVIDER,
com.android.internal.R.bool.config_enableNetworkLocationOverlay,
com.android.internal.R.string.config_networkLocationProviderPackageName);
if (networkProvider != null) {
LocationProviderManager networkManager = new LocationProviderManager(mContext,
mInjector, NETWORK_PROVIDER, mPassiveManager);
addLocationProviderManager(networkManager, networkProvider);
} else {
Log.w(TAG, "no network location provider found");
}
// ensure that a fused provider exists which will work in direct boot
Preconditions.checkState(!mContext.getPackageManager().queryIntentServicesAsUser(
new Intent(ACTION_FUSED_PROVIDER),
MATCH_DIRECT_BOOT_AWARE | MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM).isEmpty(),
"Unable to find a direct boot aware fused location provider");
ProxyLocationProvider fusedProvider = ProxyLocationProvider.create(
mContext,
FUSED_PROVIDER,
ACTION_FUSED_PROVIDER,
com.android.internal.R.bool.config_enableFusedLocationOverlay,
com.android.internal.R.string.config_fusedLocationProviderPackageName);
if (fusedProvider != null) {
LocationProviderManager fusedManager = new LocationProviderManager(mContext, mInjector,
FUSED_PROVIDER, mPassiveManager);
addLocationProviderManager(fusedManager, fusedProvider);
} else {
Log.wtf(TAG, "no fused location provider found");
}
// initialize gnss last because it has no awareness of boot phases and blindly assumes that
// all other location providers are loaded at initialization
if (GnssNative.isSupported()) {
GnssConfiguration gnssConfiguration = new GnssConfiguration(mContext);
GnssNative gnssNative = GnssNative.create(mInjector, gnssConfiguration);
mGnssManagerService = new GnssManagerService(mContext, mInjector, gnssNative);
mGnssManagerService.onSystemReady();
LocationProviderManager gnssManager = new LocationProviderManager(mContext, mInjector,
GPS_PROVIDER, mPassiveManager);
addLocationProviderManager(gnssManager, mGnssManagerService.getGnssLocationProvider());
}
// bind to geocoder provider
mGeocodeProvider = GeocoderProxy.createAndRegister(mContext);
if (mGeocodeProvider == null) {
Log.e(TAG, "no geocoder provider found");
}
// bind to hardware activity recognition
HardwareActivityRecognitionProxy hardwareActivityRecognitionProxy =
HardwareActivityRecognitionProxy.createAndRegister(mContext);
if (hardwareActivityRecognitionProxy == null) {
Log.e(TAG, "unable to bind ActivityRecognitionProxy");
}
// bind to gnss geofence proxy
if (mGnssManagerService != null) {
GeofenceProxy provider = GeofenceProxy.createAndBind(mContext,
mGnssManagerService.getGnssGeofenceProxy());
if (provider == null) {
Log.e(TAG, "unable to bind to GeofenceProxy");
}
}
// create any predefined test providers
String[] testProviderStrings = mContext.getResources().getStringArray(
com.android.internal.R.array.config_testLocationProviders);
for (String testProviderString : testProviderStrings) {
String[] fragments = testProviderString.split(",");
String name = fragments[0].trim();
ProviderProperties properties = new ProviderProperties.Builder()
.setHasNetworkRequirement(Boolean.parseBoolean(fragments[1]))
.setHasSatelliteRequirement(Boolean.parseBoolean(fragments[2]))
.setHasCellRequirement(Boolean.parseBoolean(fragments[3]))
.setHasMonetaryCost(Boolean.parseBoolean(fragments[4]))
.setHasAltitudeSupport(Boolean.parseBoolean(fragments[5]))
.setHasSpeedSupport(Boolean.parseBoolean(fragments[6]))
.setHasBearingSupport(Boolean.parseBoolean(fragments[7]))
.setPowerUsage(Integer.parseInt(fragments[8]))
.setAccuracy(Integer.parseInt(fragments[9]))
.build();
final LocationProviderManager manager = getOrAddLocationProviderManager(name);
manager.setMockProvider(new MockLocationProvider(properties,
CallerIdentity.fromContext(mContext), Collections.emptySet()));
}
}
private void onLocationUserSettingsChanged(int userId, LocationUserSettings oldSettings,
LocationUserSettings newSettings) {
if (oldSettings.isAdasGnssLocationEnabled() != newSettings.isAdasGnssLocationEnabled()) {
boolean enabled = newSettings.isAdasGnssLocationEnabled();
if (D) {
Log.d(TAG, "[u" + userId + "] adas gnss location enabled = " + enabled);
}
EVENT_LOG.logAdasLocationEnabled(userId, enabled);
Intent intent = new Intent(LocationManager.ACTION_ADAS_GNSS_ENABLED_CHANGED)
.putExtra(LocationManager.EXTRA_ADAS_GNSS_ENABLED, enabled)
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
}
}
private void onLocationModeChanged(int userId) {
boolean enabled = mInjector.getSettingsHelper().isLocationEnabled(userId);
LocationManager.invalidateLocalLocationEnabledCaches();
if (D) {
Log.d(TAG, "[u" + userId + "] location enabled = " + enabled);
}
EVENT_LOG.logLocationEnabled(userId, enabled);
Intent intent = new Intent(LocationManager.MODE_CHANGED_ACTION)
.putExtra(LocationManager.EXTRA_LOCATION_ENABLED, enabled)
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
refreshAppOpsRestrictions(userId);
}
@Override
public int getGnssYearOfHardware() {
return mGnssManagerService == null ? 0 : mGnssManagerService.getGnssYearOfHardware();
}
@Override
@Nullable
public String getGnssHardwareModelName() {
return mGnssManagerService == null ? "" : mGnssManagerService.getGnssHardwareModelName();
}
@Override
public int getGnssBatchSize() {
return mGnssManagerService == null ? 0 : mGnssManagerService.getGnssBatchSize();
}
@Override
public void startGnssBatch(long periodNanos, ILocationListener listener, String packageName,
@Nullable String attributionTag, String listenerId) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.LOCATION_HARDWARE, null);
if (mGnssManagerService == null) {
return;
}
long intervalMs = NANOSECONDS.toMillis(periodNanos);
synchronized (mDeprecatedGnssBatchingLock) {
stopGnssBatch();
registerLocationListener(
GPS_PROVIDER,
new LocationRequest.Builder(intervalMs)
.setMaxUpdateDelayMillis(
intervalMs * mGnssManagerService.getGnssBatchSize())
.setHiddenFromAppOps(true)
.build(),
listener,
packageName,
attributionTag,
listenerId);
mDeprecatedGnssBatchingListener = listener;
}
}
@Override
public void flushGnssBatch() {
mContext.enforceCallingOrSelfPermission(Manifest.permission.LOCATION_HARDWARE, null);
if (mGnssManagerService == null) {
return;
}
synchronized (mDeprecatedGnssBatchingLock) {
if (mDeprecatedGnssBatchingListener != null) {
requestListenerFlush(GPS_PROVIDER, mDeprecatedGnssBatchingListener, 0);
}
}
}
@Override
public void stopGnssBatch() {
mContext.enforceCallingOrSelfPermission(Manifest.permission.LOCATION_HARDWARE, null);
if (mGnssManagerService == null) {
return;
}
synchronized (mDeprecatedGnssBatchingLock) {
if (mDeprecatedGnssBatchingListener != null) {
ILocationListener listener = mDeprecatedGnssBatchingListener;
mDeprecatedGnssBatchingListener = null;
unregisterLocationListener(listener);
}
}
}
@Override
public boolean hasProvider(String provider) {
return getLocationProviderManager(provider) != null;
}
@Override
public List<String> getAllProviders() {
ArrayList<String> providers = new ArrayList<>(mProviderManagers.size());
for (LocationProviderManager manager : mProviderManagers) {
providers.add(manager.getName());
}
return providers;
}
@Override
public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
if (!LocationPermissions.checkCallingOrSelfLocationPermission(mContext,
PERMISSION_COARSE)) {
return Collections.emptyList();
}
synchronized (mLock) {
ArrayList<String> providers = new ArrayList<>(mProviderManagers.size());
for (LocationProviderManager manager : mProviderManagers) {
String name = manager.getName();
if (enabledOnly && !manager.isEnabled(UserHandle.getCallingUserId())) {
continue;
}
if (criteria != null && !LocationProvider.propertiesMeetCriteria(name,
manager.getProperties(), criteria)) {
continue;
}
providers.add(name);
}
return providers;
}
}
@Override
public String getBestProvider(Criteria criteria, boolean enabledOnly) {
List<String> providers;
synchronized (mLock) {
providers = getProviders(criteria, enabledOnly);
if (providers.isEmpty()) {
providers = getProviders(null, enabledOnly);
}
}
if (!providers.isEmpty()) {
if (providers.contains(FUSED_PROVIDER)) {
return FUSED_PROVIDER;
} else if (providers.contains(GPS_PROVIDER)) {
return GPS_PROVIDER;
} else if (providers.contains(NETWORK_PROVIDER)) {
return NETWORK_PROVIDER;
} else {
return providers.get(0);
}
}
return null;
}
@Override
public String[] getBackgroundThrottlingWhitelist() {
return mInjector.getSettingsHelper().getBackgroundThrottlePackageWhitelist().toArray(
new String[0]);
}
@Override
public PackageTagsList getIgnoreSettingsAllowlist() {
return mInjector.getSettingsHelper().getIgnoreSettingsAllowlist();
}
@Nullable
@Override
public ICancellationSignal getCurrentLocation(String provider, LocationRequest request,
ILocationCallback consumer, String packageName, @Nullable String attributionTag,
String listenerId) {
CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag,
listenerId);
int permissionLevel = LocationPermissions.getPermissionLevel(mContext, identity.getUid(),
identity.getPid());
LocationPermissions.enforceLocationPermission(identity.getUid(), permissionLevel,
PERMISSION_COARSE);
// clients in the system process must have an attribution tag set
Preconditions.checkState(identity.getPid() != Process.myPid() || attributionTag != null);
request = validateLocationRequest(provider, request, identity);
LocationProviderManager manager = getLocationProviderManager(provider);
Preconditions.checkArgument(manager != null,
"provider \"" + provider + "\" does not exist");
return manager.getCurrentLocation(request, identity, permissionLevel, consumer);
}
@Override
public void registerLocationListener(String provider, LocationRequest request,
ILocationListener listener, String packageName, @Nullable String attributionTag,
String listenerId) {
CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag,
listenerId);
int permissionLevel = LocationPermissions.getPermissionLevel(mContext, identity.getUid(),
identity.getPid());
LocationPermissions.enforceLocationPermission(identity.getUid(), permissionLevel,
PERMISSION_COARSE);
// clients in the system process should have an attribution tag set
if (identity.getPid() == Process.myPid() && attributionTag == null) {
Log.w(TAG, "system location request with no attribution tag",
new IllegalArgumentException());
}
request = validateLocationRequest(provider, request, identity);
LocationProviderManager manager = getLocationProviderManager(provider);
Preconditions.checkArgument(manager != null,
"provider \"" + provider + "\" does not exist");
manager.registerLocationRequest(request, identity, permissionLevel, listener);
}
@Override
public void registerLocationPendingIntent(String provider, LocationRequest request,
PendingIntent pendingIntent, String packageName, @Nullable String attributionTag) {
CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag,
AppOpsManager.toReceiverId(pendingIntent));
int permissionLevel = LocationPermissions.getPermissionLevel(mContext, identity.getUid(),
identity.getPid());
LocationPermissions.enforceLocationPermission(identity.getUid(), permissionLevel,
PERMISSION_COARSE);
// clients in the system process must have an attribution tag set
Preconditions.checkArgument(identity.getPid() != Process.myPid() || attributionTag != null);
// pending intents requests may not use system apis because we do not keep track if clients
// lose the relevant permissions, and thus should not get the benefit of those apis. its
// simplest to ensure these apis are simply never set for pending intent requests. the same
// does not apply for listener requests since those will have the process (including the
// listener) killed on permission removal
if (isChangeEnabled(BLOCK_PENDING_INTENT_SYSTEM_API_USAGE, identity.getUid())) {
boolean usesSystemApi = request.isLowPower()
|| request.isHiddenFromAppOps()
|| request.isLocationSettingsIgnored()
|| !request.getWorkSource().isEmpty();
if (usesSystemApi) {
throw new SecurityException(
"PendingIntent location requests may not use system APIs: " + request);
}
}
request = validateLocationRequest(provider, request, identity);
LocationProviderManager manager = getLocationProviderManager(provider);
Preconditions.checkArgument(manager != null,
"provider \"" + provider + "\" does not exist");
manager.registerLocationRequest(request, identity, permissionLevel, pendingIntent);
}
private LocationRequest validateLocationRequest(String provider, LocationRequest request,
CallerIdentity identity) {
// validate unsanitized request
if (!request.getWorkSource().isEmpty()) {
mContext.enforceCallingOrSelfPermission(
permission.UPDATE_DEVICE_STATS,
"setting a work source requires " + permission.UPDATE_DEVICE_STATS);
}
// sanitize request
LocationRequest.Builder sanitized = new LocationRequest.Builder(request);
if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
&& GPS_PROVIDER.equals(provider)
&& ArrayUtils.contains(mContext.getResources().getStringArray(
com.android.internal.R.array.config_locationDriverAssistancePackageNames),
identity.getPackageName())) {
sanitized.setAdasGnssBypass(true);
}
if (!CompatChanges.isChangeEnabled(LOW_POWER_EXCEPTIONS, Binder.getCallingUid())) {
if (mContext.checkCallingPermission(permission.LOCATION_HARDWARE)
!= PERMISSION_GRANTED) {
sanitized.setLowPower(false);
}
}
WorkSource workSource = new WorkSource(request.getWorkSource());
if (workSource.size() > 0 && workSource.getPackageName(0) == null) {
Log.w(TAG, "received (and ignoring) illegal worksource with no package name");
workSource.clear();
} else {
List<WorkChain> workChains = workSource.getWorkChains();
if (workChains != null && !workChains.isEmpty()
&& workChains.get(0).getAttributionTag() == null) {
Log.w(TAG,
"received (and ignoring) illegal worksource with no attribution tag");
workSource.clear();
}
}
if (workSource.isEmpty()) {
identity.addToWorkSource(workSource);
}
sanitized.setWorkSource(workSource);
request = sanitized.build();
// validate sanitized request
boolean isLocationProvider = mLocalService.isProvider(null, identity);
if (request.isLowPower() && CompatChanges.isChangeEnabled(LOW_POWER_EXCEPTIONS,
identity.getUid())) {
mContext.enforceCallingOrSelfPermission(
permission.LOCATION_HARDWARE,
"low power request requires " + permission.LOCATION_HARDWARE);
}
if (request.isHiddenFromAppOps()) {
mContext.enforceCallingOrSelfPermission(
permission.UPDATE_APP_OPS_STATS,
"hiding from app ops requires " + permission.UPDATE_APP_OPS_STATS);
}
if (request.isAdasGnssBypass()) {
if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
throw new IllegalArgumentException(
"adas gnss bypass requests are only allowed on automotive devices");
}
if (!GPS_PROVIDER.equals(provider)) {
throw new IllegalArgumentException(
"adas gnss bypass requests are only allowed on the \"gps\" provider");
}
if (!ArrayUtils.contains(mContext.getResources().getStringArray(
com.android.internal.R.array.config_locationDriverAssistancePackageNames),
identity.getPackageName())) {
throw new SecurityException(
"only verified adas packages may use adas gnss bypass requests");
}
if (!isLocationProvider) {
mContext.enforceCallingOrSelfPermission(
permission.WRITE_SECURE_SETTINGS,
"adas gnss bypass requires " + permission.WRITE_SECURE_SETTINGS);
}
}
if (request.isLocationSettingsIgnored()) {
if (!isLocationProvider) {
mContext.enforceCallingOrSelfPermission(
permission.WRITE_SECURE_SETTINGS,
"ignoring location settings requires " + permission.WRITE_SECURE_SETTINGS);
}
}
return request;
}
@Override
public void requestListenerFlush(String provider, ILocationListener listener, int requestCode) {
LocationProviderManager manager = getLocationProviderManager(provider);
Preconditions.checkArgument(manager != null,
"provider \"" + provider + "\" does not exist");
manager.flush(Objects.requireNonNull(listener), requestCode);
}
@Override
public void requestPendingIntentFlush(String provider, PendingIntent pendingIntent,
int requestCode) {
LocationProviderManager manager = getLocationProviderManager(provider);
Preconditions.checkArgument(manager != null,
"provider \"" + provider + "\" does not exist");
manager.flush(Objects.requireNonNull(pendingIntent), requestCode);
}
@Override
public void unregisterLocationListener(ILocationListener listener) {
for (LocationProviderManager manager : mProviderManagers) {
manager.unregisterLocationRequest(listener);
}
}
@Override
public void unregisterLocationPendingIntent(PendingIntent pendingIntent) {
for (LocationProviderManager manager : mProviderManagers) {
manager.unregisterLocationRequest(pendingIntent);
}
}
@Override
public Location getLastLocation(String provider, LastLocationRequest request,
String packageName, @Nullable String attributionTag) {
CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag);
int permissionLevel = LocationPermissions.getPermissionLevel(mContext, identity.getUid(),
identity.getPid());
LocationPermissions.enforceLocationPermission(identity.getUid(), permissionLevel,
PERMISSION_COARSE);
// clients in the system process must have an attribution tag set
Preconditions.checkArgument(identity.getPid() != Process.myPid() || attributionTag != null);
request = validateLastLocationRequest(provider, request, identity);
LocationProviderManager manager = getLocationProviderManager(provider);
if (manager == null) {
return null;
}
return manager.getLastLocation(request, identity, permissionLevel);
}
private LastLocationRequest validateLastLocationRequest(String provider,
LastLocationRequest request,
CallerIdentity identity) {
// sanitize request
LastLocationRequest.Builder sanitized = new LastLocationRequest.Builder(request);
if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
&& GPS_PROVIDER.equals(provider)
&& ArrayUtils.contains(mContext.getResources().getStringArray(
com.android.internal.R.array.config_locationDriverAssistancePackageNames),
identity.getPackageName())) {
sanitized.setAdasGnssBypass(true);
}
request = sanitized.build();
// validate request
boolean isLocationProvider = mLocalService.isProvider(null, identity);
if (request.isHiddenFromAppOps()) {
mContext.enforceCallingOrSelfPermission(
permission.UPDATE_APP_OPS_STATS,
"hiding from app ops requires " + permission.UPDATE_APP_OPS_STATS);
}
if (request.isAdasGnssBypass()) {
if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
throw new IllegalArgumentException(
"adas gnss bypass requests are only allowed on automotive devices");
}
if (!GPS_PROVIDER.equals(provider)) {
throw new IllegalArgumentException(
"adas gnss bypass requests are only allowed on the \"gps\" provider");
}
if (!ArrayUtils.contains(mContext.getResources().getStringArray(
com.android.internal.R.array.config_locationDriverAssistancePackageNames),
identity.getPackageName())) {
throw new SecurityException(
"only verified adas packages may use adas gnss bypass requests");
}
if (!isLocationProvider) {
mContext.enforceCallingOrSelfPermission(
permission.WRITE_SECURE_SETTINGS,
"adas gnss bypass requires " + permission.WRITE_SECURE_SETTINGS);
}
}
if (request.isLocationSettingsIgnored()) {
if (!isLocationProvider) {
mContext.enforceCallingOrSelfPermission(
permission.WRITE_SECURE_SETTINGS,
"ignoring location settings requires " + permission.WRITE_SECURE_SETTINGS);
}
}
return request;
}
@Override
public LocationTime getGnssTimeMillis() {
return mLocalService.getGnssTimeMillis();
}
@Override
public void injectLocation(Location location) {
mContext.enforceCallingPermission(permission.LOCATION_HARDWARE, null);
mContext.enforceCallingPermission(ACCESS_FINE_LOCATION, null);
Preconditions.checkArgument(location.isComplete());
int userId = UserHandle.getCallingUserId();
LocationProviderManager manager = getLocationProviderManager(location.getProvider());
if (manager != null && manager.isEnabled(userId)) {
manager.injectLastLocation(Objects.requireNonNull(location), userId);
}
}
@Override
public void requestGeofence(Geofence geofence, PendingIntent intent, String packageName,
String attributionTag) {
mGeofenceManager.addGeofence(geofence, intent, packageName, attributionTag);
}
@Override
public void removeGeofence(PendingIntent pendingIntent) {
mGeofenceManager.removeGeofence(pendingIntent);
}
@Override
public void registerGnssStatusCallback(IGnssStatusListener listener, String packageName,
@Nullable String attributionTag, String listenerId) {
if (mGnssManagerService != null) {
mGnssManagerService.registerGnssStatusCallback(listener, packageName, attributionTag,
listenerId);
}
}
@Override
public void unregisterGnssStatusCallback(IGnssStatusListener listener) {
if (mGnssManagerService != null) {
mGnssManagerService.unregisterGnssStatusCallback(listener);
}
}
@Override
public void registerGnssNmeaCallback(IGnssNmeaListener listener, String packageName,
@Nullable String attributionTag, String listenerId) {
if (mGnssManagerService != null) {
mGnssManagerService.registerGnssNmeaCallback(listener, packageName, attributionTag,
listenerId);
}
}
@Override
public void unregisterGnssNmeaCallback(IGnssNmeaListener listener) {
if (mGnssManagerService != null) {
mGnssManagerService.unregisterGnssNmeaCallback(listener);
}
}
@Override
public void addGnssMeasurementsListener(GnssMeasurementRequest request,
IGnssMeasurementsListener listener, String packageName, @Nullable String attributionTag,
String listenerId) {
if (mGnssManagerService != null) {
mGnssManagerService.addGnssMeasurementsListener(request, listener, packageName,
attributionTag, listenerId);
}
}
@Override
public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener) {
if (mGnssManagerService != null) {
mGnssManagerService.removeGnssMeasurementsListener(
listener);
}
}
@Override
public void addGnssAntennaInfoListener(IGnssAntennaInfoListener listener, String packageName,
@Nullable String attributionTag, String listenerId) {
if (mGnssManagerService != null) {
mGnssManagerService.addGnssAntennaInfoListener(listener, packageName, attributionTag,
listenerId);
}
}
@Override
public void removeGnssAntennaInfoListener(IGnssAntennaInfoListener listener) {
if (mGnssManagerService != null) {
mGnssManagerService.removeGnssAntennaInfoListener(listener);
}
}
@Override
public void addProviderRequestListener(IProviderRequestListener listener) {
for (LocationProviderManager manager : mProviderManagers) {
manager.addProviderRequestListener(listener);
}
}
@Override
public void removeProviderRequestListener(IProviderRequestListener listener) {
for (LocationProviderManager manager : mProviderManagers) {
manager.removeProviderRequestListener(listener);
}
}
@Override
public void injectGnssMeasurementCorrections(GnssMeasurementCorrections corrections) {
if (mGnssManagerService != null) {
mGnssManagerService.injectGnssMeasurementCorrections(corrections);
}
}
@Override
public GnssCapabilities getGnssCapabilities() {
return mGnssManagerService == null ? new GnssCapabilities.Builder().build()
: mGnssManagerService.getGnssCapabilities();
}
@Override
public List<GnssAntennaInfo> getGnssAntennaInfos() {
return mGnssManagerService == null ? null : mGnssManagerService.getGnssAntennaInfos();
}
@Override
public void addGnssNavigationMessageListener(IGnssNavigationMessageListener listener,
String packageName, @Nullable String attributionTag, String listenerId) {
if (mGnssManagerService != null) {
mGnssManagerService.addGnssNavigationMessageListener(listener, packageName,
attributionTag, listenerId);
}
}
@Override
public void removeGnssNavigationMessageListener(IGnssNavigationMessageListener listener) {
if (mGnssManagerService != null) {
mGnssManagerService.removeGnssNavigationMessageListener(
listener);
}
}
@Override
public void sendExtraCommand(String provider, String command, Bundle extras) {
LocationPermissions.enforceCallingOrSelfLocationPermission(mContext, PERMISSION_COARSE);
mContext.enforceCallingOrSelfPermission(
permission.ACCESS_LOCATION_EXTRA_COMMANDS, null);
LocationProviderManager manager = getLocationProviderManager(
Objects.requireNonNull(provider));
if (manager != null) {
manager.sendExtraCommand(Binder.getCallingUid(), Binder.getCallingPid(),
Objects.requireNonNull(command), extras);
}
mInjector.getLocationUsageLogger().logLocationApiUsage(
LocationStatsEnums.USAGE_STARTED,
LocationStatsEnums.API_SEND_EXTRA_COMMAND,
provider);
mInjector.getLocationUsageLogger().logLocationApiUsage(
LocationStatsEnums.USAGE_ENDED,
LocationStatsEnums.API_SEND_EXTRA_COMMAND,
provider);
}
@Override
public ProviderProperties getProviderProperties(String provider) {
LocationProviderManager manager = getLocationProviderManager(provider);
Preconditions.checkArgument(manager != null,
"provider \"" + provider + "\" does not exist");
return manager.getProperties();
}
@Override
public boolean isProviderPackage(@Nullable String provider, String packageName,
@Nullable String attributionTag) {
mContext.enforceCallingOrSelfPermission(permission.READ_DEVICE_CONFIG, null);
for (LocationProviderManager manager : mProviderManagers) {
if (provider != null && !provider.equals(manager.getName())) {
continue;
}
CallerIdentity identity = manager.getIdentity();
if (identity == null) {
continue;
}
if (identity.getPackageName().equals(packageName) && (attributionTag == null
|| Objects.equals(identity.getAttributionTag(), attributionTag))) {
return true;
}
}
return false;
}
@Override
public List<String> getProviderPackages(String provider) {
mContext.enforceCallingOrSelfPermission(permission.READ_DEVICE_CONFIG, null);
LocationProviderManager manager = getLocationProviderManager(provider);
if (manager == null) {
return Collections.emptyList();
}
CallerIdentity identity = manager.getIdentity();
if (identity == null) {
return Collections.emptyList();
}
return Collections.singletonList(identity.getPackageName());
}
@Override
public void setExtraLocationControllerPackage(String packageName) {
mContext.enforceCallingPermission(permission.LOCATION_HARDWARE,
permission.LOCATION_HARDWARE + " permission required");
synchronized (mLock) {
mExtraLocationControllerPackage = packageName;
}
}
@Override
public String getExtraLocationControllerPackage() {
synchronized (mLock) {
return mExtraLocationControllerPackage;
}
}
@Override
public void setExtraLocationControllerPackageEnabled(boolean enabled) {
mContext.enforceCallingPermission(permission.LOCATION_HARDWARE,
permission.LOCATION_HARDWARE + " permission required");
synchronized (mLock) {
mExtraLocationControllerPackageEnabled = enabled;
}
}
@Override
public boolean isExtraLocationControllerPackageEnabled() {
synchronized (mLock) {
return mExtraLocationControllerPackageEnabled
&& (mExtraLocationControllerPackage != null);
}
}
@Override
public void setLocationEnabledForUser(boolean enabled, int userId) {
userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
userId, false, false, "setLocationEnabledForUser", null);
mContext.enforceCallingOrSelfPermission(permission.WRITE_SECURE_SETTINGS, null);
LocationManager.invalidateLocalLocationEnabledCaches();
mInjector.getSettingsHelper().setLocationEnabled(enabled, userId);
}
@Override
public boolean isLocationEnabledForUser(int userId) {
userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
userId, false, false, "isLocationEnabledForUser", null);
return mInjector.getSettingsHelper().isLocationEnabled(userId);
}
@Override
public void setAdasGnssLocationEnabledForUser(boolean enabled, int userId) {
userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
userId, false, false, "setAdasGnssLocationEnabledForUser", null);
mContext.enforceCallingOrSelfPermission(permission.WRITE_SECURE_SETTINGS, null);
mInjector.getLocationSettings().updateUserSettings(userId,
settings -> settings.withAdasGnssLocationEnabled(enabled));
}
@Override
public boolean isAdasGnssLocationEnabledForUser(int userId) {
userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
userId, false, false, "isAdasGnssLocationEnabledForUser", null);
return mInjector.getLocationSettings().getUserSettings(userId).isAdasGnssLocationEnabled();
}
@Override
public boolean isProviderEnabledForUser(String provider, int userId) {
return mLocalService.isProviderEnabledForUser(provider, userId);
}
@Override
public boolean geocoderIsPresent() {
return mGeocodeProvider != null;
}
@Override
public void getFromLocation(double latitude, double longitude, int maxResults,
GeocoderParams params, IGeocodeListener listener) {
// validate identity
CallerIdentity identity = CallerIdentity.fromBinder(mContext, params.getClientPackage(),
params.getClientAttributionTag());
Preconditions.checkArgument(identity.getUid() == params.getClientUid());
if (mGeocodeProvider != null) {
mGeocodeProvider.getFromLocation(latitude, longitude, maxResults, params, listener);
} else {
try {
listener.onResults(null, Collections.emptyList());
} catch (RemoteException e) {
// ignore
}
}
}
@Override
public void getFromLocationName(String locationName,
double lowerLeftLatitude, double lowerLeftLongitude,
double upperRightLatitude, double upperRightLongitude, int maxResults,
GeocoderParams params, IGeocodeListener listener) {
// validate identity
CallerIdentity identity = CallerIdentity.fromBinder(mContext, params.getClientPackage(),
params.getClientAttributionTag());
Preconditions.checkArgument(identity.getUid() == params.getClientUid());
if (mGeocodeProvider != null) {
mGeocodeProvider.getFromLocationName(locationName, lowerLeftLatitude,
lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
maxResults, params, listener);
} else {
try {
listener.onResults(null, Collections.emptyList());
} catch (RemoteException e) {
// ignore
}
}
}
@Override
public void addTestProvider(String provider, ProviderProperties properties,
List<String> extraAttributionTags, String packageName, String attributionTag) {
// unsafe is ok because app ops will verify the package name
CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName, attributionTag);
if (!mInjector.getAppOpsHelper().noteOp(AppOpsManager.OP_MOCK_LOCATION, identity)) {
return;
}
final LocationProviderManager manager = getOrAddLocationProviderManager(provider);
manager.setMockProvider(new MockLocationProvider(properties, identity,
new ArraySet<>(extraAttributionTags)));
}
@Override
public void removeTestProvider(String provider, String packageName, String attributionTag) {
// unsafe is ok because app ops will verify the package name
CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName, attributionTag);
if (!mInjector.getAppOpsHelper().noteOp(AppOpsManager.OP_MOCK_LOCATION, identity)) {
return;
}
synchronized (mLock) {
LocationProviderManager manager = getLocationProviderManager(provider);
if (manager == null) {
return;
}
manager.setMockProvider(null);
if (!manager.hasProvider()) {
removeLocationProviderManager(manager);
}
}
}
@Override
public void setTestProviderLocation(String provider, Location location, String packageName,
String attributionTag) {
// unsafe is ok because app ops will verify the package name
CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName,
attributionTag);
if (!mInjector.getAppOpsHelper().noteOp(AppOpsManager.OP_MOCK_LOCATION, identity)) {
return;
}
Preconditions.checkArgument(location.isComplete(),
"incomplete location object, missing timestamp or accuracy?");
LocationProviderManager manager = getLocationProviderManager(provider);
if (manager == null) {
throw new IllegalArgumentException("provider doesn't exist: " + provider);
}
manager.setMockProviderLocation(location);
}
@Override
public void setTestProviderEnabled(String provider, boolean enabled, String packageName,
String attributionTag) {
// unsafe is ok because app ops will verify the package name
CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName,
attributionTag);
if (!mInjector.getAppOpsHelper().noteOp(AppOpsManager.OP_MOCK_LOCATION, identity)) {
return;
}
LocationProviderManager manager = getLocationProviderManager(provider);
if (manager == null) {
throw new IllegalArgumentException("provider doesn't exist: " + provider);
}
manager.setMockProviderAllowed(enabled);
}
@Override
public int handleShellCommand(ParcelFileDescriptor in, ParcelFileDescriptor out,
ParcelFileDescriptor err, String[] args) {
return new LocationShellCommand(mContext, this).exec(
this, in.getFileDescriptor(), out.getFileDescriptor(), err.getFileDescriptor(),
args);
}
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) {
return;
}
IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
if (args.length > 0) {
LocationProviderManager manager = getLocationProviderManager(args[0]);
if (manager != null) {
ipw.println("Provider:");
ipw.increaseIndent();
manager.dump(fd, ipw, args);
ipw.decreaseIndent();
ipw.println("Event Log:");
ipw.increaseIndent();
EVENT_LOG.iterate(manager.getName(), ipw::println);
ipw.decreaseIndent();
return;
}
if ("--gnssmetrics".equals(args[0])) {
if (mGnssManagerService != null) {
mGnssManagerService.dump(fd, ipw, args);
}
return;
}
}
ipw.println("Location Manager State:");
ipw.increaseIndent();
ipw.println("User Info:");
ipw.increaseIndent();
mInjector.getUserInfoHelper().dump(fd, ipw, args);
ipw.decreaseIndent();
ipw.println("Location Settings:");
ipw.increaseIndent();
mInjector.getSettingsHelper().dump(fd, ipw, args);
ipw.decreaseIndent();
synchronized (mLock) {
if (mExtraLocationControllerPackage != null) {
ipw.println(
"Location Controller Extra Package: " + mExtraLocationControllerPackage
+ (mExtraLocationControllerPackageEnabled ? " [enabled]"
: " [disabled]"));
}
}
ipw.println("Location Providers:");
ipw.increaseIndent();
for (LocationProviderManager manager : mProviderManagers) {
manager.dump(fd, ipw, args);
}
ipw.decreaseIndent();
ipw.println("Historical Aggregate Location Provider Data:");
ipw.increaseIndent();
ArrayMap<String, ArrayMap<CallerIdentity, LocationEventLog.AggregateStats>> aggregateStats =
EVENT_LOG.copyAggregateStats();
for (int i = 0; i < aggregateStats.size(); i++) {
ipw.print(aggregateStats.keyAt(i));
ipw.println(":");
ipw.increaseIndent();
ArrayMap<CallerIdentity, LocationEventLog.AggregateStats> providerStats =
aggregateStats.valueAt(i);
for (int j = 0; j < providerStats.size(); j++) {
ipw.print(providerStats.keyAt(j));
ipw.print(": ");
providerStats.valueAt(j).updateTotals();
ipw.println(providerStats.valueAt(j));
}
ipw.decreaseIndent();
}
ipw.decreaseIndent();
if (mGnssManagerService != null) {
ipw.println("GNSS Manager:");
ipw.increaseIndent();
mGnssManagerService.dump(fd, ipw, args);
ipw.decreaseIndent();
}
ipw.println("Geofence Manager:");
ipw.increaseIndent();
mGeofenceManager.dump(fd, ipw, args);
ipw.decreaseIndent();
ipw.println("Event Log:");
ipw.increaseIndent();
EVENT_LOG.iterate(ipw::println);
ipw.decreaseIndent();
}
@Override
public void onStateChanged(String provider, AbstractLocationProvider.State oldState,
AbstractLocationProvider.State newState) {
if (!Objects.equals(oldState.identity, newState.identity)) {
refreshAppOpsRestrictions(UserHandle.USER_ALL);
}
if (!oldState.extraAttributionTags.equals(newState.extraAttributionTags)
|| !Objects.equals(oldState.identity, newState.identity)) {
// since we're potentially affecting the tag lists for two different uids, acquire the
// lock to ensure providers cannot change while we're looping over the providers
// multiple times, which could lead to inconsistent results.
synchronized (mLock) {
LocationPackageTagsListener listener = mLocationTagsChangedListener;
if (listener != null) {
int oldUid = oldState.identity != null ? oldState.identity.getUid() : -1;
int newUid = newState.identity != null ? newState.identity.getUid() : -1;
if (oldUid != -1) {
PackageTagsList tags = calculateAppOpsLocationSourceTags(oldUid);
FgThread.getHandler().post(
() -> listener.onLocationPackageTagsChanged(oldUid, tags));
}
// if the new app id is the same as the old app id, no need to invoke the
// listener twice, it's already been taken care of
if (newUid != -1 && newUid != oldUid) {
PackageTagsList tags = calculateAppOpsLocationSourceTags(newUid);
FgThread.getHandler().post(
() -> listener.onLocationPackageTagsChanged(newUid, tags));
}
}
}
}
}
private void refreshAppOpsRestrictions(int userId) {
if (userId == UserHandle.USER_ALL) {
final int[] runningUserIds = mInjector.getUserInfoHelper().getRunningUserIds();
for (int i = 0; i < runningUserIds.length; i++) {
refreshAppOpsRestrictions(runningUserIds[i]);
}
return;
}
Preconditions.checkArgument(userId >= 0);
boolean enabled = mInjector.getSettingsHelper().isLocationEnabled(userId);
PackageTagsList allowedPackages = null;
if (!enabled) {
PackageTagsList.Builder builder = new PackageTagsList.Builder();
for (LocationProviderManager manager : mProviderManagers) {
CallerIdentity identity = manager.getIdentity();
if (identity != null) {
builder.add(identity.getPackageName(), identity.getAttributionTag());
}
}
builder.add(mInjector.getSettingsHelper().getIgnoreSettingsAllowlist());
allowedPackages = builder.build();
}
AppOpsManager appOpsManager = Objects.requireNonNull(
mContext.getSystemService(AppOpsManager.class));
appOpsManager.setUserRestrictionForUser(
AppOpsManager.OP_COARSE_LOCATION,
!enabled,
LocationManagerService.this,
allowedPackages,
userId);
appOpsManager.setUserRestrictionForUser(
AppOpsManager.OP_FINE_LOCATION,
!enabled,
LocationManagerService.this,
allowedPackages,
userId);
}
PackageTagsList calculateAppOpsLocationSourceTags(int uid) {
PackageTagsList.Builder builder = new PackageTagsList.Builder();
for (LocationProviderManager manager : mProviderManagers) {
AbstractLocationProvider.State managerState = manager.getState();
if (managerState.identity == null) {
continue;
}
if (managerState.identity.getUid() != uid) {
continue;
}
builder.add(managerState.identity.getPackageName(), managerState.extraAttributionTags);
if (managerState.extraAttributionTags.isEmpty()
|| managerState.identity.getAttributionTag() != null) {
builder.add(managerState.identity.getPackageName(),
managerState.identity.getAttributionTag());
} else {
Log.e(TAG, manager.getName() + " provider has specified a null attribution tag and "
+ "a non-empty set of extra attribution tags - dropping the null "
+ "attribution tag");
}
}
return builder.build();
}
private class LocalService extends LocationManagerInternal {
LocalService() {}
@Override
public boolean isProviderEnabledForUser(@NonNull String provider, int userId) {
userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
Binder.getCallingUid(), userId, false, false, "isProviderEnabledForUser", null);
LocationProviderManager manager = getLocationProviderManager(provider);
if (manager == null) {
return false;
}
return manager.isEnabled(userId);
}
@Override
public void addProviderEnabledListener(String provider, ProviderEnabledListener listener) {
LocationProviderManager manager = Objects.requireNonNull(
getLocationProviderManager(provider));
manager.addEnabledListener(listener);
}
@Override
public void removeProviderEnabledListener(String provider,
ProviderEnabledListener listener) {
LocationProviderManager manager = Objects.requireNonNull(
getLocationProviderManager(provider));
manager.removeEnabledListener(listener);
}
@Override
public boolean isProvider(@Nullable String provider, CallerIdentity identity) {
for (LocationProviderManager manager : mProviderManagers) {
if (provider != null && !provider.equals(manager.getName())) {
continue;
}
if (identity.equals(manager.getIdentity())) {
return true;
}
}
return false;
}
@Override
public void sendNiResponse(int notifId, int userResponse) {
if (mGnssManagerService != null) {
mGnssManagerService.sendNiResponse(notifId, userResponse);
}
}
@Override
public @Nullable LocationTime getGnssTimeMillis() {
LocationProviderManager gpsManager = getLocationProviderManager(GPS_PROVIDER);
if (gpsManager == null) {
return null;
}
Location location = gpsManager.getLastLocationUnsafe(UserHandle.USER_ALL,
PERMISSION_FINE, false, Long.MAX_VALUE);
if (location == null) {
return null;
}
return new LocationTime(location.getTime(), location.getElapsedRealtimeNanos());
}
@Override
public void setLocationPackageTagsListener(
@Nullable LocationPackageTagsListener listener) {
synchronized (mLock) {
mLocationTagsChangedListener = listener;
// calculate initial tag list and send to listener
if (listener != null) {
ArraySet<Integer> uids = new ArraySet<>(mProviderManagers.size());
for (LocationProviderManager manager : mProviderManagers) {
CallerIdentity identity = manager.getIdentity();
if (identity != null) {
uids.add(identity.getUid());
}
}
for (int uid : uids) {
PackageTagsList tags = calculateAppOpsLocationSourceTags(uid);
if (!tags.isEmpty()) {
FgThread.getHandler().post(
() -> listener.onLocationPackageTagsChanged(uid, tags));
}
}
}
}
}
}
private static final class SystemInjector implements Injector {
private final Context mContext;
private final UserInfoHelper mUserInfoHelper;
private final LocationSettings mLocationSettings;
private final AlarmHelper mAlarmHelper;
private final SystemAppOpsHelper mAppOpsHelper;
private final SystemLocationPermissionsHelper mLocationPermissionsHelper;
private final SystemSettingsHelper mSettingsHelper;
private final SystemAppForegroundHelper mAppForegroundHelper;
private final SystemLocationPowerSaveModeHelper mLocationPowerSaveModeHelper;
private final SystemScreenInteractiveHelper mScreenInteractiveHelper;
private final SystemDeviceStationaryHelper mDeviceStationaryHelper;
private final SystemDeviceIdleHelper mDeviceIdleHelper;
private final LocationAttributionHelper mLocationAttributionHelper;
private final LocationUsageLogger mLocationUsageLogger;
// lazily instantiated since they may not always be used
@GuardedBy("this")
private @Nullable SystemEmergencyHelper mEmergencyCallHelper;
@GuardedBy("this")
private boolean mSystemReady;
SystemInjector(Context context, UserInfoHelper userInfoHelper) {
mContext = context;
mUserInfoHelper = userInfoHelper;
mLocationSettings = new LocationSettings(context);
mAlarmHelper = new SystemAlarmHelper(context);
mAppOpsHelper = new SystemAppOpsHelper(context);
mLocationPermissionsHelper = new SystemLocationPermissionsHelper(context,
mAppOpsHelper);
mSettingsHelper = new SystemSettingsHelper(context);
mAppForegroundHelper = new SystemAppForegroundHelper(context);
mLocationPowerSaveModeHelper = new SystemLocationPowerSaveModeHelper(context);
mScreenInteractiveHelper = new SystemScreenInteractiveHelper(context);
mDeviceStationaryHelper = new SystemDeviceStationaryHelper();
mDeviceIdleHelper = new SystemDeviceIdleHelper(context);
mLocationAttributionHelper = new LocationAttributionHelper(mAppOpsHelper);
mLocationUsageLogger = new LocationUsageLogger();
}
synchronized void onSystemReady() {
mAppOpsHelper.onSystemReady();
mLocationPermissionsHelper.onSystemReady();
mSettingsHelper.onSystemReady();
mAppForegroundHelper.onSystemReady();
mLocationPowerSaveModeHelper.onSystemReady();
mScreenInteractiveHelper.onSystemReady();
mDeviceStationaryHelper.onSystemReady();
mDeviceIdleHelper.onSystemReady();
if (mEmergencyCallHelper != null) {
mEmergencyCallHelper.onSystemReady();
}
mSystemReady = true;
}
@Override
public UserInfoHelper getUserInfoHelper() {
return mUserInfoHelper;
}
@Override
public LocationSettings getLocationSettings() {
return mLocationSettings;
}
@Override
public AlarmHelper getAlarmHelper() {
return mAlarmHelper;
}
@Override
public AppOpsHelper getAppOpsHelper() {
return mAppOpsHelper;
}
@Override
public LocationPermissionsHelper getLocationPermissionsHelper() {
return mLocationPermissionsHelper;
}
@Override
public SettingsHelper getSettingsHelper() {
return mSettingsHelper;
}
@Override
public AppForegroundHelper getAppForegroundHelper() {
return mAppForegroundHelper;
}
@Override
public LocationPowerSaveModeHelper getLocationPowerSaveModeHelper() {
return mLocationPowerSaveModeHelper;
}
@Override
public ScreenInteractiveHelper getScreenInteractiveHelper() {
return mScreenInteractiveHelper;
}
@Override
public DeviceStationaryHelper getDeviceStationaryHelper() {
return mDeviceStationaryHelper;
}
@Override
public DeviceIdleHelper getDeviceIdleHelper() {
return mDeviceIdleHelper;
}
@Override
public LocationAttributionHelper getLocationAttributionHelper() {
return mLocationAttributionHelper;
}
@Override
public synchronized EmergencyHelper getEmergencyHelper() {
if (mEmergencyCallHelper == null) {
mEmergencyCallHelper = new SystemEmergencyHelper(mContext);
if (mSystemReady) {
mEmergencyCallHelper.onSystemReady();
}
}
return mEmergencyCallHelper;
}
@Override
public LocationUsageLogger getLocationUsageLogger() {
return mLocationUsageLogger;
}
}
}