| /* |
| * Copyright (C) 2013 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.hardware.location; |
| |
| import android.content.Context; |
| import android.content.pm.PackageManager; |
| import android.location.IGpsGeofenceHardware; |
| import android.location.Location; |
| import android.location.LocationManager; |
| import android.os.Bundle; |
| import android.os.Handler; |
| import android.os.Message; |
| import android.os.PowerManager; |
| import android.os.RemoteException; |
| import android.os.ServiceManager; |
| import android.os.SystemClock; |
| import android.util.Log; |
| import android.util.SparseArray; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| |
| /** |
| * This class manages the geofences which are handled by hardware. |
| * |
| * @hide |
| */ |
| public final class GeofenceHardwareImpl { |
| private static final String TAG = "GeofenceHardwareImpl"; |
| private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); |
| |
| private final Context mContext; |
| private static GeofenceHardwareImpl sInstance; |
| private PowerManager.WakeLock mWakeLock; |
| private SparseArray<IGeofenceHardwareCallback> mGeofences = |
| new SparseArray<IGeofenceHardwareCallback>(); |
| private ArrayList<IGeofenceHardwareCallback>[] mCallbacks = |
| new ArrayList[GeofenceHardware.NUM_MONITORS]; |
| |
| private IGpsGeofenceHardware mGpsService; |
| |
| private int[] mSupportedMonitorTypes = new int[GeofenceHardware.NUM_MONITORS]; |
| |
| // mGeofenceHandler message types |
| private static final int GEOFENCE_TRANSITION_CALLBACK = 1; |
| private static final int ADD_GEOFENCE_CALLBACK = 2; |
| private static final int REMOVE_GEOFENCE_CALLBACK = 3; |
| private static final int PAUSE_GEOFENCE_CALLBACK = 4; |
| private static final int RESUME_GEOFENCE_CALLBACK = 5; |
| private static final int ADD_GEOFENCE = 6; |
| private static final int REMOVE_GEOFENCE = 7; |
| |
| // mCallbacksHandler message types |
| private static final int GPS_GEOFENCE_STATUS = 1; |
| private static final int CALLBACK_ADD = 2; |
| private static final int CALLBACK_REMOVE = 3; |
| |
| // The following constants need to match GpsLocationFlags enum in gps.h |
| private static final int LOCATION_INVALID = 0; |
| private static final int LOCATION_HAS_LAT_LONG = 1; |
| private static final int LOCATION_HAS_ALTITUDE = 2; |
| private static final int LOCATION_HAS_SPEED = 4; |
| private static final int LOCATION_HAS_BEARING = 8; |
| private static final int LOCATION_HAS_ACCURACY = 16; |
| |
| // Resolution level constants used for permission checks. |
| // These constants must be in increasing order of finer resolution. |
| private static final int RESOLUTION_LEVEL_NONE = 1; |
| private static final int RESOLUTION_LEVEL_COARSE = 2; |
| private static final int RESOLUTION_LEVEL_FINE = 3; |
| |
| // GPS Geofence errors. Should match gps.h constants. |
| private static final int GPS_GEOFENCE_OPERATION_SUCCESS = 0; |
| private static final int GPS_GEOFENCE_ERROR_TOO_MANY_GEOFENCES = 100; |
| private static final int GPS_GEOFENCE_ERROR_ID_EXISTS = -101; |
| private static final int GPS_GEOFENCE_ERROR_ID_UNKNOWN = -102; |
| private static final int GPS_GEOFENCE_ERROR_INVALID_TRANSITION = -103; |
| private static final int GPS_GEOFENCE_ERROR_GENERIC = -149; |
| |
| |
| |
| public synchronized static GeofenceHardwareImpl getInstance(Context context) { |
| if (sInstance == null) { |
| sInstance = new GeofenceHardwareImpl(context); |
| } |
| return sInstance; |
| } |
| |
| private GeofenceHardwareImpl(Context context) { |
| mContext = context; |
| // Init everything to unsupported. |
| setMonitorAvailability(GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE, |
| GeofenceHardware.MONITOR_UNSUPPORTED); |
| |
| } |
| |
| private void acquireWakeLock() { |
| if (mWakeLock == null) { |
| PowerManager powerManager = |
| (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); |
| mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); |
| } |
| mWakeLock.acquire(); |
| } |
| |
| private void releaseWakeLock() { |
| if (mWakeLock.isHeld()) mWakeLock.release(); |
| } |
| |
| private void updateGpsHardwareAvailability() { |
| //Check which monitors are available. |
| boolean gpsSupported; |
| try { |
| gpsSupported = mGpsService.isHardwareGeofenceSupported(); |
| } catch (RemoteException e) { |
| Log.e(TAG, "Remote Exception calling LocationManagerService"); |
| gpsSupported = false; |
| } |
| |
| if (gpsSupported) { |
| // Its assumed currently available at startup. |
| // native layer will update later. |
| setMonitorAvailability(GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE, |
| GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE); |
| } |
| } |
| |
| public void setGpsHardwareGeofence(IGpsGeofenceHardware service) { |
| if (mGpsService == null) { |
| mGpsService = service; |
| updateGpsHardwareAvailability(); |
| } else if (service == null) { |
| mGpsService = null; |
| Log.w(TAG, "GPS Geofence Hardware service seems to have crashed"); |
| } else { |
| Log.e(TAG, "Error: GpsService being set again."); |
| } |
| } |
| |
| public int[] getMonitoringTypesAndStatus() { |
| synchronized (mSupportedMonitorTypes) { |
| return mSupportedMonitorTypes; |
| } |
| } |
| |
| public boolean addCircularFence(int geofenceId, double latitude, double longitude, |
| double radius, int lastTransition,int monitorTransitions, int notificationResponsivenes, |
| int unknownTimer, int monitoringType, IGeofenceHardwareCallback callback) { |
| // This API is not thread safe. Operations on the same geofence need to be serialized |
| // by upper layers |
| if (DEBUG) { |
| Log.d(TAG, "addCircularFence: GeofenceId: " + geofenceId + "Latitude: " + latitude + |
| "Longitude: " + longitude + "Radius: " + radius + "LastTransition: " |
| + lastTransition + "MonitorTransition: " + monitorTransitions + |
| "NotificationResponsiveness: " + notificationResponsivenes + |
| "UnKnown Timer: " + unknownTimer + "MonitoringType: " + monitoringType); |
| |
| } |
| boolean result; |
| Message m = mGeofenceHandler.obtainMessage(ADD_GEOFENCE, callback); |
| m.arg1 = geofenceId; |
| mGeofenceHandler.sendMessage(m); |
| |
| switch (monitoringType) { |
| case GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE: |
| if (mGpsService == null) return false; |
| try { |
| result = mGpsService.addCircularHardwareGeofence(geofenceId, latitude, |
| longitude, radius, lastTransition, monitorTransitions, |
| notificationResponsivenes, unknownTimer); |
| } catch (RemoteException e) { |
| Log.e(TAG, "AddGeofence: Remote Exception calling LocationManagerService"); |
| result = false; |
| } |
| break; |
| default: |
| result = false; |
| } |
| if (!result) { |
| m = mGeofenceHandler.obtainMessage(REMOVE_GEOFENCE); |
| m.arg1 = geofenceId; |
| mGeofenceHandler.sendMessage(m); |
| } |
| |
| if (DEBUG) Log.d(TAG, "addCircularFence: Result is: " + result); |
| return result; |
| } |
| |
| public boolean removeGeofence(int geofenceId, int monitoringType) { |
| // This API is not thread safe. Operations on the same geofence need to be serialized |
| // by upper layers |
| if (DEBUG) Log.d(TAG, "Remove Geofence: GeofenceId: " + geofenceId); |
| boolean result = false; |
| switch (monitoringType) { |
| case GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE: |
| if (mGpsService == null) return false; |
| try { |
| result = mGpsService.removeHardwareGeofence(geofenceId); |
| } catch (RemoteException e) { |
| Log.e(TAG, "RemoveGeofence: Remote Exception calling LocationManagerService"); |
| result = false; |
| } |
| break; |
| default: |
| result = false; |
| } |
| if (DEBUG) Log.d(TAG, "removeGeofence: Result is: " + result); |
| return result; |
| } |
| |
| public boolean pauseGeofence(int geofenceId, int monitoringType) { |
| // This API is not thread safe. Operations on the same geofence need to be serialized |
| // by upper layers |
| if (DEBUG) Log.d(TAG, "Pause Geofence: GeofenceId: " + geofenceId); |
| boolean result; |
| switch (monitoringType) { |
| case GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE: |
| if (mGpsService == null) return false; |
| try { |
| result = mGpsService.pauseHardwareGeofence(geofenceId); |
| } catch (RemoteException e) { |
| Log.e(TAG, "PauseGeofence: Remote Exception calling LocationManagerService"); |
| result = false; |
| } |
| break; |
| default: |
| result = false; |
| } |
| if (DEBUG) Log.d(TAG, "pauseGeofence: Result is: " + result); |
| return result; |
| } |
| |
| |
| public boolean resumeGeofence(int geofenceId, int monitorTransition, int monitoringType) { |
| // This API is not thread safe. Operations on the same geofence need to be serialized |
| // by upper layers |
| if (DEBUG) Log.d(TAG, "Resume Geofence: GeofenceId: " + geofenceId); |
| boolean result; |
| switch (monitoringType) { |
| case GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE: |
| if (mGpsService == null) return false; |
| try { |
| result = mGpsService.resumeHardwareGeofence(geofenceId, monitorTransition); |
| } catch (RemoteException e) { |
| Log.e(TAG, "ResumeGeofence: Remote Exception calling LocationManagerService"); |
| result = false; |
| } |
| break; |
| default: |
| result = false; |
| } |
| if (DEBUG) Log.d(TAG, "resumeGeofence: Result is: " + result); |
| return result; |
| } |
| |
| public boolean registerForMonitorStateChangeCallback(int monitoringType, |
| IGeofenceHardwareCallback callback) { |
| Message m = mCallbacksHandler.obtainMessage(CALLBACK_ADD, callback); |
| m.arg1 = monitoringType; |
| mCallbacksHandler.sendMessage(m); |
| return true; |
| } |
| |
| public boolean unregisterForMonitorStateChangeCallback(int monitoringType, |
| IGeofenceHardwareCallback callback) { |
| Message m = mCallbacksHandler.obtainMessage(CALLBACK_REMOVE, callback); |
| m.arg1 = monitoringType; |
| mCallbacksHandler.sendMessage(m); |
| return true; |
| } |
| |
| private Location getLocation(int flags, double latitude, |
| double longitude, double altitude, float speed, float bearing, float accuracy, |
| long timestamp) { |
| if (DEBUG) Log.d(TAG, "GetLocation: " + flags + ":" + latitude); |
| Location location = new Location(LocationManager.GPS_PROVIDER); |
| if ((flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) { |
| location.setLatitude(latitude); |
| location.setLongitude(longitude); |
| location.setTime(timestamp); |
| // It would be nice to push the elapsed real-time timestamp |
| // further down the stack, but this is still useful |
| location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos()); |
| } |
| if ((flags & LOCATION_HAS_ALTITUDE) == LOCATION_HAS_ALTITUDE) { |
| location.setAltitude(altitude); |
| } else { |
| location.removeAltitude(); |
| } |
| if ((flags & LOCATION_HAS_SPEED) == LOCATION_HAS_SPEED) { |
| location.setSpeed(speed); |
| } else { |
| location.removeSpeed(); |
| } |
| if ((flags & LOCATION_HAS_BEARING) == LOCATION_HAS_BEARING) { |
| location.setBearing(bearing); |
| } else { |
| location.removeBearing(); |
| } |
| if ((flags & LOCATION_HAS_ACCURACY) == LOCATION_HAS_ACCURACY) { |
| location.setAccuracy(accuracy); |
| } else { |
| location.removeAccuracy(); |
| } |
| return location; |
| } |
| |
| /** |
| * called from GpsLocationProvider to report geofence transition |
| */ |
| public void reportGpsGeofenceTransition(int geofenceId, int flags, double latitude, |
| double longitude, double altitude, float speed, float bearing, float accuracy, |
| long timestamp, int transition, long transitionTimestamp) { |
| if (DEBUG) Log.d(TAG, "GeofenceTransition: Flags: " + flags + " Lat: " + latitude + |
| " Long: " + longitude + " Altitude: " + altitude + " Speed: " + speed + " Bearing: " + |
| bearing + " Accuracy: " + accuracy + " Timestamp: " + timestamp + " Transition: " + |
| transition + " TransitionTimestamp: " + transitionTimestamp); |
| Location location = getLocation(flags, latitude, longitude, altitude, speed, bearing, |
| accuracy, timestamp); |
| GeofenceTransition t = new GeofenceTransition(geofenceId, transition, timestamp, location); |
| acquireWakeLock(); |
| Message m = mGeofenceHandler.obtainMessage(GEOFENCE_TRANSITION_CALLBACK, t); |
| mGeofenceHandler.sendMessage(m); |
| } |
| |
| /** |
| * called from GpsLocationProvider to report GPS status change. |
| */ |
| public void reportGpsGeofenceStatus(int status, int flags, double latitude, |
| double longitude, double altitude, float speed, float bearing, float accuracy, |
| long timestamp) { |
| Location location = getLocation(flags, latitude, longitude, altitude, speed, bearing, |
| accuracy, timestamp); |
| boolean available = false; |
| if (status == GeofenceHardware.GPS_GEOFENCE_AVAILABLE) available = true; |
| |
| int val = (available ? GeofenceHardware.MONITOR_CURRENTLY_UNAVAILABLE : |
| GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE); |
| setMonitorAvailability(GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE, val); |
| |
| acquireWakeLock(); |
| Message m = mCallbacksHandler.obtainMessage(GPS_GEOFENCE_STATUS, location); |
| m.arg1 = val; |
| mCallbacksHandler.sendMessage(m); |
| } |
| |
| /** |
| * called from GpsLocationProvider add geofence callback. |
| */ |
| public void reportGpsGeofenceAddStatus(int geofenceId, int status) { |
| if (DEBUG) Log.d(TAG, "Add Callback: GPS : Id: " + geofenceId + " Status: " + status); |
| acquireWakeLock(); |
| Message m = mGeofenceHandler.obtainMessage(ADD_GEOFENCE_CALLBACK); |
| m.arg1 = geofenceId; |
| m.arg2 = getGeofenceStatus(status); |
| mGeofenceHandler.sendMessage(m); |
| } |
| |
| /** |
| * called from GpsLocationProvider remove geofence callback. |
| */ |
| public void reportGpsGeofenceRemoveStatus(int geofenceId, int status) { |
| if (DEBUG) Log.d(TAG, "Remove Callback: GPS : Id: " + geofenceId + " Status: " + status); |
| acquireWakeLock(); |
| Message m = mGeofenceHandler.obtainMessage(REMOVE_GEOFENCE_CALLBACK); |
| m.arg1 = geofenceId; |
| m.arg2 = getGeofenceStatus(status); |
| mGeofenceHandler.sendMessage(m); |
| } |
| |
| /** |
| * called from GpsLocationProvider pause geofence callback. |
| */ |
| public void reportGpsGeofencePauseStatus(int geofenceId, int status) { |
| if (DEBUG) Log.d(TAG, "Pause Callback: GPS : Id: " + geofenceId + " Status: " + status); |
| acquireWakeLock(); |
| Message m = mGeofenceHandler.obtainMessage(PAUSE_GEOFENCE_CALLBACK); |
| m.arg1 = geofenceId; |
| m.arg2 = getGeofenceStatus(status); |
| mGeofenceHandler.sendMessage(m); |
| } |
| |
| /** |
| * called from GpsLocationProvider resume geofence callback. |
| */ |
| public void reportGpsGeofenceResumeStatus(int geofenceId, int status) { |
| if (DEBUG) Log.d(TAG, "Resume Callback: GPS : Id: " + geofenceId + " Status: " + status); |
| acquireWakeLock(); |
| Message m = mGeofenceHandler.obtainMessage(RESUME_GEOFENCE_CALLBACK); |
| m.arg1 = geofenceId; |
| m.arg2 = getGeofenceStatus(status); |
| mGeofenceHandler.sendMessage(m); |
| } |
| |
| // All operations on mGeofences |
| private Handler mGeofenceHandler = new Handler() { |
| @Override |
| public void handleMessage(Message msg) { |
| int geofenceId; |
| int status; |
| IGeofenceHardwareCallback callback; |
| switch (msg.what) { |
| case ADD_GEOFENCE: |
| geofenceId = msg.arg1; |
| callback = (IGeofenceHardwareCallback) msg.obj; |
| mGeofences.put(geofenceId, callback); |
| break; |
| case REMOVE_GEOFENCE: |
| geofenceId = msg.arg1; |
| mGeofences.remove(geofenceId); |
| break; |
| case ADD_GEOFENCE_CALLBACK: |
| geofenceId = msg.arg1; |
| callback = mGeofences.get(geofenceId); |
| if (callback == null) return; |
| |
| try { |
| callback.onGeofenceAdd(geofenceId, msg.arg2); |
| } catch (RemoteException e) {Log.i(TAG, "Remote Exception:" + e);} |
| releaseWakeLock(); |
| break; |
| case REMOVE_GEOFENCE_CALLBACK: |
| geofenceId = msg.arg1; |
| callback = mGeofences.get(geofenceId); |
| if (callback == null) return; |
| |
| try { |
| callback.onGeofenceRemove(geofenceId, msg.arg2); |
| } catch (RemoteException e) {} |
| mGeofences.remove(geofenceId); |
| releaseWakeLock(); |
| break; |
| |
| case PAUSE_GEOFENCE_CALLBACK: |
| geofenceId = msg.arg1; |
| callback = mGeofences.get(geofenceId); |
| if (callback == null) return; |
| |
| try { |
| callback.onGeofencePause(geofenceId, msg.arg2); |
| } catch (RemoteException e) {} |
| releaseWakeLock(); |
| break; |
| |
| case RESUME_GEOFENCE_CALLBACK: |
| geofenceId = msg.arg1; |
| callback = mGeofences.get(geofenceId); |
| if (callback == null) return; |
| |
| try { |
| callback.onGeofenceResume(geofenceId, msg.arg2); |
| } catch (RemoteException e) {} |
| releaseWakeLock(); |
| break; |
| |
| case GEOFENCE_TRANSITION_CALLBACK: |
| GeofenceTransition geofenceTransition = (GeofenceTransition)(msg.obj); |
| callback = mGeofences.get(geofenceTransition.mGeofenceId); |
| |
| if (DEBUG) Log.d(TAG, "GeofenceTransistionCallback: GPS : GeofenceId: " + |
| geofenceTransition.mGeofenceId + |
| "Transition: " + geofenceTransition.mTransition + |
| "Location: " + geofenceTransition.mLocation + ":" + mGeofences); |
| |
| try { |
| callback.onGeofenceChange( |
| geofenceTransition.mGeofenceId, geofenceTransition.mTransition, |
| geofenceTransition.mLocation, geofenceTransition.mTimestamp, |
| GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE); |
| } catch (RemoteException e) {} |
| releaseWakeLock(); |
| break; |
| } |
| } |
| }; |
| |
| // All operations on mCallbacks |
| private Handler mCallbacksHandler = new Handler() { |
| @Override |
| public void handleMessage(Message msg) { |
| int monitoringType; |
| ArrayList<IGeofenceHardwareCallback> callbackList; |
| IGeofenceHardwareCallback callback; |
| |
| switch (msg.what) { |
| case GPS_GEOFENCE_STATUS: |
| Location location = (Location) msg.obj; |
| int val = msg.arg1; |
| boolean available; |
| available = (val == GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE ? |
| true : false); |
| callbackList = mCallbacks[GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE]; |
| if (callbackList == null) return; |
| |
| if (DEBUG) Log.d(TAG, "MonitoringSystemChangeCallback: GPS : " + available); |
| |
| for (IGeofenceHardwareCallback c: callbackList) { |
| try { |
| c.onMonitoringSystemChange( |
| GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE, available, |
| location); |
| } catch (RemoteException e) {} |
| } |
| releaseWakeLock(); |
| break; |
| case CALLBACK_ADD: |
| monitoringType = msg.arg1; |
| callback = (IGeofenceHardwareCallback) msg.obj; |
| callbackList = mCallbacks[monitoringType]; |
| if (callbackList == null) { |
| callbackList = new ArrayList<IGeofenceHardwareCallback>(); |
| mCallbacks[monitoringType] = callbackList; |
| } |
| if (!callbackList.contains(callback)) callbackList.add(callback); |
| break; |
| case CALLBACK_REMOVE: |
| monitoringType = msg.arg1; |
| callback = (IGeofenceHardwareCallback) msg.obj; |
| callbackList = mCallbacks[monitoringType]; |
| if (callbackList != null) { |
| callbackList.remove(callback); |
| } |
| break; |
| } |
| } |
| }; |
| |
| private class GeofenceTransition { |
| private int mGeofenceId, mTransition; |
| private long mTimestamp; |
| private Location mLocation; |
| |
| GeofenceTransition(int geofenceId, int transition, long timestamp, Location location) { |
| mGeofenceId = geofenceId; |
| mTransition = transition; |
| mTimestamp = timestamp; |
| mLocation = location; |
| } |
| } |
| |
| private void setMonitorAvailability(int monitor, int val) { |
| synchronized (mSupportedMonitorTypes) { |
| mSupportedMonitorTypes[monitor] = val; |
| } |
| } |
| |
| |
| int getMonitoringResolutionLevel(int monitoringType) { |
| switch (monitoringType) { |
| case GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE: |
| return RESOLUTION_LEVEL_FINE; |
| } |
| return RESOLUTION_LEVEL_NONE; |
| } |
| |
| int getAllowedResolutionLevel(int pid, int uid) { |
| if (mContext.checkPermission(android.Manifest.permission.ACCESS_FINE_LOCATION, |
| pid, uid) == PackageManager.PERMISSION_GRANTED) { |
| return RESOLUTION_LEVEL_FINE; |
| } else if (mContext.checkPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION, |
| pid, uid) == PackageManager.PERMISSION_GRANTED) { |
| return RESOLUTION_LEVEL_COARSE; |
| } else { |
| return RESOLUTION_LEVEL_NONE; |
| } |
| } |
| |
| private int getGeofenceStatus(int status) { |
| switch (status) { |
| case GPS_GEOFENCE_OPERATION_SUCCESS: |
| return GeofenceHardware.GEOFENCE_SUCCESS; |
| case GPS_GEOFENCE_ERROR_GENERIC: |
| return GeofenceHardware.GEOFENCE_FAILURE; |
| case GPS_GEOFENCE_ERROR_ID_EXISTS: |
| return GeofenceHardware.GEOFENCE_ERROR_ID_EXISTS; |
| case GPS_GEOFENCE_ERROR_INVALID_TRANSITION: |
| return GeofenceHardware.GEOFENCE_ERROR_INVALID_TRANSITION; |
| case GPS_GEOFENCE_ERROR_TOO_MANY_GEOFENCES: |
| return GeofenceHardware.GEOFENCE_ERROR_TOO_MANY_GEOFENCES; |
| case GPS_GEOFENCE_ERROR_ID_UNKNOWN: |
| return GeofenceHardware.GEOFENCE_ERROR_ID_UNKNOWN; |
| } |
| return -1; |
| } |
| } |