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 package android.hardware.location;
17 
18 import android.annotation.SystemApi;
19 import android.compat.annotation.UnsupportedAppUsage;
20 import android.location.Location;
21 import android.os.Build;
22 import android.os.RemoteException;
23 
24 import java.lang.ref.WeakReference;
25 import java.util.HashMap;
26 
27 /**
28  * This class handles geofences managed by various hardware subsystems. It contains
29  * the public APIs that is needed to accomplish the task.
30  *
31  * <p>The APIs should not be called directly by the app developers. A higher level api
32  * which abstracts the hardware should be used instead. All the checks are done by the higher
33  * level public API. Any needed locking should be handled by the higher level API.
34  *
35  * <p> There are 3 states associated with a Geofence: Inside, Outside, Unknown.
36  * There are 3 transitions: {@link #GEOFENCE_ENTERED}, {@link #GEOFENCE_EXITED},
37  * {@link #GEOFENCE_UNCERTAIN}. The APIs only expose the transitions.
38  *
39  * <p> Inside state: The hardware subsystem is reasonably confident that the user is inside
40  * the geofence. Outside state: The hardware subsystem is reasonably confident that the user
41  * is outside the geofence Unknown state: Unknown state can be interpreted as a state in which the
42  * monitoring subsystem isn't confident enough that the user is either inside or
43  * outside the Geofence. If the accuracy does not improve for a sufficient period of time,
44  * the {@link #GEOFENCE_UNCERTAIN} transition would be triggered. If the accuracy improves later,
45  * an appropriate transition would be triggered. The "reasonably confident" parameter
46  * depends on the hardware system and the positioning algorithms used.
47  * For instance, {@link #MONITORING_TYPE_GPS_HARDWARE} uses 95% as a confidence level.
48  *
49  * @hide
50  */
51 @SystemApi
52 public final class GeofenceHardware {
53     private IGeofenceHardware mService;
54 
55     // Hardware systems that do geofence monitoring.
56     static final int NUM_MONITORS = 2;
57 
58     /**
59      * Constant for geofence monitoring done by the GPS hardware.
60      */
61     public static final int MONITORING_TYPE_GPS_HARDWARE = 0;
62 
63     /**
64      * Constant for geofence monitoring done by the Fused hardware.
65      */
66     public static final int MONITORING_TYPE_FUSED_HARDWARE = 1;
67 
68     /**
69      * Constant to indicate that the monitoring system is currently
70      * available for monitoring geofences.
71      */
72     public static final int MONITOR_CURRENTLY_AVAILABLE = 0;
73 
74     /**
75      * Constant to indicate that the monitoring system is currently
76      * unavailable for monitoring geofences.
77      */
78     public static final int MONITOR_CURRENTLY_UNAVAILABLE = 1;
79 
80     /**
81      * Constant to indicate that the monitoring system is unsupported
82      * for hardware geofence monitoring.
83      */
84     public static final int MONITOR_UNSUPPORTED = 2;
85 
86     // The following constants need to match geofence flags in gps.h and fused_location.h
87     /**
88      * The constant to indicate that the user has entered the geofence.
89      */
90     public static final int GEOFENCE_ENTERED = 1<<0L;
91 
92     /**
93      * The constant to indicate that the user has exited the geofence.
94      */
95     public static final int GEOFENCE_EXITED = 1<<1L;
96 
97     /**
98      * The constant to indicate that the user is uncertain with respect to a
99      * geofence.
100      */
101     public static final int GEOFENCE_UNCERTAIN = 1<<2L;
102 
103     /**
104      * The constant used to indicate success of the particular geofence call
105      */
106     public static final int GEOFENCE_SUCCESS = 0;
107 
108     /**
109      * The constant used to indicate that too many geofences have been registered.
110      */
111     public static final int GEOFENCE_ERROR_TOO_MANY_GEOFENCES = 1;
112 
113     /**
114      * The constant used to indicate that the geofence id already exists.
115      */
116     public static final int GEOFENCE_ERROR_ID_EXISTS  = 2;
117 
118     /**
119      * The constant used to indicate that the geofence id is unknown.
120      */
121     public static final int GEOFENCE_ERROR_ID_UNKNOWN = 3;
122 
123     /**
124      * The constant used to indicate that the transition requested for the geofence is invalid.
125      */
126     public static final int GEOFENCE_ERROR_INVALID_TRANSITION = 4;
127 
128     /**
129      * The constant used to indicate that the geofence operation has failed.
130      */
131     public static final int GEOFENCE_FAILURE = 5;
132 
133     /**
134      * The constant used to indicate that the operation failed due to insufficient memory.
135      */
136     public static final int GEOFENCE_ERROR_INSUFFICIENT_MEMORY = 6;
137 
138     // the following values must match the definitions in fused_location.h
139 
140     /**
141      * The constant used to indicate that the monitoring system supports GNSS.
142      */
143     public static final int SOURCE_TECHNOLOGY_GNSS = (1<<0);
144 
145     /**
146      * The constant used to indicate that the monitoring system supports WiFi.
147      */
148     public static final int SOURCE_TECHNOLOGY_WIFI = (1<<1);
149 
150     /**
151      * The constant used to indicate that the monitoring system supports Sensors.
152      */
153     public static final int SOURCE_TECHNOLOGY_SENSORS = (1<<2);
154 
155     /**
156      * The constant used to indicate that the monitoring system supports Cell.
157      */
158     public static final int SOURCE_TECHNOLOGY_CELL = (1<<3);
159 
160     /**
161      * The constant used to indicate that the monitoring system supports Bluetooth.
162      */
163     public static final int SOURCE_TECHNOLOGY_BLUETOOTH = (1<<4);
164 
165     private HashMap<GeofenceHardwareCallback, GeofenceHardwareCallbackWrapper>
166             mCallbacks = new HashMap<GeofenceHardwareCallback, GeofenceHardwareCallbackWrapper>();
167     private HashMap<GeofenceHardwareMonitorCallback, GeofenceHardwareMonitorCallbackWrapper>
168             mMonitorCallbacks = new HashMap<GeofenceHardwareMonitorCallback,
169                     GeofenceHardwareMonitorCallbackWrapper>();
170 
171     /** @hide */
172     @UnsupportedAppUsage
GeofenceHardware(IGeofenceHardware service)173     public GeofenceHardware(IGeofenceHardware service) {
174         mService = service;
175     }
176 
177     /**
178      * Returns all the hardware geofence monitoring systems which are supported
179      *
180      * <p> Call {@link #getStatusOfMonitoringType(int)} to know the current state
181      * of a monitoring system.
182      *
183      * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
184      * geofencing in hardware.
185      *
186      * @return An array of all the monitoring types.
187      *         An array of length 0 is returned in case of errors.
188      */
getMonitoringTypes()189     public int[] getMonitoringTypes() {
190         try {
191             return mService.getMonitoringTypes();
192         } catch (RemoteException e) {
193         }
194         return new int[0];
195     }
196 
197     /**
198      * Returns current status of a hardware geofence monitoring system.
199      *
200      * <p>Status can be one of {@link #MONITOR_CURRENTLY_AVAILABLE},
201      * {@link #MONITOR_CURRENTLY_UNAVAILABLE} or {@link #MONITOR_UNSUPPORTED}
202      *
203      * <p> Some supported hardware monitoring systems might not be available
204      * for monitoring geofences in certain scenarios. For example, when a user
205      * enters a building, the GPS hardware subsystem might not be able monitor
206      * geofences and will change from {@link #MONITOR_CURRENTLY_AVAILABLE} to
207      * {@link #MONITOR_CURRENTLY_UNAVAILABLE}.
208      *
209      * @param monitoringType
210      * @return Current status of the monitoring type.
211      */
getStatusOfMonitoringType(int monitoringType)212     public int getStatusOfMonitoringType(int monitoringType) {
213         try {
214             return mService.getStatusOfMonitoringType(monitoringType);
215         } catch (RemoteException e) {
216             return MONITOR_UNSUPPORTED;
217         }
218     }
219 
220     /**
221      * Creates a circular geofence which is monitored by subsystems in the hardware.
222      *
223      * <p> When the device detects that is has entered, exited or is uncertain
224      * about the area specified by the geofence, the given callback will be called.
225      *
226      * <p> If this call returns true, it means that the geofence has been sent to the hardware.
227      * {@link GeofenceHardwareCallback#onGeofenceAdd} will be called with the result of the
228      * add call from the hardware. The {@link GeofenceHardwareCallback#onGeofenceAdd} will be
229      * called with the following parameters when a transition event occurs.
230      * <ul>
231      * <li> The geofence Id
232      * <li> The location object indicating the last known location.
233      * <li> The transition associated with the geofence. One of
234      *      {@link #GEOFENCE_ENTERED}, {@link #GEOFENCE_EXITED}, {@link #GEOFENCE_UNCERTAIN}
235      * <li> The timestamp when the geofence transition occured.
236      * <li> The monitoring type ({@link #MONITORING_TYPE_GPS_HARDWARE} is one such example)
237      *      that was used.
238      * </ul>
239      *
240      * <p> The geofence will be monitored by the subsystem specified by monitoring_type parameter.
241      * The application does not need to hold a wakelock when the monitoring
242      * is being done by the underlying hardware subsystem. If the same geofence Id is being
243      * monitored by two different monitoring systems, the same id can be used for both calls, as
244      * long as the same callback object is used.
245      *
246      * <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when
247      * {@link #MONITORING_TYPE_GPS_HARDWARE} is used.
248      *
249      * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
250      * geofencing in hardware.
251      *
252      * <p>This API should not be called directly by the app developers. A higher level api
253      * which abstracts the hardware should be used instead. All the checks are done by the higher
254      * level public API. Any needed locking should be handled by the higher level API.
255      *
256      * <p> Create a geofence request object using the methods in {@link GeofenceHardwareRequest} to
257      * set all the characteristics of the geofence. Use the created GeofenceHardwareRequest object
258      * in this call.
259      *
260      * @param geofenceId The id associated with the geofence.
261      * @param monitoringType The type of the hardware subsystem that should be used
262      *        to monitor the geofence.
263      * @param geofenceRequest The {@link GeofenceHardwareRequest} object associated with the
264      *        geofence.
265      * @param callback {@link GeofenceHardwareCallback} that will be use to notify the
266      *        transition.
267      * @return true when the geofence is successfully sent to the hardware for addition.
268      * @throws IllegalArgumentException when the geofence request type is not supported.
269      */
addGeofence(int geofenceId, int monitoringType, GeofenceHardwareRequest geofenceRequest, GeofenceHardwareCallback callback)270     public boolean addGeofence(int geofenceId, int monitoringType, GeofenceHardwareRequest
271             geofenceRequest, GeofenceHardwareCallback callback) {
272         try {
273             if (geofenceRequest.getType() == GeofenceHardwareRequest.GEOFENCE_TYPE_CIRCLE) {
274                 return mService.addCircularFence(
275                         monitoringType,
276                         new GeofenceHardwareRequestParcelable(geofenceId, geofenceRequest),
277                         getCallbackWrapper(callback));
278             } else {
279                 throw new IllegalArgumentException("Geofence Request type not supported");
280             }
281         } catch (RemoteException e) {
282         }
283         return false;
284     }
285 
286     /**
287      * Removes a geofence added by {@link #addGeofence} call.
288      *
289      * <p> If this call returns true, it means that the geofence has been sent to the hardware.
290      * {@link GeofenceHardwareCallback#onGeofenceRemove} will be called with the result of the
291      * remove call from the hardware.
292      *
293      * <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when
294      * {@link #MONITORING_TYPE_GPS_HARDWARE} is used.
295      *
296      * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
297      * geofencing in hardware.
298      *
299      * <p>This API should not be called directly by the app developers. A higher level api
300      * which abstracts the hardware should be used instead. All the checks are done by the higher
301      * level public API. Any needed locking should be handled by the higher level API.
302      *
303      * @param geofenceId The id of the geofence.
304      * @param monitoringType The type of the hardware subsystem that should be used
305      *        to monitor the geofence.
306      * @return true when the geofence is successfully sent to the hardware for removal.                     .
307      */
removeGeofence(int geofenceId, int monitoringType)308    public boolean removeGeofence(int geofenceId, int monitoringType) {
309        try {
310            return mService.removeGeofence(geofenceId, monitoringType);
311        } catch (RemoteException e) {
312        }
313        return false;
314    }
315 
316     /**
317      * Pauses the monitoring of a geofence added by {@link #addGeofence} call.
318      *
319      * <p> If this call returns true, it means that the geofence has been sent to the hardware.
320      * {@link GeofenceHardwareCallback#onGeofencePause} will be called with the result of the
321      * pause call from the hardware.
322      *
323      * <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when
324      * {@link #MONITORING_TYPE_GPS_HARDWARE} is used.
325      *
326      * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
327      * geofencing in hardware.
328      *
329      * <p>This API should not be called directly by the app developers. A higher level api
330      * which abstracts the hardware should be used instead. All the checks are done by the higher
331      * level public API. Any needed locking should be handled by the higher level API.
332      *
333      * @param geofenceId The id of the geofence.
334      * @param monitoringType The type of the hardware subsystem that should be used
335      *        to monitor the geofence.
336      * @return true when the geofence is successfully sent to the hardware for pausing.
337      */
pauseGeofence(int geofenceId, int monitoringType)338     public boolean pauseGeofence(int geofenceId, int monitoringType) {
339         try {
340             return mService.pauseGeofence(geofenceId, monitoringType);
341         } catch (RemoteException e) {
342         }
343         return false;
344     }
345 
346     /**
347      * Resumes the monitoring of a geofence added by {@link #pauseGeofence} call.
348      *
349      * <p> If this call returns true, it means that the geofence has been sent to the hardware.
350      * {@link GeofenceHardwareCallback#onGeofenceResume} will be called with the result of the
351      * resume call from the hardware.
352      *
353      * <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when
354      * {@link #MONITORING_TYPE_GPS_HARDWARE} is used.
355      *
356      * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
357      * geofencing in hardware.
358      *
359      * <p>This API should not be called directly by the app developers. A higher level api
360      * which abstracts the hardware should be used instead. All the checks are done by the higher
361      * level public API. Any needed locking should be handled by the higher level API.
362      *
363      * @param geofenceId The id of the geofence.
364      * @param monitoringType The type of the hardware subsystem that should be used
365      *        to monitor the geofence.
366      * @param monitorTransition Bitwise OR of {@link #GEOFENCE_ENTERED},
367      *        {@link #GEOFENCE_EXITED}, {@link #GEOFENCE_UNCERTAIN}
368      * @return true when the geofence is successfully sent to the hardware for resumption.
369      */
resumeGeofence(int geofenceId, int monitoringType, int monitorTransition)370     public boolean resumeGeofence(int geofenceId, int monitoringType, int monitorTransition) {
371         try {
372             return mService.resumeGeofence(geofenceId, monitoringType, monitorTransition);
373         } catch (RemoteException e) {
374         }
375         return false;
376     }
377 
378     /**
379      * Register the callback to be notified when the state of a hardware geofence
380      * monitoring system changes. For instance, it can change from
381      * {@link #MONITOR_CURRENTLY_AVAILABLE} to {@link #MONITOR_CURRENTLY_UNAVAILABLE}
382      *
383      * <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when
384      * {@link #MONITORING_TYPE_GPS_HARDWARE} is used.
385      *
386      * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
387      * geofencing in hardware.
388      *
389      * <p>This API should not be called directly by the app developers. A higher level api
390      * which abstracts the hardware should be used instead. All the checks are done by the higher
391      * level public API. Any needed locking should be handled by the higher level API.
392      *
393      * <p> The same callback object can be used to be informed of geofence transitions
394      * and state changes of the underlying hardware subsystem.
395      *
396      * @param monitoringType Type of the monitor
397      * @param callback Callback that will be called.
398      * @return true on success
399      */
registerForMonitorStateChangeCallback(int monitoringType, GeofenceHardwareMonitorCallback callback)400     public boolean registerForMonitorStateChangeCallback(int monitoringType,
401             GeofenceHardwareMonitorCallback callback) {
402         try {
403             return mService.registerForMonitorStateChangeCallback(monitoringType,
404                     getMonitorCallbackWrapper(callback));
405         } catch (RemoteException e) {
406         }
407         return false;
408     }
409 
410     /**
411      * Unregister the callback that was used with {@link #registerForMonitorStateChangeCallback}
412      * to notify when the state of the hardware geofence monitoring system changes.
413      *
414      * <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when
415      * {@link #MONITORING_TYPE_GPS_HARDWARE} is used.
416      *
417      * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
418      * geofencing in hardware.
419      *
420      * <p>This API should not be called directly by the app developers. A higher level api
421      * which abstracts the hardware should be used instead. All the checks are done by the higher
422      * level public API. Any needed locking should be handled by the higher level API.
423      *
424      * @param monitoringType Type of the monitor
425      * @param callback Callback that will be called.
426      * @return true on success
427      */
unregisterForMonitorStateChangeCallback(int monitoringType, GeofenceHardwareMonitorCallback callback)428     public boolean unregisterForMonitorStateChangeCallback(int monitoringType,
429             GeofenceHardwareMonitorCallback callback) {
430         boolean  result = false;
431         try {
432             result = mService.unregisterForMonitorStateChangeCallback(monitoringType,
433                     getMonitorCallbackWrapper(callback));
434             if (result) removeMonitorCallback(callback);
435 
436         } catch (RemoteException e) {
437         }
438         return result;
439     }
440 
441 
removeCallback(GeofenceHardwareCallback callback)442     private void removeCallback(GeofenceHardwareCallback callback) {
443         synchronized (mCallbacks) {
444             mCallbacks.remove(callback);
445         }
446     }
447 
getCallbackWrapper(GeofenceHardwareCallback callback)448     private GeofenceHardwareCallbackWrapper getCallbackWrapper(GeofenceHardwareCallback callback) {
449         synchronized (mCallbacks) {
450             GeofenceHardwareCallbackWrapper wrapper = mCallbacks.get(callback);
451             if (wrapper == null) {
452                 wrapper = new GeofenceHardwareCallbackWrapper(callback);
453                 mCallbacks.put(callback, wrapper);
454             }
455             return wrapper;
456         }
457     }
458 
removeMonitorCallback(GeofenceHardwareMonitorCallback callback)459     private void removeMonitorCallback(GeofenceHardwareMonitorCallback callback) {
460         synchronized (mMonitorCallbacks) {
461             mMonitorCallbacks.remove(callback);
462         }
463     }
464 
getMonitorCallbackWrapper( GeofenceHardwareMonitorCallback callback)465     private GeofenceHardwareMonitorCallbackWrapper getMonitorCallbackWrapper(
466             GeofenceHardwareMonitorCallback callback) {
467         synchronized (mMonitorCallbacks) {
468             GeofenceHardwareMonitorCallbackWrapper wrapper = mMonitorCallbacks.get(callback);
469             if (wrapper == null) {
470                 wrapper = new GeofenceHardwareMonitorCallbackWrapper(callback);
471                 mMonitorCallbacks.put(callback, wrapper);
472             }
473             return wrapper;
474         }
475     }
476 
477     class GeofenceHardwareMonitorCallbackWrapper extends IGeofenceHardwareMonitorCallback.Stub {
478         private WeakReference<GeofenceHardwareMonitorCallback> mCallback;
479 
GeofenceHardwareMonitorCallbackWrapper(GeofenceHardwareMonitorCallback c)480         GeofenceHardwareMonitorCallbackWrapper(GeofenceHardwareMonitorCallback c) {
481             mCallback = new WeakReference<GeofenceHardwareMonitorCallback>(c);
482         }
483 
onMonitoringSystemChange(GeofenceHardwareMonitorEvent event)484         public void onMonitoringSystemChange(GeofenceHardwareMonitorEvent event) {
485             GeofenceHardwareMonitorCallback c = mCallback.get();
486             if (c == null) return;
487 
488             // report the legacy event first, so older clients are not broken
489             c.onMonitoringSystemChange(
490                     event.getMonitoringType(),
491                     event.getMonitoringStatus() == GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE,
492                     event.getLocation());
493 
494             // and only call the updated callback on on L and above, this complies with the
495             // documentation of GeofenceHardwareMonitorCallback
496             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
497                 c.onMonitoringSystemChange(event);
498             }
499         }
500     }
501 
502     class GeofenceHardwareCallbackWrapper extends IGeofenceHardwareCallback.Stub {
503         private WeakReference<GeofenceHardwareCallback> mCallback;
504 
GeofenceHardwareCallbackWrapper(GeofenceHardwareCallback c)505         GeofenceHardwareCallbackWrapper(GeofenceHardwareCallback c) {
506             mCallback = new WeakReference<GeofenceHardwareCallback>(c);
507         }
508 
onGeofenceTransition(int geofenceId, int transition, Location location, long timestamp, int monitoringType)509         public void onGeofenceTransition(int geofenceId, int transition, Location location,
510                 long timestamp, int monitoringType) {
511             GeofenceHardwareCallback c = mCallback.get();
512             if (c != null) {
513                 c.onGeofenceTransition(geofenceId, transition, location, timestamp,
514                         monitoringType);
515             }
516         }
517 
onGeofenceAdd(int geofenceId, int status)518         public void onGeofenceAdd(int geofenceId, int status) {
519             GeofenceHardwareCallback c = mCallback.get();
520             if (c != null) c.onGeofenceAdd(geofenceId, status);
521         }
522 
onGeofenceRemove(int geofenceId, int status)523         public void onGeofenceRemove(int geofenceId, int status) {
524             GeofenceHardwareCallback c = mCallback.get();
525             if (c != null) {
526                 c.onGeofenceRemove(geofenceId, status);
527                 removeCallback(c);
528             }
529         }
530 
onGeofencePause(int geofenceId, int status)531         public void onGeofencePause(int geofenceId, int status) {
532             GeofenceHardwareCallback c = mCallback.get();
533             if (c != null) {
534                 c.onGeofencePause(geofenceId, status);
535             }
536         }
537 
onGeofenceResume(int geofenceId, int status)538         public void onGeofenceResume(int geofenceId, int status) {
539             GeofenceHardwareCallback c = mCallback.get();
540             if (c != null) c.onGeofenceResume(geofenceId, status);
541         }
542     }
543 }
544