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