blob: 21946ca88401e4cd5b285754b90b0c4b41c905db [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.gnss;
import android.Manifest;
import android.annotation.Nullable;
import android.content.Context;
import android.content.Intent;
import android.hardware.location.GeofenceHardware;
import android.hardware.location.GeofenceHardwareImpl;
import android.location.FusedBatchOptions;
import android.location.GnssAntennaInfo;
import android.location.GnssCapabilities;
import android.location.GnssMeasurementCorrections;
import android.location.GnssMeasurementRequest;
import android.location.IGnssAntennaInfoListener;
import android.location.IGnssMeasurementsListener;
import android.location.IGnssNavigationMessageListener;
import android.location.IGnssNmeaListener;
import android.location.IGnssStatusListener;
import android.location.IGpsGeofenceHardware;
import android.location.Location;
import android.location.LocationManager;
import android.location.util.identity.CallerIdentity;
import android.os.BatteryStats;
import android.os.Binder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.util.IndentingPrintWriter;
import android.util.Log;
import com.android.internal.app.IBatteryStats;
import com.android.server.FgThread;
import com.android.server.location.gnss.hal.GnssNative;
import com.android.server.location.injector.Injector;
import java.io.FileDescriptor;
import java.util.List;
/** Manages Gnss providers and related Gnss functions for LocationManagerService. */
public class GnssManagerService {
public static final String TAG = "GnssManager";
public static final boolean D = Log.isLoggable(TAG, Log.DEBUG);
private static final String ATTRIBUTION_ID = "GnssService";
final Context mContext;
private final GnssNative mGnssNative;
private final GnssLocationProvider mGnssLocationProvider;
private final GnssStatusProvider mGnssStatusProvider;
private final GnssNmeaProvider mGnssNmeaProvider;
private final GnssMeasurementsProvider mGnssMeasurementsProvider;
private final GnssNavigationMessageProvider mGnssNavigationMessageProvider;
private final GnssAntennaInfoProvider mGnssAntennaInfoProvider;
private final IGpsGeofenceHardware mGnssGeofenceProxy;
private final GnssGeofenceHalModule mGeofenceHalModule;
private final GnssCapabilitiesHalModule mCapabilitiesHalModule;
private final GnssMetrics mGnssMetrics;
public GnssManagerService(Context context, Injector injector, GnssNative gnssNative) {
mContext = context.createAttributionContext(ATTRIBUTION_ID);
mGnssNative = gnssNative;
mGnssMetrics = new GnssMetrics(mContext, IBatteryStats.Stub.asInterface(
ServiceManager.getService(BatteryStats.SERVICE_NAME)), mGnssNative);
mGnssLocationProvider = new GnssLocationProvider(mContext, injector, mGnssNative,
mGnssMetrics);
mGnssStatusProvider = new GnssStatusProvider(injector, mGnssNative);
mGnssNmeaProvider = new GnssNmeaProvider(injector, mGnssNative);
mGnssMeasurementsProvider = new GnssMeasurementsProvider(injector, mGnssNative);
mGnssNavigationMessageProvider = new GnssNavigationMessageProvider(injector, mGnssNative);
mGnssAntennaInfoProvider = new GnssAntennaInfoProvider(mGnssNative);
mGnssGeofenceProxy = new GnssGeofenceProxy(mGnssNative);
mGeofenceHalModule = new GnssGeofenceHalModule(mGnssNative);
mCapabilitiesHalModule = new GnssCapabilitiesHalModule(mGnssNative);
// allow gnss access to begin - we must assume that callbacks can start immediately
mGnssNative.register();
}
/** Called when system is ready. */
public void onSystemReady() {
mGnssLocationProvider.onSystemReady();
}
/** Retrieve the GnssLocationProvider. */
public GnssLocationProvider getGnssLocationProvider() {
return mGnssLocationProvider;
}
/** Retrieve the IGpsGeofenceHardware. */
public IGpsGeofenceHardware getGnssGeofenceProxy() {
return mGnssGeofenceProxy;
}
/**
* Get year of GNSS hardware.
*/
public int getGnssYearOfHardware() {
return mGnssNative.getHardwareYear();
}
/**
* Get model name of GNSS hardware.
*/
@Nullable
public String getGnssHardwareModelName() {
return mGnssNative.getHardwareModelName();
}
/**
* Get GNSS hardware capabilities.
*/
public GnssCapabilities getGnssCapabilities() {
return mGnssNative.getCapabilities();
}
/**
* Get GNSS antenna information.
*/
public @Nullable List<GnssAntennaInfo> getGnssAntennaInfos() {
return mGnssAntennaInfoProvider.getAntennaInfos();
}
/**
* Get size of GNSS batch (GNSS location results are batched together for power savings).
*/
public int getGnssBatchSize() {
return mGnssLocationProvider.getBatchSize();
}
/**
* Registers listener for GNSS status changes.
*/
public void registerGnssStatusCallback(IGnssStatusListener listener, String packageName,
@Nullable String attributionTag, String listenerId) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag,
listenerId);
mGnssStatusProvider.addListener(identity, listener);
}
/**
* Unregisters listener for GNSS status changes.
*/
public void unregisterGnssStatusCallback(IGnssStatusListener listener) {
mGnssStatusProvider.removeListener(listener);
}
/**
* Registers listener for GNSS NMEA messages.
*/
public void registerGnssNmeaCallback(IGnssNmeaListener listener, String packageName,
@Nullable String attributionTag, String listenerId) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag,
listenerId);
mGnssNmeaProvider.addListener(identity, listener);
}
/**
* Unregisters listener for GNSS NMEA messages.
*/
public void unregisterGnssNmeaCallback(IGnssNmeaListener listener) {
mGnssNmeaProvider.removeListener(listener);
}
/**
* Adds a GNSS measurements listener.
*/
public void addGnssMeasurementsListener(GnssMeasurementRequest request,
IGnssMeasurementsListener listener, String packageName,
@Nullable String attributionTag, String listenerId) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
if (request.isCorrelationVectorOutputsEnabled()) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.LOCATION_HARDWARE, null);
}
CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag,
listenerId);
mGnssMeasurementsProvider.addListener(request, identity, listener);
}
/**
* Injects GNSS measurement corrections.
*/
public void injectGnssMeasurementCorrections(GnssMeasurementCorrections corrections) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.LOCATION_HARDWARE, null);
mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
if (!mGnssNative.injectMeasurementCorrections(corrections)) {
Log.w(TAG, "failed to inject GNSS measurement corrections");
}
}
/**
* Removes a GNSS measurements listener.
*/
public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener) {
mGnssMeasurementsProvider.removeListener(listener);
}
/**
* Adds a GNSS navigation message listener.
*/
public void addGnssNavigationMessageListener(IGnssNavigationMessageListener listener,
String packageName, @Nullable String attributionTag, String listenerId) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag,
listenerId);
mGnssNavigationMessageProvider.addListener(identity, listener);
}
/**
* Removes a GNSS navigation message listener.
*/
public void removeGnssNavigationMessageListener(IGnssNavigationMessageListener listener) {
mGnssNavigationMessageProvider.removeListener(listener);
}
/**
* Adds a GNSS antenna info listener.
*/
public void addGnssAntennaInfoListener(IGnssAntennaInfoListener listener, String packageName,
@Nullable String attributionTag, String listenerId) {
CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag,
listenerId);
mGnssAntennaInfoProvider.addListener(identity, listener);
}
/**
* Removes a GNSS antenna info listener.
*/
public void removeGnssAntennaInfoListener(IGnssAntennaInfoListener listener) {
mGnssAntennaInfoProvider.removeListener(listener);
}
/**
* Send Ni Response, indicating a location request initiated by a network carrier.
*/
public void sendNiResponse(int notifId, int userResponse) {
try {
mGnssLocationProvider.getNetInitiatedListener().sendNiResponse(notifId, userResponse);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Dump info for debugging.
*/
public void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) {
if (args.length > 0 && args[0].equals("--gnssmetrics")) {
ipw.append(mGnssMetrics.dumpGnssMetricsAsProtoString());
return;
}
ipw.println("Capabilities: " + mGnssNative.getCapabilities());
if (mGnssStatusProvider.isSupported()) {
ipw.println("Status Provider:");
ipw.increaseIndent();
mGnssStatusProvider.dump(fd, ipw, args);
ipw.decreaseIndent();
}
if (mGnssMeasurementsProvider.isSupported()) {
ipw.println("Measurements Provider:");
ipw.increaseIndent();
mGnssMeasurementsProvider.dump(fd, ipw, args);
ipw.decreaseIndent();
}
if (mGnssNavigationMessageProvider.isSupported()) {
ipw.println("Navigation Message Provider:");
ipw.increaseIndent();
mGnssNavigationMessageProvider.dump(fd, ipw, args);
ipw.decreaseIndent();
}
if (mGnssAntennaInfoProvider.isSupported()) {
ipw.println("Navigation Message Provider:");
ipw.increaseIndent();
ipw.println("Antenna Infos: " + mGnssAntennaInfoProvider.getAntennaInfos());
mGnssAntennaInfoProvider.dump(fd, ipw, args);
ipw.decreaseIndent();
}
GnssPowerStats powerStats = mGnssNative.getPowerStats();
if (powerStats != null) {
ipw.println("Last Power Stats:");
ipw.increaseIndent();
powerStats.dump(fd, ipw, args, mGnssNative.getCapabilities());
ipw.decreaseIndent();
}
}
private class GnssCapabilitiesHalModule implements GnssNative.BaseCallbacks {
GnssCapabilitiesHalModule(GnssNative gnssNative) {
gnssNative.addBaseCallbacks(this);
}
@Override
public void onHalRestarted() {}
@Override
public void onCapabilitiesChanged(GnssCapabilities oldCapabilities,
GnssCapabilities newCapabilities) {
long ident = Binder.clearCallingIdentity();
try {
Intent intent = new Intent(LocationManager.ACTION_GNSS_CAPABILITIES_CHANGED)
.putExtra(LocationManager.EXTRA_GNSS_CAPABILITIES, newCapabilities)
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
private class GnssGeofenceHalModule implements GnssNative.GeofenceCallbacks {
private GeofenceHardwareImpl mGeofenceHardwareImpl;
GnssGeofenceHalModule(GnssNative gnssNative) {
gnssNative.setGeofenceCallbacks(this);
}
private synchronized GeofenceHardwareImpl getGeofenceHardware() {
if (mGeofenceHardwareImpl == null) {
mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
}
return mGeofenceHardwareImpl;
}
@Override
public void onReportGeofenceTransition(int geofenceId, Location location,
@GeofenceTransition int transition, long timestamp) {
FgThread.getHandler().post(() -> getGeofenceHardware().reportGeofenceTransition(
geofenceId, location, transition, timestamp,
GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE,
FusedBatchOptions.SourceTechnologies.GNSS));
}
@Override
public void onReportGeofenceStatus(@GeofenceAvailability int status, Location location) {
FgThread.getHandler().post(() -> {
int monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_UNAVAILABLE;
if (status == GEOFENCE_AVAILABILITY_AVAILABLE) {
monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE;
}
getGeofenceHardware().reportGeofenceMonitorStatus(
GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE,
monitorStatus,
location,
FusedBatchOptions.SourceTechnologies.GNSS);
});
}
@Override
public void onReportGeofenceAddStatus(int geofenceId, @GeofenceStatus int status) {
FgThread.getHandler().post(() -> getGeofenceHardware().reportGeofenceAddStatus(
geofenceId, translateGeofenceStatus(status)));
}
@Override
public void onReportGeofenceRemoveStatus(int geofenceId, @GeofenceStatus int status) {
FgThread.getHandler().post(() -> getGeofenceHardware().reportGeofenceRemoveStatus(
geofenceId, translateGeofenceStatus(status)));
}
@Override
public void onReportGeofencePauseStatus(int geofenceId, @GeofenceStatus int status) {
FgThread.getHandler().post(() -> getGeofenceHardware().reportGeofencePauseStatus(
geofenceId, translateGeofenceStatus(status)));
}
@Override
public void onReportGeofenceResumeStatus(int geofenceId, @GeofenceStatus int status) {
FgThread.getHandler().post(() -> getGeofenceHardware().reportGeofenceResumeStatus(
geofenceId, translateGeofenceStatus(status)));
}
private int translateGeofenceStatus(@GeofenceStatus int status) {
switch (status) {
case GEOFENCE_STATUS_OPERATION_SUCCESS:
return GeofenceHardware.GEOFENCE_SUCCESS;
case GEOFENCE_STATUS_ERROR_GENERIC:
return GeofenceHardware.GEOFENCE_FAILURE;
case GEOFENCE_STATUS_ERROR_ID_EXISTS:
return GeofenceHardware.GEOFENCE_ERROR_ID_EXISTS;
case GEOFENCE_STATUS_ERROR_INVALID_TRANSITION:
return GeofenceHardware.GEOFENCE_ERROR_INVALID_TRANSITION;
case GEOFENCE_STATUS_ERROR_TOO_MANY_GEOFENCES:
return GeofenceHardware.GEOFENCE_ERROR_TOO_MANY_GEOFENCES;
case GEOFENCE_STATUS_ERROR_ID_UNKNOWN:
return GeofenceHardware.GEOFENCE_ERROR_ID_UNKNOWN;
default:
return -1;
}
}
}
}