Add support in the platform for Flp Geofencing.

Change-Id: I0fb0e276d3a06322697bb5d46323779aca1f78c5
diff --git a/Android.mk b/Android.mk
index 22ee3a6..bdc83cf 100644
--- a/Android.mk
+++ b/Android.mk
@@ -355,6 +355,7 @@
 	frameworks/base/core/java/android/content/SyncStats.aidl \
 	frameworks/base/core/java/android/content/res/Configuration.aidl \
 	frameworks/base/core/java/android/database/CursorWindow.aidl \
+	frameworks/base/core/java/android/hardware/location/GeofenceHardwareRequestParcelable.aidl \
 	frameworks/base/core/java/android/net/Uri.aidl \
 	frameworks/base/core/java/android/nfc/NdefMessage.aidl \
 	frameworks/base/core/java/android/nfc/NdefRecord.aidl \
diff --git a/core/java/android/hardware/location/GeofenceHardware.java b/core/java/android/hardware/location/GeofenceHardware.java
index e67d0d7..21de9f5 100644
--- a/core/java/android/hardware/location/GeofenceHardware.java
+++ b/core/java/android/hardware/location/GeofenceHardware.java
@@ -15,16 +15,11 @@
  */
 package android.hardware.location;
 
-import android.content.Context;
 import android.location.Location;
 import android.os.RemoteException;
-import android.util.Log;
 
 import java.lang.ref.WeakReference;
 import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-
 
 /**
  * This class handles geofences managed by various hardware subsystems. It contains
@@ -52,7 +47,7 @@
     private IGeofenceHardware mService;
 
     // Hardware systems that do geofence monitoring.
-    static final int NUM_MONITORS = 1;
+    static final int NUM_MONITORS = 2;
 
     /**
      * Constant for geofence monitoring done by the GPS hardware.
@@ -60,6 +55,13 @@
     public static final int MONITORING_TYPE_GPS_HARDWARE = 0;
 
     /**
+     * Constant for geofence monitoring done by the Fused hardware.
+     *
+     * @hide
+     */
+    public static final int MONITORING_TYPE_FUSED_HARDWARE = 1;
+
+    /**
      * Constant to indiciate that the monitoring system is currently
      * available for monitoring geofences.
      */
@@ -124,8 +126,12 @@
      */
     public static final int GEOFENCE_FAILURE = 5;
 
-    static final int GPS_GEOFENCE_UNAVAILABLE = 1<<0L;
-    static final int GPS_GEOFENCE_AVAILABLE = 1<<1L;
+    /**
+     * The constant used to indicate that the operation failed due to insufficient memory.
+     *
+     * @hide
+     */
+    public static final int GEOFENCE_ERROR_INSUFFICIENT_MEMORY = 6;
 
     private HashMap<GeofenceHardwareCallback, GeofenceHardwareCallbackWrapper>
             mCallbacks = new HashMap<GeofenceHardwareCallback, GeofenceHardwareCallbackWrapper>();
diff --git a/core/java/android/hardware/location/GeofenceHardwareImpl.java b/core/java/android/hardware/location/GeofenceHardwareImpl.java
index 77e3143..eac6620 100644
--- a/core/java/android/hardware/location/GeofenceHardwareImpl.java
+++ b/core/java/android/hardware/location/GeofenceHardwareImpl.java
@@ -18,23 +18,21 @@
 
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.location.FusedBatchOptions;
+import android.location.IFusedGeofenceHardware;
 import android.location.IGpsGeofenceHardware;
 import android.location.Location;
 import android.location.LocationManager;
-import android.os.Binder;
-import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
 import android.os.PowerManager;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.util.Log;
 import android.util.SparseArray;
 
 import java.util.ArrayList;
-import java.util.HashMap;
 
 /**
  * This class manages the geofences which are handled by hardware.
@@ -54,6 +52,7 @@
             new ArrayList[GeofenceHardware.NUM_MONITORS];
     private final ArrayList<Reaper> mReapers = new ArrayList<Reaper>();
 
+    private IFusedGeofenceHardware mFusedService;
     private IGpsGeofenceHardware mGpsService;
 
     private int[] mSupportedMonitorTypes = new int[GeofenceHardware.NUM_MONITORS];
@@ -67,7 +66,7 @@
     private static final int GEOFENCE_CALLBACK_BINDER_DIED = 6;
 
     // mCallbacksHandler message types
-    private static final int GPS_GEOFENCE_STATUS = 1;
+    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;
@@ -91,16 +90,6 @@
     private static final int RESOLUTION_LEVEL_COARSE = 2;
     private static final int RESOLUTION_LEVEL_FINE = 3;
 
-    // GPS Geofence errors. Should match gps.h constants.
-    private static final int GPS_GEOFENCE_OPERATION_SUCCESS = 0;
-    private static final int GPS_GEOFENCE_ERROR_TOO_MANY_GEOFENCES = 100;
-    private static final int GPS_GEOFENCE_ERROR_ID_EXISTS  = -101;
-    private static final int GPS_GEOFENCE_ERROR_ID_UNKNOWN = -102;
-    private static final int GPS_GEOFENCE_ERROR_INVALID_TRANSITION = -103;
-    private static final int GPS_GEOFENCE_ERROR_GENERIC = -149;
-
-
-
     public synchronized static GeofenceHardwareImpl getInstance(Context context) {
         if (sInstance == null) {
             sInstance = new GeofenceHardwareImpl(context);
@@ -113,6 +102,9 @@
         // Init everything to unsupported.
         setMonitorAvailability(GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE,
                 GeofenceHardware.MONITOR_UNSUPPORTED);
+        setMonitorAvailability(
+                GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE,
+                GeofenceHardware.MONITOR_UNSUPPORTED);
 
     }
 
@@ -147,6 +139,22 @@
         }
     }
 
+    private void updateFusedHardwareAvailability() {
+        boolean fusedSupported;
+        try {
+            fusedSupported = mFusedService.isSupported();
+        } 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;
@@ -159,12 +167,39 @@
         }
     }
 
+    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) {
-            if (mSupportedMonitorTypes[GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE] !=
-                        GeofenceHardware.MONITOR_UNSUPPORTED) {
-                return new int[] {GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE};
+            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];
         }
     }
@@ -213,6 +248,30 @@
                     result = false;
                 }
                 break;
+            case GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE:
+                if(mFusedService == null) {
+                    return false;
+                }
+                GeofenceHardwareRequest request = GeofenceHardwareRequest.createCircularGeofence(
+                        latitude,
+                        longitude,
+                        radius);
+                request.setUnknownTimer(unknownTimer);
+                request.setNotificationResponsiveness(notificationResponsivenes);
+                request.setMonitorTransitions(monitorTransitions);
+                request.setLastTransition(lastTransition);
+
+                GeofenceHardwareRequestParcelable parcelableRequest =
+                        new GeofenceHardwareRequestParcelable(geofenceId, request);
+                try {
+                    mFusedService.addGeofences(
+                            new GeofenceHardwareRequestParcelable[] { parcelableRequest });
+                    result = true;
+                } catch(RemoteException e) {
+                    Log.e(TAG, "AddGeofence: RemoteException calling LocationManagerService");
+                    result = false;
+                }
+                break;
             default:
                 result = false;
         }
@@ -251,6 +310,18 @@
                     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;
         }
@@ -278,6 +349,18 @@
                     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;
         }
@@ -306,6 +389,18 @@
                     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;
         }
@@ -334,127 +429,106 @@
         return true;
     }
 
-    private Location getLocation(int flags, double latitude,
-            double longitude, double altitude, float speed, float bearing, float accuracy,
-            long timestamp) {
-        if (DEBUG) Log.d(TAG, "GetLocation: " + flags + ":" + latitude);
-        Location location = new Location(LocationManager.GPS_PROVIDER);
-        if ((flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) {
-            location.setLatitude(latitude);
-            location.setLongitude(longitude);
-            location.setTime(timestamp);
-            // It would be nice to push the elapsed real-time timestamp
-            // further down the stack, but this is still useful
-            location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
+    /**
+     * 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=%p", location));
+            return;
         }
-        if ((flags & LOCATION_HAS_ALTITUDE) == LOCATION_HAS_ALTITUDE) {
-            location.setAltitude(altitude);
-        } else {
-            location.removeAltitude();
+        if(DEBUG) {
+            Log.d(
+                    TAG,
+                    "GeofenceTransition| " + location + ", transition:" + transition +
+                    ", transitionTimestamp:" + transitionTimestamp + ", monitoringType:" +
+                    monitoringType + ", sourcesUsed:" + sourcesUsed);
         }
-        if ((flags & LOCATION_HAS_SPEED) == LOCATION_HAS_SPEED) {
-            location.setSpeed(speed);
-        } else {
-            location.removeSpeed();
-        }
-        if ((flags & LOCATION_HAS_BEARING) == LOCATION_HAS_BEARING) {
-            location.setBearing(bearing);
-        } else {
-            location.removeBearing();
-        }
-        if ((flags & LOCATION_HAS_ACCURACY) == LOCATION_HAS_ACCURACY) {
-            location.setAccuracy(accuracy);
-        } else {
-            location.removeAccuracy();
-        }
-        return location;
+
+        GeofenceTransition geofenceTransition = new GeofenceTransition(
+                geofenceId,
+                transition,
+                transitionTimestamp,
+                location,
+                monitoringType,
+                sourcesUsed);
+        acquireWakeLock();
+
+        Message message = mGeofenceHandler.obtainMessage(
+                GEOFENCE_TRANSITION_CALLBACK,
+                geofenceTransition);
+        message.sendToTarget();
     }
 
     /**
-     * called from GpsLocationProvider to report geofence transition
+     * Used to report Monitor status changes.
      */
-    public void reportGpsGeofenceTransition(int geofenceId, int flags, double latitude,
-            double longitude, double altitude, float speed, float bearing, float accuracy,
-            long timestamp, int transition, long transitionTimestamp) {
-        if (DEBUG) Log.d(TAG, "GeofenceTransition: Flags: " + flags + " Lat: " + latitude +
-            " Long: " + longitude + " Altitude: " + altitude + " Speed: " + speed + " Bearing: " +
-            bearing + " Accuracy: " + accuracy + " Timestamp: " + timestamp + " Transition: " +
-            transition + " TransitionTimestamp: " + transitionTimestamp);
-        Location location = getLocation(flags, latitude, longitude, altitude, speed, bearing,
-                accuracy, timestamp);
-        GeofenceTransition t = new GeofenceTransition(geofenceId, transition, timestamp, location);
+    public void reportGeofenceMonitorStatus(
+            int monitoringType,
+            int monitoringStatus,
+            Location location,
+            int source) {
+        // TODO: use the source if needed in the future
+        setMonitorAvailability(monitoringType, monitoringStatus);
         acquireWakeLock();
-        Message m = mGeofenceHandler.obtainMessage(GEOFENCE_TRANSITION_CALLBACK, t);
-        mGeofenceHandler.sendMessage(m);
+        Message message = mCallbacksHandler.obtainMessage(GEOFENCE_STATUS, location);
+        message.arg1 = monitoringStatus;
+        message.arg2 = monitoringType;
+        message.sendToTarget();
     }
 
     /**
-     * called from GpsLocationProvider to report GPS status change.
+     * 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.
      */
-    public void reportGpsGeofenceStatus(int status, int flags, double latitude,
-            double longitude, double altitude, float speed, float bearing, float accuracy,
-            long timestamp) {
-        Location location = getLocation(flags, latitude, longitude, altitude, speed, bearing,
-                accuracy, timestamp);
-        boolean available = false;
-        if (status == GeofenceHardware.GPS_GEOFENCE_AVAILABLE) available = true;
-
-        int val = (available ? GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE :
-                GeofenceHardware.MONITOR_CURRENTLY_UNAVAILABLE);
-        setMonitorAvailability(GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE, val);
-
+    private void reportGeofenceOperationStatus(int operation, int geofenceId, int operationStatus) {
         acquireWakeLock();
-        Message m = mCallbacksHandler.obtainMessage(GPS_GEOFENCE_STATUS, location);
-        m.arg1 = val;
-        mCallbacksHandler.sendMessage(m);
+        Message message = mGeofenceHandler.obtainMessage(operation);
+        message.arg1 = geofenceId;
+        message.arg2 = operationStatus;
+        message.sendToTarget();
     }
 
     /**
-     * called from GpsLocationProvider add geofence callback.
+     * Used to report the status of a Geofence Add operation.
      */
-    public void reportGpsGeofenceAddStatus(int geofenceId, int status) {
-        if (DEBUG) Log.d(TAG, "Add Callback: GPS : Id: " + geofenceId + " Status: " + status);
-        acquireWakeLock();
-        Message m = mGeofenceHandler.obtainMessage(ADD_GEOFENCE_CALLBACK);
-        m.arg1 = geofenceId;
-        m.arg2 = getGeofenceStatus(status);
-        mGeofenceHandler.sendMessage(m);
+    public void reportGeofenceAddStatus(int geofenceId, int status) {
+        if(DEBUG) Log.d(TAG, "AddCallback| id:" + geofenceId + ", status:" + status);
+        reportGeofenceOperationStatus(ADD_GEOFENCE_CALLBACK, geofenceId, status);
     }
 
     /**
-     * called from GpsLocationProvider remove geofence callback.
+     * Used to report the status of a Geofence Remove operation.
      */
-    public void reportGpsGeofenceRemoveStatus(int geofenceId, int status) {
-        if (DEBUG) Log.d(TAG, "Remove Callback: GPS : Id: " + geofenceId + " Status: " + status);
-        acquireWakeLock();
-        Message m = mGeofenceHandler.obtainMessage(REMOVE_GEOFENCE_CALLBACK);
-        m.arg1 = geofenceId;
-        m.arg2 = getGeofenceStatus(status);
-        mGeofenceHandler.sendMessage(m);
+    public void reportGeofenceRemoveStatus(int geofenceId, int status) {
+        if(DEBUG) Log.d(TAG, "RemoveCallback| id:" + geofenceId + ", status:" + status);
+        reportGeofenceOperationStatus(REMOVE_GEOFENCE_CALLBACK, geofenceId, status);
     }
 
     /**
-     * called from GpsLocationProvider pause geofence callback.
+     * Used to report the status of a Geofence Pause operation.
      */
-    public void reportGpsGeofencePauseStatus(int geofenceId, int status) {
-        if (DEBUG) Log.d(TAG, "Pause Callback: GPS : Id: " + geofenceId + " Status: " + status);
-        acquireWakeLock();
-        Message m = mGeofenceHandler.obtainMessage(PAUSE_GEOFENCE_CALLBACK);
-        m.arg1 = geofenceId;
-        m.arg2 = getGeofenceStatus(status);
-        mGeofenceHandler.sendMessage(m);
+    public void reportGeofencePauseStatus(int geofenceId, int status) {
+        if(DEBUG) Log.d(TAG, "PauseCallbac| id:" + geofenceId + ", status" + status);
+        reportGeofenceOperationStatus(PAUSE_GEOFENCE_CALLBACK, geofenceId, status);
     }
 
     /**
-     * called from GpsLocationProvider resume geofence callback.
+     * Used to report the status of a Geofence Resume operation.
      */
-    public void reportGpsGeofenceResumeStatus(int geofenceId, int status) {
-        if (DEBUG) Log.d(TAG, "Resume Callback: GPS : Id: " + geofenceId + " Status: " + status);
-        acquireWakeLock();
-        Message m = mGeofenceHandler.obtainMessage(RESUME_GEOFENCE_CALLBACK);
-        m.arg1 = geofenceId;
-        m.arg2 = getGeofenceStatus(status);
-        mGeofenceHandler.sendMessage(m);
+    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
@@ -539,7 +613,7 @@
                             callback.onGeofenceTransition(
                                     geofenceTransition.mGeofenceId, geofenceTransition.mTransition,
                                     geofenceTransition.mLocation, geofenceTransition.mTimestamp,
-                                    GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE);
+                                    geofenceTransition.mMonitoringType);
                         } catch (RemoteException e) {}
                     }
                     releaseWakeLock();
@@ -571,21 +645,20 @@
             IGeofenceHardwareMonitorCallback callback;
 
             switch (msg.what) {
-                case GPS_GEOFENCE_STATUS:
+                case GEOFENCE_STATUS:
                     Location location = (Location) msg.obj;
                     int val = msg.arg1;
+                    monitoringType = msg.arg2;
                     boolean available;
                     available = (val == GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE ?
                             true : false);
-                    callbackList = mCallbacks[GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE];
+                    callbackList = mCallbacks[monitoringType];
                     if (callbackList != null) {
                         if (DEBUG) Log.d(TAG, "MonitoringSystemChangeCallback: GPS : " + available);
 
                         for (IGeofenceHardwareMonitorCallback c: callbackList) {
                             try {
-                                c.onMonitoringSystemChange(
-                                        GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE, available,
-                                        location);
+                                c.onMonitoringSystemChange(monitoringType, available, location);
                             } catch (RemoteException e) {}
                         }
                     }
@@ -666,12 +739,22 @@
         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) {
+        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;
         }
     }
 
@@ -686,6 +769,8 @@
         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;
     }
@@ -752,22 +837,4 @@
             return RESOLUTION_LEVEL_NONE;
         }
     }
-
-    private int getGeofenceStatus(int status) {
-        switch (status) {
-            case GPS_GEOFENCE_OPERATION_SUCCESS:
-                return GeofenceHardware.GEOFENCE_SUCCESS;
-            case GPS_GEOFENCE_ERROR_GENERIC:
-                return GeofenceHardware.GEOFENCE_FAILURE;
-            case GPS_GEOFENCE_ERROR_ID_EXISTS:
-                return GeofenceHardware.GEOFENCE_ERROR_ID_EXISTS;
-            case GPS_GEOFENCE_ERROR_INVALID_TRANSITION:
-                return GeofenceHardware.GEOFENCE_ERROR_INVALID_TRANSITION;
-            case GPS_GEOFENCE_ERROR_TOO_MANY_GEOFENCES:
-                return GeofenceHardware.GEOFENCE_ERROR_TOO_MANY_GEOFENCES;
-            case GPS_GEOFENCE_ERROR_ID_UNKNOWN:
-                return GeofenceHardware.GEOFENCE_ERROR_ID_UNKNOWN;
-        }
-        return -1;
-    }
 }
diff --git a/core/java/android/hardware/location/GeofenceHardwareRequestParcelable.aidl b/core/java/android/hardware/location/GeofenceHardwareRequestParcelable.aidl
new file mode 100644
index 0000000..b599d44
--- /dev/null
+++ b/core/java/android/hardware/location/GeofenceHardwareRequestParcelable.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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;
+
+parcelable GeofenceHardwareRequestParcelable;
diff --git a/core/java/android/hardware/location/GeofenceHardwareRequestParcelable.java b/core/java/android/hardware/location/GeofenceHardwareRequestParcelable.java
new file mode 100644
index 0000000..40e7fc4
--- /dev/null
+++ b/core/java/android/hardware/location/GeofenceHardwareRequestParcelable.java
@@ -0,0 +1,151 @@
+/*
+ * 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.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+/**
+ * Geofence Hardware Request used for internal location services communication.
+ *
+ * @hide
+ */
+public final class GeofenceHardwareRequestParcelable implements Parcelable {
+    private GeofenceHardwareRequest mRequest;
+    private int mId;
+
+    public GeofenceHardwareRequestParcelable(int id, GeofenceHardwareRequest request) {
+        mId = id;
+        mRequest = request;
+    }
+
+    /**
+     * Returns the id of this request.
+     */
+    public int getId() {
+        return mId;
+    }
+
+    /**
+     * Returns the latitude of this geofence.
+     */
+    public double getLatitude() {
+        return mRequest.getLatitude();
+    }
+
+    /**
+     * Returns the longitude of this geofence.
+     */
+    public double getLongitude() {
+        return mRequest.getLongitude();
+    }
+
+    /**
+     * Returns the radius of this geofence.
+     */
+    public double getRadius() {
+        return mRequest.getRadius();
+    }
+
+    /**
+     * Returns transitions monitored for this geofence.
+     */
+    public int getMonitorTransitions() {
+        return mRequest.getMonitorTransitions();
+    }
+
+    /**
+     * Returns the unknownTimer of this geofence.
+     */
+    public int getUnknownTimer() {
+        return mRequest.getUnknownTimer();
+    }
+
+    /**
+     * Returns the notification responsiveness of this geofence.
+     */
+    public int getNotificationResponsiveness() {
+        return mRequest.getNotificationResponsiveness();
+    }
+
+    /**
+     * Returns the last transition of this geofence.
+     */
+    public int getLastTransition() {
+        return mRequest.getLastTransition();
+    }
+
+    /**
+     * Returns the type of the geofence for the current request.
+     */
+    int getType() {
+        return mRequest.getType();
+    }
+
+    /**
+     * Method definitions to support Parcelable operations.
+     */
+    public static final Parcelable.Creator<GeofenceHardwareRequestParcelable> CREATOR = 
+            new Parcelable.Creator<GeofenceHardwareRequestParcelable>() {
+        @Override
+        public GeofenceHardwareRequestParcelable createFromParcel(Parcel parcel) {
+            int geofenceType = parcel.readInt();
+            if(geofenceType != GeofenceHardwareRequest.GEOFENCE_TYPE_CIRCLE) {
+                Log.e(
+                        "GeofenceHardwareRequest",
+                        String.format("Invalid Geofence type: %d", geofenceType));
+                return null;
+            }
+
+            GeofenceHardwareRequest request = GeofenceHardwareRequest.createCircularGeofence(
+                    parcel.readDouble(),
+                    parcel.readDouble(),
+                    parcel.readDouble());
+            request.setLastTransition(parcel.readInt());
+            request.setMonitorTransitions(parcel.readInt());
+            request.setUnknownTimer(parcel.readInt());
+            request.setNotificationResponsiveness(parcel.readInt());
+            
+            int id = parcel.readInt();
+            return new GeofenceHardwareRequestParcelable(id, request);
+        }
+
+        @Override
+        public GeofenceHardwareRequestParcelable[] newArray(int size) {
+            return new GeofenceHardwareRequestParcelable[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeInt(getType());
+        parcel.writeDouble(getLatitude());
+        parcel.writeDouble(getLongitude());
+        parcel.writeDouble(getRadius());
+        parcel.writeInt(getLastTransition());
+        parcel.writeInt(getMonitorTransitions());
+        parcel.writeInt(getUnknownTimer());
+        parcel.writeInt(getNotificationResponsiveness());
+        parcel.writeInt(getId());
+    }
+}
diff --git a/core/java/android/hardware/location/GeofenceHardwareService.java b/core/java/android/hardware/location/GeofenceHardwareService.java
index 3bc70ee..fb238bd 100644
--- a/core/java/android/hardware/location/GeofenceHardwareService.java
+++ b/core/java/android/hardware/location/GeofenceHardwareService.java
@@ -20,6 +20,7 @@
 import android.app.Service;
 import android.content.Context;
 import android.content.Intent;
+import android.location.IFusedGeofenceHardware;
 import android.location.IGpsGeofenceHardware;
 import android.os.Binder;
 import android.os.IBinder;
@@ -68,6 +69,10 @@
             mGeofenceHardwareImpl.setGpsHardwareGeofence(service);
         }
 
+        public void setFusedGeofenceHardware(IFusedGeofenceHardware service) {
+            mGeofenceHardwareImpl.setFusedGeofenceHardware(service);
+        }
+
         public int[] getMonitoringTypes() {
             mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
                     "Location Hardware permission not granted to access hardware geofence");
diff --git a/core/java/android/hardware/location/IGeofenceHardware.aidl b/core/java/android/hardware/location/IGeofenceHardware.aidl
index 6900070..8900166 100644
--- a/core/java/android/hardware/location/IGeofenceHardware.aidl
+++ b/core/java/android/hardware/location/IGeofenceHardware.aidl
@@ -16,6 +16,7 @@
 
 package android.hardware.location;
 
+import android.location.IFusedGeofenceHardware;
 import android.location.IGpsGeofenceHardware;
 import android.hardware.location.IGeofenceHardwareCallback;
 import android.hardware.location.IGeofenceHardwareMonitorCallback;
@@ -23,6 +24,7 @@
 /** @hide */
 interface IGeofenceHardware {
     void setGpsGeofenceHardware(in IGpsGeofenceHardware service);
+    void setFusedGeofenceHardware(in IFusedGeofenceHardware service);
     int[] getMonitoringTypes();
     int getStatusOfMonitoringType(int monitoringType);
     boolean addCircularFence(int id,  int monitoringType, double lat, double longitude,
diff --git a/location/java/android/location/IFusedGeofenceHardware.aidl b/location/java/android/location/IFusedGeofenceHardware.aidl
index 9dbf1f4..d8c3585 100644
--- a/location/java/android/location/IFusedGeofenceHardware.aidl
+++ b/location/java/android/location/IFusedGeofenceHardware.aidl
@@ -16,8 +16,8 @@
  
 package android.location;
 
-import android.location.Geofence;
- 
+import android.hardware.location.GeofenceHardwareRequestParcelable;
+
 /**
  * Fused Geofence Hardware interface.
  *
@@ -39,11 +39,9 @@
     /**
      * Adds a given list of geofences to the system.
      *
-     * @param geofenceIdsArray    The list of geofence Ids to add.
-     * @param geofencesArray      the list of geofences to add.
+     * @param geofenceRequestsArray    The list of geofences to add.
      */
-    // TODO: [GeofenceIntegration] GeofenceHardwareRequest is not a parcelable class exposed in aidl
-    void addGeofences(in int[] geofenceIdsArray, in Geofence[] geofencesArray);
+    void addGeofences(in GeofenceHardwareRequestParcelable[] geofenceRequestsArray);
 
     /**
      * Removes a give list of geofences from the system.
@@ -79,7 +77,8 @@
      *                                      the geofence.
      * @param monitorTransitions            The set of transitions to monitor.
      * @param notificationResponsiveness    The notification responsivness needed.
-     * @param unknownTimer                  The time span associated with the
+     * @param unknownTimer                  The time span associated with the.
+     * @param sourcesToUse                  The source technologies to use.
      *
      * Remarks: keep the options as separate fields to be able to leverage the class
      * GeofenceHardwareRequest without any changes
@@ -89,5 +88,6 @@
             in int lastTransition,
             in int monitorTransitions,
             in int notificationResponsiveness,
-            in int unknownTimer);
+            in int unknownTimer,
+            in int sourcesToUse);
 }
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index a32699a..d039a5e 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -420,19 +420,7 @@
             Slog.e(TAG,  "no geocoder provider found");
         }
 
-        // bind to geofence provider
-        GeofenceProxy provider = GeofenceProxy.createAndBind(mContext,
-                com.android.internal.R.bool.config_enableGeofenceOverlay,
-                com.android.internal.R.string.config_geofenceProviderPackageName,
-                com.android.internal.R.array.config_locationProviderPackageNames,
-                mLocationHandler,
-                gpsProvider.getGpsGeofenceProxy());
-        if (provider == null) {
-            Slog.e(TAG,  "no geofence provider found");
-        }
-
         // bind to fused provider
-        // TODO: [GeofenceIntegration] bind #getGeofenceHardware() with the GeofenceProxy
         FlpHardwareProvider flpHardwareProvider = FlpHardwareProvider.getInstance(mContext);
         FusedProxy fusedProxy = FusedProxy.createAndBind(
                 mContext,
@@ -441,10 +429,21 @@
                 com.android.internal.R.bool.config_enableFusedLocationOverlay,
                 com.android.internal.R.string.config_fusedLocationProviderPackageName,
                 com.android.internal.R.array.config_locationProviderPackageNames);
-
         if(fusedProxy == null) {
             Slog.e(TAG, "No FusedProvider found.");
         }
+
+        // bind to geofence provider
+        GeofenceProxy provider = GeofenceProxy.createAndBind(mContext,
+                com.android.internal.R.bool.config_enableGeofenceOverlay,
+                com.android.internal.R.string.config_geofenceProviderPackageName,
+                com.android.internal.R.array.config_locationProviderPackageNames,
+                mLocationHandler,
+                gpsProvider.getGpsGeofenceProxy(),
+                flpHardwareProvider.getGeofenceHardware());
+        if (provider == null) {
+            Slog.e(TAG,  "no geofence provider found");
+        }
     }
 
     /**
diff --git a/services/java/com/android/server/location/FlpHardwareProvider.java b/services/java/com/android/server/location/FlpHardwareProvider.java
index 226c18c..ebeccfb 100644
--- a/services/java/com/android/server/location/FlpHardwareProvider.java
+++ b/services/java/com/android/server/location/FlpHardwareProvider.java
@@ -16,12 +16,13 @@
 
 package com.android.server.location;
 
+import android.hardware.location.GeofenceHardware;
 import android.hardware.location.GeofenceHardwareImpl;
+import android.hardware.location.GeofenceHardwareRequestParcelable;
 import android.hardware.location.IFusedLocationHardware;
 import android.hardware.location.IFusedLocationHardwareSink;
 import android.location.IFusedGeofenceHardware;
 import android.location.FusedBatchOptions;
-import android.location.Geofence;
 import android.location.Location;
 import android.location.LocationListener;
 import android.location.LocationManager;
@@ -49,6 +50,15 @@
     private final Context mContext;
     private final Object mLocationSinkLock = new Object();
 
+    // FlpHal result codes, they must be equal to the ones in fused_location.h
+    private static final int FLP_RESULT_SUCCESS = 0;
+    private static final int FLP_RESULT_ERROR = -1;
+    private static final int FLP_RESULT_INSUFFICIENT_MEMORY = -2;
+    private static final int FLP_RESULT_TOO_MANY_GEOFENCES = -3;
+    private static final int FLP_RESULT_ID_EXISTS = -4;
+    private static final int FLP_RESULT_ID_UNKNOWN = -5;
+    private static final int FLP_RESULT_INVALID_GEOFENCE_TRANSITION = -6;
+
     public static FlpHardwareProvider getInstance(Context context) {
         if (sSingletonInstance == null) {
             sSingletonInstance = new FlpHardwareProvider(context);
@@ -120,29 +130,46 @@
             Location location,
             int transition,
             long timestamp,
-            int sourcesUsed
-            ) {
-        // TODO: [GeofenceIntegration] change GeofenceHardwareImpl to accept a location object
+            int sourcesUsed) {
+        getGeofenceHardwareSink().reportGeofenceTransition(
+                geofenceId,
+                updateLocationInformation(location),
+                transition,
+                timestamp,
+                GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE,
+                sourcesUsed);
     }
 
     private void onGeofenceMonitorStatus(int status, int source, Location location) {
-        // TODO: [GeofenceIntegration]
+        getGeofenceHardwareSink().reportGeofenceMonitorStatus(
+                GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE,
+                status,
+                updateLocationInformation(location),
+                source);
     }
 
     private void onGeofenceAdd(int geofenceId, int result) {
-        // TODO: [GeofenceIntegration] map between GPS and FLP results to pass a consistent status
+        getGeofenceHardwareSink().reportGeofenceAddStatus(
+                geofenceId,
+                translateToGeofenceHardwareStatus(result));
     }
 
     private void onGeofenceRemove(int geofenceId, int result) {
-        // TODO: [GeofenceIntegration] map between GPS and FLP results to pass a consistent status
+        getGeofenceHardwareSink().reportGeofenceRemoveStatus(
+                geofenceId,
+                translateToGeofenceHardwareStatus(result));
     }
 
     private void onGeofencePause(int geofenceId, int result) {
-        // TODO; [GeofenceIntegration] map between GPS and FLP results
+        getGeofenceHardwareSink().reportGeofencePauseStatus(
+                geofenceId,
+                translateToGeofenceHardwareStatus(result));
     }
 
     private void onGeofenceResume(int geofenceId, int result) {
-        // TODO: [GeofenceIntegration] map between GPS and FLP results
+        getGeofenceHardwareSink().reportGeofenceResumeStatus(
+                geofenceId,
+                translateToGeofenceHardwareStatus(result));
     }
 
     /**
@@ -175,7 +202,8 @@
 
     // FlpGeofencingInterface members
     private native boolean nativeIsGeofencingSupported();
-    private native void nativeAddGeofences(int[] geofenceIdsArray, Geofence[] geofencesArray);
+    private native void nativeAddGeofences(
+            GeofenceHardwareRequestParcelable[] geofenceRequestsArray);
     private native void nativePauseGeofence(int geofenceId);
     private native void  nativeResumeGeofence(int geofenceId, int monitorTransitions);
     private native void nativeModifyGeofenceOption(
@@ -281,8 +309,8 @@
         }
 
         @Override
-        public void addGeofences(int[] geofenceIdsArray, Geofence[] geofencesArray) {
-            nativeAddGeofences(geofenceIdsArray, geofencesArray);
+        public void addGeofences(GeofenceHardwareRequestParcelable[] geofenceRequestsArray) {
+            nativeAddGeofences(geofenceRequestsArray);
         }
 
         @Override
@@ -305,17 +333,15 @@
                 int lastTransition,
                 int monitorTransitions,
                 int notificationResponsiveness,
-                int unknownTimer
-                ) {
-            // TODO: [GeofenceIntegration] set sourcesToUse to the right value
-            // TODO: expose sourcesToUse externally when needed
+                int unknownTimer,
+                int sourcesToUse) {
             nativeModifyGeofenceOption(
                     geofenceId,
                     lastTransition,
                     monitorTransitions,
                     notificationResponsiveness,
                     unknownTimer,
-                    /* sourcesToUse */ 0xFFFF);
+                    sourcesToUse);
         }
     };
 
@@ -347,10 +373,39 @@
 
     private GeofenceHardwareImpl getGeofenceHardwareSink() {
         if (mGeofenceHardwareSink == null) {
-            // TODO: [GeofenceIntegration] we need to register ourselves with GeofenceHardwareImpl
             mGeofenceHardwareSink = GeofenceHardwareImpl.getInstance(mContext);
         }
 
         return mGeofenceHardwareSink;
     }
-}
\ No newline at end of file
+
+    private static int translateToGeofenceHardwareStatus(int flpHalResult) {
+        switch(flpHalResult) {
+            case FLP_RESULT_SUCCESS:
+                return GeofenceHardware.GEOFENCE_SUCCESS;
+            case FLP_RESULT_ERROR:
+                return GeofenceHardware.GEOFENCE_FAILURE;
+            // TODO: uncomment this once the ERROR definition is marked public
+            //case FLP_RESULT_INSUFFICIENT_MEMORY:
+            //    return GeofenceHardware.GEOFENCE_ERROR_INSUFFICIENT_MEMORY;
+            case FLP_RESULT_TOO_MANY_GEOFENCES:
+                return GeofenceHardware.GEOFENCE_ERROR_TOO_MANY_GEOFENCES;
+            case FLP_RESULT_ID_EXISTS:
+                return GeofenceHardware.GEOFENCE_ERROR_ID_EXISTS;
+            case FLP_RESULT_ID_UNKNOWN:
+                return GeofenceHardware.GEOFENCE_ERROR_ID_UNKNOWN;
+            case FLP_RESULT_INVALID_GEOFENCE_TRANSITION:
+                return GeofenceHardware.GEOFENCE_ERROR_INVALID_TRANSITION;
+            default:
+                Log.e(TAG, String.format("Invalid FlpHal result code: %d", flpHalResult));
+                return GeofenceHardware.GEOFENCE_FAILURE;
+        }
+    }
+
+    private Location updateLocationInformation(Location location) {
+        location.setProvider(LocationManager.FUSED_PROVIDER);
+        // set the elapsed time-stamp just as GPS provider does
+        location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
+        return location;
+    }
+}
diff --git a/services/java/com/android/server/location/GeofenceProxy.java b/services/java/com/android/server/location/GeofenceProxy.java
index f6be27b..a86c923 100644
--- a/services/java/com/android/server/location/GeofenceProxy.java
+++ b/services/java/com/android/server/location/GeofenceProxy.java
@@ -22,6 +22,7 @@
 import android.hardware.location.IGeofenceHardware;
 import android.location.IGeofenceProvider;
 import android.location.IGpsGeofenceHardware;
+import android.location.IFusedGeofenceHardware;
 import android.content.Context;
 import android.os.Handler;
 import android.os.IBinder;
@@ -44,6 +45,7 @@
     private Context mContext;
     private IGeofenceHardware mGeofenceHardware;
     private IGpsGeofenceHardware mGpsGeofenceHardware;
+    private IFusedGeofenceHardware mFusedGeofenceHardware;
 
     private static final int GEOFENCE_PROVIDER_CONNECTED = 1;
     private static final int GEOFENCE_HARDWARE_CONNECTED = 2;
@@ -60,9 +62,11 @@
 
     public static GeofenceProxy createAndBind(Context context,
             int overlaySwitchResId, int defaultServicePackageNameResId,
-            int initialPackageNamesResId, Handler handler, IGpsGeofenceHardware gpsGeofence) {
+            int initialPackageNamesResId, Handler handler, IGpsGeofenceHardware gpsGeofence,
+            IFusedGeofenceHardware fusedGeofenceHardware) {
         GeofenceProxy proxy = new GeofenceProxy(context, overlaySwitchResId,
-            defaultServicePackageNameResId, initialPackageNamesResId, handler, gpsGeofence);
+            defaultServicePackageNameResId, initialPackageNamesResId, handler, gpsGeofence,
+            fusedGeofenceHardware);
         if (proxy.bindGeofenceProvider()) {
             return proxy;
         } else {
@@ -72,11 +76,13 @@
 
     private GeofenceProxy(Context context,
             int overlaySwitchResId, int defaultServicePackageNameResId,
-            int initialPackageNamesResId, Handler handler, IGpsGeofenceHardware gpsGeofence) {
+            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();
     }
 
@@ -123,6 +129,13 @@
         }
     }
 
+    private void setFusedGeofence() {
+        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 class,
@@ -142,6 +155,7 @@
                     break;
                 case GEOFENCE_HARDWARE_CONNECTED:
                     setGpsGeofence();
+                    setFusedGeofence();
                     mGeofenceHardwareConnected = true;
                     if (mGeofenceProviderConnected) {
                         setGeofenceHardwareInProvider();
diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
index 38453c8..6053c61 100644
--- a/services/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -24,9 +24,10 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.database.Cursor;
+import android.hardware.location.GeofenceHardware;
 import android.hardware.location.GeofenceHardwareImpl;
-import android.hardware.location.IGeofenceHardware;
 import android.location.Criteria;
+import android.location.FusedBatchOptions;
 import android.location.IGpsGeofenceHardware;
 import android.location.IGpsStatusListener;
 import android.location.IGpsStatusProvider;
@@ -195,6 +196,17 @@
 
     private static final String PROPERTIES_FILE = "/etc/gps.conf";
 
+    private static final int GPS_GEOFENCE_UNAVAILABLE = 1<<0L;
+    private static final int GPS_GEOFENCE_AVAILABLE = 1<<1L;
+
+    // GPS Geofence errors. Should match gps.h constants.
+    private static final int GPS_GEOFENCE_OPERATION_SUCCESS = 0;
+    private static final int GPS_GEOFENCE_ERROR_TOO_MANY_GEOFENCES = 100;
+    private static final int GPS_GEOFENCE_ERROR_ID_EXISTS  = -101;
+    private static final int GPS_GEOFENCE_ERROR_ID_UNKNOWN = -102;
+    private static final int GPS_GEOFENCE_ERROR_INVALID_TRANSITION = -103;
+    private static final int GPS_GEOFENCE_ERROR_GENERIC = -149;
+
     /** simpler wrapper for ProviderRequest + Worksource */
     private static class GpsRequest {
         public ProviderRequest request;
@@ -501,7 +513,7 @@
                 LocationManager locManager =
                         (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
                 locManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER,
-                        0, 0, new NetworkLocationListener(), mHandler.getLooper());                
+                        0, 0, new NetworkLocationListener(), mHandler.getLooper());
             }
         });
     }
@@ -1405,6 +1417,62 @@
     }
 
     /**
+     * Helper method to construct a location object.
+     */
+    private Location buildLocation(
+            int flags,
+            double latitude,
+            double longitude,
+            double altitude,
+            float speed,
+            float bearing,
+            float accuracy,
+            long timestamp) {
+        Location location = new Location(LocationManager.GPS_PROVIDER);
+        if((flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) {
+            location.setLatitude(latitude);
+            location.setLongitude(longitude);
+            location.setTime(timestamp);
+            location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
+        }
+        if((flags & LOCATION_HAS_ALTITUDE) == LOCATION_HAS_ALTITUDE) {
+            location.setAltitude(altitude);
+        }
+        if((flags & LOCATION_HAS_SPEED) == LOCATION_HAS_SPEED) {
+            location.setSpeed(speed);
+        }
+        if((flags & LOCATION_HAS_BEARING) == LOCATION_HAS_BEARING) {
+            location.setBearing(bearing);
+        }
+        if((flags & LOCATION_HAS_ACCURACY) == LOCATION_HAS_ACCURACY) {
+            location.setAccuracy(accuracy);
+        }
+        return location;
+    }
+
+    /**
+     * Converts the GPS HAL status to the internal Geofence Hardware status.
+     */
+    private int getGeofenceStatus(int status) {
+        switch(status) {
+            case GPS_GEOFENCE_OPERATION_SUCCESS:
+                return GeofenceHardware.GEOFENCE_SUCCESS;
+            case GPS_GEOFENCE_ERROR_GENERIC:
+                return GeofenceHardware.GEOFENCE_FAILURE;
+            case GPS_GEOFENCE_ERROR_ID_EXISTS:
+                return GeofenceHardware.GEOFENCE_ERROR_ID_EXISTS;
+            case GPS_GEOFENCE_ERROR_INVALID_TRANSITION:
+                return GeofenceHardware.GEOFENCE_ERROR_INVALID_TRANSITION;
+            case GPS_GEOFENCE_ERROR_TOO_MANY_GEOFENCES:
+                return GeofenceHardware.GEOFENCE_ERROR_TOO_MANY_GEOFENCES;
+            case GPS_GEOFENCE_ERROR_ID_UNKNOWN:
+                return GeofenceHardware.GEOFENCE_ERROR_ID_UNKNOWN;
+            default:
+                return -1;
+        }
+    }
+
+    /**
      * Called from native to report GPS Geofence transition
      * All geofence callbacks are called on the same thread
      */
@@ -1414,8 +1482,22 @@
         if (mGeofenceHardwareImpl == null) {
             mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
         }
-        mGeofenceHardwareImpl.reportGpsGeofenceTransition(geofenceId, flags, latitude, longitude,
-           altitude, speed, bearing, accuracy, timestamp, transition, transitionTimestamp);
+        Location location = buildLocation(
+                flags,
+                latitude,
+                longitude,
+                altitude,
+                speed,
+                bearing,
+                accuracy,
+                timestamp);
+        mGeofenceHardwareImpl.reportGeofenceTransition(
+                geofenceId,
+                location,
+                transition,
+                transitionTimestamp,
+                GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE,
+                FusedBatchOptions.SourceTechnologies.GNSS);
     }
 
     /**
@@ -1427,8 +1509,24 @@
         if (mGeofenceHardwareImpl == null) {
             mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
         }
-        mGeofenceHardwareImpl.reportGpsGeofenceStatus(status, flags, latitude, longitude, altitude,
-            speed, bearing, accuracy, timestamp);
+        Location location = buildLocation(
+                flags,
+                latitude,
+                longitude,
+                altitude,
+                speed,
+                bearing,
+                accuracy,
+                timestamp);
+        int monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_UNAVAILABLE;
+        if(status == GPS_GEOFENCE_AVAILABLE) {
+            monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE;
+        }
+        mGeofenceHardwareImpl.reportGeofenceMonitorStatus(
+                GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE,
+                monitorStatus,
+                location,
+                FusedBatchOptions.SourceTechnologies.GNSS);
     }
 
     /**
@@ -1438,7 +1536,7 @@
         if (mGeofenceHardwareImpl == null) {
             mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
         }
-        mGeofenceHardwareImpl.reportGpsGeofenceAddStatus(geofenceId, status);
+        mGeofenceHardwareImpl.reportGeofenceAddStatus(geofenceId, getGeofenceStatus(status));
     }
 
     /**
@@ -1448,7 +1546,7 @@
         if (mGeofenceHardwareImpl == null) {
             mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
         }
-        mGeofenceHardwareImpl.reportGpsGeofenceRemoveStatus(geofenceId, status);
+        mGeofenceHardwareImpl.reportGeofenceRemoveStatus(geofenceId, getGeofenceStatus(status));
     }
 
     /**
@@ -1458,7 +1556,7 @@
         if (mGeofenceHardwareImpl == null) {
             mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
         }
-        mGeofenceHardwareImpl.reportGpsGeofencePauseStatus(geofenceId, status);
+        mGeofenceHardwareImpl.reportGeofencePauseStatus(geofenceId, getGeofenceStatus(status));
     }
 
     /**
@@ -1468,7 +1566,7 @@
         if (mGeofenceHardwareImpl == null) {
             mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
         }
-        mGeofenceHardwareImpl.reportGpsGeofenceResumeStatus(geofenceId, status);
+        mGeofenceHardwareImpl.reportGeofenceResumeStatus(geofenceId, getGeofenceStatus(status));
     }
 
     //=============================================================
diff --git a/services/jni/com_android_server_location_FlpHardwareProvider.cpp b/services/jni/com_android_server_location_FlpHardwareProvider.cpp
index 48b86db..c871828 100644
--- a/services/jni/com_android_server_location_FlpHardwareProvider.cpp
+++ b/services/jni/com_android_server_location_FlpHardwareProvider.cpp
@@ -261,6 +261,75 @@
 }
 
 /*
+ * Helper function to unwrap Geofence structures from the Java Runtime calls.
+ */
+static void TranslateGeofenceFromGeofenceHardwareRequestParcelable(
+    JNIEnv* env,
+    jobject geofenceRequestObject,
+    Geofence& geofence) {
+  jclass geofenceRequestClass = env->GetObjectClass(geofenceRequestObject);
+
+  jmethodID getId = env->GetMethodID(geofenceRequestClass, "getId", "()I");
+  geofence.geofence_id = env->CallIntMethod(geofenceRequestObject, getId);
+
+  jmethodID getType = env->GetMethodID(geofenceRequestClass, "getType", "()I");
+  // this works because GeofenceHardwareRequest.java and fused_location.h have
+  // the same notion of geofence types
+  GeofenceType type = (GeofenceType)env->CallIntMethod(geofenceRequestObject, getType);
+  if(type != TYPE_CIRCLE) {
+    ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
+  }
+  geofence.data->type = type;
+  GeofenceCircle& circle = geofence.data->geofence.circle;
+
+  jmethodID getLatitude = env->GetMethodID(
+      geofenceRequestClass,
+      "getLatitude",
+      "()D");
+  circle.latitude = env->CallDoubleMethod(geofenceRequestObject, getLatitude);
+
+  jmethodID getLongitude = env->GetMethodID(
+      geofenceRequestClass,
+      "getLongitude",
+      "()D");
+  circle.longitude = env->CallDoubleMethod(geofenceRequestObject, getLongitude);
+
+  jmethodID getRadius = env->GetMethodID(geofenceRequestClass, "getRadius", "()D");
+  circle.radius_m = env->CallDoubleMethod(geofenceRequestObject, getRadius);
+
+  GeofenceOptions* options = geofence.options;
+  jmethodID getMonitorTransitions = env->GetMethodID(
+      geofenceRequestClass,
+      "getMonitorTransitions",
+      "()I");
+  options->monitor_transitions = env->CallIntMethod(
+      geofenceRequestObject,
+      getMonitorTransitions);
+
+  jmethodID getUnknownTimer = env->GetMethodID(
+      geofenceRequestClass,
+      "getUnknownTimer",
+      "()I");
+  options->unknown_timer_ms = env->CallIntMethod(geofenceRequestObject, getUnknownTimer);
+
+  jmethodID getNotificationResponsiveness = env->GetMethodID(
+      geofenceRequestClass,
+      "getNotificationResponsiveness",
+      "()D");
+  options->notification_responsivenes_ms = env->CallIntMethod(
+      geofenceRequestObject,
+      getNotificationResponsiveness);
+
+  jmethodID getLastTransition = env->GetMethodID(
+      geofenceRequestClass,
+      "getLastTransition",
+      "()I");
+  options->last_transition = env->CallIntMethod(geofenceRequestObject, getLastTransition);
+
+  // TODO: set data.sources_to_use when available
+}
+
+/*
  * Helper function to transform FlpLocation into a java object.
  */
 static void TranslateToObject(const FlpLocation* location, jobject& locationObject) {
@@ -559,7 +628,7 @@
   }
 
   err = module->methods->open(
-        module, 
+        module,
         FUSED_LOCATION_HARDWARE_MODULE_ID, &sHardwareDevice);
   if(err != 0) {
     ALOGE("Error opening device '%s': %d", FUSED_LOCATION_HARDWARE_MODULE_ID, err);
@@ -749,10 +818,9 @@
 static void AddGeofences(
     JNIEnv* env,
     jobject object,
-    jintArray geofenceIdsArray,
-    jobjectArray geofencesArray) {
-  if(geofencesArray == NULL) {
-    ALOGE("Invalid Geofences to add: %p", geofencesArray);
+    jobjectArray geofenceRequestsArray) {
+  if(geofenceRequestsArray == NULL) {
+    ALOGE("Invalid Geofences to add: %p", geofenceRequestsArray);
     ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
   }
 
@@ -760,23 +828,32 @@
     ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
   }
 
-  jint geofencesCount = env->GetArrayLength(geofenceIdsArray);
-  Geofence* geofences = new Geofence[geofencesCount];
+  jint geofenceRequestsCount = env->GetArrayLength(geofenceRequestsArray);
+  if(geofenceRequestsCount == 0) {
+    return;
+  }
+
+  Geofence* geofences = new Geofence[geofenceRequestsCount];
   if (geofences == NULL) {
     ThrowOnError(env, FLP_RESULT_INSUFFICIENT_MEMORY, __FUNCTION__);
   }
 
-  jint* ids = env->GetIntArrayElements(geofenceIdsArray, /* isCopy */ NULL);
-  for (int i = 0; i < geofencesCount; ++i) {
-    geofences[i].geofence_id = ids[i];
+  for (int i = 0; i < geofenceRequestsCount; ++i) {
+    geofences[i].data = new GeofenceData();
+    geofences[i].options = new GeofenceOptions();
+    jobject geofenceObject = env->GetObjectArrayElement(geofenceRequestsArray, i);
 
-    // TODO: fill in the GeofenceData
-
-    // TODO: fill in the GeofenceOptions
+    TranslateGeofenceFromGeofenceHardwareRequestParcelable(env, geofenceObject, geofences[i]);
   }
 
-  sFlpGeofencingInterface->add_geofences(geofencesCount, &geofences);
-  if (geofences != NULL) delete[] geofences;
+  sFlpGeofencingInterface->add_geofences(geofenceRequestsCount, &geofences);
+  if (geofences != NULL) {
+    for(int i = 0; i < geofenceRequestsCount; ++i) {
+      delete geofences[i].data;
+      delete geofences[i].options;
+    }
+    delete[] geofences;
+  }
 }
 
 static void PauseGeofence(JNIEnv* env, jobject object, jint geofenceId) {
@@ -847,41 +924,41 @@
   {"nativeCleanup", "()V", reinterpret_cast<void*>(Cleanup)},
   {"nativeIsSupported", "()Z", reinterpret_cast<void*>(IsSupported)},
   {"nativeGetBatchSize", "()I", reinterpret_cast<void*>(GetBatchSize)},
-  {"nativeStartBatching", 
-        "(ILandroid/location/FusedBatchOptions;)V", 
+  {"nativeStartBatching",
+        "(ILandroid/location/FusedBatchOptions;)V",
         reinterpret_cast<void*>(StartBatching)},
-  {"nativeUpdateBatchingOptions", 
-        "(ILandroid/location/FusedBatchOptions;)V", 
+  {"nativeUpdateBatchingOptions",
+        "(ILandroid/location/FusedBatchOptions;)V",
         reinterpret_cast<void*>(UpdateBatchingOptions)},
   {"nativeStopBatching", "(I)V", reinterpret_cast<void*>(StopBatching)},
-  {"nativeRequestBatchedLocation", 
-        "(I)V", 
+  {"nativeRequestBatchedLocation",
+        "(I)V",
         reinterpret_cast<void*>(GetBatchedLocation)},
-  {"nativeInjectLocation", 
-        "(Landroid/location/Location;)V", 
+  {"nativeInjectLocation",
+        "(Landroid/location/Location;)V",
         reinterpret_cast<void*>(InjectLocation)},
-  {"nativeIsDiagnosticSupported", 
-        "()Z", 
+  {"nativeIsDiagnosticSupported",
+        "()Z",
         reinterpret_cast<void*>(IsDiagnosticSupported)},
-  {"nativeInjectDiagnosticData", 
-        "(Ljava/lang/String;)V", 
+  {"nativeInjectDiagnosticData",
+        "(Ljava/lang/String;)V",
         reinterpret_cast<void*>(InjectDiagnosticData)},
-  {"nativeIsDeviceContextSupported", 
-        "()Z", 
+  {"nativeIsDeviceContextSupported",
+        "()Z",
         reinterpret_cast<void*>(IsDeviceContextSupported)},
-  {"nativeInjectDeviceContext", 
-        "(I)V", 
+  {"nativeInjectDeviceContext",
+        "(I)V",
         reinterpret_cast<void*>(InjectDeviceContext)},
-  {"nativeIsGeofencingSupported", 
-        "()Z", 
+  {"nativeIsGeofencingSupported",
+        "()Z",
         reinterpret_cast<void*>(IsGeofencingSupported)},
-  {"nativeAddGeofences", 
-        "([I[Landroid/location/Geofence;)V", 
+  {"nativeAddGeofences",
+        "([Landroid/hardware/location/GeofenceHardwareRequestParcelable;)V",
         reinterpret_cast<void*>(AddGeofences)},
   {"nativePauseGeofence", "(I)V", reinterpret_cast<void*>(PauseGeofence)},
   {"nativeResumeGeofence", "(II)V", reinterpret_cast<void*>(ResumeGeofence)},
-  {"nativeModifyGeofenceOption", 
-        "(IIIIII)V", 
+  {"nativeModifyGeofenceOption",
+        "(IIIIII)V",
         reinterpret_cast<void*>(ModifyGeofenceOption)},
   {"nativeRemoveGeofences", "([I)V", reinterpret_cast<void*>(RemoveGeofences)}
 };