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