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 
     17 package android.hardware.location;
     18 
     19 import android.content.Context;
     20 import android.content.pm.PackageManager;
     21 import android.location.IFusedGeofenceHardware;
     22 import android.location.IGpsGeofenceHardware;
     23 import android.location.Location;
     24 import android.os.Handler;
     25 import android.os.IBinder;
     26 import android.os.Message;
     27 import android.os.PowerManager;
     28 import android.os.RemoteException;
     29 import android.util.Log;
     30 import android.util.SparseArray;
     31 
     32 import java.util.ArrayList;
     33 
     34 /**
     35  * This class manages the geofences which are handled by hardware.
     36  *
     37  * @hide
     38  */
     39 public final class GeofenceHardwareImpl {
     40     private static final String TAG = "GeofenceHardwareImpl";
     41     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     42 
     43     private final Context mContext;
     44     private static GeofenceHardwareImpl sInstance;
     45     private PowerManager.WakeLock mWakeLock;
     46     private final SparseArray<IGeofenceHardwareCallback> mGeofences =
     47             new SparseArray<IGeofenceHardwareCallback>();
     48     private final ArrayList<IGeofenceHardwareMonitorCallback>[] mCallbacks =
     49             new ArrayList[GeofenceHardware.NUM_MONITORS];
     50     private final ArrayList<Reaper> mReapers = new ArrayList<Reaper>();
     51 
     52     private IFusedGeofenceHardware mFusedService;
     53     private IGpsGeofenceHardware mGpsService;
     54 
     55     private int[] mSupportedMonitorTypes = new int[GeofenceHardware.NUM_MONITORS];
     56 
     57     // mGeofenceHandler message types
     58     private static final int GEOFENCE_TRANSITION_CALLBACK = 1;
     59     private static final int ADD_GEOFENCE_CALLBACK = 2;
     60     private static final int REMOVE_GEOFENCE_CALLBACK = 3;
     61     private static final int PAUSE_GEOFENCE_CALLBACK = 4;
     62     private static final int RESUME_GEOFENCE_CALLBACK = 5;
     63     private static final int GEOFENCE_CALLBACK_BINDER_DIED = 6;
     64 
     65     // mCallbacksHandler message types
     66     private static final int GEOFENCE_STATUS = 1;
     67     private static final int CALLBACK_ADD = 2;
     68     private static final int CALLBACK_REMOVE = 3;
     69     private static final int MONITOR_CALLBACK_BINDER_DIED = 4;
     70 
     71     // mReaperHandler message types
     72     private static final int REAPER_GEOFENCE_ADDED = 1;
     73     private static final int REAPER_MONITOR_CALLBACK_ADDED = 2;
     74     private static final int REAPER_REMOVED = 3;
     75 
     76     // The following constants need to match GpsLocationFlags enum in gps.h
     77     private static final int LOCATION_INVALID = 0;
     78     private static final int LOCATION_HAS_LAT_LONG = 1;
     79     private static final int LOCATION_HAS_ALTITUDE = 2;
     80     private static final int LOCATION_HAS_SPEED = 4;
     81     private static final int LOCATION_HAS_BEARING = 8;
     82     private static final int LOCATION_HAS_ACCURACY = 16;
     83 
     84     // Resolution level constants used for permission checks.
     85     // These constants must be in increasing order of finer resolution.
     86     private static final int RESOLUTION_LEVEL_NONE = 1;
     87     private static final int RESOLUTION_LEVEL_COARSE = 2;
     88     private static final int RESOLUTION_LEVEL_FINE = 3;
     89 
     90     public synchronized static GeofenceHardwareImpl getInstance(Context context) {
     91         if (sInstance == null) {
     92             sInstance = new GeofenceHardwareImpl(context);
     93         }
     94         return sInstance;
     95     }
     96 
     97     private GeofenceHardwareImpl(Context context) {
     98         mContext = context;
     99         // Init everything to unsupported.
    100         setMonitorAvailability(GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE,
    101                 GeofenceHardware.MONITOR_UNSUPPORTED);
    102         setMonitorAvailability(
    103                 GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE,
    104                 GeofenceHardware.MONITOR_UNSUPPORTED);
    105 
    106     }
    107 
    108     private void acquireWakeLock() {
    109         if (mWakeLock == null) {
    110             PowerManager powerManager =
    111                     (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
    112             mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
    113         }
    114         mWakeLock.acquire();
    115     }
    116 
    117     private void releaseWakeLock() {
    118         if (mWakeLock.isHeld()) mWakeLock.release();
    119     }
    120 
    121     private void updateGpsHardwareAvailability() {
    122         //Check which monitors are available.
    123         boolean gpsSupported;
    124         try {
    125             gpsSupported = mGpsService.isHardwareGeofenceSupported();
    126         } catch (RemoteException e) {
    127             Log.e(TAG, "Remote Exception calling LocationManagerService");
    128             gpsSupported = false;
    129         }
    130 
    131         if (gpsSupported) {
    132             // Its assumed currently available at startup.
    133             // native layer will update later.
    134             setMonitorAvailability(GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE,
    135                     GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE);
    136         }
    137     }
    138 
    139     private void updateFusedHardwareAvailability() {
    140         boolean fusedSupported;
    141         try {
    142             fusedSupported = mFusedService.isSupported();
    143         } catch(RemoteException e) {
    144             Log.e(TAG, "RemoteException calling LocationManagerService");
    145             fusedSupported = false;
    146         }
    147 
    148         if(fusedSupported) {
    149             setMonitorAvailability(
    150                     GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE,
    151                     GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE);
    152         }
    153     }
    154 
    155     public void setGpsHardwareGeofence(IGpsGeofenceHardware service) {
    156         if (mGpsService == null) {
    157             mGpsService = service;
    158             updateGpsHardwareAvailability();
    159         } else if (service == null) {
    160             mGpsService = null;
    161             Log.w(TAG, "GPS Geofence Hardware service seems to have crashed");
    162         } else {
    163             Log.e(TAG, "Error: GpsService being set again.");
    164         }
    165     }
    166 
    167     public void setFusedGeofenceHardware(IFusedGeofenceHardware service) {
    168         if(mFusedService == null) {
    169             mFusedService = service;
    170             updateFusedHardwareAvailability();
    171         } else if(service == null) {
    172             mFusedService = null;
    173             Log.w(TAG, "Fused Geofence Hardware service seems to have crashed");
    174         } else {
    175             Log.e(TAG, "Error: FusedService being set again");
    176         }
    177     }
    178 
    179     public int[] getMonitoringTypes() {
    180         boolean gpsSupported;
    181         boolean fusedSupported;
    182         synchronized (mSupportedMonitorTypes) {
    183             gpsSupported = mSupportedMonitorTypes[GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE]
    184                     != GeofenceHardware.MONITOR_UNSUPPORTED;
    185             fusedSupported = mSupportedMonitorTypes[GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE]
    186                     != GeofenceHardware.MONITOR_UNSUPPORTED;
    187         }
    188 
    189         if(gpsSupported) {
    190             if(fusedSupported) {
    191                 return new int[] {
    192                         GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE,
    193                         GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE };
    194             } else {
    195                 return new int[] { GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE };
    196             }
    197         } else if (fusedSupported) {
    198             return new int[] { GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE };
    199         } else {
    200             return new int[0];
    201         }
    202     }
    203 
    204     public int getStatusOfMonitoringType(int monitoringType) {
    205         synchronized (mSupportedMonitorTypes) {
    206             if (monitoringType >= mSupportedMonitorTypes.length || monitoringType < 0) {
    207                 throw new IllegalArgumentException("Unknown monitoring type");
    208             }
    209             return mSupportedMonitorTypes[monitoringType];
    210         }
    211     }
    212 
    213     public boolean addCircularFence(int geofenceId,  int monitoringType, double latitude,
    214             double longitude, double radius, int lastTransition,int monitorTransitions,
    215             int notificationResponsivenes, int unknownTimer, IGeofenceHardwareCallback callback) {
    216         // This API is not thread safe. Operations on the same geofence need to be serialized
    217         // by upper layers
    218         if (DEBUG) {
    219             Log.d(TAG, "addCircularFence: GeofenceId: " + geofenceId + " Latitude: " + latitude +
    220                     " Longitude: " + longitude + " Radius: " + radius + " LastTransition: "
    221                     + lastTransition + " MonitorTransition: " + monitorTransitions +
    222                     " NotificationResponsiveness: " + notificationResponsivenes +
    223                     " UnKnown Timer: " + unknownTimer + " MonitoringType: " + monitoringType);
    224 
    225         }
    226         boolean result;
    227 
    228         // The callback must be added before addCircularHardwareGeofence is called otherwise the
    229         // callback might not be called after the geofence is added in the geofence hardware.
    230         // This also means that the callback must be removed if the addCircularHardwareGeofence
    231         // operations is not called or fails.
    232         synchronized (mGeofences) {
    233             mGeofences.put(geofenceId, callback);
    234         }
    235 
    236         switch (monitoringType) {
    237             case GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE:
    238                 if (mGpsService == null) return false;
    239                 try {
    240                     result = mGpsService.addCircularHardwareGeofence(geofenceId, latitude,
    241                             longitude, radius, lastTransition, monitorTransitions,
    242                             notificationResponsivenes, unknownTimer);
    243                 } catch (RemoteException e) {
    244                     Log.e(TAG, "AddGeofence: Remote Exception calling LocationManagerService");
    245                     result = false;
    246                 }
    247                 break;
    248             case GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE:
    249                 if(mFusedService == null) {
    250                     return false;
    251                 }
    252                 GeofenceHardwareRequest request = GeofenceHardwareRequest.createCircularGeofence(
    253                         latitude,
    254                         longitude,
    255                         radius);
    256                 request.setUnknownTimer(unknownTimer);
    257                 request.setNotificationResponsiveness(notificationResponsivenes);
    258                 request.setMonitorTransitions(monitorTransitions);
    259                 request.setLastTransition(lastTransition);
    260 
    261                 GeofenceHardwareRequestParcelable parcelableRequest =
    262                         new GeofenceHardwareRequestParcelable(geofenceId, request);
    263                 try {
    264                     mFusedService.addGeofences(
    265                             new GeofenceHardwareRequestParcelable[] { parcelableRequest });
    266                     result = true;
    267                 } catch(RemoteException e) {
    268                     Log.e(TAG, "AddGeofence: RemoteException calling LocationManagerService");
    269                     result = false;
    270                 }
    271                 break;
    272             default:
    273                 result = false;
    274         }
    275         if (result) {
    276             Message m = mReaperHandler.obtainMessage(REAPER_GEOFENCE_ADDED, callback);
    277             m.arg1 = monitoringType;
    278             mReaperHandler.sendMessage(m);
    279         } else {
    280             synchronized (mGeofences) {
    281                 mGeofences.remove(geofenceId);
    282             }
    283         }
    284 
    285         if (DEBUG) Log.d(TAG, "addCircularFence: Result is: " + result);
    286         return result;
    287     }
    288 
    289     public boolean removeGeofence(int geofenceId, int monitoringType) {
    290         // This API is not thread safe. Operations on the same geofence need to be serialized
    291         // by upper layers
    292         if (DEBUG) Log.d(TAG, "Remove Geofence: GeofenceId: " + geofenceId);
    293         boolean result = false;
    294 
    295         synchronized (mGeofences) {
    296             if (mGeofences.get(geofenceId) == null) {
    297                 throw new IllegalArgumentException("Geofence " + geofenceId + " not registered.");
    298             }
    299         }
    300         switch (monitoringType) {
    301             case GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE:
    302                 if (mGpsService == null) return false;
    303                 try {
    304                     result = mGpsService.removeHardwareGeofence(geofenceId);
    305                 } catch (RemoteException e) {
    306                     Log.e(TAG, "RemoveGeofence: Remote Exception calling LocationManagerService");
    307                     result = false;
    308                 }
    309                 break;
    310             case GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE:
    311                 if(mFusedService == null) {
    312                     return false;
    313                 }
    314                 try {
    315                     mFusedService.removeGeofences(new int[] { geofenceId });
    316                     result = true;
    317                 } catch(RemoteException e) {
    318                     Log.e(TAG, "RemoveGeofence: RemoteException calling LocationManagerService");
    319                     result = false;
    320                 }
    321                 break;
    322             default:
    323                 result = false;
    324         }
    325         if (DEBUG) Log.d(TAG, "removeGeofence: Result is: " + result);
    326         return result;
    327     }
    328 
    329     public boolean pauseGeofence(int geofenceId, int monitoringType) {
    330         // This API is not thread safe. Operations on the same geofence need to be serialized
    331         // by upper layers
    332         if (DEBUG) Log.d(TAG, "Pause Geofence: GeofenceId: " + geofenceId);
    333         boolean result;
    334         synchronized (mGeofences) {
    335             if (mGeofences.get(geofenceId) == null) {
    336                 throw new IllegalArgumentException("Geofence " + geofenceId + " not registered.");
    337             }
    338         }
    339         switch (monitoringType) {
    340             case GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE:
    341                 if (mGpsService == null) return false;
    342                 try {
    343                     result = mGpsService.pauseHardwareGeofence(geofenceId);
    344                 } catch (RemoteException e) {
    345                     Log.e(TAG, "PauseGeofence: Remote Exception calling LocationManagerService");
    346                     result = false;
    347                 }
    348                 break;
    349             case GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE:
    350                 if(mFusedService == null) {
    351                     return false;
    352                 }
    353                 try {
    354                     mFusedService.pauseMonitoringGeofence(geofenceId);
    355                     result = true;
    356                 } catch(RemoteException e) {
    357                     Log.e(TAG, "PauseGeofence: RemoteException calling LocationManagerService");
    358                     result = false;
    359                 }
    360                 break;
    361             default:
    362                 result = false;
    363         }
    364         if (DEBUG) Log.d(TAG, "pauseGeofence: Result is: " + result);
    365         return result;
    366     }
    367 
    368 
    369     public boolean resumeGeofence(int geofenceId,  int monitoringType, int monitorTransition) {
    370         // This API is not thread safe. Operations on the same geofence need to be serialized
    371         // by upper layers
    372         if (DEBUG) Log.d(TAG, "Resume Geofence: GeofenceId: " + geofenceId);
    373         boolean result;
    374         synchronized (mGeofences) {
    375             if (mGeofences.get(geofenceId) == null) {
    376                 throw new IllegalArgumentException("Geofence " + geofenceId + " not registered.");
    377             }
    378         }
    379         switch (monitoringType) {
    380             case GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE:
    381                 if (mGpsService == null) return false;
    382                 try {
    383                     result = mGpsService.resumeHardwareGeofence(geofenceId, monitorTransition);
    384                 } catch (RemoteException e) {
    385                     Log.e(TAG, "ResumeGeofence: Remote Exception calling LocationManagerService");
    386                     result = false;
    387                 }
    388                 break;
    389             case GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE:
    390                 if(mFusedService == null) {
    391                     return false;
    392                 }
    393                 try {
    394                     mFusedService.resumeMonitoringGeofence(geofenceId, monitorTransition);
    395                     result = true;
    396                 } catch(RemoteException e) {
    397                     Log.e(TAG, "ResumeGeofence: RemoteException calling LocationManagerService");
    398                     result = false;
    399                 }
    400                 break;
    401             default:
    402                 result = false;
    403         }
    404         if (DEBUG) Log.d(TAG, "resumeGeofence: Result is: " + result);
    405         return result;
    406     }
    407 
    408     public boolean registerForMonitorStateChangeCallback(int monitoringType,
    409             IGeofenceHardwareMonitorCallback callback) {
    410         Message reaperMessage =
    411                 mReaperHandler.obtainMessage(REAPER_MONITOR_CALLBACK_ADDED, callback);
    412         reaperMessage.arg1 = monitoringType;
    413         mReaperHandler.sendMessage(reaperMessage);
    414 
    415         Message m = mCallbacksHandler.obtainMessage(CALLBACK_ADD, callback);
    416         m.arg1 = monitoringType;
    417         mCallbacksHandler.sendMessage(m);
    418         return true;
    419     }
    420 
    421     public boolean unregisterForMonitorStateChangeCallback(int monitoringType,
    422             IGeofenceHardwareMonitorCallback callback) {
    423         Message m = mCallbacksHandler.obtainMessage(CALLBACK_REMOVE, callback);
    424         m.arg1 = monitoringType;
    425         mCallbacksHandler.sendMessage(m);
    426         return true;
    427     }
    428 
    429     /**
    430      * Used to report geofence transitions
    431      */
    432     public void reportGeofenceTransition(
    433             int geofenceId,
    434             Location location,
    435             int transition,
    436             long transitionTimestamp,
    437             int monitoringType,
    438             int sourcesUsed) {
    439         if(location == null) {
    440             Log.e(TAG, String.format("Invalid Geofence Transition: location=%p", location));
    441             return;
    442         }
    443         if(DEBUG) {
    444             Log.d(
    445                     TAG,
    446                     "GeofenceTransition| " + location + ", transition:" + transition +
    447                     ", transitionTimestamp:" + transitionTimestamp + ", monitoringType:" +
    448                     monitoringType + ", sourcesUsed:" + sourcesUsed);
    449         }
    450 
    451         GeofenceTransition geofenceTransition = new GeofenceTransition(
    452                 geofenceId,
    453                 transition,
    454                 transitionTimestamp,
    455                 location,
    456                 monitoringType,
    457                 sourcesUsed);
    458         acquireWakeLock();
    459 
    460         Message message = mGeofenceHandler.obtainMessage(
    461                 GEOFENCE_TRANSITION_CALLBACK,
    462                 geofenceTransition);
    463         message.sendToTarget();
    464     }
    465 
    466     /**
    467      * Used to report Monitor status changes.
    468      */
    469     public void reportGeofenceMonitorStatus(
    470             int monitoringType,
    471             int monitoringStatus,
    472             Location location,
    473             int source) {
    474         // TODO: use the source if needed in the future
    475         setMonitorAvailability(monitoringType, monitoringStatus);
    476         acquireWakeLock();
    477         Message message = mCallbacksHandler.obtainMessage(GEOFENCE_STATUS, location);
    478         message.arg1 = monitoringStatus;
    479         message.arg2 = monitoringType;
    480         message.sendToTarget();
    481     }
    482 
    483     /**
    484      * Internal generic status report function for Geofence operations.
    485      *
    486      * @param operation The operation to be reported as defined internally.
    487      * @param geofenceId The id of the geofence the operation is related to.
    488      * @param operationStatus The status of the operation as defined in GeofenceHardware class. This
    489      *                        status is independent of the statuses reported by different HALs.
    490      */
    491     private void reportGeofenceOperationStatus(int operation, int geofenceId, int operationStatus) {
    492         acquireWakeLock();
    493         Message message = mGeofenceHandler.obtainMessage(operation);
    494         message.arg1 = geofenceId;
    495         message.arg2 = operationStatus;
    496         message.sendToTarget();
    497     }
    498 
    499     /**
    500      * Used to report the status of a Geofence Add operation.
    501      */
    502     public void reportGeofenceAddStatus(int geofenceId, int status) {
    503         if(DEBUG) Log.d(TAG, "AddCallback| id:" + geofenceId + ", status:" + status);
    504         reportGeofenceOperationStatus(ADD_GEOFENCE_CALLBACK, geofenceId, status);
    505     }
    506 
    507     /**
    508      * Used to report the status of a Geofence Remove operation.
    509      */
    510     public void reportGeofenceRemoveStatus(int geofenceId, int status) {
    511         if(DEBUG) Log.d(TAG, "RemoveCallback| id:" + geofenceId + ", status:" + status);
    512         reportGeofenceOperationStatus(REMOVE_GEOFENCE_CALLBACK, geofenceId, status);
    513     }
    514 
    515     /**
    516      * Used to report the status of a Geofence Pause operation.
    517      */
    518     public void reportGeofencePauseStatus(int geofenceId, int status) {
    519         if(DEBUG) Log.d(TAG, "PauseCallbac| id:" + geofenceId + ", status" + status);
    520         reportGeofenceOperationStatus(PAUSE_GEOFENCE_CALLBACK, geofenceId, status);
    521     }
    522 
    523     /**
    524      * Used to report the status of a Geofence Resume operation.
    525      */
    526     public void reportGeofenceResumeStatus(int geofenceId, int status) {
    527         if(DEBUG) Log.d(TAG, "ResumeCallback| id:" + geofenceId + ", status:" + status);
    528         reportGeofenceOperationStatus(RESUME_GEOFENCE_CALLBACK, geofenceId, status);
    529     }
    530 
    531     // All operations on mGeofences
    532     private Handler mGeofenceHandler = new Handler() {
    533         @Override
    534         public void handleMessage(Message msg) {
    535             int geofenceId;
    536             int status;
    537             IGeofenceHardwareCallback callback;
    538             switch (msg.what) {
    539                 case ADD_GEOFENCE_CALLBACK:
    540                     geofenceId = msg.arg1;
    541                     synchronized (mGeofences) {
    542                         callback = mGeofences.get(geofenceId);
    543                     }
    544 
    545                     if (callback != null) {
    546                         try {
    547                             callback.onGeofenceAdd(geofenceId, msg.arg2);
    548                         } catch (RemoteException e) {Log.i(TAG, "Remote Exception:" + e);}
    549                     }
    550                     releaseWakeLock();
    551                     break;
    552                 case REMOVE_GEOFENCE_CALLBACK:
    553                     geofenceId = msg.arg1;
    554                     synchronized (mGeofences) {
    555                         callback = mGeofences.get(geofenceId);
    556                     }
    557 
    558                     if (callback != null) {
    559                         try {
    560                             callback.onGeofenceRemove(geofenceId, msg.arg2);
    561                         } catch (RemoteException e) {}
    562                         synchronized (mGeofences) {
    563                             mGeofences.remove(geofenceId);
    564                         }
    565                     }
    566                     releaseWakeLock();
    567                     break;
    568 
    569                 case PAUSE_GEOFENCE_CALLBACK:
    570                     geofenceId = msg.arg1;
    571                     synchronized (mGeofences) {
    572                         callback = mGeofences.get(geofenceId);
    573                     }
    574 
    575                     if (callback != null) {
    576                         try {
    577                             callback.onGeofencePause(geofenceId, msg.arg2);
    578                         } catch (RemoteException e) {}
    579                     }
    580                     releaseWakeLock();
    581                     break;
    582 
    583                 case RESUME_GEOFENCE_CALLBACK:
    584                     geofenceId = msg.arg1;
    585                     synchronized (mGeofences) {
    586                         callback = mGeofences.get(geofenceId);
    587                     }
    588 
    589                     if (callback != null) {
    590                         try {
    591                             callback.onGeofenceResume(geofenceId, msg.arg2);
    592                         } catch (RemoteException e) {}
    593                     }
    594                     releaseWakeLock();
    595                     break;
    596 
    597                 case GEOFENCE_TRANSITION_CALLBACK:
    598                     GeofenceTransition geofenceTransition = (GeofenceTransition)(msg.obj);
    599                     synchronized (mGeofences) {
    600                         callback = mGeofences.get(geofenceTransition.mGeofenceId);
    601 
    602                         // need to keep access to mGeofences synchronized at all times
    603                         if (DEBUG) Log.d(TAG, "GeofenceTransistionCallback: GPS : GeofenceId: " +
    604                                 geofenceTransition.mGeofenceId +
    605                                 " Transition: " + geofenceTransition.mTransition +
    606                                 " Location: " + geofenceTransition.mLocation + ":" + mGeofences);
    607                     }
    608 
    609                     if (callback != null) {
    610                         try {
    611                             callback.onGeofenceTransition(
    612                                     geofenceTransition.mGeofenceId, geofenceTransition.mTransition,
    613                                     geofenceTransition.mLocation, geofenceTransition.mTimestamp,
    614                                     geofenceTransition.mMonitoringType);
    615                         } catch (RemoteException e) {}
    616                     }
    617                     releaseWakeLock();
    618                     break;
    619                 case GEOFENCE_CALLBACK_BINDER_DIED:
    620                    // Find all geofences associated with this callback and remove them.
    621                    callback = (IGeofenceHardwareCallback) (msg.obj);
    622                    if (DEBUG) Log.d(TAG, "Geofence callback reaped:" + callback);
    623                    int monitoringType = msg.arg1;
    624                    synchronized (mGeofences) {
    625                        for (int i = 0; i < mGeofences.size(); i++) {
    626                             if (mGeofences.valueAt(i).equals(callback)) {
    627                                 geofenceId = mGeofences.keyAt(i);
    628                                 removeGeofence(mGeofences.keyAt(i), monitoringType);
    629                                 mGeofences.remove(geofenceId);
    630                             }
    631                        }
    632                    }
    633             }
    634         }
    635     };
    636 
    637     // All operations on mCallbacks
    638     private Handler mCallbacksHandler = new Handler() {
    639         @Override
    640         public void handleMessage(Message msg) {
    641             int monitoringType;
    642             ArrayList<IGeofenceHardwareMonitorCallback> callbackList;
    643             IGeofenceHardwareMonitorCallback callback;
    644 
    645             switch (msg.what) {
    646                 case GEOFENCE_STATUS:
    647                     Location location = (Location) msg.obj;
    648                     int val = msg.arg1;
    649                     monitoringType = msg.arg2;
    650                     boolean available;
    651                     available = (val == GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE ?
    652                             true : false);
    653                     callbackList = mCallbacks[monitoringType];
    654                     if (callbackList != null) {
    655                         if (DEBUG) Log.d(TAG, "MonitoringSystemChangeCallback: GPS : " + available);
    656 
    657                         for (IGeofenceHardwareMonitorCallback c: callbackList) {
    658                             try {
    659                                 c.onMonitoringSystemChange(monitoringType, available, location);
    660                             } catch (RemoteException e) {}
    661                         }
    662                     }
    663                     releaseWakeLock();
    664                     break;
    665                 case CALLBACK_ADD:
    666                     monitoringType = msg.arg1;
    667                     callback = (IGeofenceHardwareMonitorCallback) msg.obj;
    668                     callbackList = mCallbacks[monitoringType];
    669                     if (callbackList == null) {
    670                         callbackList = new ArrayList<IGeofenceHardwareMonitorCallback>();
    671                         mCallbacks[monitoringType] = callbackList;
    672                     }
    673                     if (!callbackList.contains(callback)) callbackList.add(callback);
    674                     break;
    675                 case CALLBACK_REMOVE:
    676                     monitoringType = msg.arg1;
    677                     callback = (IGeofenceHardwareMonitorCallback) msg.obj;
    678                     callbackList = mCallbacks[monitoringType];
    679                     if (callbackList != null) {
    680                         callbackList.remove(callback);
    681                     }
    682                     break;
    683                 case MONITOR_CALLBACK_BINDER_DIED:
    684                     callback = (IGeofenceHardwareMonitorCallback) msg.obj;
    685                     if (DEBUG) Log.d(TAG, "Monitor callback reaped:" + callback);
    686                     callbackList = mCallbacks[msg.arg1];
    687                     if (callbackList != null && callbackList.contains(callback)) {
    688                         callbackList.remove(callback);
    689                     }
    690             }
    691         }
    692     };
    693 
    694     // All operations on mReaper
    695     private Handler mReaperHandler = new Handler() {
    696         @Override
    697         public void handleMessage(Message msg) {
    698             Reaper r;
    699             IGeofenceHardwareCallback callback;
    700             IGeofenceHardwareMonitorCallback monitorCallback;
    701             int monitoringType;
    702 
    703             switch (msg.what) {
    704                 case REAPER_GEOFENCE_ADDED:
    705                     callback = (IGeofenceHardwareCallback) msg.obj;
    706                     monitoringType = msg.arg1;
    707                     r = new Reaper(callback, monitoringType);
    708                     if (!mReapers.contains(r)) {
    709                         mReapers.add(r);
    710                         IBinder b = callback.asBinder();
    711                         try {
    712                             b.linkToDeath(r, 0);
    713                         } catch (RemoteException e) {}
    714                     }
    715                     break;
    716                 case REAPER_MONITOR_CALLBACK_ADDED:
    717                     monitorCallback = (IGeofenceHardwareMonitorCallback) msg.obj;
    718                     monitoringType = msg.arg1;
    719 
    720                     r = new Reaper(monitorCallback, monitoringType);
    721                     if (!mReapers.contains(r)) {
    722                         mReapers.add(r);
    723                         IBinder b = monitorCallback.asBinder();
    724                         try {
    725                             b.linkToDeath(r, 0);
    726                         } catch (RemoteException e) {}
    727                     }
    728                     break;
    729                 case REAPER_REMOVED:
    730                     r = (Reaper) msg.obj;
    731                     mReapers.remove(r);
    732             }
    733         }
    734     };
    735 
    736     private class GeofenceTransition {
    737         private int mGeofenceId, mTransition;
    738         private long mTimestamp;
    739         private Location mLocation;
    740         private int mMonitoringType;
    741         private int mSourcesUsed;
    742 
    743         GeofenceTransition(
    744                 int geofenceId,
    745                 int transition,
    746                 long timestamp,
    747                 Location location,
    748                 int monitoringType,
    749                 int sourcesUsed) {
    750             mGeofenceId = geofenceId;
    751             mTransition = transition;
    752             mTimestamp = timestamp;
    753             mLocation = location;
    754             mMonitoringType = monitoringType;
    755             mSourcesUsed = sourcesUsed;
    756         }
    757     }
    758 
    759     private void setMonitorAvailability(int monitor, int val) {
    760         synchronized (mSupportedMonitorTypes) {
    761             mSupportedMonitorTypes[monitor] = val;
    762         }
    763     }
    764 
    765 
    766     int getMonitoringResolutionLevel(int monitoringType) {
    767         switch (monitoringType) {
    768             case GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE:
    769                 return RESOLUTION_LEVEL_FINE;
    770             case GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE:
    771                 return RESOLUTION_LEVEL_FINE;
    772         }
    773         return RESOLUTION_LEVEL_NONE;
    774     }
    775 
    776     class Reaper implements IBinder.DeathRecipient {
    777         private IGeofenceHardwareMonitorCallback mMonitorCallback;
    778         private IGeofenceHardwareCallback mCallback;
    779         private int mMonitoringType;
    780 
    781         Reaper(IGeofenceHardwareCallback c, int monitoringType) {
    782             mCallback = c;
    783             mMonitoringType = monitoringType;
    784         }
    785 
    786         Reaper(IGeofenceHardwareMonitorCallback c, int monitoringType) {
    787             mMonitorCallback = c;
    788             mMonitoringType = monitoringType;
    789         }
    790 
    791         @Override
    792         public void binderDied() {
    793             Message m;
    794             if (mCallback != null) {
    795                 m = mGeofenceHandler.obtainMessage(GEOFENCE_CALLBACK_BINDER_DIED, mCallback);
    796                 m.arg1 = mMonitoringType;
    797                 mGeofenceHandler.sendMessage(m);
    798             } else if (mMonitorCallback != null) {
    799                 m = mCallbacksHandler.obtainMessage(MONITOR_CALLBACK_BINDER_DIED, mMonitorCallback);
    800                 m.arg1 = mMonitoringType;
    801                 mCallbacksHandler.sendMessage(m);
    802             }
    803             Message reaperMessage = mReaperHandler.obtainMessage(REAPER_REMOVED, this);
    804             mReaperHandler.sendMessage(reaperMessage);
    805         }
    806 
    807         @Override
    808         public int hashCode() {
    809             int result = 17;
    810             result = 31 * result + (mCallback != null ? mCallback.hashCode() : 0);
    811             result = 31 * result + (mMonitorCallback != null ? mMonitorCallback.hashCode() : 0);
    812             result = 31 * result + mMonitoringType;
    813             return result;
    814         }
    815 
    816         @Override
    817         public boolean equals(Object obj) {
    818             if (obj == null) return false;
    819             if (obj == this) return true;
    820 
    821             Reaper rhs = (Reaper) obj;
    822             return rhs.mCallback == mCallback && rhs.mMonitorCallback == mMonitorCallback &&
    823                     rhs.mMonitoringType == mMonitoringType;
    824         }
    825     }
    826 
    827     int getAllowedResolutionLevel(int pid, int uid) {
    828         if (mContext.checkPermission(android.Manifest.permission.ACCESS_FINE_LOCATION,
    829                 pid, uid) == PackageManager.PERMISSION_GRANTED) {
    830             return RESOLUTION_LEVEL_FINE;
    831         } else if (mContext.checkPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION,
    832                 pid, uid) == PackageManager.PERMISSION_GRANTED) {
    833             return RESOLUTION_LEVEL_COARSE;
    834         } else {
    835             return RESOLUTION_LEVEL_NONE;
    836         }
    837     }
    838 }
    839