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 98 public synchronized static GeofenceHardwareImpl getInstance(Context context) { 99 if (sInstance == null) { 100 sInstance = new GeofenceHardwareImpl(context); 101 } 102 return sInstance; 103 } 104 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 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 125 private void releaseWakeLock() { 126 if (mWakeLock.isHeld()) mWakeLock.release(); 127 } 128 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 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 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 179 public void onCapabilities(int capabilities) { 180 mCapabilities = capabilities; 181 updateFusedHardwareAvailability(); 182 } 183 184 public void setVersion(int version) { 185 mVersion = version; 186 updateFusedHardwareAvailability(); 187 } 188 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 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 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 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 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 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 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 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 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 822 private void setMonitorAvailability(int monitor, int val) { 823 synchronized (mSupportedMonitorTypes) { 824 mSupportedMonitorTypes[monitor] = val; 825 } 826 } 827 828 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 844 Reaper(IGeofenceHardwareCallback c, int monitoringType) { 845 mCallback = c; 846 mMonitoringType = monitoringType; 847 } 848 849 Reaper(IGeofenceHardwareMonitorCallback c, int monitoringType) { 850 mMonitorCallback = c; 851 mMonitoringType = monitoringType; 852 } 853 854 @Override 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 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 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 */ 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 */ 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 915 private boolean callbackEquals(IGeofenceHardwareCallback cb) { 916 return mCallback != null && mCallback.asBinder() == cb.asBinder(); 917 } 918 } 919 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