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 
getInstance(Context context)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 
FlpHardwareProvider(Context context)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 
isSupported()103     public static boolean isSupported() {
104         return nativeIsSupported();
105     }
106 
107     /**
108      * Private callback functions used by FLP HAL.
109      */
110     // FlpCallbacks members
onLocationReport(Location[] locations)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 
onBatchingCapabilities(int capabilities)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 
onBatchingStatus(int status)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.
getVersion()162     private int getVersion() {
163         synchronized (mLocationSinkLock) {
164             if (mHaveBatchingCapabilities) {
165                 return mVersion;
166             }
167         }
168         return 1;
169     }
170 
setVersion(int version)171     private void setVersion(int version) {
172         mVersion = version;
173         if (mGeofenceHardwareSink != null) {
174             mGeofenceHardwareSink.setVersion(getVersion());
175         }
176     }
177 
maybeSendCapabilities()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
onDataReport(String data)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
onGeofenceTransition( int geofenceId, Location location, int transition, long timestamp, int sourcesUsed)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 
onGeofenceMonitorStatus(int status, int source, Location location)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 
onGeofenceAdd(int geofenceId, int result)257     private void onGeofenceAdd(int geofenceId, int result) {
258         getGeofenceHardwareSink().reportGeofenceAddStatus(
259                 geofenceId,
260                 translateToGeofenceHardwareStatus(result));
261     }
262 
onGeofenceRemove(int geofenceId, int result)263     private void onGeofenceRemove(int geofenceId, int result) {
264         getGeofenceHardwareSink().reportGeofenceRemoveStatus(
265                 geofenceId,
266                 translateToGeofenceHardwareStatus(result));
267     }
268 
onGeofencePause(int geofenceId, int result)269     private void onGeofencePause(int geofenceId, int result) {
270         getGeofenceHardwareSink().reportGeofencePauseStatus(
271                 geofenceId,
272                 translateToGeofenceHardwareStatus(result));
273     }
274 
onGeofenceResume(int geofenceId, int result)275     private void onGeofenceResume(int geofenceId, int result) {
276         getGeofenceHardwareSink().reportGeofenceResumeStatus(
277                 geofenceId,
278                 translateToGeofenceHardwareStatus(result));
279     }
280 
onGeofencingCapabilities(int capabilities)281     private void onGeofencingCapabilities(int capabilities) {
282         getGeofenceHardwareSink().onCapabilities(capabilities);
283     }
284 
285     /**
286      * Private native methods accessing FLP HAL.
287      */
nativeClassInit()288     static { nativeClassInit(); }
289 
290     // Core members
nativeClassInit()291     private static native void nativeClassInit();
nativeIsSupported()292     private static native boolean nativeIsSupported();
293 
294     // FlpLocationInterface members
nativeInit()295     private native void nativeInit();
nativeGetBatchSize()296     private native int nativeGetBatchSize();
nativeStartBatching(int requestId, FusedBatchOptions options)297     private native void nativeStartBatching(int requestId, FusedBatchOptions options);
nativeUpdateBatchingOptions(int requestId, FusedBatchOptions optionsObject)298     private native void nativeUpdateBatchingOptions(int requestId, FusedBatchOptions optionsObject);
nativeStopBatching(int id)299     private native void nativeStopBatching(int id);
nativeRequestBatchedLocation(int lastNLocations)300     private native void nativeRequestBatchedLocation(int lastNLocations);
nativeFlushBatchedLocations()301     private native void nativeFlushBatchedLocations();
nativeInjectLocation(Location location)302     private native void nativeInjectLocation(Location location);
nativeCleanup()303     private native void nativeCleanup();
304 
305     // FlpDiagnosticsInterface members
nativeIsDiagnosticSupported()306     private native boolean nativeIsDiagnosticSupported();
nativeInjectDiagnosticData(String data)307     private native void nativeInjectDiagnosticData(String data);
308 
309     // FlpDeviceContextInterface members
nativeIsDeviceContextSupported()310     private native boolean nativeIsDeviceContextSupported();
nativeInjectDeviceContext(int deviceEnabledContext)311     private native void nativeInjectDeviceContext(int deviceEnabledContext);
312 
313     // FlpGeofencingInterface members
nativeIsGeofencingSupported()314     private native boolean nativeIsGeofencingSupported();
nativeAddGeofences( GeofenceHardwareRequestParcelable[] geofenceRequestsArray)315     private native void nativeAddGeofences(
316             GeofenceHardwareRequestParcelable[] geofenceRequestsArray);
nativePauseGeofence(int geofenceId)317     private native void nativePauseGeofence(int geofenceId);
nativeResumeGeofence(int geofenceId, int monitorTransitions)318     private native void  nativeResumeGeofence(int geofenceId, int monitorTransitions);
nativeModifyGeofenceOption( int geofenceId, int lastTransition, int monitorTransitions, int notificationResponsiveness, int unknownTimer, int sourcesToUse)319     private native void nativeModifyGeofenceOption(
320         int geofenceId,
321         int lastTransition,
322         int monitorTransitions,
323         int notificationResponsiveness,
324         int unknownTimer,
325         int sourcesToUse);
nativeRemoveGeofences(int[] geofenceIdsArray)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 
getLocationHardware()334     public IFusedLocationHardware getLocationHardware() {
335         return mLocationHardware;
336     }
337 
getGeofenceHardware()338     public IFusedGeofenceHardware getGeofenceHardware() {
339         return mGeofenceHardwareService;
340     }
341 
cleanup()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
onLocationChanged(Location location)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
onStatusChanged(String provider, int status, Bundle extras)493         public void onStatusChanged(String provider, int status, Bundle extras) { }
494 
495         @Override
onProviderEnabled(String provider)496         public void onProviderEnabled(String provider) { }
497 
498         @Override
onProviderDisabled(String provider)499         public void onProviderDisabled(String provider) { }
500     }
501 
getGeofenceHardwareSink()502     private GeofenceHardwareImpl getGeofenceHardwareSink() {
503         if (mGeofenceHardwareSink == null) {
504             mGeofenceHardwareSink = GeofenceHardwareImpl.getInstance(mContext);
505             mGeofenceHardwareSink.setVersion(getVersion());
506         }
507 
508         return mGeofenceHardwareSink;
509     }
510 
translateToGeofenceHardwareStatus(int flpHalResult)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 
updateLocationInformation(Location location)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