Home | History | Annotate | Download | only in location
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.server.location;
     18 
     19 import android.hardware.location.GeofenceHardware;
     20 import android.hardware.location.GeofenceHardwareImpl;
     21 import android.hardware.location.GeofenceHardwareRequestParcelable;
     22 import android.hardware.location.IFusedLocationHardware;
     23 import android.hardware.location.IFusedLocationHardwareSink;
     24 import android.location.IFusedGeofenceHardware;
     25 import android.location.FusedBatchOptions;
     26 import android.location.Location;
     27 import android.location.LocationListener;
     28 import android.location.LocationManager;
     29 import android.location.LocationRequest;
     30 
     31 import android.content.Context;
     32 import android.os.Bundle;
     33 import android.os.Looper;
     34 import android.os.RemoteException;
     35 import android.os.SystemClock;
     36 import android.util.Log;
     37 
     38 /**
     39  * This class is an interop layer for JVM types and the JNI code that interacts
     40  * with the FLP HAL implementation.
     41  *
     42  * {@hide}
     43  */
     44 public class FlpHardwareProvider {
     45     private GeofenceHardwareImpl mGeofenceHardwareSink = null;
     46     private IFusedLocationHardwareSink mLocationSink = null;
     47 
     48     private static FlpHardwareProvider sSingletonInstance = null;
     49 
     50     private final static String TAG = "FlpHardwareProvider";
     51     private final Context mContext;
     52     private final Object mLocationSinkLock = new Object();
     53 
     54     // FlpHal result codes, they must be equal to the ones in fused_location.h
     55     private static final int FLP_RESULT_SUCCESS = 0;
     56     private static final int FLP_RESULT_ERROR = -1;
     57     private static final int FLP_RESULT_INSUFFICIENT_MEMORY = -2;
     58     private static final int FLP_RESULT_TOO_MANY_GEOFENCES = -3;
     59     private static final int FLP_RESULT_ID_EXISTS = -4;
     60     private static final int FLP_RESULT_ID_UNKNOWN = -5;
     61     private static final int FLP_RESULT_INVALID_GEOFENCE_TRANSITION = -6;
     62 
     63     // FlpHal monitor status codes, they must be equal to the ones in fused_location.h
     64     private static final int FLP_GEOFENCE_MONITOR_STATUS_UNAVAILABLE = 1<<0;
     65     private static final int FLP_GEOFENCE_MONITOR_STATUS_AVAILABLE = 1<<1;
     66 
     67     public static FlpHardwareProvider getInstance(Context context) {
     68         if (sSingletonInstance == null) {
     69             sSingletonInstance = new FlpHardwareProvider(context);
     70             sSingletonInstance.nativeInit();
     71         }
     72 
     73         return sSingletonInstance;
     74     }
     75 
     76     private FlpHardwareProvider(Context context) {
     77         mContext = context;
     78 
     79         // register for listening for passive provider data
     80         LocationManager manager = (LocationManager) mContext.getSystemService(
     81                 Context.LOCATION_SERVICE);
     82         final long minTime = 0;
     83         final float minDistance = 0;
     84         final boolean oneShot = false;
     85         LocationRequest request = LocationRequest.createFromDeprecatedProvider(
     86                 LocationManager.PASSIVE_PROVIDER,
     87                 minTime,
     88                 minDistance,
     89                 oneShot);
     90         // Don't keep track of this request since it's done on behalf of other clients
     91         // (which are kept track of separately).
     92         request.setHideFromAppOps(true);
     93         manager.requestLocationUpdates(
     94                 request,
     95                 new NetworkLocationListener(),
     96                 Looper.myLooper());
     97     }
     98 
     99     public static boolean isSupported() {
    100         return nativeIsSupported();
    101     }
    102 
    103     /**
    104      * Private callback functions used by FLP HAL.
    105      */
    106     // FlpCallbacks members
    107     private void onLocationReport(Location[] locations) {
    108         for (Location location : locations) {
    109             location.setProvider(LocationManager.FUSED_PROVIDER);
    110             // set the elapsed time-stamp just as GPS provider does
    111             location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
    112         }
    113 
    114         IFusedLocationHardwareSink sink;
    115         synchronized (mLocationSinkLock) {
    116             sink = mLocationSink;
    117         }
    118         try {
    119             if (sink != null) {
    120                 sink.onLocationAvailable(locations);
    121             }
    122         } catch (RemoteException e) {
    123             Log.e(TAG, "RemoteException calling onLocationAvailable");
    124         }
    125     }
    126 
    127     // FlpDiagnosticCallbacks members
    128     private void onDataReport(String data) {
    129         IFusedLocationHardwareSink sink;
    130         synchronized (mLocationSinkLock) {
    131             sink = mLocationSink;
    132         }
    133         try {
    134             if (mLocationSink != null) {
    135                 sink.onDiagnosticDataAvailable(data);
    136             }
    137         } catch (RemoteException e) {
    138             Log.e(TAG, "RemoteException calling onDiagnosticDataAvailable");
    139         }
    140     }
    141 
    142     // FlpGeofenceCallbacks members
    143     private void onGeofenceTransition(
    144             int geofenceId,
    145             Location location,
    146             int transition,
    147             long timestamp,
    148             int sourcesUsed) {
    149         // the transition Id does not require translation because the values in fused_location.h
    150         // and GeofenceHardware are in sync
    151         getGeofenceHardwareSink().reportGeofenceTransition(
    152                 geofenceId,
    153                 updateLocationInformation(location),
    154                 transition,
    155                 timestamp,
    156                 GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE,
    157                 sourcesUsed);
    158     }
    159 
    160     private void onGeofenceMonitorStatus(int status, int source, Location location) {
    161         // allow the location to be optional in this event
    162         Location updatedLocation = null;
    163         if(location != null) {
    164             updatedLocation = updateLocationInformation(location);
    165         }
    166 
    167         int monitorStatus;
    168         switch (status) {
    169             case FLP_GEOFENCE_MONITOR_STATUS_UNAVAILABLE:
    170                 monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_UNAVAILABLE;
    171                 break;
    172             case FLP_GEOFENCE_MONITOR_STATUS_AVAILABLE:
    173                 monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE;
    174                 break;
    175             default:
    176                 Log.e(TAG, "Invalid FlpHal Geofence monitor status: " + status);
    177                 monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_UNAVAILABLE;
    178                 break;
    179         }
    180 
    181         getGeofenceHardwareSink().reportGeofenceMonitorStatus(
    182                 GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE,
    183                 monitorStatus,
    184                 updatedLocation,
    185                 source);
    186     }
    187 
    188     private void onGeofenceAdd(int geofenceId, int result) {
    189         getGeofenceHardwareSink().reportGeofenceAddStatus(
    190                 geofenceId,
    191                 translateToGeofenceHardwareStatus(result));
    192     }
    193 
    194     private void onGeofenceRemove(int geofenceId, int result) {
    195         getGeofenceHardwareSink().reportGeofenceRemoveStatus(
    196                 geofenceId,
    197                 translateToGeofenceHardwareStatus(result));
    198     }
    199 
    200     private void onGeofencePause(int geofenceId, int result) {
    201         getGeofenceHardwareSink().reportGeofencePauseStatus(
    202                 geofenceId,
    203                 translateToGeofenceHardwareStatus(result));
    204     }
    205 
    206     private void onGeofenceResume(int geofenceId, int result) {
    207         getGeofenceHardwareSink().reportGeofenceResumeStatus(
    208                 geofenceId,
    209                 translateToGeofenceHardwareStatus(result));
    210     }
    211 
    212     /**
    213      * Private native methods accessing FLP HAL.
    214      */
    215     static { nativeClassInit(); }
    216 
    217     // Core members
    218     private static native void nativeClassInit();
    219     private static native boolean nativeIsSupported();
    220 
    221     // FlpLocationInterface members
    222     private native void nativeInit();
    223     private native int nativeGetBatchSize();
    224     private native void nativeStartBatching(int requestId, FusedBatchOptions options);
    225     private native void nativeUpdateBatchingOptions(int requestId, FusedBatchOptions optionsObject);
    226     private native void nativeStopBatching(int id);
    227     private native void nativeRequestBatchedLocation(int lastNLocations);
    228     private native void nativeInjectLocation(Location location);
    229     // TODO [Fix] sort out the lifetime of the instance
    230     private native void nativeCleanup();
    231 
    232     // FlpDiagnosticsInterface members
    233     private native boolean nativeIsDiagnosticSupported();
    234     private native void nativeInjectDiagnosticData(String data);
    235 
    236     // FlpDeviceContextInterface members
    237     private native boolean nativeIsDeviceContextSupported();
    238     private native void nativeInjectDeviceContext(int deviceEnabledContext);
    239 
    240     // FlpGeofencingInterface members
    241     private native boolean nativeIsGeofencingSupported();
    242     private native void nativeAddGeofences(
    243             GeofenceHardwareRequestParcelable[] geofenceRequestsArray);
    244     private native void nativePauseGeofence(int geofenceId);
    245     private native void  nativeResumeGeofence(int geofenceId, int monitorTransitions);
    246     private native void nativeModifyGeofenceOption(
    247         int geofenceId,
    248         int lastTransition,
    249         int monitorTransitions,
    250         int notificationResponsiveness,
    251         int unknownTimer,
    252         int sourcesToUse);
    253     private native void nativeRemoveGeofences(int[] geofenceIdsArray);
    254 
    255     /**
    256      * Interface implementations for services built on top of this functionality.
    257      */
    258     public static final String LOCATION = "Location";
    259     public static final String GEOFENCING = "Geofencing";
    260 
    261     public IFusedLocationHardware getLocationHardware() {
    262         return mLocationHardware;
    263     }
    264 
    265     public IFusedGeofenceHardware getGeofenceHardware() {
    266         return mGeofenceHardwareService;
    267     }
    268 
    269     private final IFusedLocationHardware mLocationHardware = new IFusedLocationHardware.Stub() {
    270         @Override
    271         public void registerSink(IFusedLocationHardwareSink eventSink) {
    272             synchronized (mLocationSinkLock) {
    273                 // only one sink is allowed at the moment
    274                 if (mLocationSink != null) {
    275                     Log.e(TAG, "Replacing an existing IFusedLocationHardware sink");
    276                 }
    277 
    278                 mLocationSink = eventSink;
    279             }
    280         }
    281 
    282         @Override
    283         public void unregisterSink(IFusedLocationHardwareSink eventSink) {
    284             synchronized (mLocationSinkLock) {
    285                 // don't throw if the sink is not registered, simply make it a no-op
    286                 if (mLocationSink == eventSink) {
    287                     mLocationSink = null;
    288                 }
    289             }
    290         }
    291 
    292         @Override
    293         public int getSupportedBatchSize() {
    294             return nativeGetBatchSize();
    295         }
    296 
    297         @Override
    298         public void startBatching(int requestId, FusedBatchOptions options) {
    299             nativeStartBatching(requestId, options);
    300         }
    301 
    302         @Override
    303         public void stopBatching(int requestId) {
    304             nativeStopBatching(requestId);
    305         }
    306 
    307         @Override
    308         public void updateBatchingOptions(int requestId, FusedBatchOptions options) {
    309             nativeUpdateBatchingOptions(requestId, options);
    310         }
    311 
    312         @Override
    313         public void requestBatchOfLocations(int batchSizeRequested) {
    314             nativeRequestBatchedLocation(batchSizeRequested);
    315         }
    316 
    317         @Override
    318         public boolean supportsDiagnosticDataInjection() {
    319             return nativeIsDiagnosticSupported();
    320         }
    321 
    322         @Override
    323         public void injectDiagnosticData(String data) {
    324             nativeInjectDiagnosticData(data);
    325         }
    326 
    327         @Override
    328         public boolean supportsDeviceContextInjection() {
    329             return nativeIsDeviceContextSupported();
    330         }
    331 
    332         @Override
    333         public void injectDeviceContext(int deviceEnabledContext) {
    334             nativeInjectDeviceContext(deviceEnabledContext);
    335         }
    336     };
    337 
    338     private final IFusedGeofenceHardware mGeofenceHardwareService =
    339             new IFusedGeofenceHardware.Stub() {
    340         @Override
    341         public boolean isSupported() {
    342             return nativeIsGeofencingSupported();
    343         }
    344 
    345         @Override
    346         public void addGeofences(GeofenceHardwareRequestParcelable[] geofenceRequestsArray) {
    347             nativeAddGeofences(geofenceRequestsArray);
    348         }
    349 
    350         @Override
    351         public void removeGeofences(int[] geofenceIds) {
    352             nativeRemoveGeofences(geofenceIds);
    353         }
    354 
    355         @Override
    356         public void pauseMonitoringGeofence(int geofenceId) {
    357             nativePauseGeofence(geofenceId);
    358         }
    359 
    360         @Override
    361         public void resumeMonitoringGeofence(int geofenceId, int monitorTransitions) {
    362             nativeResumeGeofence(geofenceId, monitorTransitions);
    363         }
    364 
    365         @Override
    366         public void modifyGeofenceOptions(int geofenceId,
    367                 int lastTransition,
    368                 int monitorTransitions,
    369                 int notificationResponsiveness,
    370                 int unknownTimer,
    371                 int sourcesToUse) {
    372             nativeModifyGeofenceOption(
    373                     geofenceId,
    374                     lastTransition,
    375                     monitorTransitions,
    376                     notificationResponsiveness,
    377                     unknownTimer,
    378                     sourcesToUse);
    379         }
    380     };
    381 
    382     /**
    383      * Internal classes and functions used by the provider.
    384      */
    385     private final class NetworkLocationListener implements LocationListener {
    386         @Override
    387         public void onLocationChanged(Location location) {
    388             if (
    389                 !LocationManager.NETWORK_PROVIDER.equals(location.getProvider()) ||
    390                 !location.hasAccuracy()
    391                 ) {
    392                 return;
    393             }
    394 
    395             nativeInjectLocation(location);
    396         }
    397 
    398         @Override
    399         public void onStatusChanged(String provider, int status, Bundle extras) { }
    400 
    401         @Override
    402         public void onProviderEnabled(String provider) { }
    403 
    404         @Override
    405         public void onProviderDisabled(String provider) { }
    406     }
    407 
    408     private GeofenceHardwareImpl getGeofenceHardwareSink() {
    409         if (mGeofenceHardwareSink == null) {
    410             mGeofenceHardwareSink = GeofenceHardwareImpl.getInstance(mContext);
    411         }
    412 
    413         return mGeofenceHardwareSink;
    414     }
    415 
    416     private static int translateToGeofenceHardwareStatus(int flpHalResult) {
    417         switch(flpHalResult) {
    418             case FLP_RESULT_SUCCESS:
    419                 return GeofenceHardware.GEOFENCE_SUCCESS;
    420             case FLP_RESULT_ERROR:
    421                 return GeofenceHardware.GEOFENCE_FAILURE;
    422             case FLP_RESULT_INSUFFICIENT_MEMORY:
    423                 return GeofenceHardware.GEOFENCE_ERROR_INSUFFICIENT_MEMORY;
    424             case FLP_RESULT_TOO_MANY_GEOFENCES:
    425                 return GeofenceHardware.GEOFENCE_ERROR_TOO_MANY_GEOFENCES;
    426             case FLP_RESULT_ID_EXISTS:
    427                 return GeofenceHardware.GEOFENCE_ERROR_ID_EXISTS;
    428             case FLP_RESULT_ID_UNKNOWN:
    429                 return GeofenceHardware.GEOFENCE_ERROR_ID_UNKNOWN;
    430             case FLP_RESULT_INVALID_GEOFENCE_TRANSITION:
    431                 return GeofenceHardware.GEOFENCE_ERROR_INVALID_TRANSITION;
    432             default:
    433                 Log.e(TAG, String.format("Invalid FlpHal result code: %d", flpHalResult));
    434                 return GeofenceHardware.GEOFENCE_FAILURE;
    435         }
    436     }
    437 
    438     private Location updateLocationInformation(Location location) {
    439         location.setProvider(LocationManager.FUSED_PROVIDER);
    440         // set the elapsed time-stamp just as GPS provider does
    441         location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
    442         return location;
    443     }
    444 }
    445