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