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