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