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