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.content.Context;
     20 import android.hardware.location.GeofenceHardware;
     21 import android.hardware.location.GeofenceHardwareImpl;
     22 import android.hardware.location.GeofenceHardwareRequestParcelable;
     23 import android.hardware.location.IFusedLocationHardware;
     24 import android.hardware.location.IFusedLocationHardwareSink;
     25 import android.location.FusedBatchOptions;
     26 import android.location.IFusedGeofenceHardware;
     27 import android.location.Location;
     28 import android.location.LocationListener;
     29 import android.location.LocationManager;
     30 import android.location.LocationRequest;
     31 import android.os.Bundle;
     32 import android.os.Looper;
     33 import android.os.RemoteException;
     34 import android.os.SystemClock;
     35 import android.util.Log;
     36 
     37 /**
     38  * This class is an interop layer for JVM types and the JNI code that interacts
     39  * with the FLP HAL implementation.
     40  *
     41  * {@hide}
     42  */
     43 public class FlpHardwareProvider {
     44     private static final int FIRST_VERSION_WITH_FLUSH_LOCATIONS = 2;
     45     private GeofenceHardwareImpl mGeofenceHardwareSink = null;
     46     private IFusedLocationHardwareSink mLocationSink = null;
     47     // Capabilities provided by FlpCallbacks
     48     private boolean mHaveBatchingCapabilities;
     49     private int mBatchingCapabilities;
     50     private int mVersion = 1;
     51 
     52     private static FlpHardwareProvider sSingletonInstance = null;
     53 
     54     private final static String TAG = "FlpHardwareProvider";
     55     private final Context mContext;
     56     private final Object mLocationSinkLock = new Object();
     57 
     58     // FlpHal result codes, they must be equal to the ones in fused_location.h
     59     private static final int FLP_RESULT_SUCCESS = 0;
     60     private static final int FLP_RESULT_ERROR = -1;
     61     private static final int FLP_RESULT_INSUFFICIENT_MEMORY = -2;
     62     private static final int FLP_RESULT_TOO_MANY_GEOFENCES = -3;
     63     private static final int FLP_RESULT_ID_EXISTS = -4;
     64     private static final int FLP_RESULT_ID_UNKNOWN = -5;
     65     private static final int FLP_RESULT_INVALID_GEOFENCE_TRANSITION = -6;
     66 
     67     // FlpHal monitor status codes, they must be equal to the ones in fused_location.h
     68     private static final int FLP_GEOFENCE_MONITOR_STATUS_UNAVAILABLE = 1<<0;
     69     private static final int FLP_GEOFENCE_MONITOR_STATUS_AVAILABLE = 1<<1;
     70 
     71     public static FlpHardwareProvider getInstance(Context context) {
     72         if (sSingletonInstance == null) {
     73             sSingletonInstance = new FlpHardwareProvider(context);
     74             sSingletonInstance.nativeInit();
     75         }
     76 
     77         return sSingletonInstance;
     78     }
     79 
     80     private FlpHardwareProvider(Context context) {
     81         mContext = context;
     82 
     83         // register for listening for passive provider data
     84         LocationManager manager = (LocationManager) mContext.getSystemService(
     85                 Context.LOCATION_SERVICE);
     86         final long minTime = 0;
     87         final float minDistance = 0;
     88         final boolean oneShot = false;
     89         LocationRequest request = LocationRequest.createFromDeprecatedProvider(
     90                 LocationManager.PASSIVE_PROVIDER,
     91                 minTime,
     92                 minDistance,
     93                 oneShot);
     94         // Don't keep track of this request since it's done on behalf of other clients
     95         // (which are kept track of separately).
     96         request.setHideFromAppOps(true);
     97         manager.requestLocationUpdates(
     98                 request,
     99                 new NetworkLocationListener(),
    100                 Looper.myLooper());
    101     }
    102 
    103     public static boolean isSupported() {
    104         return nativeIsSupported();
    105     }
    106 
    107     /**
    108      * Private callback functions used by FLP HAL.
    109      */
    110     // FlpCallbacks members
    111     private void onLocationReport(Location[] locations) {
    112         for (Location location : locations) {
    113             location.setProvider(LocationManager.FUSED_PROVIDER);
    114             // set the elapsed time-stamp just as GPS provider does
    115             location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
    116         }
    117 
    118         IFusedLocationHardwareSink sink;
    119         synchronized (mLocationSinkLock) {
    120             sink = mLocationSink;
    121         }
    122         try {
    123             if (sink != null) {
    124                 sink.onLocationAvailable(locations);
    125             }
    126         } catch (RemoteException e) {
    127             Log.e(TAG, "RemoteException calling onLocationAvailable");
    128         }
    129     }
    130 
    131     private void onBatchingCapabilities(int capabilities) {
    132         synchronized (mLocationSinkLock) {
    133             mHaveBatchingCapabilities = true;
    134             mBatchingCapabilities = capabilities;
    135         }
    136 
    137         maybeSendCapabilities();
    138 
    139         if (mGeofenceHardwareSink != null) {
    140             mGeofenceHardwareSink.setVersion(getVersion());
    141         }
    142     }
    143 
    144     private void onBatchingStatus(int status) {
    145         IFusedLocationHardwareSink sink;
    146         synchronized (mLocationSinkLock) {
    147             sink = mLocationSink;
    148         }
    149         try {
    150             if (sink != null) {
    151                 sink.onStatusChanged(status);
    152             }
    153         } catch (RemoteException e) {
    154             Log.e(TAG, "RemoteException calling onBatchingStatus");
    155         }
    156     }
    157 
    158     // Returns the current version of the FLP HAL.  This depends both on the version of the
    159     // structure returned by the hardware layer, and whether or not we've received the
    160     // capabilities callback on initialization.  Assume original version until we get
    161     // the new initialization callback.
    162     private int getVersion() {
    163         synchronized (mLocationSinkLock) {
    164             if (mHaveBatchingCapabilities) {
    165                 return mVersion;
    166             }
    167         }
    168         return 1;
    169     }
    170 
    171     private void setVersion(int version) {
    172         mVersion = version;
    173         if (mGeofenceHardwareSink != null) {
    174             mGeofenceHardwareSink.setVersion(getVersion());
    175         }
    176     }
    177 
    178     private void maybeSendCapabilities() {
    179         IFusedLocationHardwareSink sink;
    180         boolean haveBatchingCapabilities;
    181         int batchingCapabilities;
    182         synchronized (mLocationSinkLock) {
    183             sink = mLocationSink;
    184             haveBatchingCapabilities = mHaveBatchingCapabilities;
    185             batchingCapabilities = mBatchingCapabilities;
    186         }
    187         try {
    188             if (sink != null && haveBatchingCapabilities) {
    189                 sink.onCapabilities(batchingCapabilities);
    190             }
    191         } catch (RemoteException e) {
    192             Log.e(TAG, "RemoteException calling onLocationAvailable");
    193         }
    194     }
    195 
    196     // FlpDiagnosticCallbacks members
    197     private void onDataReport(String data) {
    198         IFusedLocationHardwareSink sink;
    199         synchronized (mLocationSinkLock) {
    200             sink = mLocationSink;
    201         }
    202         try {
    203             if (mLocationSink != null) {
    204                 sink.onDiagnosticDataAvailable(data);
    205             }
    206         } catch (RemoteException e) {
    207             Log.e(TAG, "RemoteException calling onDiagnosticDataAvailable");
    208         }
    209     }
    210 
    211     // FlpGeofenceCallbacks members
    212     private void onGeofenceTransition(
    213             int geofenceId,
    214             Location location,
    215             int transition,
    216             long timestamp,
    217             int sourcesUsed) {
    218         // the transition Id does not require translation because the values in fused_location.h
    219         // and GeofenceHardware are in sync
    220         getGeofenceHardwareSink().reportGeofenceTransition(
    221                 geofenceId,
    222                 updateLocationInformation(location),
    223                 transition,
    224                 timestamp,
    225                 GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE,
    226                 sourcesUsed);
    227     }
    228 
    229     private void onGeofenceMonitorStatus(int status, int source, Location location) {
    230         // allow the location to be optional in this event
    231         Location updatedLocation = null;
    232         if(location != null) {
    233             updatedLocation = updateLocationInformation(location);
    234         }
    235 
    236         int monitorStatus;
    237         switch (status) {
    238             case FLP_GEOFENCE_MONITOR_STATUS_UNAVAILABLE:
    239                 monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_UNAVAILABLE;
    240                 break;
    241             case FLP_GEOFENCE_MONITOR_STATUS_AVAILABLE:
    242                 monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE;
    243                 break;
    244             default:
    245                 Log.e(TAG, "Invalid FlpHal Geofence monitor status: " + status);
    246                 monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_UNAVAILABLE;
    247                 break;
    248         }
    249 
    250         getGeofenceHardwareSink().reportGeofenceMonitorStatus(
    251                 GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE,
    252                 monitorStatus,
    253                 updatedLocation,
    254                 source);
    255     }
    256 
    257     private void onGeofenceAdd(int geofenceId, int result) {
    258         getGeofenceHardwareSink().reportGeofenceAddStatus(
    259                 geofenceId,
    260                 translateToGeofenceHardwareStatus(result));
    261     }
    262 
    263     private void onGeofenceRemove(int geofenceId, int result) {
    264         getGeofenceHardwareSink().reportGeofenceRemoveStatus(
    265                 geofenceId,
    266                 translateToGeofenceHardwareStatus(result));
    267     }
    268 
    269     private void onGeofencePause(int geofenceId, int result) {
    270         getGeofenceHardwareSink().reportGeofencePauseStatus(
    271                 geofenceId,
    272                 translateToGeofenceHardwareStatus(result));
    273     }
    274 
    275     private void onGeofenceResume(int geofenceId, int result) {
    276         getGeofenceHardwareSink().reportGeofenceResumeStatus(
    277                 geofenceId,
    278                 translateToGeofenceHardwareStatus(result));
    279     }
    280 
    281     private void onGeofencingCapabilities(int capabilities) {
    282         getGeofenceHardwareSink().onCapabilities(capabilities);
    283     }
    284 
    285     /**
    286      * Private native methods accessing FLP HAL.
    287      */
    288     static { nativeClassInit(); }
    289 
    290     // Core members
    291     private static native void nativeClassInit();
    292     private static native boolean nativeIsSupported();
    293 
    294     // FlpLocationInterface members
    295     private native void nativeInit();
    296     private native int nativeGetBatchSize();
    297     private native void nativeStartBatching(int requestId, FusedBatchOptions options);
    298     private native void nativeUpdateBatchingOptions(int requestId, FusedBatchOptions optionsObject);
    299     private native void nativeStopBatching(int id);
    300     private native void nativeRequestBatchedLocation(int lastNLocations);
    301     private native void nativeFlushBatchedLocations();
    302     private native void nativeInjectLocation(Location location);
    303     private native void nativeCleanup();
    304 
    305     // FlpDiagnosticsInterface members
    306     private native boolean nativeIsDiagnosticSupported();
    307     private native void nativeInjectDiagnosticData(String data);
    308 
    309     // FlpDeviceContextInterface members
    310     private native boolean nativeIsDeviceContextSupported();
    311     private native void nativeInjectDeviceContext(int deviceEnabledContext);
    312 
    313     // FlpGeofencingInterface members
    314     private native boolean nativeIsGeofencingSupported();
    315     private native void nativeAddGeofences(
    316             GeofenceHardwareRequestParcelable[] geofenceRequestsArray);
    317     private native void nativePauseGeofence(int geofenceId);
    318     private native void  nativeResumeGeofence(int geofenceId, int monitorTransitions);
    319     private native void nativeModifyGeofenceOption(
    320         int geofenceId,
    321         int lastTransition,
    322         int monitorTransitions,
    323         int notificationResponsiveness,
    324         int unknownTimer,
    325         int sourcesToUse);
    326     private native void nativeRemoveGeofences(int[] geofenceIdsArray);
    327 
    328     /**
    329      * Interface implementations for services built on top of this functionality.
    330      */
    331     public static final String LOCATION = "Location";
    332     public static final String GEOFENCING = "Geofencing";
    333 
    334     public IFusedLocationHardware getLocationHardware() {
    335         return mLocationHardware;
    336     }
    337 
    338     public IFusedGeofenceHardware getGeofenceHardware() {
    339         return mGeofenceHardwareService;
    340     }
    341 
    342     public void cleanup() {
    343         Log.i(TAG, "Calling nativeCleanup()");
    344         nativeCleanup();
    345     }
    346 
    347     private final IFusedLocationHardware mLocationHardware = new IFusedLocationHardware.Stub() {
    348         @Override
    349         public void registerSink(IFusedLocationHardwareSink eventSink) {
    350             synchronized (mLocationSinkLock) {
    351                 // only one sink is allowed at the moment
    352                 if (mLocationSink != null) {
    353                     Log.e(TAG, "Replacing an existing IFusedLocationHardware sink");
    354                 }
    355 
    356                 mLocationSink = eventSink;
    357             }
    358             maybeSendCapabilities();
    359         }
    360 
    361         @Override
    362         public void unregisterSink(IFusedLocationHardwareSink eventSink) {
    363             synchronized (mLocationSinkLock) {
    364                 // don't throw if the sink is not registered, simply make it a no-op
    365                 if (mLocationSink == eventSink) {
    366                     mLocationSink = null;
    367                 }
    368             }
    369         }
    370 
    371         @Override
    372         public int getSupportedBatchSize() {
    373             return nativeGetBatchSize();
    374         }
    375 
    376         @Override
    377         public void startBatching(int requestId, FusedBatchOptions options) {
    378             nativeStartBatching(requestId, options);
    379         }
    380 
    381         @Override
    382         public void stopBatching(int requestId) {
    383             nativeStopBatching(requestId);
    384         }
    385 
    386         @Override
    387         public void updateBatchingOptions(int requestId, FusedBatchOptions options) {
    388             nativeUpdateBatchingOptions(requestId, options);
    389         }
    390 
    391         @Override
    392         public void requestBatchOfLocations(int batchSizeRequested) {
    393             nativeRequestBatchedLocation(batchSizeRequested);
    394         }
    395 
    396         @Override
    397         public void flushBatchedLocations() {
    398             if (getVersion() >= FIRST_VERSION_WITH_FLUSH_LOCATIONS) {
    399                 nativeFlushBatchedLocations();
    400             } else {
    401                 Log.wtf(TAG,
    402                         "Tried to call flushBatchedLocations on an unsupported implementation");
    403             }
    404         }
    405 
    406         @Override
    407         public boolean supportsDiagnosticDataInjection() {
    408             return nativeIsDiagnosticSupported();
    409         }
    410 
    411         @Override
    412         public void injectDiagnosticData(String data) {
    413             nativeInjectDiagnosticData(data);
    414         }
    415 
    416         @Override
    417         public boolean supportsDeviceContextInjection() {
    418             return nativeIsDeviceContextSupported();
    419         }
    420 
    421         @Override
    422         public void injectDeviceContext(int deviceEnabledContext) {
    423             nativeInjectDeviceContext(deviceEnabledContext);
    424         }
    425 
    426         @Override
    427         public int getVersion() {
    428             return FlpHardwareProvider.this.getVersion();
    429         }
    430     };
    431 
    432     private final IFusedGeofenceHardware mGeofenceHardwareService =
    433             new IFusedGeofenceHardware.Stub() {
    434         @Override
    435         public boolean isSupported() {
    436             return nativeIsGeofencingSupported();
    437         }
    438 
    439         @Override
    440         public void addGeofences(GeofenceHardwareRequestParcelable[] geofenceRequestsArray) {
    441             nativeAddGeofences(geofenceRequestsArray);
    442         }
    443 
    444         @Override
    445         public void removeGeofences(int[] geofenceIds) {
    446             nativeRemoveGeofences(geofenceIds);
    447         }
    448 
    449         @Override
    450         public void pauseMonitoringGeofence(int geofenceId) {
    451             nativePauseGeofence(geofenceId);
    452         }
    453 
    454         @Override
    455         public void resumeMonitoringGeofence(int geofenceId, int monitorTransitions) {
    456             nativeResumeGeofence(geofenceId, monitorTransitions);
    457         }
    458 
    459         @Override
    460         public void modifyGeofenceOptions(int geofenceId,
    461                 int lastTransition,
    462                 int monitorTransitions,
    463                 int notificationResponsiveness,
    464                 int unknownTimer,
    465                 int sourcesToUse) {
    466             nativeModifyGeofenceOption(
    467                     geofenceId,
    468                     lastTransition,
    469                     monitorTransitions,
    470                     notificationResponsiveness,
    471                     unknownTimer,
    472                     sourcesToUse);
    473         }
    474     };
    475 
    476     /**
    477      * Internal classes and functions used by the provider.
    478      */
    479     private final class NetworkLocationListener implements LocationListener {
    480         @Override
    481         public void onLocationChanged(Location location) {
    482             if (
    483                 !LocationManager.NETWORK_PROVIDER.equals(location.getProvider()) ||
    484                 !location.hasAccuracy()
    485                 ) {
    486                 return;
    487             }
    488 
    489             nativeInjectLocation(location);
    490         }
    491 
    492         @Override
    493         public void onStatusChanged(String provider, int status, Bundle extras) { }
    494 
    495         @Override
    496         public void onProviderEnabled(String provider) { }
    497 
    498         @Override
    499         public void onProviderDisabled(String provider) { }
    500     }
    501 
    502     private GeofenceHardwareImpl getGeofenceHardwareSink() {
    503         if (mGeofenceHardwareSink == null) {
    504             mGeofenceHardwareSink = GeofenceHardwareImpl.getInstance(mContext);
    505             mGeofenceHardwareSink.setVersion(getVersion());
    506         }
    507 
    508         return mGeofenceHardwareSink;
    509     }
    510 
    511     private static int translateToGeofenceHardwareStatus(int flpHalResult) {
    512         switch(flpHalResult) {
    513             case FLP_RESULT_SUCCESS:
    514                 return GeofenceHardware.GEOFENCE_SUCCESS;
    515             case FLP_RESULT_ERROR:
    516                 return GeofenceHardware.GEOFENCE_FAILURE;
    517             case FLP_RESULT_INSUFFICIENT_MEMORY:
    518                 return GeofenceHardware.GEOFENCE_ERROR_INSUFFICIENT_MEMORY;
    519             case FLP_RESULT_TOO_MANY_GEOFENCES:
    520                 return GeofenceHardware.GEOFENCE_ERROR_TOO_MANY_GEOFENCES;
    521             case FLP_RESULT_ID_EXISTS:
    522                 return GeofenceHardware.GEOFENCE_ERROR_ID_EXISTS;
    523             case FLP_RESULT_ID_UNKNOWN:
    524                 return GeofenceHardware.GEOFENCE_ERROR_ID_UNKNOWN;
    525             case FLP_RESULT_INVALID_GEOFENCE_TRANSITION:
    526                 return GeofenceHardware.GEOFENCE_ERROR_INVALID_TRANSITION;
    527             default:
    528                 Log.e(TAG, String.format("Invalid FlpHal result code: %d", flpHalResult));
    529                 return GeofenceHardware.GEOFENCE_FAILURE;
    530         }
    531     }
    532 
    533     private Location updateLocationInformation(Location location) {
    534         location.setProvider(LocationManager.FUSED_PROVIDER);
    535         // set the elapsed time-stamp just as GPS provider does
    536         location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
    537         return location;
    538     }
    539 }
    540