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     /** @hide */
    171     public GeofenceHardware(IGeofenceHardware service) {
    172         mService = service;
    173     }
    174 
    175     /**
    176      * Returns all the hardware geofence monitoring systems which are supported
    177      *
    178      * <p> Call {@link #getStatusOfMonitoringType(int)} to know the current state
    179      * of a monitoring system.
    180      *
    181      * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
    182      * geofencing in hardware.
    183      *
    184      * @return An array of all the monitoring types.
    185      *         An array of length 0 is returned in case of errors.
    186      */
    187     public int[] getMonitoringTypes() {
    188         try {
    189             return mService.getMonitoringTypes();
    190         } catch (RemoteException e) {
    191         }
    192         return new int[0];
    193     }
    194 
    195     /**
    196      * Returns current status of a hardware geofence monitoring system.
    197      *
    198      * <p>Status can be one of {@link #MONITOR_CURRENTLY_AVAILABLE},
    199      * {@link #MONITOR_CURRENTLY_UNAVAILABLE} or {@link #MONITOR_UNSUPPORTED}
    200      *
    201      * <p> Some supported hardware monitoring systems might not be available
    202      * for monitoring geofences in certain scenarios. For example, when a user
    203      * enters a building, the GPS hardware subsystem might not be able monitor
    204      * geofences and will change from {@link #MONITOR_CURRENTLY_AVAILABLE} to
    205      * {@link #MONITOR_CURRENTLY_UNAVAILABLE}.
    206      *
    207      * @param monitoringType
    208      * @return Current status of the monitoring type.
    209      */
    210     public int getStatusOfMonitoringType(int monitoringType) {
    211         try {
    212             return mService.getStatusOfMonitoringType(monitoringType);
    213         } catch (RemoteException e) {
    214             return MONITOR_UNSUPPORTED;
    215         }
    216     }
    217 
    218     /**
    219      * Creates a circular geofence which is monitored by subsystems in the hardware.
    220      *
    221      * <p> When the device detects that is has entered, exited or is uncertain
    222      * about the area specified by the geofence, the given callback will be called.
    223      *
    224      * <p> If this call returns true, it means that the geofence has been sent to the hardware.
    225      * {@link GeofenceHardwareCallback#onGeofenceAdd} will be called with the result of the
    226      * add call from the hardware. The {@link GeofenceHardwareCallback#onGeofenceAdd} will be
    227      * called with the following parameters when a transition event occurs.
    228      * <ul>
    229      * <li> The geofence Id
    230      * <li> The location object indicating the last known location.
    231      * <li> The transition associated with the geofence. One of
    232      *      {@link #GEOFENCE_ENTERED}, {@link #GEOFENCE_EXITED}, {@link #GEOFENCE_UNCERTAIN}
    233      * <li> The timestamp when the geofence transition occured.
    234      * <li> The monitoring type ({@link #MONITORING_TYPE_GPS_HARDWARE} is one such example)
    235      *      that was used.
    236      * </ul>
    237      *
    238      * <p> The geofence will be monitored by the subsystem specified by monitoring_type parameter.
    239      * The application does not need to hold a wakelock when the monitoring
    240      * is being done by the underlying hardware subsystem. If the same geofence Id is being
    241      * monitored by two different monitoring systems, the same id can be used for both calls, as
    242      * long as the same callback object is used.
    243      *
    244      * <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when
    245      * {@link #MONITORING_TYPE_GPS_HARDWARE} is used.
    246      *
    247      * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
    248      * geofencing in hardware.
    249      *
    250      * <p>This API should not be called directly by the app developers. A higher level api
    251      * which abstracts the hardware should be used instead. All the checks are done by the higher
    252      * level public API. Any needed locking should be handled by the higher level API.
    253      *
    254      * <p> Create a geofence request object using the methods in {@link GeofenceHardwareRequest} to
    255      * set all the characteristics of the geofence. Use the created GeofenceHardwareRequest object
    256      * in this call.
    257      *
    258      * @param geofenceId The id associated with the geofence.
    259      * @param monitoringType The type of the hardware subsystem that should be used
    260      *        to monitor the geofence.
    261      * @param geofenceRequest The {@link GeofenceHardwareRequest} object associated with the
    262      *        geofence.
    263      * @param callback {@link GeofenceHardwareCallback} that will be use to notify the
    264      *        transition.
    265      * @return true when the geofence is successfully sent to the hardware for addition.
    266      * @throws IllegalArgumentException when the geofence request type is not supported.
    267      */
    268     public boolean addGeofence(int geofenceId, int monitoringType, GeofenceHardwareRequest
    269             geofenceRequest, GeofenceHardwareCallback callback) {
    270         try {
    271             if (geofenceRequest.getType() == GeofenceHardwareRequest.GEOFENCE_TYPE_CIRCLE) {
    272                 return mService.addCircularFence(
    273                         monitoringType,
    274                         new GeofenceHardwareRequestParcelable(geofenceId, geofenceRequest),
    275                         getCallbackWrapper(callback));
    276             } else {
    277                 throw new IllegalArgumentException("Geofence Request type not supported");
    278             }
    279         } catch (RemoteException e) {
    280         }
    281         return false;
    282     }
    283 
    284     /**
    285      * Removes a geofence added by {@link #addGeofence} call.
    286      *
    287      * <p> If this call returns true, it means that the geofence has been sent to the hardware.
    288      * {@link GeofenceHardwareCallback#onGeofenceRemove} will be called with the result of the
    289      * remove call from the hardware.
    290      *
    291      * <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when
    292      * {@link #MONITORING_TYPE_GPS_HARDWARE} is used.
    293      *
    294      * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
    295      * geofencing in hardware.
    296      *
    297      * <p>This API should not be called directly by the app developers. A higher level api
    298      * which abstracts the hardware should be used instead. All the checks are done by the higher
    299      * level public API. Any needed locking should be handled by the higher level API.
    300      *
    301      * @param geofenceId The id of the geofence.
    302      * @param monitoringType The type of the hardware subsystem that should be used
    303      *        to monitor the geofence.
    304      * @return true when the geofence is successfully sent to the hardware for removal.                     .
    305      */
    306    public boolean removeGeofence(int geofenceId, int monitoringType) {
    307        try {
    308            return mService.removeGeofence(geofenceId, monitoringType);
    309        } catch (RemoteException e) {
    310        }
    311        return false;
    312    }
    313 
    314     /**
    315      * Pauses the monitoring of a geofence added by {@link #addGeofence} call.
    316      *
    317      * <p> If this call returns true, it means that the geofence has been sent to the hardware.
    318      * {@link GeofenceHardwareCallback#onGeofencePause} will be called with the result of the
    319      * pause call from the hardware.
    320      *
    321      * <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when
    322      * {@link #MONITORING_TYPE_GPS_HARDWARE} is used.
    323      *
    324      * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
    325      * geofencing in hardware.
    326      *
    327      * <p>This API should not be called directly by the app developers. A higher level api
    328      * which abstracts the hardware should be used instead. All the checks are done by the higher
    329      * level public API. Any needed locking should be handled by the higher level API.
    330      *
    331      * @param geofenceId The id of the geofence.
    332      * @param monitoringType The type of the hardware subsystem that should be used
    333      *        to monitor the geofence.
    334      * @return true when the geofence is successfully sent to the hardware for pausing.
    335      */
    336     public boolean pauseGeofence(int geofenceId, int monitoringType) {
    337         try {
    338             return mService.pauseGeofence(geofenceId, monitoringType);
    339         } catch (RemoteException e) {
    340         }
    341         return false;
    342     }
    343 
    344     /**
    345      * Resumes the monitoring of a geofence added by {@link #pauseGeofence} call.
    346      *
    347      * <p> If this call returns true, it means that the geofence has been sent to the hardware.
    348      * {@link GeofenceHardwareCallback#onGeofenceResume} will be called with the result of the
    349      * resume call from the hardware.
    350      *
    351      * <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when
    352      * {@link #MONITORING_TYPE_GPS_HARDWARE} is used.
    353      *
    354      * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
    355      * geofencing in hardware.
    356      *
    357      * <p>This API should not be called directly by the app developers. A higher level api
    358      * which abstracts the hardware should be used instead. All the checks are done by the higher
    359      * level public API. Any needed locking should be handled by the higher level API.
    360      *
    361      * @param geofenceId The id of the geofence.
    362      * @param monitoringType The type of the hardware subsystem that should be used
    363      *        to monitor the geofence.
    364      * @param monitorTransition Bitwise OR of {@link #GEOFENCE_ENTERED},
    365      *        {@link #GEOFENCE_EXITED}, {@link #GEOFENCE_UNCERTAIN}
    366      * @return true when the geofence is successfully sent to the hardware for resumption.
    367      */
    368     public boolean resumeGeofence(int geofenceId, int monitoringType, int monitorTransition) {
    369         try {
    370             return mService.resumeGeofence(geofenceId, monitoringType, monitorTransition);
    371         } catch (RemoteException e) {
    372         }
    373         return false;
    374     }
    375 
    376     /**
    377      * Register the callback to be notified when the state of a hardware geofence
    378      * monitoring system changes. For instance, it can change from
    379      * {@link #MONITOR_CURRENTLY_AVAILABLE} to {@link #MONITOR_CURRENTLY_UNAVAILABLE}
    380      *
    381      * <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when
    382      * {@link #MONITORING_TYPE_GPS_HARDWARE} is used.
    383      *
    384      * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
    385      * geofencing in hardware.
    386      *
    387      * <p>This API should not be called directly by the app developers. A higher level api
    388      * which abstracts the hardware should be used instead. All the checks are done by the higher
    389      * level public API. Any needed locking should be handled by the higher level API.
    390      *
    391      * <p> The same callback object can be used to be informed of geofence transitions
    392      * and state changes of the underlying hardware subsystem.
    393      *
    394      * @param monitoringType Type of the monitor
    395      * @param callback Callback that will be called.
    396      * @return true on success
    397      */
    398     public boolean registerForMonitorStateChangeCallback(int monitoringType,
    399             GeofenceHardwareMonitorCallback callback) {
    400         try {
    401             return mService.registerForMonitorStateChangeCallback(monitoringType,
    402                     getMonitorCallbackWrapper(callback));
    403         } catch (RemoteException e) {
    404         }
    405         return false;
    406     }
    407 
    408     /**
    409      * Unregister the callback that was used with {@link #registerForMonitorStateChangeCallback}
    410      * to notify when the state of the hardware geofence monitoring system changes.
    411      *
    412      * <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when
    413      * {@link #MONITORING_TYPE_GPS_HARDWARE} is used.
    414      *
    415      * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
    416      * geofencing in hardware.
    417      *
    418      * <p>This API should not be called directly by the app developers. A higher level api
    419      * which abstracts the hardware should be used instead. All the checks are done by the higher
    420      * level public API. Any needed locking should be handled by the higher level API.
    421      *
    422      * @param monitoringType Type of the monitor
    423      * @param callback Callback that will be called.
    424      * @return true on success
    425      */
    426     public boolean unregisterForMonitorStateChangeCallback(int monitoringType,
    427             GeofenceHardwareMonitorCallback callback) {
    428         boolean  result = false;
    429         try {
    430             result = mService.unregisterForMonitorStateChangeCallback(monitoringType,
    431                     getMonitorCallbackWrapper(callback));
    432             if (result) removeMonitorCallback(callback);
    433 
    434         } catch (RemoteException e) {
    435         }
    436         return result;
    437     }
    438 
    439 
    440     private void removeCallback(GeofenceHardwareCallback callback) {
    441         synchronized (mCallbacks) {
    442             mCallbacks.remove(callback);
    443         }
    444     }
    445 
    446     private GeofenceHardwareCallbackWrapper getCallbackWrapper(GeofenceHardwareCallback callback) {
    447         synchronized (mCallbacks) {
    448             GeofenceHardwareCallbackWrapper wrapper = mCallbacks.get(callback);
    449             if (wrapper == null) {
    450                 wrapper = new GeofenceHardwareCallbackWrapper(callback);
    451                 mCallbacks.put(callback, wrapper);
    452             }
    453             return wrapper;
    454         }
    455     }
    456 
    457     private void removeMonitorCallback(GeofenceHardwareMonitorCallback callback) {
    458         synchronized (mMonitorCallbacks) {
    459             mMonitorCallbacks.remove(callback);
    460         }
    461     }
    462 
    463     private GeofenceHardwareMonitorCallbackWrapper getMonitorCallbackWrapper(
    464             GeofenceHardwareMonitorCallback callback) {
    465         synchronized (mMonitorCallbacks) {
    466             GeofenceHardwareMonitorCallbackWrapper wrapper = mMonitorCallbacks.get(callback);
    467             if (wrapper == null) {
    468                 wrapper = new GeofenceHardwareMonitorCallbackWrapper(callback);
    469                 mMonitorCallbacks.put(callback, wrapper);
    470             }
    471             return wrapper;
    472         }
    473     }
    474 
    475     class GeofenceHardwareMonitorCallbackWrapper extends IGeofenceHardwareMonitorCallback.Stub {
    476         private WeakReference<GeofenceHardwareMonitorCallback> mCallback;
    477 
    478         GeofenceHardwareMonitorCallbackWrapper(GeofenceHardwareMonitorCallback c) {
    479             mCallback = new WeakReference<GeofenceHardwareMonitorCallback>(c);
    480         }
    481 
    482         public void onMonitoringSystemChange(GeofenceHardwareMonitorEvent event) {
    483             GeofenceHardwareMonitorCallback c = mCallback.get();
    484             if (c == null) return;
    485 
    486             // report the legacy event first, so older clients are not broken
    487             c.onMonitoringSystemChange(
    488                     event.getMonitoringType(),
    489                     event.getMonitoringStatus() == GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE,
    490                     event.getLocation());
    491 
    492             // and only call the updated callback on on L and above, this complies with the
    493             // documentation of GeofenceHardwareMonitorCallback
    494             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    495                 c.onMonitoringSystemChange(event);
    496             }
    497         }
    498     }
    499 
    500     class GeofenceHardwareCallbackWrapper extends IGeofenceHardwareCallback.Stub {
    501         private WeakReference<GeofenceHardwareCallback> mCallback;
    502 
    503         GeofenceHardwareCallbackWrapper(GeofenceHardwareCallback c) {
    504             mCallback = new WeakReference<GeofenceHardwareCallback>(c);
    505         }
    506 
    507         public void onGeofenceTransition(int geofenceId, int transition, Location location,
    508                 long timestamp, int monitoringType) {
    509             GeofenceHardwareCallback c = mCallback.get();
    510             if (c != null) {
    511                 c.onGeofenceTransition(geofenceId, transition, location, timestamp,
    512                         monitoringType);
    513             }
    514         }
    515 
    516         public void onGeofenceAdd(int geofenceId, int status) {
    517             GeofenceHardwareCallback c = mCallback.get();
    518             if (c != null) c.onGeofenceAdd(geofenceId, status);
    519         }
    520 
    521         public void onGeofenceRemove(int geofenceId, int status) {
    522             GeofenceHardwareCallback c = mCallback.get();
    523             if (c != null) {
    524                 c.onGeofenceRemove(geofenceId, status);
    525                 removeCallback(c);
    526             }
    527         }
    528 
    529         public void onGeofencePause(int geofenceId, int status) {
    530             GeofenceHardwareCallback c = mCallback.get();
    531             if (c != null) {
    532                 c.onGeofencePause(geofenceId, status);
    533             }
    534         }
    535 
    536         public void onGeofenceResume(int geofenceId, int status) {
    537             GeofenceHardwareCallback c = mCallback.get();
    538             if (c != null) c.onGeofenceResume(geofenceId, status);
    539         }
    540     }
    541 }
    542