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