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