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     public static FlpHardwareProvider getInstance(Context context) {
     64         if (sSingletonInstance == null) {
     65             sSingletonInstance = new FlpHardwareProvider(context);
     66         }
     67 
     68         return sSingletonInstance;
     69     }
     70 
     71     private FlpHardwareProvider(Context context) {
     72         mContext = context;
     73 
     74         // register for listening for passive provider data
     75         LocationManager manager = (LocationManager) mContext.getSystemService(
     76                 Context.LOCATION_SERVICE);
     77         final long minTime = 0;
     78         final float minDistance = 0;
     79         final boolean oneShot = false;
     80         LocationRequest request = LocationRequest.createFromDeprecatedProvider(
     81                 LocationManager.PASSIVE_PROVIDER,
     82                 minTime,
     83                 minDistance,
     84                 oneShot);
     85         // Don't keep track of this request since it's done on behalf of other clients
     86         // (which are kept track of separately).
     87         request.setHideFromAppOps(true);
     88         manager.requestLocationUpdates(
     89                 request,
     90                 new NetworkLocationListener(),
     91                 Looper.myLooper());
     92     }
     93 
     94     public static boolean isSupported() {
     95         return nativeIsSupported();
     96     }
     97 
     98     /**
     99      * Private callback functions used by FLP HAL.
    100      */
    101     // FlpCallbacks members
    102     private void onLocationReport(Location[] locations) {
    103         for (Location location : locations) {
    104             location.setProvider(LocationManager.FUSED_PROVIDER);
    105             // set the elapsed time-stamp just as GPS provider does
    106             location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
    107         }
    108 
    109         IFusedLocationHardwareSink sink;
    110         synchronized (mLocationSinkLock) {
    111             sink = mLocationSink;
    112         }
    113         try {
    114             if (sink != null) {
    115                 sink.onLocationAvailable(locations);
    116             }
    117         } catch (RemoteException e) {
    118             Log.e(TAG, "RemoteException calling onLocationAvailable");
    119         }
    120     }
    121 
    122     // FlpDiagnosticCallbacks members
    123     private void onDataReport(String data) {
    124         IFusedLocationHardwareSink sink;
    125         synchronized (mLocationSinkLock) {
    126             sink = mLocationSink;
    127         }
    128         try {
    129             if (mLocationSink != null) {
    130                 sink.onDiagnosticDataAvailable(data);
    131             }
    132         } catch (RemoteException e) {
    133             Log.e(TAG, "RemoteException calling onDiagnosticDataAvailable");
    134         }
    135     }
    136 
    137     // FlpGeofenceCallbacks members
    138     private void onGeofenceTransition(
    139             int geofenceId,
    140             Location location,
    141             int transition,
    142             long timestamp,
    143             int sourcesUsed) {
    144         getGeofenceHardwareSink().reportGeofenceTransition(
    145                 geofenceId,
    146                 updateLocationInformation(location),
    147                 transition,
    148                 timestamp,
    149                 GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE,
    150                 sourcesUsed);
    151     }
    152 
    153     private void onGeofenceMonitorStatus(int status, int source, Location location) {
    154         // allow the location to be optional in this event
    155         Location updatedLocation = null;
    156         if(location != null) {
    157             updatedLocation = updateLocationInformation(location);
    158         }
    159 
    160         getGeofenceHardwareSink().reportGeofenceMonitorStatus(
    161                 GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE,
    162                 status,
    163                 updatedLocation,
    164                 source);
    165     }
    166 
    167     private void onGeofenceAdd(int geofenceId, int result) {
    168         getGeofenceHardwareSink().reportGeofenceAddStatus(
    169                 geofenceId,
    170                 translateToGeofenceHardwareStatus(result));
    171     }
    172 
    173     private void onGeofenceRemove(int geofenceId, int result) {
    174         getGeofenceHardwareSink().reportGeofenceRemoveStatus(
    175                 geofenceId,
    176                 translateToGeofenceHardwareStatus(result));
    177     }
    178 
    179     private void onGeofencePause(int geofenceId, int result) {
    180         getGeofenceHardwareSink().reportGeofencePauseStatus(
    181                 geofenceId,
    182                 translateToGeofenceHardwareStatus(result));
    183     }
    184 
    185     private void onGeofenceResume(int geofenceId, int result) {
    186         getGeofenceHardwareSink().reportGeofenceResumeStatus(
    187                 geofenceId,
    188                 translateToGeofenceHardwareStatus(result));
    189     }
    190 
    191     /**
    192      * Private native methods accessing FLP HAL.
    193      */
    194     static { nativeClassInit(); }
    195 
    196     // Core members
    197     private static native void nativeClassInit();
    198     private static native boolean nativeIsSupported();
    199 
    200     // FlpLocationInterface members
    201     private native void nativeInit();
    202     private native int nativeGetBatchSize();
    203     private native void nativeStartBatching(int requestId, FusedBatchOptions options);
    204     private native void nativeUpdateBatchingOptions(int requestId, FusedBatchOptions optionsObject);
    205     private native void nativeStopBatching(int id);
    206     private native void nativeRequestBatchedLocation(int lastNLocations);
    207     private native void nativeInjectLocation(Location location);
    208     // TODO [Fix] sort out the lifetime of the instance
    209     private native void nativeCleanup();
    210 
    211     // FlpDiagnosticsInterface members
    212     private native boolean nativeIsDiagnosticSupported();
    213     private native void nativeInjectDiagnosticData(String data);
    214 
    215     // FlpDeviceContextInterface members
    216     private native boolean nativeIsDeviceContextSupported();
    217     private native void nativeInjectDeviceContext(int deviceEnabledContext);
    218 
    219     // FlpGeofencingInterface members
    220     private native boolean nativeIsGeofencingSupported();
    221     private native void nativeAddGeofences(
    222             GeofenceHardwareRequestParcelable[] geofenceRequestsArray);
    223     private native void nativePauseGeofence(int geofenceId);
    224     private native void  nativeResumeGeofence(int geofenceId, int monitorTransitions);
    225     private native void nativeModifyGeofenceOption(
    226         int geofenceId,
    227         int lastTransition,
    228         int monitorTransitions,
    229         int notificationResponsiveness,
    230         int unknownTimer,
    231         int sourcesToUse);
    232     private native void nativeRemoveGeofences(int[] geofenceIdsArray);
    233 
    234     /**
    235      * Interface implementations for services built on top of this functionality.
    236      */
    237     public static final String LOCATION = "Location";
    238     public static final String GEOFENCING = "Geofencing";
    239 
    240     public IFusedLocationHardware getLocationHardware() {
    241         nativeInit();
    242         return mLocationHardware;
    243     }
    244 
    245     public IFusedGeofenceHardware getGeofenceHardware() {
    246         nativeInit();
    247         return mGeofenceHardwareService;
    248     }
    249 
    250     private final IFusedLocationHardware mLocationHardware = new IFusedLocationHardware.Stub() {
    251         @Override
    252         public void registerSink(IFusedLocationHardwareSink eventSink) {
    253             synchronized (mLocationSinkLock) {
    254                 // only one sink is allowed at the moment
    255                 if (mLocationSink != null) {
    256                     throw new RuntimeException(
    257                             "IFusedLocationHardware does not support multiple sinks");
    258                 }
    259 
    260                 mLocationSink = eventSink;
    261             }
    262         }
    263 
    264         @Override
    265         public void unregisterSink(IFusedLocationHardwareSink eventSink) {
    266             synchronized (mLocationSinkLock) {
    267                 // don't throw if the sink is not registered, simply make it a no-op
    268                 if (mLocationSink == eventSink) {
    269                     mLocationSink = null;
    270                 }
    271             }
    272         }
    273 
    274         @Override
    275         public int getSupportedBatchSize() {
    276             return nativeGetBatchSize();
    277         }
    278 
    279         @Override
    280         public void startBatching(int requestId, FusedBatchOptions options) {
    281             nativeStartBatching(requestId, options);
    282         }
    283 
    284         @Override
    285         public void stopBatching(int requestId) {
    286             nativeStopBatching(requestId);
    287         }
    288 
    289         @Override
    290         public void updateBatchingOptions(int requestId, FusedBatchOptions options) {
    291             nativeUpdateBatchingOptions(requestId, options);
    292         }
    293 
    294         @Override
    295         public void requestBatchOfLocations(int batchSizeRequested) {
    296             nativeRequestBatchedLocation(batchSizeRequested);
    297         }
    298 
    299         @Override
    300         public boolean supportsDiagnosticDataInjection() {
    301             return nativeIsDiagnosticSupported();
    302         }
    303 
    304         @Override
    305         public void injectDiagnosticData(String data) {
    306             nativeInjectDiagnosticData(data);
    307         }
    308 
    309         @Override
    310         public boolean supportsDeviceContextInjection() {
    311             return nativeIsDeviceContextSupported();
    312         }
    313 
    314         @Override
    315         public void injectDeviceContext(int deviceEnabledContext) {
    316             nativeInjectDeviceContext(deviceEnabledContext);
    317         }
    318     };
    319 
    320     private final IFusedGeofenceHardware mGeofenceHardwareService =
    321             new IFusedGeofenceHardware.Stub() {
    322         @Override
    323         public boolean isSupported() {
    324             return nativeIsGeofencingSupported();
    325         }
    326 
    327         @Override
    328         public void addGeofences(GeofenceHardwareRequestParcelable[] geofenceRequestsArray) {
    329             nativeAddGeofences(geofenceRequestsArray);
    330         }
    331 
    332         @Override
    333         public void removeGeofences(int[] geofenceIds) {
    334             nativeRemoveGeofences(geofenceIds);
    335         }
    336 
    337         @Override
    338         public void pauseMonitoringGeofence(int geofenceId) {
    339             nativePauseGeofence(geofenceId);
    340         }
    341 
    342         @Override
    343         public void resumeMonitoringGeofence(int geofenceId, int monitorTransitions) {
    344             nativeResumeGeofence(geofenceId, monitorTransitions);
    345         }
    346 
    347         @Override
    348         public void modifyGeofenceOptions(int geofenceId,
    349                 int lastTransition,
    350                 int monitorTransitions,
    351                 int notificationResponsiveness,
    352                 int unknownTimer,
    353                 int sourcesToUse) {
    354             nativeModifyGeofenceOption(
    355                     geofenceId,
    356                     lastTransition,
    357                     monitorTransitions,
    358                     notificationResponsiveness,
    359                     unknownTimer,
    360                     sourcesToUse);
    361         }
    362     };
    363 
    364     /**
    365      * Internal classes and functions used by the provider.
    366      */
    367     private final class NetworkLocationListener implements LocationListener {
    368         @Override
    369         public void onLocationChanged(Location location) {
    370             if (
    371                 !LocationManager.NETWORK_PROVIDER.equals(location.getProvider()) ||
    372                 !location.hasAccuracy()
    373                 ) {
    374                 return;
    375             }
    376 
    377             nativeInjectLocation(location);
    378         }
    379 
    380         @Override
    381         public void onStatusChanged(String provider, int status, Bundle extras) { }
    382 
    383         @Override
    384         public void onProviderEnabled(String provider) { }
    385 
    386         @Override
    387         public void onProviderDisabled(String provider) { }
    388     }
    389 
    390     private GeofenceHardwareImpl getGeofenceHardwareSink() {
    391         if (mGeofenceHardwareSink == null) {
    392             mGeofenceHardwareSink = GeofenceHardwareImpl.getInstance(mContext);
    393         }
    394 
    395         return mGeofenceHardwareSink;
    396     }
    397 
    398     private static int translateToGeofenceHardwareStatus(int flpHalResult) {
    399         switch(flpHalResult) {
    400             case FLP_RESULT_SUCCESS:
    401                 return GeofenceHardware.GEOFENCE_SUCCESS;
    402             case FLP_RESULT_ERROR:
    403                 return GeofenceHardware.GEOFENCE_FAILURE;
    404             // TODO: uncomment this once the ERROR definition is marked public
    405             //case FLP_RESULT_INSUFFICIENT_MEMORY:
    406             //    return GeofenceHardware.GEOFENCE_ERROR_INSUFFICIENT_MEMORY;
    407             case FLP_RESULT_TOO_MANY_GEOFENCES:
    408                 return GeofenceHardware.GEOFENCE_ERROR_TOO_MANY_GEOFENCES;
    409             case FLP_RESULT_ID_EXISTS:
    410                 return GeofenceHardware.GEOFENCE_ERROR_ID_EXISTS;
    411             case FLP_RESULT_ID_UNKNOWN:
    412                 return GeofenceHardware.GEOFENCE_ERROR_ID_UNKNOWN;
    413             case FLP_RESULT_INVALID_GEOFENCE_TRANSITION:
    414                 return GeofenceHardware.GEOFENCE_ERROR_INVALID_TRANSITION;
    415             default:
    416                 Log.e(TAG, String.format("Invalid FlpHal result code: %d", flpHalResult));
    417                 return GeofenceHardware.GEOFENCE_FAILURE;
    418         }
    419     }
    420 
    421     private Location updateLocationInformation(Location location) {
    422         location.setProvider(LocationManager.FUSED_PROVIDER);
    423         // set the elapsed time-stamp just as GPS provider does
    424         location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
    425         return location;
    426     }
    427 }
    428