| /* |
| * 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; |
| |
| // FlpHal monitor status codes, they must be equal to the ones in fused_location.h |
| private static final int FLP_GEOFENCE_MONITOR_STATUS_UNAVAILABLE = 1<<0; |
| private static final int FLP_GEOFENCE_MONITOR_STATUS_AVAILABLE = 1<<1; |
| |
| public static FlpHardwareProvider getInstance(Context context) { |
| if (sSingletonInstance == null) { |
| sSingletonInstance = new FlpHardwareProvider(context); |
| sSingletonInstance.nativeInit(); |
| } |
| |
| 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) { |
| // the transition Id does not require translation because the values in fused_location.h |
| // and GeofenceHardware are in sync |
| 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); |
| } |
| |
| int monitorStatus; |
| switch (status) { |
| case FLP_GEOFENCE_MONITOR_STATUS_UNAVAILABLE: |
| monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_UNAVAILABLE; |
| break; |
| case FLP_GEOFENCE_MONITOR_STATUS_AVAILABLE: |
| monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE; |
| break; |
| default: |
| Log.e(TAG, "Invalid FlpHal Geofence monitor status: " + status); |
| monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_UNAVAILABLE; |
| break; |
| } |
| |
| getGeofenceHardwareSink().reportGeofenceMonitorStatus( |
| GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE, |
| monitorStatus, |
| 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() { |
| return mLocationHardware; |
| } |
| |
| public IFusedGeofenceHardware getGeofenceHardware() { |
| 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; |
| } |
| } |