GeofenceHardwareImpl.java revision 8ce470dd4ba0608abb6b5eae117cefca927af96b
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.IGpsGeofenceHardware; 22import android.location.Location; 23import android.location.LocationManager; 24import android.os.Bundle; 25import android.os.Handler; 26import android.os.Message; 27import android.os.PowerManager; 28import android.os.RemoteException; 29import android.os.ServiceManager; 30import android.os.SystemClock; 31import android.util.Log; 32import android.util.SparseArray; 33 34import java.util.ArrayList; 35import java.util.HashMap; 36 37/** 38 * This class manages the geofences which are handled by hardware. 39 * 40 * @hide 41 */ 42public final class GeofenceHardwareImpl { 43 private static final String TAG = "GeofenceHardwareImpl"; 44 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 45 46 private final Context mContext; 47 private static GeofenceHardwareImpl sInstance; 48 private PowerManager.WakeLock mWakeLock; 49 private SparseArray<IGeofenceHardwareCallback> mGeofences = 50 new SparseArray<IGeofenceHardwareCallback>(); 51 private ArrayList<IGeofenceHardwareCallback>[] mCallbacks = 52 new ArrayList[GeofenceHardware.NUM_MONITORS]; 53 54 private IGpsGeofenceHardware mGpsService; 55 56 private int[] mSupportedMonitorTypes = new int[GeofenceHardware.NUM_MONITORS]; 57 58 // mGeofenceHandler message types 59 private static final int GEOFENCE_TRANSITION_CALLBACK = 1; 60 private static final int ADD_GEOFENCE_CALLBACK = 2; 61 private static final int REMOVE_GEOFENCE_CALLBACK = 3; 62 private static final int PAUSE_GEOFENCE_CALLBACK = 4; 63 private static final int RESUME_GEOFENCE_CALLBACK = 5; 64 private static final int ADD_GEOFENCE = 6; 65 private static final int REMOVE_GEOFENCE = 7; 66 67 // mCallbacksHandler message types 68 private static final int GPS_GEOFENCE_STATUS = 1; 69 private static final int CALLBACK_ADD = 2; 70 private static final int CALLBACK_REMOVE = 3; 71 72 // The following constants need to match GpsLocationFlags enum in gps.h 73 private static final int LOCATION_INVALID = 0; 74 private static final int LOCATION_HAS_LAT_LONG = 1; 75 private static final int LOCATION_HAS_ALTITUDE = 2; 76 private static final int LOCATION_HAS_SPEED = 4; 77 private static final int LOCATION_HAS_BEARING = 8; 78 private static final int LOCATION_HAS_ACCURACY = 16; 79 80 // Resolution level constants used for permission checks. 81 // These constants must be in increasing order of finer resolution. 82 private static final int RESOLUTION_LEVEL_NONE = 1; 83 private static final int RESOLUTION_LEVEL_COARSE = 2; 84 private static final int RESOLUTION_LEVEL_FINE = 3; 85 86 // GPS Geofence errors. Should match gps.h constants. 87 private static final int GPS_GEOFENCE_OPERATION_SUCCESS = 0; 88 private static final int GPS_GEOFENCE_ERROR_TOO_MANY_GEOFENCES = 100; 89 private static final int GPS_GEOFENCE_ERROR_ID_EXISTS = -101; 90 private static final int GPS_GEOFENCE_ERROR_ID_UNKNOWN = -102; 91 private static final int GPS_GEOFENCE_ERROR_INVALID_TRANSITION = -103; 92 private static final int GPS_GEOFENCE_ERROR_GENERIC = -149; 93 94 95 96 public synchronized static GeofenceHardwareImpl getInstance(Context context) { 97 if (sInstance == null) { 98 sInstance = new GeofenceHardwareImpl(context); 99 } 100 return sInstance; 101 } 102 103 private GeofenceHardwareImpl(Context context) { 104 mContext = context; 105 // Init everything to unsupported. 106 setMonitorAvailability(GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE, 107 GeofenceHardware.MONITOR_UNSUPPORTED); 108 109 } 110 111 private void acquireWakeLock() { 112 if (mWakeLock == null) { 113 PowerManager powerManager = 114 (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); 115 mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); 116 } 117 mWakeLock.acquire(); 118 } 119 120 private void releaseWakeLock() { 121 if (mWakeLock.isHeld()) mWakeLock.release(); 122 } 123 124 private void updateGpsHardwareAvailability() { 125 //Check which monitors are available. 126 boolean gpsSupported; 127 try { 128 gpsSupported = mGpsService.isHardwareGeofenceSupported(); 129 } catch (RemoteException e) { 130 Log.e(TAG, "Remote Exception calling LocationManagerService"); 131 gpsSupported = false; 132 } 133 134 if (gpsSupported) { 135 // Its assumed currently available at startup. 136 // native layer will update later. 137 setMonitorAvailability(GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE, 138 GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE); 139 } 140 } 141 142 public void setGpsHardwareGeofence(IGpsGeofenceHardware service) { 143 if (mGpsService == null) { 144 mGpsService = service; 145 updateGpsHardwareAvailability(); 146 } else if (service == null) { 147 mGpsService = null; 148 Log.w(TAG, "GPS Geofence Hardware service seems to have crashed"); 149 } else { 150 Log.e(TAG, "Error: GpsService being set again."); 151 } 152 } 153 154 public int[] getMonitoringTypesAndStatus() { 155 synchronized (mSupportedMonitorTypes) { 156 return mSupportedMonitorTypes; 157 } 158 } 159 160 public boolean addCircularFence(int geofenceId, double latitude, double longitude, 161 double radius, int lastTransition,int monitorTransitions, int notificationResponsivenes, 162 int unknownTimer, int monitoringType, IGeofenceHardwareCallback callback) { 163 // This API is not thread safe. Operations on the same geofence need to be serialized 164 // by upper layers 165 if (DEBUG) { 166 Log.d(TAG, "addCircularFence: GeofenceId: " + geofenceId + "Latitude: " + latitude + 167 "Longitude: " + longitude + "Radius: " + radius + "LastTransition: " 168 + lastTransition + "MonitorTransition: " + monitorTransitions + 169 "NotificationResponsiveness: " + notificationResponsivenes + 170 "UnKnown Timer: " + unknownTimer + "MonitoringType: " + monitoringType); 171 172 } 173 boolean result; 174 Message m = mGeofenceHandler.obtainMessage(ADD_GEOFENCE, callback); 175 m.arg1 = geofenceId; 176 mGeofenceHandler.sendMessage(m); 177 178 switch (monitoringType) { 179 case GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE: 180 if (mGpsService == null) return false; 181 try { 182 result = mGpsService.addCircularHardwareGeofence(geofenceId, latitude, 183 longitude, radius, lastTransition, monitorTransitions, 184 notificationResponsivenes, unknownTimer); 185 } catch (RemoteException e) { 186 Log.e(TAG, "AddGeofence: Remote Exception calling LocationManagerService"); 187 result = false; 188 } 189 break; 190 default: 191 result = false; 192 } 193 if (!result) { 194 m = mGeofenceHandler.obtainMessage(REMOVE_GEOFENCE); 195 m.arg1 = geofenceId; 196 mGeofenceHandler.sendMessage(m); 197 } 198 199 if (DEBUG) Log.d(TAG, "addCircularFence: Result is: " + result); 200 return result; 201 } 202 203 public boolean removeGeofence(int geofenceId, int monitoringType) { 204 // This API is not thread safe. Operations on the same geofence need to be serialized 205 // by upper layers 206 if (DEBUG) Log.d(TAG, "Remove Geofence: GeofenceId: " + geofenceId); 207 boolean result = false; 208 switch (monitoringType) { 209 case GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE: 210 if (mGpsService == null) return false; 211 try { 212 result = mGpsService.removeHardwareGeofence(geofenceId); 213 } catch (RemoteException e) { 214 Log.e(TAG, "RemoveGeofence: Remote Exception calling LocationManagerService"); 215 result = false; 216 } 217 break; 218 default: 219 result = false; 220 } 221 if (DEBUG) Log.d(TAG, "removeGeofence: Result is: " + result); 222 return result; 223 } 224 225 public boolean pauseGeofence(int geofenceId, int monitoringType) { 226 // This API is not thread safe. Operations on the same geofence need to be serialized 227 // by upper layers 228 if (DEBUG) Log.d(TAG, "Pause Geofence: GeofenceId: " + geofenceId); 229 boolean result; 230 switch (monitoringType) { 231 case GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE: 232 if (mGpsService == null) return false; 233 try { 234 result = mGpsService.pauseHardwareGeofence(geofenceId); 235 } catch (RemoteException e) { 236 Log.e(TAG, "PauseGeofence: Remote Exception calling LocationManagerService"); 237 result = false; 238 } 239 break; 240 default: 241 result = false; 242 } 243 if (DEBUG) Log.d(TAG, "pauseGeofence: Result is: " + result); 244 return result; 245 } 246 247 248 public boolean resumeGeofence(int geofenceId, int monitorTransition, int monitoringType) { 249 // This API is not thread safe. Operations on the same geofence need to be serialized 250 // by upper layers 251 if (DEBUG) Log.d(TAG, "Resume Geofence: GeofenceId: " + geofenceId); 252 boolean result; 253 switch (monitoringType) { 254 case GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE: 255 if (mGpsService == null) return false; 256 try { 257 result = mGpsService.resumeHardwareGeofence(geofenceId, monitorTransition); 258 } catch (RemoteException e) { 259 Log.e(TAG, "ResumeGeofence: Remote Exception calling LocationManagerService"); 260 result = false; 261 } 262 break; 263 default: 264 result = false; 265 } 266 if (DEBUG) Log.d(TAG, "resumeGeofence: Result is: " + result); 267 return result; 268 } 269 270 public boolean registerForMonitorStateChangeCallback(int monitoringType, 271 IGeofenceHardwareCallback callback) { 272 Message m = mCallbacksHandler.obtainMessage(CALLBACK_ADD, callback); 273 m.arg1 = monitoringType; 274 mCallbacksHandler.sendMessage(m); 275 return true; 276 } 277 278 public boolean unregisterForMonitorStateChangeCallback(int monitoringType, 279 IGeofenceHardwareCallback callback) { 280 Message m = mCallbacksHandler.obtainMessage(CALLBACK_REMOVE, callback); 281 m.arg1 = monitoringType; 282 mCallbacksHandler.sendMessage(m); 283 return true; 284 } 285 286 private Location getLocation(int flags, double latitude, 287 double longitude, double altitude, float speed, float bearing, float accuracy, 288 long timestamp) { 289 if (DEBUG) Log.d(TAG, "GetLocation: " + flags + ":" + latitude); 290 Location location = new Location(LocationManager.GPS_PROVIDER); 291 if ((flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) { 292 location.setLatitude(latitude); 293 location.setLongitude(longitude); 294 location.setTime(timestamp); 295 // It would be nice to push the elapsed real-time timestamp 296 // further down the stack, but this is still useful 297 location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos()); 298 } 299 if ((flags & LOCATION_HAS_ALTITUDE) == LOCATION_HAS_ALTITUDE) { 300 location.setAltitude(altitude); 301 } else { 302 location.removeAltitude(); 303 } 304 if ((flags & LOCATION_HAS_SPEED) == LOCATION_HAS_SPEED) { 305 location.setSpeed(speed); 306 } else { 307 location.removeSpeed(); 308 } 309 if ((flags & LOCATION_HAS_BEARING) == LOCATION_HAS_BEARING) { 310 location.setBearing(bearing); 311 } else { 312 location.removeBearing(); 313 } 314 if ((flags & LOCATION_HAS_ACCURACY) == LOCATION_HAS_ACCURACY) { 315 location.setAccuracy(accuracy); 316 } else { 317 location.removeAccuracy(); 318 } 319 return location; 320 } 321 322 /** 323 * called from GpsLocationProvider to report geofence transition 324 */ 325 public void reportGpsGeofenceTransition(int geofenceId, int flags, double latitude, 326 double longitude, double altitude, float speed, float bearing, float accuracy, 327 long timestamp, int transition, long transitionTimestamp) { 328 if (DEBUG) Log.d(TAG, "GeofenceTransition: Flags: " + flags + " Lat: " + latitude + 329 " Long: " + longitude + " Altitude: " + altitude + " Speed: " + speed + " Bearing: " + 330 bearing + " Accuracy: " + accuracy + " Timestamp: " + timestamp + " Transition: " + 331 transition + " TransitionTimestamp: " + transitionTimestamp); 332 Location location = getLocation(flags, latitude, longitude, altitude, speed, bearing, 333 accuracy, timestamp); 334 GeofenceTransition t = new GeofenceTransition(geofenceId, transition, timestamp, location); 335 acquireWakeLock(); 336 Message m = mGeofenceHandler.obtainMessage(GEOFENCE_TRANSITION_CALLBACK, t); 337 mGeofenceHandler.sendMessage(m); 338 } 339 340 /** 341 * called from GpsLocationProvider to report GPS status change. 342 */ 343 public void reportGpsGeofenceStatus(int status, int flags, double latitude, 344 double longitude, double altitude, float speed, float bearing, float accuracy, 345 long timestamp) { 346 Location location = getLocation(flags, latitude, longitude, altitude, speed, bearing, 347 accuracy, timestamp); 348 boolean available = false; 349 if (status == GeofenceHardware.GPS_GEOFENCE_AVAILABLE) available = true; 350 351 int val = (available ? GeofenceHardware.MONITOR_CURRENTLY_UNAVAILABLE : 352 GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE); 353 setMonitorAvailability(GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE, val); 354 355 acquireWakeLock(); 356 Message m = mCallbacksHandler.obtainMessage(GPS_GEOFENCE_STATUS, location); 357 m.arg1 = val; 358 mCallbacksHandler.sendMessage(m); 359 } 360 361 /** 362 * called from GpsLocationProvider add geofence callback. 363 */ 364 public void reportGpsGeofenceAddStatus(int geofenceId, int status) { 365 if (DEBUG) Log.d(TAG, "Add Callback: GPS : Id: " + geofenceId + " Status: " + status); 366 acquireWakeLock(); 367 Message m = mGeofenceHandler.obtainMessage(ADD_GEOFENCE_CALLBACK); 368 m.arg1 = geofenceId; 369 m.arg2 = getGeofenceStatus(status); 370 mGeofenceHandler.sendMessage(m); 371 } 372 373 /** 374 * called from GpsLocationProvider remove geofence callback. 375 */ 376 public void reportGpsGeofenceRemoveStatus(int geofenceId, int status) { 377 if (DEBUG) Log.d(TAG, "Remove Callback: GPS : Id: " + geofenceId + " Status: " + status); 378 acquireWakeLock(); 379 Message m = mGeofenceHandler.obtainMessage(REMOVE_GEOFENCE_CALLBACK); 380 m.arg1 = geofenceId; 381 m.arg2 = getGeofenceStatus(status); 382 mGeofenceHandler.sendMessage(m); 383 } 384 385 /** 386 * called from GpsLocationProvider pause geofence callback. 387 */ 388 public void reportGpsGeofencePauseStatus(int geofenceId, int status) { 389 if (DEBUG) Log.d(TAG, "Pause Callback: GPS : Id: " + geofenceId + " Status: " + status); 390 acquireWakeLock(); 391 Message m = mGeofenceHandler.obtainMessage(PAUSE_GEOFENCE_CALLBACK); 392 m.arg1 = geofenceId; 393 m.arg2 = getGeofenceStatus(status); 394 mGeofenceHandler.sendMessage(m); 395 } 396 397 /** 398 * called from GpsLocationProvider resume geofence callback. 399 */ 400 public void reportGpsGeofenceResumeStatus(int geofenceId, int status) { 401 if (DEBUG) Log.d(TAG, "Resume Callback: GPS : Id: " + geofenceId + " Status: " + status); 402 acquireWakeLock(); 403 Message m = mGeofenceHandler.obtainMessage(RESUME_GEOFENCE_CALLBACK); 404 m.arg1 = geofenceId; 405 m.arg2 = getGeofenceStatus(status); 406 mGeofenceHandler.sendMessage(m); 407 } 408 409 // All operations on mGeofences 410 private Handler mGeofenceHandler = new Handler() { 411 @Override 412 public void handleMessage(Message msg) { 413 int geofenceId; 414 int status; 415 IGeofenceHardwareCallback callback; 416 switch (msg.what) { 417 case ADD_GEOFENCE: 418 geofenceId = msg.arg1; 419 callback = (IGeofenceHardwareCallback) msg.obj; 420 mGeofences.put(geofenceId, callback); 421 break; 422 case REMOVE_GEOFENCE: 423 geofenceId = msg.arg1; 424 mGeofences.remove(geofenceId); 425 break; 426 case ADD_GEOFENCE_CALLBACK: 427 geofenceId = msg.arg1; 428 callback = mGeofences.get(geofenceId); 429 if (callback == null) return; 430 431 try { 432 callback.onGeofenceAdd(geofenceId, msg.arg2); 433 } catch (RemoteException e) {Log.i(TAG, "Remote Exception:" + e);} 434 releaseWakeLock(); 435 break; 436 case REMOVE_GEOFENCE_CALLBACK: 437 geofenceId = msg.arg1; 438 callback = mGeofences.get(geofenceId); 439 if (callback == null) return; 440 441 try { 442 callback.onGeofenceRemove(geofenceId, msg.arg2); 443 } catch (RemoteException e) {} 444 mGeofences.remove(geofenceId); 445 releaseWakeLock(); 446 break; 447 448 case PAUSE_GEOFENCE_CALLBACK: 449 geofenceId = msg.arg1; 450 callback = mGeofences.get(geofenceId); 451 if (callback == null) return; 452 453 try { 454 callback.onGeofencePause(geofenceId, msg.arg2); 455 } catch (RemoteException e) {} 456 releaseWakeLock(); 457 break; 458 459 case RESUME_GEOFENCE_CALLBACK: 460 geofenceId = msg.arg1; 461 callback = mGeofences.get(geofenceId); 462 if (callback == null) return; 463 464 try { 465 callback.onGeofenceResume(geofenceId, msg.arg2); 466 } catch (RemoteException e) {} 467 releaseWakeLock(); 468 break; 469 470 case GEOFENCE_TRANSITION_CALLBACK: 471 GeofenceTransition geofenceTransition = (GeofenceTransition)(msg.obj); 472 callback = mGeofences.get(geofenceTransition.mGeofenceId); 473 474 if (DEBUG) Log.d(TAG, "GeofenceTransistionCallback: GPS : GeofenceId: " + 475 geofenceTransition.mGeofenceId + 476 "Transition: " + geofenceTransition.mTransition + 477 "Location: " + geofenceTransition.mLocation + ":" + mGeofences); 478 479 try { 480 callback.onGeofenceChange( 481 geofenceTransition.mGeofenceId, geofenceTransition.mTransition, 482 geofenceTransition.mLocation, geofenceTransition.mTimestamp, 483 GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE); 484 } catch (RemoteException e) {} 485 releaseWakeLock(); 486 break; 487 } 488 } 489 }; 490 491 // All operations on mCallbacks 492 private Handler mCallbacksHandler = new Handler() { 493 @Override 494 public void handleMessage(Message msg) { 495 int monitoringType; 496 ArrayList<IGeofenceHardwareCallback> callbackList; 497 IGeofenceHardwareCallback callback; 498 499 switch (msg.what) { 500 case GPS_GEOFENCE_STATUS: 501 Location location = (Location) msg.obj; 502 int val = msg.arg1; 503 boolean available; 504 available = (val == GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE ? 505 true : false); 506 callbackList = mCallbacks[GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE]; 507 if (callbackList == null) return; 508 509 if (DEBUG) Log.d(TAG, "MonitoringSystemChangeCallback: GPS : " + available); 510 511 for (IGeofenceHardwareCallback c: callbackList) { 512 try { 513 c.onMonitoringSystemChange( 514 GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE, available, 515 location); 516 } catch (RemoteException e) {} 517 } 518 releaseWakeLock(); 519 break; 520 case CALLBACK_ADD: 521 monitoringType = msg.arg1; 522 callback = (IGeofenceHardwareCallback) msg.obj; 523 callbackList = mCallbacks[monitoringType]; 524 if (callbackList == null) { 525 callbackList = new ArrayList<IGeofenceHardwareCallback>(); 526 mCallbacks[monitoringType] = callbackList; 527 } 528 if (!callbackList.contains(callback)) callbackList.add(callback); 529 break; 530 case CALLBACK_REMOVE: 531 monitoringType = msg.arg1; 532 callback = (IGeofenceHardwareCallback) msg.obj; 533 callbackList = mCallbacks[monitoringType]; 534 if (callbackList != null) { 535 callbackList.remove(callback); 536 } 537 break; 538 } 539 } 540 }; 541 542 private class GeofenceTransition { 543 private int mGeofenceId, mTransition; 544 private long mTimestamp; 545 private Location mLocation; 546 547 GeofenceTransition(int geofenceId, int transition, long timestamp, Location location) { 548 mGeofenceId = geofenceId; 549 mTransition = transition; 550 mTimestamp = timestamp; 551 mLocation = location; 552 } 553 } 554 555 private void setMonitorAvailability(int monitor, int val) { 556 synchronized (mSupportedMonitorTypes) { 557 mSupportedMonitorTypes[monitor] = val; 558 } 559 } 560 561 562 int getMonitoringResolutionLevel(int monitoringType) { 563 switch (monitoringType) { 564 case GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE: 565 return RESOLUTION_LEVEL_FINE; 566 } 567 return RESOLUTION_LEVEL_NONE; 568 } 569 570 int getAllowedResolutionLevel(int pid, int uid) { 571 if (mContext.checkPermission(android.Manifest.permission.ACCESS_FINE_LOCATION, 572 pid, uid) == PackageManager.PERMISSION_GRANTED) { 573 return RESOLUTION_LEVEL_FINE; 574 } else if (mContext.checkPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION, 575 pid, uid) == PackageManager.PERMISSION_GRANTED) { 576 return RESOLUTION_LEVEL_COARSE; 577 } else { 578 return RESOLUTION_LEVEL_NONE; 579 } 580 } 581 582 private int getGeofenceStatus(int status) { 583 switch (status) { 584 case GPS_GEOFENCE_OPERATION_SUCCESS: 585 return GeofenceHardware.GEOFENCE_SUCCESS; 586 case GPS_GEOFENCE_ERROR_GENERIC: 587 return GeofenceHardware.GEOFENCE_FAILURE; 588 case GPS_GEOFENCE_ERROR_ID_EXISTS: 589 return GeofenceHardware.GEOFENCE_ERROR_ID_EXISTS; 590 case GPS_GEOFENCE_ERROR_INVALID_TRANSITION: 591 return GeofenceHardware.GEOFENCE_ERROR_INVALID_TRANSITION; 592 case GPS_GEOFENCE_ERROR_TOO_MANY_GEOFENCES: 593 return GeofenceHardware.GEOFENCE_ERROR_TOO_MANY_GEOFENCES; 594 case GPS_GEOFENCE_ERROR_ID_UNKNOWN: 595 return GeofenceHardware.GEOFENCE_ERROR_ID_UNKNOWN; 596 } 597 return -1; 598 } 599} 600