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 
170     /** @hide */
GeofenceHardware(IGeofenceHardware service)171     public GeofenceHardware(IGeofenceHardware service) {
172         mService = service;
173     }
174 
175     /**
176      * Returns all the hardware geofence monitoring systems which are supported
177      *
178      * <p> Call {@link #getStatusOfMonitoringType(int)} to know the current state
179      * of a monitoring system.
180      *
181      * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
182      * geofencing in hardware.
183      *
184      * @return An array of all the monitoring types.
185      *         An array of length 0 is returned in case of errors.
186      */
getMonitoringTypes()187     public int[] getMonitoringTypes() {
188         try {
189             return mService.getMonitoringTypes();
190         } catch (RemoteException e) {
191         }
192         return new int[0];
193     }
194 
195     /**
196      * Returns current status of a hardware geofence monitoring system.
197      *
198      * <p>Status can be one of {@link #MONITOR_CURRENTLY_AVAILABLE},
199      * {@link #MONITOR_CURRENTLY_UNAVAILABLE} or {@link #MONITOR_UNSUPPORTED}
200      *
201      * <p> Some supported hardware monitoring systems might not be available
202      * for monitoring geofences in certain scenarios. For example, when a user
203      * enters a building, the GPS hardware subsystem might not be able monitor
204      * geofences and will change from {@link #MONITOR_CURRENTLY_AVAILABLE} to
205      * {@link #MONITOR_CURRENTLY_UNAVAILABLE}.
206      *
207      * @param monitoringType
208      * @return Current status of the monitoring type.
209      */
getStatusOfMonitoringType(int monitoringType)210     public int getStatusOfMonitoringType(int monitoringType) {
211         try {
212             return mService.getStatusOfMonitoringType(monitoringType);
213         } catch (RemoteException e) {
214             return MONITOR_UNSUPPORTED;
215         }
216     }
217 
218     /**
219      * Creates a circular geofence which is monitored by subsystems in the hardware.
220      *
221      * <p> When the device detects that is has entered, exited or is uncertain
222      * about the area specified by the geofence, the given callback will be called.
223      *
224      * <p> If this call returns true, it means that the geofence has been sent to the hardware.
225      * {@link GeofenceHardwareCallback#onGeofenceAdd} will be called with the result of the
226      * add call from the hardware. The {@link GeofenceHardwareCallback#onGeofenceAdd} will be
227      * called with the following parameters when a transition event occurs.
228      * <ul>
229      * <li> The geofence Id
230      * <li> The location object indicating the last known location.
231      * <li> The transition associated with the geofence. One of
232      *      {@link #GEOFENCE_ENTERED}, {@link #GEOFENCE_EXITED}, {@link #GEOFENCE_UNCERTAIN}
233      * <li> The timestamp when the geofence transition occured.
234      * <li> The monitoring type ({@link #MONITORING_TYPE_GPS_HARDWARE} is one such example)
235      *      that was used.
236      * </ul>
237      *
238      * <p> The geofence will be monitored by the subsystem specified by monitoring_type parameter.
239      * The application does not need to hold a wakelock when the monitoring
240      * is being done by the underlying hardware subsystem. If the same geofence Id is being
241      * monitored by two different monitoring systems, the same id can be used for both calls, as
242      * long as the same callback object is used.
243      *
244      * <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when
245      * {@link #MONITORING_TYPE_GPS_HARDWARE} is used.
246      *
247      * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
248      * geofencing in hardware.
249      *
250      * <p>This API should not be called directly by the app developers. A higher level api
251      * which abstracts the hardware should be used instead. All the checks are done by the higher
252      * level public API. Any needed locking should be handled by the higher level API.
253      *
254      * <p> Create a geofence request object using the methods in {@link GeofenceHardwareRequest} to
255      * set all the characteristics of the geofence. Use the created GeofenceHardwareRequest object
256      * in this call.
257      *
258      * @param geofenceId The id associated with the geofence.
259      * @param monitoringType The type of the hardware subsystem that should be used
260      *        to monitor the geofence.
261      * @param geofenceRequest The {@link GeofenceHardwareRequest} object associated with the
262      *        geofence.
263      * @param callback {@link GeofenceHardwareCallback} that will be use to notify the
264      *        transition.
265      * @return true when the geofence is successfully sent to the hardware for addition.
266      * @throws IllegalArgumentException when the geofence request type is not supported.
267      */
addGeofence(int geofenceId, int monitoringType, GeofenceHardwareRequest geofenceRequest, GeofenceHardwareCallback callback)268     public boolean addGeofence(int geofenceId, int monitoringType, GeofenceHardwareRequest
269             geofenceRequest, GeofenceHardwareCallback callback) {
270         try {
271             if (geofenceRequest.getType() == GeofenceHardwareRequest.GEOFENCE_TYPE_CIRCLE) {
272                 return mService.addCircularFence(
273                         monitoringType,
274                         new GeofenceHardwareRequestParcelable(geofenceId, geofenceRequest),
275                         getCallbackWrapper(callback));
276             } else {
277                 throw new IllegalArgumentException("Geofence Request type not supported");
278             }
279         } catch (RemoteException e) {
280         }
281         return false;
282     }
283 
284     /**
285      * Removes a geofence added by {@link #addGeofence} call.
286      *
287      * <p> If this call returns true, it means that the geofence has been sent to the hardware.
288      * {@link GeofenceHardwareCallback#onGeofenceRemove} will be called with the result of the
289      * remove call from the hardware.
290      *
291      * <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when
292      * {@link #MONITORING_TYPE_GPS_HARDWARE} is used.
293      *
294      * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
295      * geofencing in hardware.
296      *
297      * <p>This API should not be called directly by the app developers. A higher level api
298      * which abstracts the hardware should be used instead. All the checks are done by the higher
299      * level public API. Any needed locking should be handled by the higher level API.
300      *
301      * @param geofenceId The id of the geofence.
302      * @param monitoringType The type of the hardware subsystem that should be used
303      *        to monitor the geofence.
304      * @return true when the geofence is successfully sent to the hardware for removal.                     .
305      */
removeGeofence(int geofenceId, int monitoringType)306    public boolean removeGeofence(int geofenceId, int monitoringType) {
307        try {
308            return mService.removeGeofence(geofenceId, monitoringType);
309        } catch (RemoteException e) {
310        }
311        return false;
312    }
313 
314     /**
315      * Pauses the monitoring of a geofence added by {@link #addGeofence} call.
316      *
317      * <p> If this call returns true, it means that the geofence has been sent to the hardware.
318      * {@link GeofenceHardwareCallback#onGeofencePause} will be called with the result of the
319      * pause call from the hardware.
320      *
321      * <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when
322      * {@link #MONITORING_TYPE_GPS_HARDWARE} is used.
323      *
324      * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
325      * geofencing in hardware.
326      *
327      * <p>This API should not be called directly by the app developers. A higher level api
328      * which abstracts the hardware should be used instead. All the checks are done by the higher
329      * level public API. Any needed locking should be handled by the higher level API.
330      *
331      * @param geofenceId The id of the geofence.
332      * @param monitoringType The type of the hardware subsystem that should be used
333      *        to monitor the geofence.
334      * @return true when the geofence is successfully sent to the hardware for pausing.
335      */
pauseGeofence(int geofenceId, int monitoringType)336     public boolean pauseGeofence(int geofenceId, int monitoringType) {
337         try {
338             return mService.pauseGeofence(geofenceId, monitoringType);
339         } catch (RemoteException e) {
340         }
341         return false;
342     }
343 
344     /**
345      * Resumes the monitoring of a geofence added by {@link #pauseGeofence} call.
346      *
347      * <p> If this call returns true, it means that the geofence has been sent to the hardware.
348      * {@link GeofenceHardwareCallback#onGeofenceResume} will be called with the result of the
349      * resume call from the hardware.
350      *
351      * <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when
352      * {@link #MONITORING_TYPE_GPS_HARDWARE} is used.
353      *
354      * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
355      * geofencing in hardware.
356      *
357      * <p>This API should not be called directly by the app developers. A higher level api
358      * which abstracts the hardware should be used instead. All the checks are done by the higher
359      * level public API. Any needed locking should be handled by the higher level API.
360      *
361      * @param geofenceId The id of the geofence.
362      * @param monitoringType The type of the hardware subsystem that should be used
363      *        to monitor the geofence.
364      * @param monitorTransition Bitwise OR of {@link #GEOFENCE_ENTERED},
365      *        {@link #GEOFENCE_EXITED}, {@link #GEOFENCE_UNCERTAIN}
366      * @return true when the geofence is successfully sent to the hardware for resumption.
367      */
resumeGeofence(int geofenceId, int monitoringType, int monitorTransition)368     public boolean resumeGeofence(int geofenceId, int monitoringType, int monitorTransition) {
369         try {
370             return mService.resumeGeofence(geofenceId, monitoringType, monitorTransition);
371         } catch (RemoteException e) {
372         }
373         return false;
374     }
375 
376     /**
377      * Register the callback to be notified when the state of a hardware geofence
378      * monitoring system changes. For instance, it can change from
379      * {@link #MONITOR_CURRENTLY_AVAILABLE} to {@link #MONITOR_CURRENTLY_UNAVAILABLE}
380      *
381      * <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when
382      * {@link #MONITORING_TYPE_GPS_HARDWARE} is used.
383      *
384      * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
385      * geofencing in hardware.
386      *
387      * <p>This API should not be called directly by the app developers. A higher level api
388      * which abstracts the hardware should be used instead. All the checks are done by the higher
389      * level public API. Any needed locking should be handled by the higher level API.
390      *
391      * <p> The same callback object can be used to be informed of geofence transitions
392      * and state changes of the underlying hardware subsystem.
393      *
394      * @param monitoringType Type of the monitor
395      * @param callback Callback that will be called.
396      * @return true on success
397      */
registerForMonitorStateChangeCallback(int monitoringType, GeofenceHardwareMonitorCallback callback)398     public boolean registerForMonitorStateChangeCallback(int monitoringType,
399             GeofenceHardwareMonitorCallback callback) {
400         try {
401             return mService.registerForMonitorStateChangeCallback(monitoringType,
402                     getMonitorCallbackWrapper(callback));
403         } catch (RemoteException e) {
404         }
405         return false;
406     }
407 
408     /**
409      * Unregister the callback that was used with {@link #registerForMonitorStateChangeCallback}
410      * to notify when the state of the hardware geofence monitoring system changes.
411      *
412      * <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when
413      * {@link #MONITORING_TYPE_GPS_HARDWARE} is used.
414      *
415      * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
416      * geofencing in hardware.
417      *
418      * <p>This API should not be called directly by the app developers. A higher level api
419      * which abstracts the hardware should be used instead. All the checks are done by the higher
420      * level public API. Any needed locking should be handled by the higher level API.
421      *
422      * @param monitoringType Type of the monitor
423      * @param callback Callback that will be called.
424      * @return true on success
425      */
unregisterForMonitorStateChangeCallback(int monitoringType, GeofenceHardwareMonitorCallback callback)426     public boolean unregisterForMonitorStateChangeCallback(int monitoringType,
427             GeofenceHardwareMonitorCallback callback) {
428         boolean  result = false;
429         try {
430             result = mService.unregisterForMonitorStateChangeCallback(monitoringType,
431                     getMonitorCallbackWrapper(callback));
432             if (result) removeMonitorCallback(callback);
433 
434         } catch (RemoteException e) {
435         }
436         return result;
437     }
438 
439 
removeCallback(GeofenceHardwareCallback callback)440     private void removeCallback(GeofenceHardwareCallback callback) {
441         synchronized (mCallbacks) {
442             mCallbacks.remove(callback);
443         }
444     }
445 
getCallbackWrapper(GeofenceHardwareCallback callback)446     private GeofenceHardwareCallbackWrapper getCallbackWrapper(GeofenceHardwareCallback callback) {
447         synchronized (mCallbacks) {
448             GeofenceHardwareCallbackWrapper wrapper = mCallbacks.get(callback);
449             if (wrapper == null) {
450                 wrapper = new GeofenceHardwareCallbackWrapper(callback);
451                 mCallbacks.put(callback, wrapper);
452             }
453             return wrapper;
454         }
455     }
456 
removeMonitorCallback(GeofenceHardwareMonitorCallback callback)457     private void removeMonitorCallback(GeofenceHardwareMonitorCallback callback) {
458         synchronized (mMonitorCallbacks) {
459             mMonitorCallbacks.remove(callback);
460         }
461     }
462 
getMonitorCallbackWrapper( GeofenceHardwareMonitorCallback callback)463     private GeofenceHardwareMonitorCallbackWrapper getMonitorCallbackWrapper(
464             GeofenceHardwareMonitorCallback callback) {
465         synchronized (mMonitorCallbacks) {
466             GeofenceHardwareMonitorCallbackWrapper wrapper = mMonitorCallbacks.get(callback);
467             if (wrapper == null) {
468                 wrapper = new GeofenceHardwareMonitorCallbackWrapper(callback);
469                 mMonitorCallbacks.put(callback, wrapper);
470             }
471             return wrapper;
472         }
473     }
474 
475     class GeofenceHardwareMonitorCallbackWrapper extends IGeofenceHardwareMonitorCallback.Stub {
476         private WeakReference<GeofenceHardwareMonitorCallback> mCallback;
477 
GeofenceHardwareMonitorCallbackWrapper(GeofenceHardwareMonitorCallback c)478         GeofenceHardwareMonitorCallbackWrapper(GeofenceHardwareMonitorCallback c) {
479             mCallback = new WeakReference<GeofenceHardwareMonitorCallback>(c);
480         }
481 
onMonitoringSystemChange(GeofenceHardwareMonitorEvent event)482         public void onMonitoringSystemChange(GeofenceHardwareMonitorEvent event) {
483             GeofenceHardwareMonitorCallback c = mCallback.get();
484             if (c == null) return;
485 
486             // report the legacy event first, so older clients are not broken
487             c.onMonitoringSystemChange(
488                     event.getMonitoringType(),
489                     event.getMonitoringStatus() == GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE,
490                     event.getLocation());
491 
492             // and only call the updated callback on on L and above, this complies with the
493             // documentation of GeofenceHardwareMonitorCallback
494             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
495                 c.onMonitoringSystemChange(event);
496             }
497         }
498     }
499 
500     class GeofenceHardwareCallbackWrapper extends IGeofenceHardwareCallback.Stub {
501         private WeakReference<GeofenceHardwareCallback> mCallback;
502 
GeofenceHardwareCallbackWrapper(GeofenceHardwareCallback c)503         GeofenceHardwareCallbackWrapper(GeofenceHardwareCallback c) {
504             mCallback = new WeakReference<GeofenceHardwareCallback>(c);
505         }
506 
onGeofenceTransition(int geofenceId, int transition, Location location, long timestamp, int monitoringType)507         public void onGeofenceTransition(int geofenceId, int transition, Location location,
508                 long timestamp, int monitoringType) {
509             GeofenceHardwareCallback c = mCallback.get();
510             if (c != null) {
511                 c.onGeofenceTransition(geofenceId, transition, location, timestamp,
512                         monitoringType);
513             }
514         }
515 
onGeofenceAdd(int geofenceId, int status)516         public void onGeofenceAdd(int geofenceId, int status) {
517             GeofenceHardwareCallback c = mCallback.get();
518             if (c != null) c.onGeofenceAdd(geofenceId, status);
519         }
520 
onGeofenceRemove(int geofenceId, int status)521         public void onGeofenceRemove(int geofenceId, int status) {
522             GeofenceHardwareCallback c = mCallback.get();
523             if (c != null) {
524                 c.onGeofenceRemove(geofenceId, status);
525                 removeCallback(c);
526             }
527         }
528 
onGeofencePause(int geofenceId, int status)529         public void onGeofencePause(int geofenceId, int status) {
530             GeofenceHardwareCallback c = mCallback.get();
531             if (c != null) {
532                 c.onGeofencePause(geofenceId, status);
533             }
534         }
535 
onGeofenceResume(int geofenceId, int status)536         public void onGeofenceResume(int geofenceId, int status) {
537             GeofenceHardwareCallback c = mCallback.get();
538             if (c != null) c.onGeofenceResume(geofenceId, status);
539         }
540     }
541 }
542