GeofenceHardware.java revision 955d8d69ea6caabce1461dc25b339b9bf9dc61a6
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 */
16package android.hardware.location;
17
18import android.annotation.SystemApi;
19import android.location.Location;
20import android.os.Build;
21import android.os.RemoteException;
22
23import java.lang.ref.WeakReference;
24import 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
51public 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    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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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
439    private void removeCallback(GeofenceHardwareCallback callback) {
440        synchronized (mCallbacks) {
441            mCallbacks.remove(callback);
442        }
443    }
444
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
456    private void removeMonitorCallback(GeofenceHardwareMonitorCallback callback) {
457        synchronized (mMonitorCallbacks) {
458            mMonitorCallbacks.remove(callback);
459        }
460    }
461
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
477        GeofenceHardwareMonitorCallbackWrapper(GeofenceHardwareMonitorCallback c) {
478            mCallback = new WeakReference<GeofenceHardwareMonitorCallback>(c);
479        }
480
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
502        GeofenceHardwareCallbackWrapper(GeofenceHardwareCallback c) {
503            mCallback = new WeakReference<GeofenceHardwareCallback>(c);
504        }
505
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
515        public void onGeofenceAdd(int geofenceId, int status) {
516            GeofenceHardwareCallback c = mCallback.get();
517            if (c != null) c.onGeofenceAdd(geofenceId, status);
518        }
519
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
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
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