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