android.location.IFusedGeofenceHardware - java examples

Here are the examples of the java api android.location.IFusedGeofenceHardware taken from open source projects. By voting up you can indicate which examples are most useful and appropriate.

4 Examples 7

18 View Complete Implementation : GeofenceProxy.java
Copyright Apache License 2.0
Author : lulululbj
public static GeofenceProxy createAndBind(Context context, int overlaySwitchResId, int defaultServicePackageNameResId, int initialPackageNamesResId, Handler handler, IGpsGeofenceHardware gpsGeofence, IFusedGeofenceHardware fusedGeofenceHardware) {
    GeofenceProxy proxy = new GeofenceProxy(context, overlaySwitchResId, defaultServicePackageNameResId, initialPackageNamesResId, handler, gpsGeofence, fusedGeofenceHardware);
    if (proxy.bindGeofenceProvider()) {
        return proxy;
    } else {
        return null;
    }
}

18 View Complete Implementation : GeofenceProxy.java
Copyright Apache License 2.0
Author : lulululbj
/**
 * @hide
 */
public final clreplaced GeofenceProxy {

    private static final String TAG = "GeofenceProxy";

    private static final String SERVICE_ACTION = "com.android.location.service.GeofenceProvider";

    private final ServiceWatcher mServiceWatcher;

    private final Context mContext;

    private final IGpsGeofenceHardware mGpsGeofenceHardware;

    private final IFusedGeofenceHardware mFusedGeofenceHardware;

    private final Object mLock = new Object();

    // Access to mGeofenceHardware needs to be synchronized by mLock.
    private IGeofenceHardware mGeofenceHardware;

    private static final int GEOFENCE_PROVIDER_CONNECTED = 1;

    private static final int GEOFENCE_HARDWARE_CONNECTED = 2;

    private static final int GEOFENCE_HARDWARE_DISCONNECTED = 3;

    private static final int GEOFENCE_GPS_HARDWARE_CONNECTED = 4;

    private static final int GEOFENCE_GPS_HARDWARE_DISCONNECTED = 5;

    private Runnable mRunnable = new Runnable() {

        @Override
        public void run() {
            mHandler.sendEmptyMessage(GEOFENCE_PROVIDER_CONNECTED);
        }
    };

    public static GeofenceProxy createAndBind(Context context, int overlaySwitchResId, int defaultServicePackageNameResId, int initialPackageNamesResId, Handler handler, IGpsGeofenceHardware gpsGeofence, IFusedGeofenceHardware fusedGeofenceHardware) {
        GeofenceProxy proxy = new GeofenceProxy(context, overlaySwitchResId, defaultServicePackageNameResId, initialPackageNamesResId, handler, gpsGeofence, fusedGeofenceHardware);
        if (proxy.bindGeofenceProvider()) {
            return proxy;
        } else {
            return null;
        }
    }

    private GeofenceProxy(Context context, int overlaySwitchResId, int defaultServicePackageNameResId, int initialPackageNamesResId, Handler handler, IGpsGeofenceHardware gpsGeofence, IFusedGeofenceHardware fusedGeofenceHardware) {
        mContext = context;
        mServiceWatcher = new ServiceWatcher(context, TAG, SERVICE_ACTION, overlaySwitchResId, defaultServicePackageNameResId, initialPackageNamesResId, mRunnable, handler);
        mGpsGeofenceHardware = gpsGeofence;
        mFusedGeofenceHardware = fusedGeofenceHardware;
        bindHardwareGeofence();
    }

    private boolean bindGeofenceProvider() {
        return mServiceWatcher.start();
    }

    private void bindHardwareGeofence() {
        mContext.bindServiceAsUser(new Intent(mContext, GeofenceHardwareService.clreplaced), mServiceConnection, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM);
    }

    private ServiceConnection mServiceConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            synchronized (mLock) {
                mGeofenceHardware = IGeofenceHardware.Stub.asInterface(service);
                mHandler.sendEmptyMessage(GEOFENCE_HARDWARE_CONNECTED);
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            synchronized (mLock) {
                mGeofenceHardware = null;
                mHandler.sendEmptyMessage(GEOFENCE_HARDWARE_DISCONNECTED);
            }
        }
    };

    private void setGeofenceHardwareInProviderLocked() {
        mServiceWatcher.runOnBinder(new ServiceWatcher.BinderRunner() {

            @Override
            public void run(IBinder binder) {
                final IGeofenceProvider provider = IGeofenceProvider.Stub.asInterface(binder);
                try {
                    provider.setGeofenceHardware(mGeofenceHardware);
                } catch (RemoteException e) {
                    Log.e(TAG, "Remote Exception: setGeofenceHardwareInProviderLocked: " + e);
                }
            }
        });
    }

    private void setGpsGeofenceLocked() {
        try {
            if (mGpsGeofenceHardware != null) {
                mGeofenceHardware.setGpsGeofenceHardware(mGpsGeofenceHardware);
            }
        } catch (RemoteException e) {
            Log.e(TAG, "Error while connecting to GeofenceHardwareService");
        }
    }

    private void setFusedGeofenceLocked() {
        try {
            mGeofenceHardware.setFusedGeofenceHardware(mFusedGeofenceHardware);
        } catch (RemoteException e) {
            Log.e(TAG, "Error while connecting to GeofenceHardwareService");
        }
    }

    // This needs to be reworked, when more services get added,
    // Might need a state machine or add a framework utility clreplaced,
    private Handler mHandler = new Handler() {

        @Override
        public void handleMessage(Message msg) {
            switch(msg.what) {
                case GEOFENCE_PROVIDER_CONNECTED:
                    synchronized (mLock) {
                        if (mGeofenceHardware != null) {
                            setGeofenceHardwareInProviderLocked();
                        }
                    // else: the geofence provider will be notified when the connection to
                    // GeofenceHardwareService is established.
                    }
                    break;
                case GEOFENCE_HARDWARE_CONNECTED:
                    synchronized (mLock) {
                        // Theoretically this won't happen because once the GeofenceHardwareService
                        // is connected to, we won't lose connection to it because it's a system
                        // service. But this check does make the code more robust.
                        if (mGeofenceHardware != null) {
                            setGpsGeofenceLocked();
                            setFusedGeofenceLocked();
                            setGeofenceHardwareInProviderLocked();
                        }
                    }
                    break;
                case GEOFENCE_HARDWARE_DISCONNECTED:
                    synchronized (mLock) {
                        if (mGeofenceHardware == null) {
                            setGeofenceHardwareInProviderLocked();
                        }
                    }
                    break;
            }
        }
    };
}

17 View Complete Implementation : GeofenceHardwareImpl.java
Copyright Apache License 2.0
Author : lulululbj
public void setFusedGeofenceHardware(IFusedGeofenceHardware service) {
    if (mFusedService == null) {
        mFusedService = service;
        updateFusedHardwareAvailability();
    } else if (service == null) {
        mFusedService = null;
        Log.w(TAG, "Fused Geofence Hardware service seems to have crashed");
    } else {
        Log.e(TAG, "Error: FusedService being set again");
    }
}

17 View Complete Implementation : GeofenceHardwareImpl.java
Copyright Apache License 2.0
Author : lulululbj
/**
 * This clreplaced manages the geofences which are handled by hardware.
 *
 * @hide
 */
public final clreplaced GeofenceHardwareImpl {

    private static final String TAG = "GeofenceHardwareImpl";

    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);

    private static final int FIRST_VERSION_WITH_CAPABILITIES = 2;

    private final Context mContext;

    private static GeofenceHardwareImpl sInstance;

    private PowerManager.WakeLock mWakeLock;

    private final SparseArray<IGeofenceHardwareCallback> mGeofences = new SparseArray<IGeofenceHardwareCallback>();

    private final ArrayList<IGeofenceHardwareMonitorCallback>[] mCallbacks = new ArrayList[GeofenceHardware.NUM_MONITORS];

    private final ArrayList<Reaper> mReapers = new ArrayList<Reaper>();

    private IFusedGeofenceHardware mFusedService;

    private IGpsGeofenceHardware mGpsService;

    private int mCapabilities;

    private int mVersion = 1;

    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 GEOFENCE_CALLBACK_BINDER_DIED = 6;

    // mCallbacksHandler message types
    private static final int GEOFENCE_STATUS = 1;

    private static final int CALLBACK_ADD = 2;

    private static final int CALLBACK_REMOVE = 3;

    private static final int MONITOR_CALLBACK_BINDER_DIED = 4;

    // mReaperHandler message types
    private static final int REAPER_GEOFENCE_ADDED = 1;

    private static final int REAPER_MONITOR_CALLBACK_ADDED = 2;

    private static final int REAPER_REMOVED = 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_ALreplacedUDE = 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;

    // Capability constant corresponding to fused_location.h entry when geofencing supports GNNS.
    private static final int CAPABILITY_GNSS = 1;

    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);
        setMonitorAvailability(GeofenceHardware.MONITORING_TYPE_FUSED_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 replacedumed currently available at startup.
            // native layer will update later.
            setMonitorAvailability(GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE, GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE);
        }
    }

    private void updateFusedHardwareAvailability() {
        boolean fusedSupported;
        try {
            final boolean hasGnnsCapabilities = (mVersion < FIRST_VERSION_WITH_CAPABILITIES) || (mCapabilities & CAPABILITY_GNSS) != 0;
            fusedSupported = (mFusedService != null ? mFusedService.isSupported() && hasGnnsCapabilities : false);
        } catch (RemoteException e) {
            Log.e(TAG, "RemoteException calling LocationManagerService");
            fusedSupported = false;
        }
        if (fusedSupported) {
            setMonitorAvailability(GeofenceHardware.MONITORING_TYPE_FUSED_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 void onCapabilities(int capabilities) {
        mCapabilities = capabilities;
        updateFusedHardwareAvailability();
    }

    public void setVersion(int version) {
        mVersion = version;
        updateFusedHardwareAvailability();
    }

    public void setFusedGeofenceHardware(IFusedGeofenceHardware service) {
        if (mFusedService == null) {
            mFusedService = service;
            updateFusedHardwareAvailability();
        } else if (service == null) {
            mFusedService = null;
            Log.w(TAG, "Fused Geofence Hardware service seems to have crashed");
        } else {
            Log.e(TAG, "Error: FusedService being set again");
        }
    }

    public int[] getMonitoringTypes() {
        boolean gpsSupported;
        boolean fusedSupported;
        synchronized (mSupportedMonitorTypes) {
            gpsSupported = mSupportedMonitorTypes[GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE] != GeofenceHardware.MONITOR_UNSUPPORTED;
            fusedSupported = mSupportedMonitorTypes[GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE] != GeofenceHardware.MONITOR_UNSUPPORTED;
        }
        if (gpsSupported) {
            if (fusedSupported) {
                return new int[] { GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE, GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE };
            } else {
                return new int[] { GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE };
            }
        } else if (fusedSupported) {
            return new int[] { GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE };
        } else {
            return new int[0];
        }
    }

    public int getStatusOfMonitoringType(int monitoringType) {
        synchronized (mSupportedMonitorTypes) {
            if (monitoringType >= mSupportedMonitorTypes.length || monitoringType < 0) {
                throw new IllegalArgumentException("Unknown monitoring type");
            }
            return mSupportedMonitorTypes[monitoringType];
        }
    }

    public int getCapabilitiesForMonitoringType(int monitoringType) {
        switch(mSupportedMonitorTypes[monitoringType]) {
            case GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE:
                switch(monitoringType) {
                    case GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE:
                        return CAPABILITY_GNSS;
                    case GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE:
                        if (mVersion >= FIRST_VERSION_WITH_CAPABILITIES) {
                            return mCapabilities;
                        }
                        // This was the implied capability on old FLP HAL versions that didn't
                        // have the capability callback.
                        return CAPABILITY_GNSS;
                }
                break;
        }
        return 0;
    }

    public boolean addCircularFence(int monitoringType, GeofenceHardwareRequestParcelable request, IGeofenceHardwareCallback callback) {
        int geofenceId = request.getId();
        // This API is not thread safe. Operations on the same geofence need to be serialized
        // by upper layers
        if (DEBUG) {
            String message = String.format("addCircularFence: monitoringType=%d, %s", monitoringType, request);
            Log.d(TAG, message);
        }
        boolean result;
        // The callback must be added before addCircularHardwareGeofence is called otherwise the
        // callback might not be called after the geofence is added in the geofence hardware.
        // This also means that the callback must be removed if the addCircularHardwareGeofence
        // operations is not called or fails.
        synchronized (mGeofences) {
            mGeofences.put(geofenceId, callback);
        }
        switch(monitoringType) {
            case GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE:
                if (mGpsService == null)
                    return false;
                try {
                    result = mGpsService.addCircularHardwareGeofence(request.getId(), request.getLareplacedude(), request.getLongitude(), request.getRadius(), request.getLastTransition(), request.getMonitorTransitions(), request.getNotificationResponsiveness(), request.getUnknownTimer());
                } catch (RemoteException e) {
                    Log.e(TAG, "AddGeofence: Remote Exception calling LocationManagerService");
                    result = false;
                }
                break;
            case GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE:
                if (mFusedService == null) {
                    return false;
                }
                try {
                    mFusedService.addGeofences(new GeofenceHardwareRequestParcelable[] { request });
                    result = true;
                } catch (RemoteException e) {
                    Log.e(TAG, "AddGeofence: RemoteException calling LocationManagerService");
                    result = false;
                }
                break;
            default:
                result = false;
        }
        if (result) {
            Message m = mReaperHandler.obtainMessage(REAPER_GEOFENCE_ADDED, callback);
            m.arg1 = monitoringType;
            mReaperHandler.sendMessage(m);
        } else {
            synchronized (mGeofences) {
                mGeofences.remove(geofenceId);
            }
        }
        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;
        synchronized (mGeofences) {
            if (mGeofences.get(geofenceId) == null) {
                throw new IllegalArgumentException("Geofence " + geofenceId + " not registered.");
            }
        }
        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;
            case GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE:
                if (mFusedService == null) {
                    return false;
                }
                try {
                    mFusedService.removeGeofences(new int[] { geofenceId });
                    result = true;
                } catch (RemoteException e) {
                    Log.e(TAG, "RemoveGeofence: RemoteException 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;
        synchronized (mGeofences) {
            if (mGeofences.get(geofenceId) == null) {
                throw new IllegalArgumentException("Geofence " + geofenceId + " not registered.");
            }
        }
        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;
            case GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE:
                if (mFusedService == null) {
                    return false;
                }
                try {
                    mFusedService.pauseMonitoringGeofence(geofenceId);
                    result = true;
                } catch (RemoteException e) {
                    Log.e(TAG, "PauseGeofence: RemoteException 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 monitoringType, int monitorTransition) {
        // 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;
        synchronized (mGeofences) {
            if (mGeofences.get(geofenceId) == null) {
                throw new IllegalArgumentException("Geofence " + geofenceId + " not registered.");
            }
        }
        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;
            case GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE:
                if (mFusedService == null) {
                    return false;
                }
                try {
                    mFusedService.resumeMonitoringGeofence(geofenceId, monitorTransition);
                    result = true;
                } catch (RemoteException e) {
                    Log.e(TAG, "ResumeGeofence: RemoteException calling LocationManagerService");
                    result = false;
                }
                break;
            default:
                result = false;
        }
        if (DEBUG)
            Log.d(TAG, "resumeGeofence: Result is: " + result);
        return result;
    }

    public boolean registerForMonitorStateChangeCallback(int monitoringType, IGeofenceHardwareMonitorCallback callback) {
        Message reaperMessage = mReaperHandler.obtainMessage(REAPER_MONITOR_CALLBACK_ADDED, callback);
        reaperMessage.arg1 = monitoringType;
        mReaperHandler.sendMessage(reaperMessage);
        Message m = mCallbacksHandler.obtainMessage(CALLBACK_ADD, callback);
        m.arg1 = monitoringType;
        mCallbacksHandler.sendMessage(m);
        return true;
    }

    public boolean unregisterForMonitorStateChangeCallback(int monitoringType, IGeofenceHardwareMonitorCallback callback) {
        Message m = mCallbacksHandler.obtainMessage(CALLBACK_REMOVE, callback);
        m.arg1 = monitoringType;
        mCallbacksHandler.sendMessage(m);
        return true;
    }

    /**
     * Used to report geofence transitions
     */
    public void reportGeofenceTransition(int geofenceId, Location location, int transition, long transitionTimestamp, int monitoringType, int sourcesUsed) {
        if (location == null) {
            Log.e(TAG, String.format("Invalid Geofence Transition: location=null"));
            return;
        }
        if (DEBUG) {
            Log.d(TAG, "GeofenceTransition| " + location + ", transition:" + transition + ", transitionTimestamp:" + transitionTimestamp + ", monitoringType:" + monitoringType + ", sourcesUsed:" + sourcesUsed);
        }
        GeofenceTransition geofenceTransition = new GeofenceTransition(geofenceId, transition, transitionTimestamp, location, monitoringType, sourcesUsed);
        acquireWakeLock();
        Message message = mGeofenceHandler.obtainMessage(GEOFENCE_TRANSITION_CALLBACK, geofenceTransition);
        message.sendToTarget();
    }

    /**
     * Used to report Monitor status changes.
     */
    public void reportGeofenceMonitorStatus(int monitoringType, int monitoringStatus, Location location, int source) {
        setMonitorAvailability(monitoringType, monitoringStatus);
        acquireWakeLock();
        GeofenceHardwareMonitorEvent event = new GeofenceHardwareMonitorEvent(monitoringType, monitoringStatus, source, location);
        Message message = mCallbacksHandler.obtainMessage(GEOFENCE_STATUS, event);
        message.sendToTarget();
    }

    /**
     * Internal generic status report function for Geofence operations.
     *
     * @param operation The operation to be reported as defined internally.
     * @param geofenceId The id of the geofence the operation is related to.
     * @param operationStatus The status of the operation as defined in GeofenceHardware clreplaced. This
     *                        status is independent of the statuses reported by different HALs.
     */
    private void reportGeofenceOperationStatus(int operation, int geofenceId, int operationStatus) {
        acquireWakeLock();
        Message message = mGeofenceHandler.obtainMessage(operation);
        message.arg1 = geofenceId;
        message.arg2 = operationStatus;
        message.sendToTarget();
    }

    /**
     * Used to report the status of a Geofence Add operation.
     */
    public void reportGeofenceAddStatus(int geofenceId, int status) {
        if (DEBUG)
            Log.d(TAG, "AddCallback| id:" + geofenceId + ", status:" + status);
        reportGeofenceOperationStatus(ADD_GEOFENCE_CALLBACK, geofenceId, status);
    }

    /**
     * Used to report the status of a Geofence Remove operation.
     */
    public void reportGeofenceRemoveStatus(int geofenceId, int status) {
        if (DEBUG)
            Log.d(TAG, "RemoveCallback| id:" + geofenceId + ", status:" + status);
        reportGeofenceOperationStatus(REMOVE_GEOFENCE_CALLBACK, geofenceId, status);
    }

    /**
     * Used to report the status of a Geofence Pause operation.
     */
    public void reportGeofencePauseStatus(int geofenceId, int status) {
        if (DEBUG)
            Log.d(TAG, "PauseCallbac| id:" + geofenceId + ", status" + status);
        reportGeofenceOperationStatus(PAUSE_GEOFENCE_CALLBACK, geofenceId, status);
    }

    /**
     * Used to report the status of a Geofence Resume operation.
     */
    public void reportGeofenceResumeStatus(int geofenceId, int status) {
        if (DEBUG)
            Log.d(TAG, "ResumeCallback| id:" + geofenceId + ", status:" + status);
        reportGeofenceOperationStatus(RESUME_GEOFENCE_CALLBACK, geofenceId, status);
    }

    // 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_CALLBACK:
                    geofenceId = msg.arg1;
                    synchronized (mGeofences) {
                        callback = mGeofences.get(geofenceId);
                    }
                    if (callback != null) {
                        try {
                            callback.onGeofenceAdd(geofenceId, msg.arg2);
                        } catch (RemoteException e) {
                            Log.i(TAG, "Remote Exception:" + e);
                        }
                    }
                    releaseWakeLock();
                    break;
                case REMOVE_GEOFENCE_CALLBACK:
                    geofenceId = msg.arg1;
                    synchronized (mGeofences) {
                        callback = mGeofences.get(geofenceId);
                    }
                    if (callback != null) {
                        try {
                            callback.onGeofenceRemove(geofenceId, msg.arg2);
                        } catch (RemoteException e) {
                        }
                        IBinder callbackBinder = callback.asBinder();
                        boolean callbackInUse = false;
                        synchronized (mGeofences) {
                            mGeofences.remove(geofenceId);
                            // Check if the underlying binder is still useful for other geofences,
                            // if no, unlink the DeathRecipient to avoid memory leak.
                            for (int i = 0; i < mGeofences.size(); i++) {
                                if (mGeofences.valueAt(i).asBinder() == callbackBinder) {
                                    callbackInUse = true;
                                    break;
                                }
                            }
                        }
                        // Remove the reaper replacedociated with this binder.
                        if (!callbackInUse) {
                            for (Iterator<Reaper> iterator = mReapers.iterator(); iterator.hasNext(); ) {
                                Reaper reaper = iterator.next();
                                if (reaper.mCallback != null && reaper.mCallback.asBinder() == callbackBinder) {
                                    iterator.remove();
                                    reaper.unlinkToDeath();
                                    if (DEBUG)
                                        Log.d(TAG, String.format("Removed reaper %s " + "because binder %s is no longer needed.", reaper, callbackBinder));
                                }
                            }
                        }
                    }
                    releaseWakeLock();
                    break;
                case PAUSE_GEOFENCE_CALLBACK:
                    geofenceId = msg.arg1;
                    synchronized (mGeofences) {
                        callback = mGeofences.get(geofenceId);
                    }
                    if (callback != null) {
                        try {
                            callback.onGeofencePause(geofenceId, msg.arg2);
                        } catch (RemoteException e) {
                        }
                    }
                    releaseWakeLock();
                    break;
                case RESUME_GEOFENCE_CALLBACK:
                    geofenceId = msg.arg1;
                    synchronized (mGeofences) {
                        callback = mGeofences.get(geofenceId);
                    }
                    if (callback != null) {
                        try {
                            callback.onGeofenceResume(geofenceId, msg.arg2);
                        } catch (RemoteException e) {
                        }
                    }
                    releaseWakeLock();
                    break;
                case GEOFENCE_TRANSITION_CALLBACK:
                    GeofenceTransition geofenceTransition = (GeofenceTransition) (msg.obj);
                    synchronized (mGeofences) {
                        callback = mGeofences.get(geofenceTransition.mGeofenceId);
                        // need to keep access to mGeofences synchronized at all times
                        if (DEBUG)
                            Log.d(TAG, "GeofenceTransistionCallback: GPS : GeofenceId: " + geofenceTransition.mGeofenceId + " Transition: " + geofenceTransition.mTransition + " Location: " + geofenceTransition.mLocation + ":" + mGeofences);
                    }
                    if (callback != null) {
                        try {
                            callback.onGeofenceTransition(geofenceTransition.mGeofenceId, geofenceTransition.mTransition, geofenceTransition.mLocation, geofenceTransition.mTimestamp, geofenceTransition.mMonitoringType);
                        } catch (RemoteException e) {
                        }
                    }
                    releaseWakeLock();
                    break;
                case GEOFENCE_CALLBACK_BINDER_DIED:
                    // Find all geofences replacedociated with this callback and remove them.
                    callback = (IGeofenceHardwareCallback) (msg.obj);
                    if (DEBUG)
                        Log.d(TAG, "Geofence callback reaped:" + callback);
                    int monitoringType = msg.arg1;
                    synchronized (mGeofences) {
                        for (int i = 0; i < mGeofences.size(); i++) {
                            if (mGeofences.valueAt(i).equals(callback)) {
                                geofenceId = mGeofences.keyAt(i);
                                removeGeofence(mGeofences.keyAt(i), monitoringType);
                                mGeofences.remove(geofenceId);
                            }
                        }
                    }
            }
        }
    };

    // All operations on mCallbacks
    private Handler mCallbacksHandler = new Handler() {

        @Override
        public void handleMessage(Message msg) {
            int monitoringType;
            ArrayList<IGeofenceHardwareMonitorCallback> callbackList;
            IGeofenceHardwareMonitorCallback callback;
            switch(msg.what) {
                case GEOFENCE_STATUS:
                    GeofenceHardwareMonitorEvent event = (GeofenceHardwareMonitorEvent) msg.obj;
                    callbackList = mCallbacks[event.getMonitoringType()];
                    if (callbackList != null) {
                        if (DEBUG)
                            Log.d(TAG, "MonitoringSystemChangeCallback: " + event);
                        for (IGeofenceHardwareMonitorCallback c : callbackList) {
                            try {
                                c.onMonitoringSystemChange(event);
                            } catch (RemoteException e) {
                                Log.d(TAG, "Error reporting onMonitoringSystemChange.", e);
                            }
                        }
                    }
                    releaseWakeLock();
                    break;
                case CALLBACK_ADD:
                    monitoringType = msg.arg1;
                    callback = (IGeofenceHardwareMonitorCallback) msg.obj;
                    callbackList = mCallbacks[monitoringType];
                    if (callbackList == null) {
                        callbackList = new ArrayList<IGeofenceHardwareMonitorCallback>();
                        mCallbacks[monitoringType] = callbackList;
                    }
                    if (!callbackList.contains(callback))
                        callbackList.add(callback);
                    break;
                case CALLBACK_REMOVE:
                    monitoringType = msg.arg1;
                    callback = (IGeofenceHardwareMonitorCallback) msg.obj;
                    callbackList = mCallbacks[monitoringType];
                    if (callbackList != null) {
                        callbackList.remove(callback);
                    }
                    break;
                case MONITOR_CALLBACK_BINDER_DIED:
                    callback = (IGeofenceHardwareMonitorCallback) msg.obj;
                    if (DEBUG)
                        Log.d(TAG, "Monitor callback reaped:" + callback);
                    callbackList = mCallbacks[msg.arg1];
                    if (callbackList != null && callbackList.contains(callback)) {
                        callbackList.remove(callback);
                    }
            }
        }
    };

    // All operations on mReaper
    private Handler mReaperHandler = new Handler() {

        @Override
        public void handleMessage(Message msg) {
            Reaper r;
            IGeofenceHardwareCallback callback;
            IGeofenceHardwareMonitorCallback monitorCallback;
            int monitoringType;
            switch(msg.what) {
                case REAPER_GEOFENCE_ADDED:
                    callback = (IGeofenceHardwareCallback) msg.obj;
                    monitoringType = msg.arg1;
                    r = new Reaper(callback, monitoringType);
                    if (!mReapers.contains(r)) {
                        mReapers.add(r);
                        IBinder b = callback.asBinder();
                        try {
                            b.linkToDeath(r, 0);
                        } catch (RemoteException e) {
                        }
                    }
                    break;
                case REAPER_MONITOR_CALLBACK_ADDED:
                    monitorCallback = (IGeofenceHardwareMonitorCallback) msg.obj;
                    monitoringType = msg.arg1;
                    r = new Reaper(monitorCallback, monitoringType);
                    if (!mReapers.contains(r)) {
                        mReapers.add(r);
                        IBinder b = monitorCallback.asBinder();
                        try {
                            b.linkToDeath(r, 0);
                        } catch (RemoteException e) {
                        }
                    }
                    break;
                case REAPER_REMOVED:
                    r = (Reaper) msg.obj;
                    mReapers.remove(r);
            }
        }
    };

    private clreplaced GeofenceTransition {

        private int mGeofenceId, mTransition;

        private long mTimestamp;

        private Location mLocation;

        private int mMonitoringType;

        private int mSourcesUsed;

        GeofenceTransition(int geofenceId, int transition, long timestamp, Location location, int monitoringType, int sourcesUsed) {
            mGeofenceId = geofenceId;
            mTransition = transition;
            mTimestamp = timestamp;
            mLocation = location;
            mMonitoringType = monitoringType;
            mSourcesUsed = sourcesUsed;
        }
    }

    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;
            case GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE:
                return RESOLUTION_LEVEL_FINE;
        }
        return RESOLUTION_LEVEL_NONE;
    }

    clreplaced Reaper implements IBinder.DeathRecipient {

        private IGeofenceHardwareMonitorCallback mMonitorCallback;

        private IGeofenceHardwareCallback mCallback;

        private int mMonitoringType;

        Reaper(IGeofenceHardwareCallback c, int monitoringType) {
            mCallback = c;
            mMonitoringType = monitoringType;
        }

        Reaper(IGeofenceHardwareMonitorCallback c, int monitoringType) {
            mMonitorCallback = c;
            mMonitoringType = monitoringType;
        }

        @Override
        public void binderDied() {
            Message m;
            if (mCallback != null) {
                m = mGeofenceHandler.obtainMessage(GEOFENCE_CALLBACK_BINDER_DIED, mCallback);
                m.arg1 = mMonitoringType;
                mGeofenceHandler.sendMessage(m);
            } else if (mMonitorCallback != null) {
                m = mCallbacksHandler.obtainMessage(MONITOR_CALLBACK_BINDER_DIED, mMonitorCallback);
                m.arg1 = mMonitoringType;
                mCallbacksHandler.sendMessage(m);
            }
            Message reaperMessage = mReaperHandler.obtainMessage(REAPER_REMOVED, this);
            mReaperHandler.sendMessage(reaperMessage);
        }

        @Override
        public int hashCode() {
            int result = 17;
            result = 31 * result + (mCallback != null ? mCallback.asBinder().hashCode() : 0);
            result = 31 * result + (mMonitorCallback != null ? mMonitorCallback.asBinder().hashCode() : 0);
            result = 31 * result + mMonitoringType;
            return result;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == null)
                return false;
            if (obj == this)
                return true;
            Reaper rhs = (Reaper) obj;
            return binderEquals(rhs.mCallback, mCallback) && binderEquals(rhs.mMonitorCallback, mMonitorCallback) && rhs.mMonitoringType == mMonitoringType;
        }

        /**
         * Compares the underlying Binder of the given two IInterface objects and returns true if
         * they equals. null values are accepted.
         */
        private boolean binderEquals(IInterface left, IInterface right) {
            if (left == null) {
                return right == null;
            } else {
                return right == null ? false : left.asBinder() == right.asBinder();
            }
        }

        /**
         * Unlinks this DeathRecipient.
         */
        private boolean unlinkToDeath() {
            if (mMonitorCallback != null) {
                return mMonitorCallback.asBinder().unlinkToDeath(this, 0);
            } else if (mCallback != null) {
                return mCallback.asBinder().unlinkToDeath(this, 0);
            }
            return true;
        }

        private boolean callbackEquals(IGeofenceHardwareCallback cb) {
            return mCallback != null && mCallback.asBinder() == cb.asBinder();
        }
    }

    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;
        }
    }
}