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