AppOps stats are used to populate the "apps recently using location" list in settings->location. There is no reason to show Android OS in that list simply because of internal location requests supporting other clients. Change-Id: I6908aa63deb19d22733b8d9cdae6ea5dbbea55e0
428 lines
15 KiB
Java
428 lines
15 KiB
Java
/*
|
|
* Copyright (C) 2013 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 android.hardware.location.GeofenceHardware;
|
|
import android.hardware.location.GeofenceHardwareImpl;
|
|
import android.hardware.location.GeofenceHardwareRequestParcelable;
|
|
import android.hardware.location.IFusedLocationHardware;
|
|
import android.hardware.location.IFusedLocationHardwareSink;
|
|
import android.location.IFusedGeofenceHardware;
|
|
import android.location.FusedBatchOptions;
|
|
import android.location.Location;
|
|
import android.location.LocationListener;
|
|
import android.location.LocationManager;
|
|
import android.location.LocationRequest;
|
|
|
|
import android.content.Context;
|
|
import android.os.Bundle;
|
|
import android.os.Looper;
|
|
import android.os.RemoteException;
|
|
import android.os.SystemClock;
|
|
import android.util.Log;
|
|
|
|
/**
|
|
* This class is an interop layer for JVM types and the JNI code that interacts
|
|
* with the FLP HAL implementation.
|
|
*
|
|
* {@hide}
|
|
*/
|
|
public class FlpHardwareProvider {
|
|
private GeofenceHardwareImpl mGeofenceHardwareSink = null;
|
|
private IFusedLocationHardwareSink mLocationSink = null;
|
|
|
|
private static FlpHardwareProvider sSingletonInstance = null;
|
|
|
|
private final static String TAG = "FlpHardwareProvider";
|
|
private final Context mContext;
|
|
private final Object mLocationSinkLock = new Object();
|
|
|
|
// FlpHal result codes, they must be equal to the ones in fused_location.h
|
|
private static final int FLP_RESULT_SUCCESS = 0;
|
|
private static final int FLP_RESULT_ERROR = -1;
|
|
private static final int FLP_RESULT_INSUFFICIENT_MEMORY = -2;
|
|
private static final int FLP_RESULT_TOO_MANY_GEOFENCES = -3;
|
|
private static final int FLP_RESULT_ID_EXISTS = -4;
|
|
private static final int FLP_RESULT_ID_UNKNOWN = -5;
|
|
private static final int FLP_RESULT_INVALID_GEOFENCE_TRANSITION = -6;
|
|
|
|
public static FlpHardwareProvider getInstance(Context context) {
|
|
if (sSingletonInstance == null) {
|
|
sSingletonInstance = new FlpHardwareProvider(context);
|
|
}
|
|
|
|
return sSingletonInstance;
|
|
}
|
|
|
|
private FlpHardwareProvider(Context context) {
|
|
mContext = context;
|
|
|
|
// register for listening for passive provider data
|
|
LocationManager manager = (LocationManager) mContext.getSystemService(
|
|
Context.LOCATION_SERVICE);
|
|
final long minTime = 0;
|
|
final float minDistance = 0;
|
|
final boolean oneShot = false;
|
|
LocationRequest request = LocationRequest.createFromDeprecatedProvider(
|
|
LocationManager.PASSIVE_PROVIDER,
|
|
minTime,
|
|
minDistance,
|
|
oneShot);
|
|
// Don't keep track of this request since it's done on behalf of other clients
|
|
// (which are kept track of separately).
|
|
request.setHideFromAppOps(true);
|
|
manager.requestLocationUpdates(
|
|
request,
|
|
new NetworkLocationListener(),
|
|
Looper.myLooper());
|
|
}
|
|
|
|
public static boolean isSupported() {
|
|
return nativeIsSupported();
|
|
}
|
|
|
|
/**
|
|
* Private callback functions used by FLP HAL.
|
|
*/
|
|
// FlpCallbacks members
|
|
private void onLocationReport(Location[] locations) {
|
|
for (Location location : locations) {
|
|
location.setProvider(LocationManager.FUSED_PROVIDER);
|
|
// set the elapsed time-stamp just as GPS provider does
|
|
location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
|
|
}
|
|
|
|
IFusedLocationHardwareSink sink;
|
|
synchronized (mLocationSinkLock) {
|
|
sink = mLocationSink;
|
|
}
|
|
try {
|
|
if (sink != null) {
|
|
sink.onLocationAvailable(locations);
|
|
}
|
|
} catch (RemoteException e) {
|
|
Log.e(TAG, "RemoteException calling onLocationAvailable");
|
|
}
|
|
}
|
|
|
|
// FlpDiagnosticCallbacks members
|
|
private void onDataReport(String data) {
|
|
IFusedLocationHardwareSink sink;
|
|
synchronized (mLocationSinkLock) {
|
|
sink = mLocationSink;
|
|
}
|
|
try {
|
|
if (mLocationSink != null) {
|
|
sink.onDiagnosticDataAvailable(data);
|
|
}
|
|
} catch (RemoteException e) {
|
|
Log.e(TAG, "RemoteException calling onDiagnosticDataAvailable");
|
|
}
|
|
}
|
|
|
|
// FlpGeofenceCallbacks members
|
|
private void onGeofenceTransition(
|
|
int geofenceId,
|
|
Location location,
|
|
int transition,
|
|
long timestamp,
|
|
int sourcesUsed) {
|
|
getGeofenceHardwareSink().reportGeofenceTransition(
|
|
geofenceId,
|
|
updateLocationInformation(location),
|
|
transition,
|
|
timestamp,
|
|
GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE,
|
|
sourcesUsed);
|
|
}
|
|
|
|
private void onGeofenceMonitorStatus(int status, int source, Location location) {
|
|
// allow the location to be optional in this event
|
|
Location updatedLocation = null;
|
|
if(location != null) {
|
|
updatedLocation = updateLocationInformation(location);
|
|
}
|
|
|
|
getGeofenceHardwareSink().reportGeofenceMonitorStatus(
|
|
GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE,
|
|
status,
|
|
updatedLocation,
|
|
source);
|
|
}
|
|
|
|
private void onGeofenceAdd(int geofenceId, int result) {
|
|
getGeofenceHardwareSink().reportGeofenceAddStatus(
|
|
geofenceId,
|
|
translateToGeofenceHardwareStatus(result));
|
|
}
|
|
|
|
private void onGeofenceRemove(int geofenceId, int result) {
|
|
getGeofenceHardwareSink().reportGeofenceRemoveStatus(
|
|
geofenceId,
|
|
translateToGeofenceHardwareStatus(result));
|
|
}
|
|
|
|
private void onGeofencePause(int geofenceId, int result) {
|
|
getGeofenceHardwareSink().reportGeofencePauseStatus(
|
|
geofenceId,
|
|
translateToGeofenceHardwareStatus(result));
|
|
}
|
|
|
|
private void onGeofenceResume(int geofenceId, int result) {
|
|
getGeofenceHardwareSink().reportGeofenceResumeStatus(
|
|
geofenceId,
|
|
translateToGeofenceHardwareStatus(result));
|
|
}
|
|
|
|
/**
|
|
* Private native methods accessing FLP HAL.
|
|
*/
|
|
static { nativeClassInit(); }
|
|
|
|
// Core members
|
|
private static native void nativeClassInit();
|
|
private static native boolean nativeIsSupported();
|
|
|
|
// FlpLocationInterface members
|
|
private native void nativeInit();
|
|
private native int nativeGetBatchSize();
|
|
private native void nativeStartBatching(int requestId, FusedBatchOptions options);
|
|
private native void nativeUpdateBatchingOptions(int requestId, FusedBatchOptions optionsObject);
|
|
private native void nativeStopBatching(int id);
|
|
private native void nativeRequestBatchedLocation(int lastNLocations);
|
|
private native void nativeInjectLocation(Location location);
|
|
// TODO [Fix] sort out the lifetime of the instance
|
|
private native void nativeCleanup();
|
|
|
|
// FlpDiagnosticsInterface members
|
|
private native boolean nativeIsDiagnosticSupported();
|
|
private native void nativeInjectDiagnosticData(String data);
|
|
|
|
// FlpDeviceContextInterface members
|
|
private native boolean nativeIsDeviceContextSupported();
|
|
private native void nativeInjectDeviceContext(int deviceEnabledContext);
|
|
|
|
// FlpGeofencingInterface members
|
|
private native boolean nativeIsGeofencingSupported();
|
|
private native void nativeAddGeofences(
|
|
GeofenceHardwareRequestParcelable[] geofenceRequestsArray);
|
|
private native void nativePauseGeofence(int geofenceId);
|
|
private native void nativeResumeGeofence(int geofenceId, int monitorTransitions);
|
|
private native void nativeModifyGeofenceOption(
|
|
int geofenceId,
|
|
int lastTransition,
|
|
int monitorTransitions,
|
|
int notificationResponsiveness,
|
|
int unknownTimer,
|
|
int sourcesToUse);
|
|
private native void nativeRemoveGeofences(int[] geofenceIdsArray);
|
|
|
|
/**
|
|
* Interface implementations for services built on top of this functionality.
|
|
*/
|
|
public static final String LOCATION = "Location";
|
|
public static final String GEOFENCING = "Geofencing";
|
|
|
|
public IFusedLocationHardware getLocationHardware() {
|
|
nativeInit();
|
|
return mLocationHardware;
|
|
}
|
|
|
|
public IFusedGeofenceHardware getGeofenceHardware() {
|
|
nativeInit();
|
|
return mGeofenceHardwareService;
|
|
}
|
|
|
|
private final IFusedLocationHardware mLocationHardware = new IFusedLocationHardware.Stub() {
|
|
@Override
|
|
public void registerSink(IFusedLocationHardwareSink eventSink) {
|
|
synchronized (mLocationSinkLock) {
|
|
// only one sink is allowed at the moment
|
|
if (mLocationSink != null) {
|
|
throw new RuntimeException(
|
|
"IFusedLocationHardware does not support multiple sinks");
|
|
}
|
|
|
|
mLocationSink = eventSink;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void unregisterSink(IFusedLocationHardwareSink eventSink) {
|
|
synchronized (mLocationSinkLock) {
|
|
// don't throw if the sink is not registered, simply make it a no-op
|
|
if (mLocationSink == eventSink) {
|
|
mLocationSink = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public int getSupportedBatchSize() {
|
|
return nativeGetBatchSize();
|
|
}
|
|
|
|
@Override
|
|
public void startBatching(int requestId, FusedBatchOptions options) {
|
|
nativeStartBatching(requestId, options);
|
|
}
|
|
|
|
@Override
|
|
public void stopBatching(int requestId) {
|
|
nativeStopBatching(requestId);
|
|
}
|
|
|
|
@Override
|
|
public void updateBatchingOptions(int requestId, FusedBatchOptions options) {
|
|
nativeUpdateBatchingOptions(requestId, options);
|
|
}
|
|
|
|
@Override
|
|
public void requestBatchOfLocations(int batchSizeRequested) {
|
|
nativeRequestBatchedLocation(batchSizeRequested);
|
|
}
|
|
|
|
@Override
|
|
public boolean supportsDiagnosticDataInjection() {
|
|
return nativeIsDiagnosticSupported();
|
|
}
|
|
|
|
@Override
|
|
public void injectDiagnosticData(String data) {
|
|
nativeInjectDiagnosticData(data);
|
|
}
|
|
|
|
@Override
|
|
public boolean supportsDeviceContextInjection() {
|
|
return nativeIsDeviceContextSupported();
|
|
}
|
|
|
|
@Override
|
|
public void injectDeviceContext(int deviceEnabledContext) {
|
|
nativeInjectDeviceContext(deviceEnabledContext);
|
|
}
|
|
};
|
|
|
|
private final IFusedGeofenceHardware mGeofenceHardwareService =
|
|
new IFusedGeofenceHardware.Stub() {
|
|
@Override
|
|
public boolean isSupported() {
|
|
return nativeIsGeofencingSupported();
|
|
}
|
|
|
|
@Override
|
|
public void addGeofences(GeofenceHardwareRequestParcelable[] geofenceRequestsArray) {
|
|
nativeAddGeofences(geofenceRequestsArray);
|
|
}
|
|
|
|
@Override
|
|
public void removeGeofences(int[] geofenceIds) {
|
|
nativeRemoveGeofences(geofenceIds);
|
|
}
|
|
|
|
@Override
|
|
public void pauseMonitoringGeofence(int geofenceId) {
|
|
nativePauseGeofence(geofenceId);
|
|
}
|
|
|
|
@Override
|
|
public void resumeMonitoringGeofence(int geofenceId, int monitorTransitions) {
|
|
nativeResumeGeofence(geofenceId, monitorTransitions);
|
|
}
|
|
|
|
@Override
|
|
public void modifyGeofenceOptions(int geofenceId,
|
|
int lastTransition,
|
|
int monitorTransitions,
|
|
int notificationResponsiveness,
|
|
int unknownTimer,
|
|
int sourcesToUse) {
|
|
nativeModifyGeofenceOption(
|
|
geofenceId,
|
|
lastTransition,
|
|
monitorTransitions,
|
|
notificationResponsiveness,
|
|
unknownTimer,
|
|
sourcesToUse);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Internal classes and functions used by the provider.
|
|
*/
|
|
private final class NetworkLocationListener implements LocationListener {
|
|
@Override
|
|
public void onLocationChanged(Location location) {
|
|
if (
|
|
!LocationManager.NETWORK_PROVIDER.equals(location.getProvider()) ||
|
|
!location.hasAccuracy()
|
|
) {
|
|
return;
|
|
}
|
|
|
|
nativeInjectLocation(location);
|
|
}
|
|
|
|
@Override
|
|
public void onStatusChanged(String provider, int status, Bundle extras) { }
|
|
|
|
@Override
|
|
public void onProviderEnabled(String provider) { }
|
|
|
|
@Override
|
|
public void onProviderDisabled(String provider) { }
|
|
}
|
|
|
|
private GeofenceHardwareImpl getGeofenceHardwareSink() {
|
|
if (mGeofenceHardwareSink == null) {
|
|
mGeofenceHardwareSink = GeofenceHardwareImpl.getInstance(mContext);
|
|
}
|
|
|
|
return mGeofenceHardwareSink;
|
|
}
|
|
|
|
private static int translateToGeofenceHardwareStatus(int flpHalResult) {
|
|
switch(flpHalResult) {
|
|
case FLP_RESULT_SUCCESS:
|
|
return GeofenceHardware.GEOFENCE_SUCCESS;
|
|
case FLP_RESULT_ERROR:
|
|
return GeofenceHardware.GEOFENCE_FAILURE;
|
|
// TODO: uncomment this once the ERROR definition is marked public
|
|
//case FLP_RESULT_INSUFFICIENT_MEMORY:
|
|
// return GeofenceHardware.GEOFENCE_ERROR_INSUFFICIENT_MEMORY;
|
|
case FLP_RESULT_TOO_MANY_GEOFENCES:
|
|
return GeofenceHardware.GEOFENCE_ERROR_TOO_MANY_GEOFENCES;
|
|
case FLP_RESULT_ID_EXISTS:
|
|
return GeofenceHardware.GEOFENCE_ERROR_ID_EXISTS;
|
|
case FLP_RESULT_ID_UNKNOWN:
|
|
return GeofenceHardware.GEOFENCE_ERROR_ID_UNKNOWN;
|
|
case FLP_RESULT_INVALID_GEOFENCE_TRANSITION:
|
|
return GeofenceHardware.GEOFENCE_ERROR_INVALID_TRANSITION;
|
|
default:
|
|
Log.e(TAG, String.format("Invalid FlpHal result code: %d", flpHalResult));
|
|
return GeofenceHardware.GEOFENCE_FAILURE;
|
|
}
|
|
}
|
|
|
|
private Location updateLocationInformation(Location location) {
|
|
location.setProvider(LocationManager.FUSED_PROVIDER);
|
|
// set the elapsed time-stamp just as GPS provider does
|
|
location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
|
|
return location;
|
|
}
|
|
}
|