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