Home | History | Annotate | Download | only in location
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package android.hardware.location;
     17 
     18 import android.location.Location;
     19 import android.os.RemoteException;
     20 
     21 import java.lang.ref.WeakReference;
     22 import 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  */
     46 public 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
     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.                                                  nn
     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