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.Message; 27 import android.os.PowerManager; 28 import android.os.RemoteException; 29 import android.util.Log; 30 import android.util.SparseArray; 31 32 import java.util.ArrayList; 33 34 /** 35 * This class manages the geofences which are handled by hardware. 36 * 37 * @hide 38 */ 39 public 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.isSupported(); 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(int geofenceId, int monitoringType, double latitude, 214 double longitude, double radius, int lastTransition,int monitorTransitions, 215 int notificationResponsivenes, int unknownTimer, IGeofenceHardwareCallback callback) { 216 // This API is not thread safe. Operations on the same geofence need to be serialized 217 // by upper layers 218 if (DEBUG) { 219 Log.d(TAG, "addCircularFence: GeofenceId: " + geofenceId + " Latitude: " + latitude + 220 " Longitude: " + longitude + " Radius: " + radius + " LastTransition: " 221 + lastTransition + " MonitorTransition: " + monitorTransitions + 222 " NotificationResponsiveness: " + notificationResponsivenes + 223 " UnKnown Timer: " + unknownTimer + " MonitoringType: " + monitoringType); 224 225 } 226 boolean result; 227 228 // The callback must be added before addCircularHardwareGeofence is called otherwise the 229 // callback might not be called after the geofence is added in the geofence hardware. 230 // This also means that the callback must be removed if the addCircularHardwareGeofence 231 // operations is not called or fails. 232 synchronized (mGeofences) { 233 mGeofences.put(geofenceId, callback); 234 } 235 236 switch (monitoringType) { 237 case GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE: 238 if (mGpsService == null) return false; 239 try { 240 result = mGpsService.addCircularHardwareGeofence(geofenceId, latitude, 241 longitude, radius, lastTransition, monitorTransitions, 242 notificationResponsivenes, unknownTimer); 243 } catch (RemoteException e) { 244 Log.e(TAG, "AddGeofence: Remote Exception calling LocationManagerService"); 245 result = false; 246 } 247 break; 248 case GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE: 249 if(mFusedService == null) { 250 return false; 251 } 252 GeofenceHardwareRequest request = GeofenceHardwareRequest.createCircularGeofence( 253 latitude, 254 longitude, 255 radius); 256 request.setUnknownTimer(unknownTimer); 257 request.setNotificationResponsiveness(notificationResponsivenes); 258 request.setMonitorTransitions(monitorTransitions); 259 request.setLastTransition(lastTransition); 260 261 GeofenceHardwareRequestParcelable parcelableRequest = 262 new GeofenceHardwareRequestParcelable(geofenceId, request); 263 try { 264 mFusedService.addGeofences( 265 new GeofenceHardwareRequestParcelable[] { parcelableRequest }); 266 result = true; 267 } catch(RemoteException e) { 268 Log.e(TAG, "AddGeofence: RemoteException calling LocationManagerService"); 269 result = false; 270 } 271 break; 272 default: 273 result = false; 274 } 275 if (result) { 276 Message m = mReaperHandler.obtainMessage(REAPER_GEOFENCE_ADDED, callback); 277 m.arg1 = monitoringType; 278 mReaperHandler.sendMessage(m); 279 } else { 280 synchronized (mGeofences) { 281 mGeofences.remove(geofenceId); 282 } 283 } 284 285 if (DEBUG) Log.d(TAG, "addCircularFence: Result is: " + result); 286 return result; 287 } 288 289 public boolean removeGeofence(int geofenceId, int monitoringType) { 290 // This API is not thread safe. Operations on the same geofence need to be serialized 291 // by upper layers 292 if (DEBUG) Log.d(TAG, "Remove Geofence: GeofenceId: " + geofenceId); 293 boolean result = false; 294 295 synchronized (mGeofences) { 296 if (mGeofences.get(geofenceId) == null) { 297 throw new IllegalArgumentException("Geofence " + geofenceId + " not registered."); 298 } 299 } 300 switch (monitoringType) { 301 case GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE: 302 if (mGpsService == null) return false; 303 try { 304 result = mGpsService.removeHardwareGeofence(geofenceId); 305 } catch (RemoteException e) { 306 Log.e(TAG, "RemoveGeofence: Remote Exception calling LocationManagerService"); 307 result = false; 308 } 309 break; 310 case GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE: 311 if(mFusedService == null) { 312 return false; 313 } 314 try { 315 mFusedService.removeGeofences(new int[] { geofenceId }); 316 result = true; 317 } catch(RemoteException e) { 318 Log.e(TAG, "RemoveGeofence: RemoteException calling LocationManagerService"); 319 result = false; 320 } 321 break; 322 default: 323 result = false; 324 } 325 if (DEBUG) Log.d(TAG, "removeGeofence: Result is: " + result); 326 return result; 327 } 328 329 public boolean pauseGeofence(int geofenceId, int monitoringType) { 330 // This API is not thread safe. Operations on the same geofence need to be serialized 331 // by upper layers 332 if (DEBUG) Log.d(TAG, "Pause Geofence: GeofenceId: " + geofenceId); 333 boolean result; 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.pauseHardwareGeofence(geofenceId); 344 } catch (RemoteException e) { 345 Log.e(TAG, "PauseGeofence: 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.pauseMonitoringGeofence(geofenceId); 355 result = true; 356 } catch(RemoteException e) { 357 Log.e(TAG, "PauseGeofence: RemoteException calling LocationManagerService"); 358 result = false; 359 } 360 break; 361 default: 362 result = false; 363 } 364 if (DEBUG) Log.d(TAG, "pauseGeofence: Result is: " + result); 365 return result; 366 } 367 368 369 public boolean resumeGeofence(int geofenceId, int monitoringType, int monitorTransition) { 370 // This API is not thread safe. Operations on the same geofence need to be serialized 371 // by upper layers 372 if (DEBUG) Log.d(TAG, "Resume Geofence: GeofenceId: " + geofenceId); 373 boolean result; 374 synchronized (mGeofences) { 375 if (mGeofences.get(geofenceId) == null) { 376 throw new IllegalArgumentException("Geofence " + geofenceId + " not registered."); 377 } 378 } 379 switch (monitoringType) { 380 case GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE: 381 if (mGpsService == null) return false; 382 try { 383 result = mGpsService.resumeHardwareGeofence(geofenceId, monitorTransition); 384 } catch (RemoteException e) { 385 Log.e(TAG, "ResumeGeofence: Remote Exception calling LocationManagerService"); 386 result = false; 387 } 388 break; 389 case GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE: 390 if(mFusedService == null) { 391 return false; 392 } 393 try { 394 mFusedService.resumeMonitoringGeofence(geofenceId, monitorTransition); 395 result = true; 396 } catch(RemoteException e) { 397 Log.e(TAG, "ResumeGeofence: RemoteException calling LocationManagerService"); 398 result = false; 399 } 400 break; 401 default: 402 result = false; 403 } 404 if (DEBUG) Log.d(TAG, "resumeGeofence: Result is: " + result); 405 return result; 406 } 407 408 public boolean registerForMonitorStateChangeCallback(int monitoringType, 409 IGeofenceHardwareMonitorCallback callback) { 410 Message reaperMessage = 411 mReaperHandler.obtainMessage(REAPER_MONITOR_CALLBACK_ADDED, callback); 412 reaperMessage.arg1 = monitoringType; 413 mReaperHandler.sendMessage(reaperMessage); 414 415 Message m = mCallbacksHandler.obtainMessage(CALLBACK_ADD, callback); 416 m.arg1 = monitoringType; 417 mCallbacksHandler.sendMessage(m); 418 return true; 419 } 420 421 public boolean unregisterForMonitorStateChangeCallback(int monitoringType, 422 IGeofenceHardwareMonitorCallback callback) { 423 Message m = mCallbacksHandler.obtainMessage(CALLBACK_REMOVE, callback); 424 m.arg1 = monitoringType; 425 mCallbacksHandler.sendMessage(m); 426 return true; 427 } 428 429 /** 430 * Used to report geofence transitions 431 */ 432 public void reportGeofenceTransition( 433 int geofenceId, 434 Location location, 435 int transition, 436 long transitionTimestamp, 437 int monitoringType, 438 int sourcesUsed) { 439 if(location == null) { 440 Log.e(TAG, String.format("Invalid Geofence Transition: location=%p", location)); 441 return; 442 } 443 if(DEBUG) { 444 Log.d( 445 TAG, 446 "GeofenceTransition| " + location + ", transition:" + transition + 447 ", transitionTimestamp:" + transitionTimestamp + ", monitoringType:" + 448 monitoringType + ", sourcesUsed:" + sourcesUsed); 449 } 450 451 GeofenceTransition geofenceTransition = new GeofenceTransition( 452 geofenceId, 453 transition, 454 transitionTimestamp, 455 location, 456 monitoringType, 457 sourcesUsed); 458 acquireWakeLock(); 459 460 Message message = mGeofenceHandler.obtainMessage( 461 GEOFENCE_TRANSITION_CALLBACK, 462 geofenceTransition); 463 message.sendToTarget(); 464 } 465 466 /** 467 * Used to report Monitor status changes. 468 */ 469 public void reportGeofenceMonitorStatus( 470 int monitoringType, 471 int monitoringStatus, 472 Location location, 473 int source) { 474 // TODO: use the source if needed in the future 475 setMonitorAvailability(monitoringType, monitoringStatus); 476 acquireWakeLock(); 477 Message message = mCallbacksHandler.obtainMessage(GEOFENCE_STATUS, location); 478 message.arg1 = monitoringStatus; 479 message.arg2 = monitoringType; 480 message.sendToTarget(); 481 } 482 483 /** 484 * Internal generic status report function for Geofence operations. 485 * 486 * @param operation The operation to be reported as defined internally. 487 * @param geofenceId The id of the geofence the operation is related to. 488 * @param operationStatus The status of the operation as defined in GeofenceHardware class. This 489 * status is independent of the statuses reported by different HALs. 490 */ 491 private void reportGeofenceOperationStatus(int operation, int geofenceId, int operationStatus) { 492 acquireWakeLock(); 493 Message message = mGeofenceHandler.obtainMessage(operation); 494 message.arg1 = geofenceId; 495 message.arg2 = operationStatus; 496 message.sendToTarget(); 497 } 498 499 /** 500 * Used to report the status of a Geofence Add operation. 501 */ 502 public void reportGeofenceAddStatus(int geofenceId, int status) { 503 if(DEBUG) Log.d(TAG, "AddCallback| id:" + geofenceId + ", status:" + status); 504 reportGeofenceOperationStatus(ADD_GEOFENCE_CALLBACK, geofenceId, status); 505 } 506 507 /** 508 * Used to report the status of a Geofence Remove operation. 509 */ 510 public void reportGeofenceRemoveStatus(int geofenceId, int status) { 511 if(DEBUG) Log.d(TAG, "RemoveCallback| id:" + geofenceId + ", status:" + status); 512 reportGeofenceOperationStatus(REMOVE_GEOFENCE_CALLBACK, geofenceId, status); 513 } 514 515 /** 516 * Used to report the status of a Geofence Pause operation. 517 */ 518 public void reportGeofencePauseStatus(int geofenceId, int status) { 519 if(DEBUG) Log.d(TAG, "PauseCallbac| id:" + geofenceId + ", status" + status); 520 reportGeofenceOperationStatus(PAUSE_GEOFENCE_CALLBACK, geofenceId, status); 521 } 522 523 /** 524 * Used to report the status of a Geofence Resume operation. 525 */ 526 public void reportGeofenceResumeStatus(int geofenceId, int status) { 527 if(DEBUG) Log.d(TAG, "ResumeCallback| id:" + geofenceId + ", status:" + status); 528 reportGeofenceOperationStatus(RESUME_GEOFENCE_CALLBACK, geofenceId, status); 529 } 530 531 // All operations on mGeofences 532 private Handler mGeofenceHandler = new Handler() { 533 @Override 534 public void handleMessage(Message msg) { 535 int geofenceId; 536 int status; 537 IGeofenceHardwareCallback callback; 538 switch (msg.what) { 539 case ADD_GEOFENCE_CALLBACK: 540 geofenceId = msg.arg1; 541 synchronized (mGeofences) { 542 callback = mGeofences.get(geofenceId); 543 } 544 545 if (callback != null) { 546 try { 547 callback.onGeofenceAdd(geofenceId, msg.arg2); 548 } catch (RemoteException e) {Log.i(TAG, "Remote Exception:" + e);} 549 } 550 releaseWakeLock(); 551 break; 552 case REMOVE_GEOFENCE_CALLBACK: 553 geofenceId = msg.arg1; 554 synchronized (mGeofences) { 555 callback = mGeofences.get(geofenceId); 556 } 557 558 if (callback != null) { 559 try { 560 callback.onGeofenceRemove(geofenceId, msg.arg2); 561 } catch (RemoteException e) {} 562 synchronized (mGeofences) { 563 mGeofences.remove(geofenceId); 564 } 565 } 566 releaseWakeLock(); 567 break; 568 569 case PAUSE_GEOFENCE_CALLBACK: 570 geofenceId = msg.arg1; 571 synchronized (mGeofences) { 572 callback = mGeofences.get(geofenceId); 573 } 574 575 if (callback != null) { 576 try { 577 callback.onGeofencePause(geofenceId, msg.arg2); 578 } catch (RemoteException e) {} 579 } 580 releaseWakeLock(); 581 break; 582 583 case RESUME_GEOFENCE_CALLBACK: 584 geofenceId = msg.arg1; 585 synchronized (mGeofences) { 586 callback = mGeofences.get(geofenceId); 587 } 588 589 if (callback != null) { 590 try { 591 callback.onGeofenceResume(geofenceId, msg.arg2); 592 } catch (RemoteException e) {} 593 } 594 releaseWakeLock(); 595 break; 596 597 case GEOFENCE_TRANSITION_CALLBACK: 598 GeofenceTransition geofenceTransition = (GeofenceTransition)(msg.obj); 599 synchronized (mGeofences) { 600 callback = mGeofences.get(geofenceTransition.mGeofenceId); 601 602 // need to keep access to mGeofences synchronized at all times 603 if (DEBUG) Log.d(TAG, "GeofenceTransistionCallback: GPS : GeofenceId: " + 604 geofenceTransition.mGeofenceId + 605 " Transition: " + geofenceTransition.mTransition + 606 " Location: " + geofenceTransition.mLocation + ":" + mGeofences); 607 } 608 609 if (callback != null) { 610 try { 611 callback.onGeofenceTransition( 612 geofenceTransition.mGeofenceId, geofenceTransition.mTransition, 613 geofenceTransition.mLocation, geofenceTransition.mTimestamp, 614 geofenceTransition.mMonitoringType); 615 } catch (RemoteException e) {} 616 } 617 releaseWakeLock(); 618 break; 619 case GEOFENCE_CALLBACK_BINDER_DIED: 620 // Find all geofences associated with this callback and remove them. 621 callback = (IGeofenceHardwareCallback) (msg.obj); 622 if (DEBUG) Log.d(TAG, "Geofence callback reaped:" + callback); 623 int monitoringType = msg.arg1; 624 synchronized (mGeofences) { 625 for (int i = 0; i < mGeofences.size(); i++) { 626 if (mGeofences.valueAt(i).equals(callback)) { 627 geofenceId = mGeofences.keyAt(i); 628 removeGeofence(mGeofences.keyAt(i), monitoringType); 629 mGeofences.remove(geofenceId); 630 } 631 } 632 } 633 } 634 } 635 }; 636 637 // All operations on mCallbacks 638 private Handler mCallbacksHandler = new Handler() { 639 @Override 640 public void handleMessage(Message msg) { 641 int monitoringType; 642 ArrayList<IGeofenceHardwareMonitorCallback> callbackList; 643 IGeofenceHardwareMonitorCallback callback; 644 645 switch (msg.what) { 646 case GEOFENCE_STATUS: 647 Location location = (Location) msg.obj; 648 int val = msg.arg1; 649 monitoringType = msg.arg2; 650 boolean available; 651 available = (val == GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE ? 652 true : false); 653 callbackList = mCallbacks[monitoringType]; 654 if (callbackList != null) { 655 if (DEBUG) Log.d(TAG, "MonitoringSystemChangeCallback: GPS : " + available); 656 657 for (IGeofenceHardwareMonitorCallback c: callbackList) { 658 try { 659 c.onMonitoringSystemChange(monitoringType, available, location); 660 } catch (RemoteException e) {} 661 } 662 } 663 releaseWakeLock(); 664 break; 665 case CALLBACK_ADD: 666 monitoringType = msg.arg1; 667 callback = (IGeofenceHardwareMonitorCallback) msg.obj; 668 callbackList = mCallbacks[monitoringType]; 669 if (callbackList == null) { 670 callbackList = new ArrayList<IGeofenceHardwareMonitorCallback>(); 671 mCallbacks[monitoringType] = callbackList; 672 } 673 if (!callbackList.contains(callback)) callbackList.add(callback); 674 break; 675 case CALLBACK_REMOVE: 676 monitoringType = msg.arg1; 677 callback = (IGeofenceHardwareMonitorCallback) msg.obj; 678 callbackList = mCallbacks[monitoringType]; 679 if (callbackList != null) { 680 callbackList.remove(callback); 681 } 682 break; 683 case MONITOR_CALLBACK_BINDER_DIED: 684 callback = (IGeofenceHardwareMonitorCallback) msg.obj; 685 if (DEBUG) Log.d(TAG, "Monitor callback reaped:" + callback); 686 callbackList = mCallbacks[msg.arg1]; 687 if (callbackList != null && callbackList.contains(callback)) { 688 callbackList.remove(callback); 689 } 690 } 691 } 692 }; 693 694 // All operations on mReaper 695 private Handler mReaperHandler = new Handler() { 696 @Override 697 public void handleMessage(Message msg) { 698 Reaper r; 699 IGeofenceHardwareCallback callback; 700 IGeofenceHardwareMonitorCallback monitorCallback; 701 int monitoringType; 702 703 switch (msg.what) { 704 case REAPER_GEOFENCE_ADDED: 705 callback = (IGeofenceHardwareCallback) msg.obj; 706 monitoringType = msg.arg1; 707 r = new Reaper(callback, monitoringType); 708 if (!mReapers.contains(r)) { 709 mReapers.add(r); 710 IBinder b = callback.asBinder(); 711 try { 712 b.linkToDeath(r, 0); 713 } catch (RemoteException e) {} 714 } 715 break; 716 case REAPER_MONITOR_CALLBACK_ADDED: 717 monitorCallback = (IGeofenceHardwareMonitorCallback) msg.obj; 718 monitoringType = msg.arg1; 719 720 r = new Reaper(monitorCallback, monitoringType); 721 if (!mReapers.contains(r)) { 722 mReapers.add(r); 723 IBinder b = monitorCallback.asBinder(); 724 try { 725 b.linkToDeath(r, 0); 726 } catch (RemoteException e) {} 727 } 728 break; 729 case REAPER_REMOVED: 730 r = (Reaper) msg.obj; 731 mReapers.remove(r); 732 } 733 } 734 }; 735 736 private class GeofenceTransition { 737 private int mGeofenceId, mTransition; 738 private long mTimestamp; 739 private Location mLocation; 740 private int mMonitoringType; 741 private int mSourcesUsed; 742 743 GeofenceTransition( 744 int geofenceId, 745 int transition, 746 long timestamp, 747 Location location, 748 int monitoringType, 749 int sourcesUsed) { 750 mGeofenceId = geofenceId; 751 mTransition = transition; 752 mTimestamp = timestamp; 753 mLocation = location; 754 mMonitoringType = monitoringType; 755 mSourcesUsed = sourcesUsed; 756 } 757 } 758 759 private void setMonitorAvailability(int monitor, int val) { 760 synchronized (mSupportedMonitorTypes) { 761 mSupportedMonitorTypes[monitor] = val; 762 } 763 } 764 765 766 int getMonitoringResolutionLevel(int monitoringType) { 767 switch (monitoringType) { 768 case GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE: 769 return RESOLUTION_LEVEL_FINE; 770 case GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE: 771 return RESOLUTION_LEVEL_FINE; 772 } 773 return RESOLUTION_LEVEL_NONE; 774 } 775 776 class Reaper implements IBinder.DeathRecipient { 777 private IGeofenceHardwareMonitorCallback mMonitorCallback; 778 private IGeofenceHardwareCallback mCallback; 779 private int mMonitoringType; 780 781 Reaper(IGeofenceHardwareCallback c, int monitoringType) { 782 mCallback = c; 783 mMonitoringType = monitoringType; 784 } 785 786 Reaper(IGeofenceHardwareMonitorCallback c, int monitoringType) { 787 mMonitorCallback = c; 788 mMonitoringType = monitoringType; 789 } 790 791 @Override 792 public void binderDied() { 793 Message m; 794 if (mCallback != null) { 795 m = mGeofenceHandler.obtainMessage(GEOFENCE_CALLBACK_BINDER_DIED, mCallback); 796 m.arg1 = mMonitoringType; 797 mGeofenceHandler.sendMessage(m); 798 } else if (mMonitorCallback != null) { 799 m = mCallbacksHandler.obtainMessage(MONITOR_CALLBACK_BINDER_DIED, mMonitorCallback); 800 m.arg1 = mMonitoringType; 801 mCallbacksHandler.sendMessage(m); 802 } 803 Message reaperMessage = mReaperHandler.obtainMessage(REAPER_REMOVED, this); 804 mReaperHandler.sendMessage(reaperMessage); 805 } 806 807 @Override 808 public int hashCode() { 809 int result = 17; 810 result = 31 * result + (mCallback != null ? mCallback.hashCode() : 0); 811 result = 31 * result + (mMonitorCallback != null ? mMonitorCallback.hashCode() : 0); 812 result = 31 * result + mMonitoringType; 813 return result; 814 } 815 816 @Override 817 public boolean equals(Object obj) { 818 if (obj == null) return false; 819 if (obj == this) return true; 820 821 Reaper rhs = (Reaper) obj; 822 return rhs.mCallback == mCallback && rhs.mMonitorCallback == mMonitorCallback && 823 rhs.mMonitoringType == mMonitoringType; 824 } 825 } 826 827 int getAllowedResolutionLevel(int pid, int uid) { 828 if (mContext.checkPermission(android.Manifest.permission.ACCESS_FINE_LOCATION, 829 pid, uid) == PackageManager.PERMISSION_GRANTED) { 830 return RESOLUTION_LEVEL_FINE; 831 } else if (mContext.checkPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION, 832 pid, uid) == PackageManager.PERMISSION_GRANTED) { 833 return RESOLUTION_LEVEL_COARSE; 834 } else { 835 return RESOLUTION_LEVEL_NONE; 836 } 837 } 838 } 839