blob: ee2d43ce45f320a579a4bcf434166ea1bf5a9bbe [file] [log] [blame]
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.hardware.location;
import android.content.Context;
import android.content.pm.PackageManager;
import android.location.IFusedGeofenceHardware;
import android.location.IGpsGeofenceHardware;
import android.location.Location;
import android.os.Handler;
import android.os.IBinder;
import android.os.IInterface;
import android.os.Message;
import android.os.PowerManager;
import android.os.RemoteException;
import android.util.Log;
import android.util.SparseArray;
import java.util.ArrayList;
import java.util.Iterator;
/**
* This class manages the geofences which are handled by hardware.
*
* @hide
*/
public final class GeofenceHardwareImpl {
private static final String TAG = "GeofenceHardwareImpl";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private 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_ALTITUDE = 2;
private static final int LOCATION_HAS_SPEED = 4;
private static final int LOCATION_HAS_BEARING = 8;
private static final int LOCATION_HAS_ACCURACY = 16;
// Resolution level constants used for permission checks.
// These constants must be in increasing order of finer resolution.
private static final int RESOLUTION_LEVEL_NONE = 1;
private static final int RESOLUTION_LEVEL_COARSE = 2;
private static final int RESOLUTION_LEVEL_FINE = 3;
// 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 assumed 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.getLatitude(),
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 class. 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 associated 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 associated 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 class 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;
}
class 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;
}
}
}