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