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