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