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