Merge "Use new ISurfaceComposer::destroyDisplay method" into klp-dev
diff --git a/Android.mk b/Android.mk
index 7e34c84..df10876 100644
--- a/Android.mk
+++ b/Android.mk
@@ -128,6 +128,8 @@
 	core/java/android/hardware/display/IDisplayManagerCallback.aidl \
 	core/java/android/hardware/input/IInputManager.aidl \
 	core/java/android/hardware/input/IInputDevicesChangedListener.aidl \
+	core/java/android/hardware/location/IFusedLocationHardware.aidl \
+	core/java/android/hardware/location/IFusedLocationHardwareSink.aidl \
 	core/java/android/hardware/location/IGeofenceHardware.aidl \
 	core/java/android/hardware/location/IGeofenceHardwareCallback.aidl \
 	core/java/android/hardware/location/IGeofenceHardwareMonitorCallback.aidl \
@@ -232,12 +234,14 @@
 	keystore/java/android/security/IKeyChainService.aidl \
 	location/java/android/location/ICountryDetector.aidl \
 	location/java/android/location/ICountryListener.aidl \
+	location/java/android/location/IFusedProvider.aidl \
 	location/java/android/location/IGeocodeProvider.aidl \
 	location/java/android/location/IGeofenceProvider.aidl \
 	location/java/android/location/IGpsStatusListener.aidl \
 	location/java/android/location/IGpsStatusProvider.aidl \
 	location/java/android/location/ILocationListener.aidl \
 	location/java/android/location/ILocationManager.aidl \
+	location/java/android/location/IFusedGeofenceHardware.aidl \
 	location/java/android/location/IGpsGeofenceHardware.aidl \
 	location/java/android/location/INetInitiatedListener.aidl \
 	location/java/com/android/internal/location/ILocationProvider.aidl \
@@ -379,6 +383,7 @@
 	frameworks/base/location/java/android/location/Geofence.aidl \
 	frameworks/base/location/java/android/location/Location.aidl \
 	frameworks/base/location/java/android/location/LocationRequest.aidl \
+	frameworks/base/location/java/android/location/FusedBatchOptions.aidl \
 	frameworks/base/location/java/com/android/internal/location/ProviderProperties.aidl \
 	frameworks/base/location/java/com/android/internal/location/ProviderRequest.aidl \
 	frameworks/base/telephony/java/android/telephony/ServiceState.aidl \
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 4e6c3dc..c65f17e 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -16,14 +16,13 @@
 
 package android.app;
 
-import android.R;
 import android.os.BatteryStats;
 import android.os.IBinder;
 import com.android.internal.app.IUsageStats;
+import com.android.internal.app.ProcessStats;
 import com.android.internal.os.PkgUsageStats;
 import com.android.internal.os.TransferPipe;
 import com.android.internal.util.FastPrintWriter;
-import com.android.internal.util.MemInfoReader;
 
 import android.content.ComponentName;
 import android.content.Context;
@@ -35,9 +34,7 @@
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
-import android.graphics.Point;
 import android.graphics.Rect;
-import android.hardware.display.DisplayManagerGlobal;
 import android.os.Bundle;
 import android.os.Debug;
 import android.os.Handler;
@@ -52,7 +49,6 @@
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.Slog;
-import android.view.Display;
 
 import java.io.FileDescriptor;
 import java.io.FileOutputStream;
@@ -2253,7 +2249,7 @@
         PrintWriter pw = new FastPrintWriter(fout);
         dumpService(pw, fd, Context.ACTIVITY_SERVICE, new String[] { "package", packageName });
         pw.println();
-        dumpService(pw, fd, "procstats", new String[] { packageName });
+        dumpService(pw, fd, ProcessStats.SERVICE_NAME, new String[] { packageName });
         pw.println();
         dumpService(pw, fd, "usagestats", new String[] { "--packages", packageName });
         pw.println();
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 6318e38..1c28138 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -786,10 +786,14 @@
             // 2 most significant bits in screenLayout).
             setLayoutDirection(locale);
         }
+        if (delta.screenLayout != 0 && screenLayout != delta.screenLayout) {
+            changed |= ActivityInfo.CONFIG_LAYOUT_DIRECTION;
+            setLayoutDirection(locale);
+        }
         if (delta.userSetLocale && (!userSetLocale || ((changed & ActivityInfo.CONFIG_LOCALE) != 0)))
         {
-            userSetLocale = true;
             changed |= ActivityInfo.CONFIG_LOCALE;
+            userSetLocale = true;
         }
         if (delta.touchscreen != TOUCHSCREEN_UNDEFINED
                 && touchscreen != delta.touchscreen) {
@@ -933,6 +937,9 @@
             changed |= ActivityInfo.CONFIG_LOCALE;
             changed |= ActivityInfo.CONFIG_LAYOUT_DIRECTION;
         }
+        if (delta.screenLayout != 0 && screenLayout != delta.screenLayout) {
+            changed |= ActivityInfo.CONFIG_LAYOUT_DIRECTION;
+        }
         if (delta.touchscreen != TOUCHSCREEN_UNDEFINED
                 && touchscreen != delta.touchscreen) {
             changed |= ActivityInfo.CONFIG_TOUCHSCREEN;
@@ -1005,7 +1012,7 @@
     public static boolean needNewResources(int configChanges, int interestingChanges) {
         return (configChanges & (interestingChanges|ActivityInfo.CONFIG_FONT_SCALE)) != 0;
     }
-    
+
     /**
      * @hide Return true if the sequence of 'other' is better than this.  Assumes
      * that 'this' is your current sequence and 'other' is a new one you have
diff --git a/core/java/android/database/DefaultDatabaseErrorHandler.java b/core/java/android/database/DefaultDatabaseErrorHandler.java
old mode 100644
new mode 100755
index a9e39c3..b234e34
--- a/core/java/android/database/DefaultDatabaseErrorHandler.java
+++ b/core/java/android/database/DefaultDatabaseErrorHandler.java
@@ -99,7 +99,7 @@
         }
         Log.e(TAG, "deleting the database file: " + fileName);
         try {
-            new File(fileName).delete();
+            SQLiteDatabase.deleteDatabase(new File(fileName));
         } catch (Exception e) {
             /* print warning and ignore exception */
             Log.w(TAG, "delete failed: " + e.getMessage());
diff --git a/core/java/android/hardware/location/IFusedLocationHardware.aidl b/core/java/android/hardware/location/IFusedLocationHardware.aidl
new file mode 100644
index 0000000..382c12c
--- /dev/null
+++ b/core/java/android/hardware/location/IFusedLocationHardware.aidl
@@ -0,0 +1,117 @@
+/*
+ * 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/license/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.hardware.location.IFusedLocationHardwareSink;
+import android.location.FusedBatchOptions;
+
+/**
+ * Fused Location hardware interface.
+ * This interface is the basic set of supported functionality by Fused Hardware
+ * modules that offer Location batching capabilities.
+ *
+ * @hide
+ */
+interface IFusedLocationHardware {
+    /**
+     * Registers a sink with the Location Hardware object.
+     *
+     * @param eventSink     The sink to register.
+     */
+    void registerSink(in IFusedLocationHardwareSink eventSink);
+
+    /**
+     * Unregisters a sink with the Location Hardware object.
+     *
+     * @param eventSink     The sink to unregister.
+     */
+    void unregisterSink(in IFusedLocationHardwareSink eventSink);
+
+    /**
+     * Provides access to the batch size available in Hardware.
+     *
+     * @return The batch size the hardware supports.
+     */
+    int getSupportedBatchSize();
+
+    /**
+     * Requests the Hardware to start batching locations.
+     *
+     * @param id            An Id associated with the request.
+     * @param batchOptions  The options required for batching.
+     *
+     * @throws RuntimeException if the request Id exists.
+     */
+    void startBatching(in int id, in FusedBatchOptions batchOptions);
+
+    /**
+     * Requests the Hardware to stop batching for the given Id.
+     *
+     * @param id    The request that needs to be stopped.
+     * @throws RuntimeException if the request Id is unknown.
+     */
+    void stopBatching(in int id);
+
+    /**
+     * Updates a batching operation in progress.
+     *
+     * @param id                The Id of the operation to update.
+     * @param batchOptions     The options to apply to the given operation.
+     *
+     * @throws RuntimeException if the Id of the request is unknown.
+     */
+    void updateBatchingOptions(in int id, in FusedBatchOptions batchOptions);
+
+    /**
+     * Requests the most recent locations available in Hardware.
+     * This operation does not dequeue the locations, so still other batching
+     * events will continue working.
+     *
+     * @param batchSizeRequested    The number of locations requested.
+     */
+    void requestBatchOfLocations(in int batchSizeRequested);
+
+    /**
+     * Flags if the Hardware supports injection of diagnostic data.
+     *
+     * @return True if data injection is supported, false otherwise.
+     */
+    boolean supportsDiagnosticDataInjection();
+
+    /**
+     * Injects diagnostic data into the Hardware subsystem.
+     *
+     * @param data  The data to inject.
+     * @throws RuntimeException if injection is not supported.
+     */
+    void injectDiagnosticData(in String data);
+
+    /**
+     * Flags if the Hardware supports injection of device context information.
+     *
+     * @return True if device context injection is supported, false otherwise.
+     */
+    boolean supportsDeviceContextInjection();
+
+    /**
+     * Injects device context information into the Hardware subsystem.
+     *
+     * @param deviceEnabledContext  The context to inject.
+     * @throws RuntimeException if injection is not supported.
+     */
+    void injectDeviceContext(in int deviceEnabledContext);
+}
diff --git a/core/java/android/hardware/location/IFusedLocationHardwareSink.aidl b/core/java/android/hardware/location/IFusedLocationHardwareSink.aidl
new file mode 100644
index 0000000..a11d8ab
--- /dev/null
+++ b/core/java/android/hardware/location/IFusedLocationHardwareSink.aidl
@@ -0,0 +1,41 @@
+/*
+ * 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/license/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.location.Location;
+
+/**
+ * Fused Location hardware event sink interface.
+ * This interface defines the set of events that the FusedLocationHardware provides.
+ *
+ * @hide
+ */
+interface IFusedLocationHardwareSink {
+    /**
+     * Event generated when a batch of location information is available.
+     *
+     * @param locations     The batch of location information available.
+     */
+    void onLocationAvailable(in Location[] locations);
+
+    /**
+     * Event generated from FLP HAL to provide diagnostic data to the platform.
+     *
+     * @param data      The diagnostic data provided by FLP HAL.
+     */
+    void onDiagnosticDataAvailable(in String data);
+}
\ No newline at end of file
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 12646bd..38ffb96 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -155,6 +155,7 @@
     private static final String BATTERY_LEVEL_DATA = "lv";
     private static final String WIFI_DATA = "wfl";
     private static final String MISC_DATA = "m";
+    private static final String HISTORY_DATA = "h";
     private static final String SCREEN_BRIGHTNESS_DATA = "br";
     private static final String SIGNAL_STRENGTH_TIME_DATA = "sgt";
     private static final String SIGNAL_SCANNING_TIME_DATA = "sst";
@@ -2390,7 +2391,7 @@
             while (getNextHistoryLocked(rec)) {
                 pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
                 pw.print(0); pw.print(',');
-                pw.print("h"); pw.print(',');
+                pw.print(HISTORY_DATA); pw.print(',');
                 hprinter.printNextItemCheckin(pw, rec, now);
                 pw.println();
             }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 1ba39b3..4865fd0 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5662,6 +5662,12 @@
         public static final String SELINUX_STATUS = "selinux_status";
 
         /**
+         * Developer setting to force RTL layout.
+         * @hide
+         */
+        public static final String DEVELOPMENT_FORCE_RTL = "debug.force_rtl";
+
+        /**
          * Settings to backup. This is here so that it's in the same place as the settings
          * keys and easy to update.
          *
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index e2035c2..596ca8c 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -19,6 +19,8 @@
 import android.content.res.Resources;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.SystemProperties;
+import android.provider.Settings;
 import android.text.style.AbsoluteSizeSpan;
 import android.text.style.AlignmentSpan;
 import android.text.style.BackgroundColorSpan;
@@ -1743,8 +1745,10 @@
                 return View.LAYOUT_DIRECTION_RTL;
             }
         }
-
-        return View.LAYOUT_DIRECTION_LTR;
+        // If forcing into RTL layout mode, return RTL as default, else LTR
+        return SystemProperties.getBoolean(Settings.Global.DEVELOPMENT_FORCE_RTL, false)
+                ? View.LAYOUT_DIRECTION_RTL
+                : View.LAYOUT_DIRECTION_LTR;
     }
 
     /**
diff --git a/services/java/com/android/server/ProcessMap.java b/core/java/com/android/internal/app/ProcessMap.java
similarity index 97%
rename from services/java/com/android/server/ProcessMap.java
rename to core/java/com/android/internal/app/ProcessMap.java
index 20c97ba..6ff0304 100644
--- a/services/java/com/android/server/ProcessMap.java
+++ b/core/java/com/android/internal/app/ProcessMap.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server;
+package com.android.internal.app;
 
 import android.util.ArrayMap;
 import android.util.SparseArray;
diff --git a/services/java/com/android/internal/app/ProcessStats.java b/core/java/com/android/internal/app/ProcessStats.java
similarity index 92%
rename from services/java/com/android/internal/app/ProcessStats.java
rename to core/java/com/android/internal/app/ProcessStats.java
index 5e9b6a1..39c23cf 100644
--- a/services/java/com/android/internal/app/ProcessStats.java
+++ b/core/java/com/android/internal/app/ProcessStats.java
@@ -29,7 +29,6 @@
 import android.util.TimeUtils;
 import android.webkit.WebViewFactory;
 import com.android.internal.util.ArrayUtils;
-import com.android.server.ProcessMap;
 import dalvik.system.VMRuntime;
 
 import java.io.PrintWriter;
@@ -39,10 +38,12 @@
 import java.util.Comparator;
 import java.util.Objects;
 
-final public class ProcessStats implements Parcelable {
+public final class ProcessStats implements Parcelable {
     static final String TAG = "ProcessStats";
     static final boolean DEBUG = false;
-    
+
+    public static final String SERVICE_NAME = "procstats";
+
     public static final int STATE_NOTHING = -1;
     public static final int STATE_PERSISTENT = 0;
     public static final int STATE_TOP = 1;
@@ -84,13 +85,20 @@
     public static final int FLAG_SHUTDOWN = 1<<1;
     public static final int FLAG_SYSPROPS = 1<<2;
 
-    static final int[] ALL_MEM_ADJ = new int[] { ADJ_MEM_FACTOR_NORMAL, ADJ_MEM_FACTOR_MODERATE,
-            ADJ_MEM_FACTOR_LOW, ADJ_MEM_FACTOR_CRITICAL };
-    static final int[] ALL_SCREEN_ADJ = new int[] { ADJ_SCREEN_OFF, ADJ_SCREEN_ON };
-    static final int[] NON_CACHED_PROC_STATES = new int[] { STATE_PERSISTENT,
-            STATE_TOP, STATE_IMPORTANT_FOREGROUND,
+    public static final int[] ALL_MEM_ADJ = new int[] { ADJ_MEM_FACTOR_NORMAL,
+            ADJ_MEM_FACTOR_MODERATE, ADJ_MEM_FACTOR_LOW, ADJ_MEM_FACTOR_CRITICAL };
+
+    public static final int[] ALL_SCREEN_ADJ = new int[] { ADJ_SCREEN_OFF, ADJ_SCREEN_ON };
+
+    public static final int[] NON_CACHED_PROC_STATES = new int[] {
+            STATE_PERSISTENT, STATE_TOP, STATE_IMPORTANT_FOREGROUND,
             STATE_IMPORTANT_BACKGROUND, STATE_BACKUP, STATE_HEAVY_WEIGHT,
-            STATE_SERVICE, STATE_SERVICE_RESTARTING, STATE_RECEIVER, STATE_HOME
+            STATE_SERVICE, STATE_SERVICE_RESTARTING, STATE_RECEIVER
+    };
+
+    public static final int[] BACKGROUND_PROC_STATES = new int[] {
+            STATE_IMPORTANT_FOREGROUND, STATE_IMPORTANT_BACKGROUND, STATE_BACKUP,
+            STATE_HEAVY_WEIGHT, STATE_SERVICE, STATE_SERVICE_RESTARTING, STATE_RECEIVER
     };
 
     // Map from process states to the states we track.
@@ -215,7 +223,7 @@
         }
     };
 
-    static private void printScreenLabel(PrintWriter pw, int offset) {
+    private static void printScreenLabel(PrintWriter pw, int offset) {
         switch (offset) {
             case ADJ_NOTHING:
                 pw.print("             ");
@@ -232,7 +240,7 @@
         }
     }
 
-    static public void printScreenLabelCsv(PrintWriter pw, int offset) {
+    public static void printScreenLabelCsv(PrintWriter pw, int offset) {
         switch (offset) {
             case ADJ_NOTHING:
                 break;
@@ -248,7 +256,7 @@
         }
     }
 
-    static private void printMemLabel(PrintWriter pw, int offset) {
+    private static void printMemLabel(PrintWriter pw, int offset) {
         switch (offset) {
             case ADJ_NOTHING:
                 pw.print("       ");
@@ -271,7 +279,7 @@
         }
     }
 
-    static public void printMemLabelCsv(PrintWriter pw, int offset) {
+    public static void printMemLabelCsv(PrintWriter pw, int offset) {
         if (offset >= ADJ_MEM_FACTOR_NORMAL) {
             if (offset <= ADJ_MEM_FACTOR_CRITICAL) {
                 pw.print(ADJ_MEM_NAMES_CSV[offset]);
@@ -281,7 +289,7 @@
         }
     }
 
-    static long dumpSingleTime(PrintWriter pw, String prefix, long[] durations,
+    public static long dumpSingleTime(PrintWriter pw, String prefix, long[] durations,
             int curState, long curStartTime, long now) {
         long totalTime = 0;
         int printedScreen = -1;
@@ -361,7 +369,7 @@
             if (type != serviceType) {
                 continue;
             }
-            long time = svc.mProcessStats.getLong(off, 0);
+            long time = svc.mStats.getLong(off, 0);
             if (curState == memFactor) {
                 didCurState = true;
                 time += now - curStartTime;
@@ -374,7 +382,7 @@
         pw.println();
     }
 
-    static void computeProcessData(ProcessState proc, ProcessDataCollection data, long now) {
+    public static void computeProcessData(ProcessState proc, ProcessDataCollection data, long now) {
         data.totalTime = 0;
         data.numPss = data.minPss = data.avgPss = data.maxPss =
                 data.minUss = data.avgUss = data.maxUss = 0;
@@ -815,7 +823,7 @@
         for (int i=0; i<proc.mDurationsTableSize; i++) {
             int off = proc.mDurationsTable[i];
             int type = (off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
-            long time = proc.mProcessStats.getLong(off, 0);
+            long time = proc.mStats.getLong(off, 0);
             if (proc.mCurState == type) {
                 didCurState = true;
                 time += now - proc.mStartTime;
@@ -831,13 +839,13 @@
         for (int i=0; i<proc.mPssTableSize; i++) {
             int off = proc.mPssTable[i];
             int type = (off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
-            long count = proc.mProcessStats.getLong(off, PSS_SAMPLE_COUNT);
-            long min = proc.mProcessStats.getLong(off, PSS_MINIMUM);
-            long avg = proc.mProcessStats.getLong(off, PSS_AVERAGE);
-            long max = proc.mProcessStats.getLong(off, PSS_MAXIMUM);
-            long umin = proc.mProcessStats.getLong(off, PSS_USS_MINIMUM);
-            long uavg = proc.mProcessStats.getLong(off, PSS_USS_AVERAGE);
-            long umax = proc.mProcessStats.getLong(off, PSS_USS_MAXIMUM);
+            long count = proc.mStats.getLong(off, PSS_SAMPLE_COUNT);
+            long min = proc.mStats.getLong(off, PSS_MINIMUM);
+            long avg = proc.mStats.getLong(off, PSS_AVERAGE);
+            long max = proc.mStats.getLong(off, PSS_MAXIMUM);
+            long umin = proc.mStats.getLong(off, PSS_USS_MINIMUM);
+            long uavg = proc.mStats.getLong(off, PSS_USS_AVERAGE);
+            long umax = proc.mStats.getLong(off, PSS_USS_MAXIMUM);
             pw.print(',');
             printProcStateTag(pw, type);
             pw.print(':');
@@ -979,6 +987,37 @@
         out.writeInt(PSS_COUNT);
         out.writeInt(LONGS_SIZE);
 
+        // First commit all running times.
+        ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
+        final int NPROC = procMap.size();
+        for (int ip=0; ip<NPROC; ip++) {
+            SparseArray<ProcessState> uids = procMap.valueAt(ip);
+            final int NUID = uids.size();
+            for (int iu=0; iu<NUID; iu++) {
+                uids.valueAt(iu).commitStateTime(now);
+            }
+        }
+        ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap();
+        final int NPKG = pkgMap.size();
+        for (int ip=0; ip<NPKG; ip++) {
+            SparseArray<PackageState> uids = pkgMap.valueAt(ip);
+            final int NUID = uids.size();
+            for (int iu=0; iu<NUID; iu++) {
+                PackageState pkgState = uids.valueAt(iu);
+                final int NPROCS = pkgState.mProcesses.size();
+                for (int iproc=0; iproc<NPROCS; iproc++) {
+                    ProcessState proc = pkgState.mProcesses.valueAt(iproc);
+                    if (proc.mCommonProcess != proc) {
+                        proc.commitStateTime(now);
+                    }
+                }
+                final int NSRVS = pkgState.mServices.size();
+                for (int isvc=0; isvc<NSRVS; isvc++) {
+                    pkgState.mServices.valueAt(isvc).commitStateTime(now);
+                }
+            }
+        }
+
         out.writeLong(mTimePeriodStartClock);
         out.writeLong(mTimePeriodStartRealtime);
         out.writeLong(mTimePeriodEndRealtime);
@@ -991,7 +1030,7 @@
         for (int i=0; i<(mLongs.size()-1); i++) {
             out.writeLongArray(mLongs.get(i));
         }
-        long[] lastLongs = mLongs.get(mLongs.size()-1);
+        long[] lastLongs = mLongs.get(mLongs.size() - 1);
         for (int i=0; i<mNextLong; i++) {
             out.writeLong(lastLongs[i]);
             if (DEBUG) Slog.d(TAG, "Writing last long #" + i + ": " + lastLongs[i]);
@@ -1003,8 +1042,6 @@
         }
         out.writeLongArray(mMemFactorDurations);
 
-        ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
-        final int NPROC = procMap.size();
         out.writeInt(NPROC);
         for (int ip=0; ip<NPROC; ip++) {
             out.writeString(procMap.keyAt(ip));
@@ -1018,8 +1055,6 @@
                 proc.writeToParcel(out, now);
             }
         }
-        ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap();
-        final int NPKG = pkgMap.size();
         out.writeInt(NPKG);
         for (int ip=0; ip<NPKG; ip++) {
             out.writeString(pkgMap.keyAt(ip));
@@ -1530,11 +1565,8 @@
                 long time = service.getDuration(serviceType, curState, curStartTime,
                         state, now);
                 String running = "";
-                if (curState == state) {
-                    time += now - curStartTime;
-                    if (pw != null) {
-                        running = " (running)";
-                    }
+                if (curState == state && pw != null) {
+                    running = " (running)";
                 }
                 if (time != 0) {
                     if (pw != null) {
@@ -1824,11 +1856,11 @@
     }
 
     public static final class ProcessState {
-        final ProcessStats mProcessStats;
-        final ProcessState mCommonProcess;
-        final String mPackage;
-        final int mUid;
-        final String mName;
+        public final ProcessStats mStats;
+        public final ProcessState mCommonProcess;
+        public final String mPackage;
+        public final int mUid;
+        public final String mName;
 
         int[] mDurationsTable;
         int mDurationsTableSize;
@@ -1849,14 +1881,14 @@
 
         boolean mMultiPackage;
 
-        long mTmpTotalTime;
+        public long mTmpTotalTime;
 
         /**
          * Create a new top-level process state, for the initial case where there is only
          * a single package running in a process.  The initial state is not running.
          */
         public ProcessState(ProcessStats processStats, String pkg, int uid, String name) {
-            mProcessStats = processStats;
+            mStats = processStats;
             mCommonProcess = this;
             mPackage = pkg;
             mUid = uid;
@@ -1870,7 +1902,7 @@
          */
         public ProcessState(ProcessState commonProcess, String pkg, int uid, String name,
                 long now) {
-            mProcessStats = commonProcess.mProcessStats;
+            mStats = commonProcess.mStats;
             mCommonProcess = commonProcess;
             mPackage = pkg;
             mUid = uid;
@@ -1882,32 +1914,32 @@
         ProcessState clone(String pkg, long now) {
             ProcessState pnew = new ProcessState(this, pkg, mUid, mName, now);
             if (mDurationsTable != null) {
-                mProcessStats.mAddLongTable = new int[mDurationsTable.length];
-                mProcessStats.mAddLongTableSize = 0;
+                mStats.mAddLongTable = new int[mDurationsTable.length];
+                mStats.mAddLongTableSize = 0;
                 for (int i=0; i<mDurationsTableSize; i++) {
                     int origEnt = mDurationsTable[i];
                     int type = (origEnt>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
-                    int newOff = mProcessStats.addLongData(i, type, 1);
-                    mProcessStats.mAddLongTable[i] = newOff | type;
-                    mProcessStats.setLong(newOff, 0, mProcessStats.getLong(origEnt, 0));
+                    int newOff = mStats.addLongData(i, type, 1);
+                    mStats.mAddLongTable[i] = newOff | type;
+                    mStats.setLong(newOff, 0, mStats.getLong(origEnt, 0));
                 }
-                pnew.mDurationsTable = mProcessStats.mAddLongTable;
-                pnew.mDurationsTableSize = mProcessStats.mAddLongTableSize;
+                pnew.mDurationsTable = mStats.mAddLongTable;
+                pnew.mDurationsTableSize = mStats.mAddLongTableSize;
             }
             if (mPssTable != null) {
-                mProcessStats.mAddLongTable = new int[mPssTable.length];
-                mProcessStats.mAddLongTableSize = 0;
+                mStats.mAddLongTable = new int[mPssTable.length];
+                mStats.mAddLongTableSize = 0;
                 for (int i=0; i<mPssTableSize; i++) {
                     int origEnt = mPssTable[i];
                     int type = (origEnt>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
-                    int newOff = mProcessStats.addLongData(i, type, PSS_COUNT);
-                    mProcessStats.mAddLongTable[i] = newOff | type;
+                    int newOff = mStats.addLongData(i, type, PSS_COUNT);
+                    mStats.mAddLongTable[i] = newOff | type;
                     for (int j=0; j<PSS_COUNT; j++) {
-                        mProcessStats.setLong(newOff, j, mProcessStats.getLong(origEnt, j));
+                        mStats.setLong(newOff, j, mStats.getLong(origEnt, j));
                     }
                 }
-                pnew.mPssTable = mProcessStats.mAddLongTable;
-                pnew.mPssTableSize = mProcessStats.mAddLongTableSize;
+                pnew.mPssTable = mStats.mAddLongTable;
+                pnew.mPssTableSize = mStats.mAddLongTableSize;
             }
             pnew.mNumExcessiveWake = mNumExcessiveWake;
             pnew.mNumExcessiveCpu = mNumExcessiveCpu;
@@ -1928,7 +1960,6 @@
         }
 
         void writeToParcel(Parcel out, long now) {
-            commitStateTime(now);
             out.writeInt(mMultiPackage ? 1 : 0);
             out.writeInt(mDurationsTableSize);
             for (int i=0; i<mDurationsTableSize; i++) {
@@ -1952,13 +1983,13 @@
                 mMultiPackage = multiPackage;
             }
             if (DEBUG) Slog.d(TAG, "Reading durations table...");
-            mDurationsTable = mProcessStats.readTableFromParcel(in, mName, "durations");
+            mDurationsTable = mStats.readTableFromParcel(in, mName, "durations");
             if (mDurationsTable == BAD_TABLE) {
                 return false;
             }
             mDurationsTableSize = mDurationsTable != null ? mDurationsTable.length : 0;
             if (DEBUG) Slog.d(TAG, "Reading pss table...");
-            mPssTable = mProcessStats.readTableFromParcel(in, mName, "pss");
+            mPssTable = mStats.readTableFromParcel(in, mName, "pss");
             if (mPssTable == BAD_TABLE) {
                 return false;
             }
@@ -2017,13 +2048,13 @@
                     if (idx >= 0) {
                         off = mDurationsTable[idx];
                     } else {
-                        mProcessStats.mAddLongTable = mDurationsTable;
-                        mProcessStats.mAddLongTableSize = mDurationsTableSize;
-                        off = mProcessStats.addLongData(~idx, mCurState, 1);
-                        mDurationsTable = mProcessStats.mAddLongTable;
-                        mDurationsTableSize = mProcessStats.mAddLongTableSize;
+                        mStats.mAddLongTable = mDurationsTable;
+                        mStats.mAddLongTableSize = mDurationsTableSize;
+                        off = mStats.addLongData(~idx, mCurState, 1);
+                        mDurationsTable = mStats.mAddLongTable;
+                        mDurationsTableSize = mStats.mAddLongTableSize;
                     }
-                    long[] longs = mProcessStats.mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
+                    long[] longs = mStats.mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
                     longs[(off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK] += dur;
                 }
             }
@@ -2068,13 +2099,13 @@
                 if (idx >= 0) {
                     off = mPssTable[idx];
                 } else {
-                    mProcessStats.mAddLongTable = mPssTable;
-                    mProcessStats.mAddLongTableSize = mPssTableSize;
-                    off = mProcessStats.addLongData(~idx, mCurState, PSS_COUNT);
-                    mPssTable = mProcessStats.mAddLongTable;
-                    mPssTableSize = mProcessStats.mAddLongTableSize;
+                    mStats.mAddLongTable = mPssTable;
+                    mStats.mAddLongTableSize = mPssTableSize;
+                    off = mStats.addLongData(~idx, mCurState, PSS_COUNT);
+                    mPssTable = mStats.mAddLongTable;
+                    mPssTableSize = mStats.mAddLongTableSize;
                 }
-                long[] longs = mProcessStats.mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
+                long[] longs = mStats.mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
                 idx = (off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK;
                 long count = longs[idx+PSS_SAMPLE_COUNT];
                 if (count == 0) {
@@ -2134,7 +2165,7 @@
                 // The array map is still pointing to a common process state
                 // that is now shared across packages.  Update it to point to
                 // the new per-package state.
-                ProcessState proc = mProcessStats.mPackages.get(pkgName,
+                ProcessState proc = mStats.mPackages.get(pkgName,
                         mUid).mProcesses.get(mName);
                 if (proc == null) {
                     throw new IllegalStateException("Didn't create per-package process");
@@ -2151,7 +2182,7 @@
                 // The array map is still pointing to a common process state
                 // that is now shared across packages.  Update it to point to
                 // the new per-package state.
-                proc = mProcessStats.mPackages.get(pkgList.keyAt(index),
+                proc = mStats.mPackages.get(pkgList.keyAt(index),
                         proc.mUid).mProcesses.get(proc.mName);
                 if (proc == null) {
                     throw new IllegalStateException("Didn't create per-package process");
@@ -2163,7 +2194,7 @@
 
         long getDuration(int state, long now) {
             int idx = binarySearch(mDurationsTable, mDurationsTableSize, state);
-            long time = idx >= 0 ? mProcessStats.getLong(mDurationsTable[idx], 0) : 0;
+            long time = idx >= 0 ? mStats.getLong(mDurationsTable[idx], 0) : 0;
             if (mCurState == state) {
                 time += now - mStartTime;
             }
@@ -2172,42 +2203,42 @@
 
         long getPssSampleCount(int state) {
             int idx = binarySearch(mPssTable, mPssTableSize, state);
-            return idx >= 0 ? mProcessStats.getLong(mPssTable[idx], PSS_SAMPLE_COUNT) : 0;
+            return idx >= 0 ? mStats.getLong(mPssTable[idx], PSS_SAMPLE_COUNT) : 0;
         }
 
         long getPssMinimum(int state) {
             int idx = binarySearch(mPssTable, mPssTableSize, state);
-            return idx >= 0 ? mProcessStats.getLong(mPssTable[idx], PSS_MINIMUM) : 0;
+            return idx >= 0 ? mStats.getLong(mPssTable[idx], PSS_MINIMUM) : 0;
         }
 
         long getPssAverage(int state) {
             int idx = binarySearch(mPssTable, mPssTableSize, state);
-            return idx >= 0 ? mProcessStats.getLong(mPssTable[idx], PSS_AVERAGE) : 0;
+            return idx >= 0 ? mStats.getLong(mPssTable[idx], PSS_AVERAGE) : 0;
         }
 
         long getPssMaximum(int state) {
             int idx = binarySearch(mPssTable, mPssTableSize, state);
-            return idx >= 0 ? mProcessStats.getLong(mPssTable[idx], PSS_MAXIMUM) : 0;
+            return idx >= 0 ? mStats.getLong(mPssTable[idx], PSS_MAXIMUM) : 0;
         }
 
         long getPssUssMinimum(int state) {
             int idx = binarySearch(mPssTable, mPssTableSize, state);
-            return idx >= 0 ? mProcessStats.getLong(mPssTable[idx], PSS_USS_MINIMUM) : 0;
+            return idx >= 0 ? mStats.getLong(mPssTable[idx], PSS_USS_MINIMUM) : 0;
         }
 
         long getPssUssAverage(int state) {
             int idx = binarySearch(mPssTable, mPssTableSize, state);
-            return idx >= 0 ? mProcessStats.getLong(mPssTable[idx], PSS_USS_AVERAGE) : 0;
+            return idx >= 0 ? mStats.getLong(mPssTable[idx], PSS_USS_AVERAGE) : 0;
         }
 
         long getPssUssMaximum(int state) {
             int idx = binarySearch(mPssTable, mPssTableSize, state);
-            return idx >= 0 ? mProcessStats.getLong(mPssTable[idx], PSS_USS_MAXIMUM) : 0;
+            return idx >= 0 ? mStats.getLong(mPssTable[idx], PSS_USS_MAXIMUM) : 0;
         }
     }
 
     public static final class ServiceState {
-        final ProcessStats mProcessStats;
+        final ProcessStats mStats;
         final String mPackage;
         ProcessState mProc;
 
@@ -2234,7 +2265,7 @@
         long mExecStartTime;
 
         public ServiceState(ProcessStats processStats, String pkg, ProcessState proc) {
-            mProcessStats = processStats;
+            mStats = processStats;
             mPackage = pkg;
             mProc = proc;
         }
@@ -2266,18 +2297,6 @@
         }
 
         void writeToParcel(Parcel out, long now) {
-            if (mStartedState != STATE_NOTHING) {
-                addStateTime(SERVICE_STARTED, mStartedState, now - mStartedStartTime);
-                mStartedStartTime = now;
-            }
-            if (mBoundState != STATE_NOTHING) {
-                addStateTime(SERVICE_BOUND, mBoundState, now - mBoundStartTime);
-                mBoundStartTime = now;
-            }
-            if (mExecState != STATE_NOTHING) {
-                addStateTime(SERVICE_EXEC, mExecState, now - mExecStartTime);
-                mExecStartTime = now;
-            }
             out.writeInt(mDurationsTableSize);
             for (int i=0; i<mDurationsTableSize; i++) {
                 if (DEBUG) Slog.i(TAG, "Writing service in " + mPackage + " dur #" + i + ": "
@@ -2291,7 +2310,7 @@
 
         boolean readFromParcel(Parcel in) {
             if (DEBUG) Slog.d(TAG, "Reading durations table...");
-            mDurationsTable = mProcessStats.readTableFromParcel(in, mPackage, "service");
+            mDurationsTable = mStats.readTableFromParcel(in, mPackage, "service");
             if (mDurationsTable == BAD_TABLE) {
                 return false;
             }
@@ -2310,17 +2329,32 @@
                 if (idx >= 0) {
                     off = mDurationsTable[idx];
                 } else {
-                    mProcessStats.mAddLongTable = mDurationsTable;
-                    mProcessStats.mAddLongTableSize = mDurationsTableSize;
-                    off = mProcessStats.addLongData(~idx, state, 1);
-                    mDurationsTable = mProcessStats.mAddLongTable;
-                    mDurationsTableSize = mProcessStats.mAddLongTableSize;
+                    mStats.mAddLongTable = mDurationsTable;
+                    mStats.mAddLongTableSize = mDurationsTableSize;
+                    off = mStats.addLongData(~idx, state, 1);
+                    mDurationsTable = mStats.mAddLongTable;
+                    mDurationsTableSize = mStats.mAddLongTableSize;
                 }
-                long[] longs = mProcessStats.mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
+                long[] longs = mStats.mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
                 longs[(off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK] += time;
             }
         }
 
+        void commitStateTime(long now) {
+            if (mStartedState != STATE_NOTHING) {
+                addStateTime(SERVICE_STARTED, mStartedState, now - mStartedStartTime);
+                mStartedStartTime = now;
+            }
+            if (mBoundState != STATE_NOTHING) {
+                addStateTime(SERVICE_BOUND, mBoundState, now - mBoundStartTime);
+                mBoundStartTime = now;
+            }
+            if (mExecState != STATE_NOTHING) {
+                addStateTime(SERVICE_EXEC, mExecState, now - mExecStartTime);
+                mExecStartTime = now;
+            }
+        }
+
         public void setStarted(boolean started, int memFactor, long now) {
             if (mActive <= 0) {
                 throw new IllegalStateException("Service " + this + " has mActive=" + mActive);
@@ -2377,25 +2411,11 @@
             }
         }
 
-        long getStartDuration(int opType, int memFactor, long now) {
-            switch (opType) {
-                case SERVICE_STARTED:
-                    return getDuration(opType, mStartedState, mStartedStartTime, memFactor, now);
-                case SERVICE_BOUND:
-                    return getDuration(opType, mBoundState, mBoundStartTime, memFactor, now);
-                case SERVICE_EXEC:
-                    return getDuration(opType, mExecState, mExecStartTime, memFactor, now);
-                default:
-                    throw new IllegalArgumentException("Bad opType: " + opType);
-            }
-        }
-
-
         private long getDuration(int opType, int curState, long startTime, int memFactor,
                 long now) {
             int state = opType + (memFactor*SERVICE_COUNT);
             int idx = binarySearch(mDurationsTable, mDurationsTableSize, state);
-            long time = idx >= 0 ? mProcessStats.getLong(mDurationsTable[idx], 0) : 0;
+            long time = idx >= 0 ? mStats.getLong(mDurationsTable[idx], 0) : 0;
             if (curState == memFactor) {
                 time += now - startTime;
             }
@@ -2415,21 +2435,21 @@
         }
     }
 
-    static final class ProcessDataCollection {
+    public static final class ProcessDataCollection {
         final int[] screenStates;
         final int[] memStates;
         final int[] procStates;
 
-        long totalTime;
-        long numPss;
-        long minPss;
-        long avgPss;
-        long maxPss;
-        long minUss;
-        long avgUss;
-        long maxUss;
+        public long totalTime;
+        public long numPss;
+        public long minPss;
+        public long avgPss;
+        public long maxPss;
+        public long minUss;
+        public long avgUss;
+        public long maxUss;
 
-        ProcessDataCollection(int[] _screenStates, int[] _memStates, int[] _procStates) {
+        public ProcessDataCollection(int[] _screenStates, int[] _memStates, int[] _procStates) {
             screenStates = _screenStates;
             memStates = _memStates;
             procStates = _procStates;
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 7e70c7c..6d23c32 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -141,7 +141,7 @@
                 dns, server, &lease, vendorInfo, domains, mtu);
     }
     if (result != 0) {
-        ALOGD("dhcp_do_request failed");
+        ALOGD("dhcp_do_request failed : %s (%s)", nameStr, renew ? "renew" : "new");
     }
 
     env->ReleaseStringUTFChars(ifname, nameStr);
diff --git a/core/jni/android_net_wifi_Wifi.cpp b/core/jni/android_net_wifi_Wifi.cpp
index 08962e2..aa6dbf3 100644
--- a/core/jni/android_net_wifi_Wifi.cpp
+++ b/core/jni/android_net_wifi_Wifi.cpp
@@ -33,11 +33,11 @@
 
 static jint DBG = false;
 
-static int doCommand(const char *ifname, char *cmd, char *replybuf, int replybuflen)
+static int doCommand(char *cmd, char *replybuf, int replybuflen)
 {
     size_t reply_len = replybuflen - 1;
 
-    if (::wifi_command(ifname, cmd, BUF_SIZE, replybuf, &reply_len) != 0)
+    if (::wifi_command(cmd, replybuf, &reply_len) != 0)
         return -1;
     else {
         // Strip off trailing newline
@@ -49,7 +49,7 @@
     }
 }
 
-static jint doIntCommand(const char *ifname, const char* fmt, ...)
+static jint doIntCommand(const char* fmt, ...)
 {
     char buf[BUF_SIZE];
     va_list args;
@@ -60,13 +60,13 @@
         return -1;
     }
     char reply[BUF_SIZE];
-    if (doCommand(ifname, buf, reply, sizeof(reply)) != 0) {
+    if (doCommand(buf, reply, sizeof(reply)) != 0) {
         return -1;
     }
     return static_cast<jint>(atoi(reply));
 }
 
-static jboolean doBooleanCommand(const char *ifname, const char* expect, const char* fmt, ...)
+static jboolean doBooleanCommand(const char* expect, const char* fmt, ...)
 {
     char buf[BUF_SIZE];
     va_list args;
@@ -77,14 +77,14 @@
         return JNI_FALSE;
     }
     char reply[BUF_SIZE];
-    if (doCommand(ifname, buf, reply, sizeof(reply)) != 0) {
+    if (doCommand(buf, reply, sizeof(reply)) != 0) {
         return JNI_FALSE;
     }
     return (strcmp(reply, expect) == 0);
 }
 
 // Send a command to the supplicant, and return the reply as a String
-static jstring doStringCommand(JNIEnv* env, const char *ifname, const char* fmt, ...) {
+static jstring doStringCommand(JNIEnv* env, const char* fmt, ...) {
     char buf[BUF_SIZE];
     va_list args;
     va_start(args, fmt);
@@ -94,7 +94,7 @@
         return NULL;
     }
     char reply[4096];
-    if (doCommand(ifname, buf, reply, sizeof(reply)) != 0) {
+    if (doCommand(buf, reply, sizeof(reply)) != 0) {
         return NULL;
     }
     // TODO: why not just NewStringUTF?
@@ -127,23 +127,20 @@
     return (jboolean)(::wifi_stop_supplicant(p2pSupported) == 0);
 }
 
-static jboolean android_net_wifi_connectToSupplicant(JNIEnv* env, jobject, jstring jIface)
+static jboolean android_net_wifi_connectToSupplicant(JNIEnv* env, jobject)
 {
-    ScopedUtfChars ifname(env, jIface);
-    return (jboolean)(::wifi_connect_to_supplicant(ifname.c_str()) == 0);
+    return (jboolean)(::wifi_connect_to_supplicant() == 0);
 }
 
-static void android_net_wifi_closeSupplicantConnection(JNIEnv* env, jobject, jstring jIface)
+static void android_net_wifi_closeSupplicantConnection(JNIEnv* env, jobject)
 {
-    ScopedUtfChars ifname(env, jIface);
-    ::wifi_close_supplicant_connection(ifname.c_str());
+    ::wifi_close_supplicant_connection();
 }
 
-static jstring android_net_wifi_waitForEvent(JNIEnv* env, jobject, jstring jIface)
+static jstring android_net_wifi_waitForEvent(JNIEnv* env, jobject)
 {
     char buf[EVENT_BUF_SIZE];
-    ScopedUtfChars ifname(env, jIface);
-    int nread = ::wifi_wait_for_event(ifname.c_str(), buf, sizeof buf);
+    int nread = ::wifi_wait_for_event(buf, sizeof buf);
     if (nread > 0) {
         return env->NewStringUTF(buf);
     } else {
@@ -151,43 +148,36 @@
     }
 }
 
-static jboolean android_net_wifi_doBooleanCommand(JNIEnv* env, jobject, jstring jIface,
-        jstring jCommand)
+static jboolean android_net_wifi_doBooleanCommand(JNIEnv* env, jobject, jstring jCommand)
 {
-    ScopedUtfChars ifname(env, jIface);
     ScopedUtfChars command(env, jCommand);
 
     if (command.c_str() == NULL) {
         return JNI_FALSE;
     }
     if (DBG) ALOGD("doBoolean: %s", command.c_str());
-    return doBooleanCommand(ifname.c_str(), "OK", "%s", command.c_str());
+    return doBooleanCommand("OK", "%s", command.c_str());
 }
 
-static jint android_net_wifi_doIntCommand(JNIEnv* env, jobject, jstring jIface,
-        jstring jCommand)
+static jint android_net_wifi_doIntCommand(JNIEnv* env, jobject, jstring jCommand)
 {
-    ScopedUtfChars ifname(env, jIface);
     ScopedUtfChars command(env, jCommand);
 
     if (command.c_str() == NULL) {
         return -1;
     }
     if (DBG) ALOGD("doInt: %s", command.c_str());
-    return doIntCommand(ifname.c_str(), "%s", command.c_str());
+    return doIntCommand("%s", command.c_str());
 }
 
-static jstring android_net_wifi_doStringCommand(JNIEnv* env, jobject, jstring jIface,
-        jstring jCommand)
+static jstring android_net_wifi_doStringCommand(JNIEnv* env, jobject, jstring jCommand)
 {
-    ScopedUtfChars ifname(env, jIface);
-
     ScopedUtfChars command(env, jCommand);
     if (command.c_str() == NULL) {
         return NULL;
     }
     if (DBG) ALOGD("doString: %s", command.c_str());
-    return doStringCommand(env, ifname.c_str(), "%s", command.c_str());
+    return doStringCommand(env, "%s", command.c_str());
 }
 
 
@@ -205,17 +195,13 @@
     { "unloadDriver", "()Z",  (void *)android_net_wifi_unloadDriver },
     { "startSupplicant", "(Z)Z",  (void *)android_net_wifi_startSupplicant },
     { "killSupplicant", "(Z)Z",  (void *)android_net_wifi_killSupplicant },
-    { "connectToSupplicant", "(Ljava/lang/String;)Z",
-            (void *)android_net_wifi_connectToSupplicant },
-    { "closeSupplicantConnection", "(Ljava/lang/String;)V",
+    { "connectToSupplicantNative", "()Z", (void *)android_net_wifi_connectToSupplicant },
+    { "closeSupplicantConnectionNative", "()V",
             (void *)android_net_wifi_closeSupplicantConnection },
-    { "waitForEvent", "(Ljava/lang/String;)Ljava/lang/String;",
-            (void*) android_net_wifi_waitForEvent },
-    { "doBooleanCommand", "(Ljava/lang/String;Ljava/lang/String;)Z",
-            (void*) android_net_wifi_doBooleanCommand },
-    { "doIntCommand", "(Ljava/lang/String;Ljava/lang/String;)I",
-            (void*) android_net_wifi_doIntCommand },
-    { "doStringCommand", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
+    { "waitForEventNative", "()Ljava/lang/String;", (void*)android_net_wifi_waitForEvent },
+    { "doBooleanCommandNative", "(Ljava/lang/String;)Z", (void*)android_net_wifi_doBooleanCommand },
+    { "doIntCommandNative", "(Ljava/lang/String;)I", (void*)android_net_wifi_doIntCommand },
+    { "doStringCommandNative", "(Ljava/lang/String;)Ljava/lang/String;",
             (void*) android_net_wifi_doStringCommand },
 };
 
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index b9840e2..0908f36 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -26,7 +26,7 @@
        <item><xliff:g id="id">ime</xliff:g></item>
        <item><xliff:g id="id">sync_failing</xliff:g></item>
        <item><xliff:g id="id">sync_active</xliff:g></item>
-       <item><xliff:g id="id">gps</xliff:g></item>
+       <item><xliff:g id="id">location</xliff:g></item>
        <item><xliff:g id="id">bluetooth</xliff:g></item>
        <item><xliff:g id="id">nfc</xliff:g></item>
        <item><xliff:g id="id">tty</xliff:g></item>
diff --git a/docs/html/guide/topics/providers/content-provider-creating.jd b/docs/html/guide/topics/providers/content-provider-creating.jd
index ebd7c25..6ec1e1b 100644
--- a/docs/html/guide/topics/providers/content-provider-creating.jd
+++ b/docs/html/guide/topics/providers/content-provider-creating.jd
@@ -680,7 +680,7 @@
          * Notice that the database itself isn't created or opened
          * until SQLiteOpenHelper.getWritableDatabase is called
          */
-        mOpenHelper = new SQLiteOpenHelper(
+        mOpenHelper = new MainDatabaseHelper(
             getContext(),        // the application context
             DBNAME,              // the name of the database)
             null,                // uses the default SQLite cursor
diff --git a/docs/html/guide/topics/ui/notifiers/notifications.jd b/docs/html/guide/topics/ui/notifiers/notifications.jd
index e0c87d7..3b1292e 100644
--- a/docs/html/guide/topics/ui/notifiers/notifications.jd
+++ b/docs/html/guide/topics/ui/notifiers/notifications.jd
@@ -285,7 +285,7 @@
 <p>
     Starting an {@link android.app.Activity} when the user clicks the notification is the most
     common action scenario. You can also start an {@link android.app.Activity} when the user
-    dismisses an {@link android.app.Activity}. In Android 4.1 and later, you can start an
+    dismisses a notification. In Android 4.1 and later, you can start an
     {@link android.app.Activity} from an action button. To learn more, read the reference guide for
     {@link android.support.v4.app.NotificationCompat.Builder}.
 </p>
diff --git a/location/java/android/location/FusedBatchOptions.aidl b/location/java/android/location/FusedBatchOptions.aidl
new file mode 100644
index 0000000..94cb6e1
--- /dev/null
+++ b/location/java/android/location/FusedBatchOptions.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.location;
+
+parcelable FusedBatchOptions;
\ No newline at end of file
diff --git a/location/java/android/location/FusedBatchOptions.java b/location/java/android/location/FusedBatchOptions.java
new file mode 100644
index 0000000..623d707
--- /dev/null
+++ b/location/java/android/location/FusedBatchOptions.java
@@ -0,0 +1,135 @@
+/*
+ * 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.location;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A data class representing a set of options to configure batching sessions.
+ * @hide
+ */
+public class FusedBatchOptions implements Parcelable {
+    private volatile long mPeriodInNS = 0;
+    private volatile int mSourcesToUse = 0;
+    private volatile int mFlags = 0;
+
+    // the default value is set to request fixes at no cost
+    private volatile double mMaxPowerAllocationInMW = 0;
+
+    /*
+     * Getters and setters for properties needed to hold the options.
+     */
+    public void setMaxPowerAllocationInMW(double value) {
+        mMaxPowerAllocationInMW = value;
+    }
+
+    public double getMaxPowerAllocationInMW() {
+        return mMaxPowerAllocationInMW;
+    }
+
+    public void setPeriodInNS(long value) {
+        mPeriodInNS = value;
+    }
+
+    public long getPeriodInNS() {
+        return mPeriodInNS;
+    }
+
+    public void setSourceToUse(int source) {
+        mSourcesToUse |= source;
+    }
+
+    public void resetSourceToUse(int source) {
+        mSourcesToUse &= ~source;
+    }
+
+    public boolean isSourceToUseSet(int source) {
+        return (mSourcesToUse & source) != 0;
+    }
+
+    public int getSourcesToUse() {
+        return mSourcesToUse;
+    }
+
+    public void setFlag(int flag) {
+        mFlags |= flag;
+    }
+
+    public void resetFlag(int flag) {
+        mFlags &= ~flag;
+    }
+
+    public boolean isFlagSet(int flag) {
+        return (mFlags & flag) != 0;
+    }
+
+    public int getFlags() {
+        return mFlags;
+    }
+
+    /**
+     * Definition of enum flag sets needed by this class.
+     * Such values need to be kept in sync with the ones in fused_location.h
+     */
+    public static final class SourceTechnologies {
+        public static int GNSS = 1<<0;
+        public static int WIFI = 1<<1;
+        public static int SENSORS = 1<<2;
+        public static int CELL = 1<<3;
+        public static int BLUETOOTH = 1<<4;
+    }
+
+    public static final class BatchFlags {
+        public static int WAKEUP_ON_FIFO_FULL = 1<<0;
+        public static int CALLBACK_ON_LOCATION_FIX = 1<<1;
+    }
+
+    /*
+     * Method definitions to support Parcelable operations.
+     */
+    public static final Parcelable.Creator<FusedBatchOptions> CREATOR =
+            new Parcelable.Creator<FusedBatchOptions>() {
+        @Override
+        public FusedBatchOptions createFromParcel(Parcel parcel) {
+            FusedBatchOptions options = new FusedBatchOptions();
+            options.setMaxPowerAllocationInMW(parcel.readDouble());
+            options.setPeriodInNS(parcel.readLong());
+            options.setSourceToUse(parcel.readInt());
+            options.setFlag(parcel.readInt());
+            return options;
+        }
+
+        @Override
+        public FusedBatchOptions[] newArray(int size) {
+            return new FusedBatchOptions[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeDouble(mMaxPowerAllocationInMW);
+        parcel.writeLong(mPeriodInNS);
+        parcel.writeInt(mSourcesToUse);
+        parcel.writeInt(mFlags);
+    }
+}
diff --git a/location/java/android/location/IFusedGeofenceHardware.aidl b/location/java/android/location/IFusedGeofenceHardware.aidl
new file mode 100644
index 0000000..9dbf1f4
--- /dev/null
+++ b/location/java/android/location/IFusedGeofenceHardware.aidl
@@ -0,0 +1,93 @@
+/*
+ * 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/license/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.location;
+
+import android.location.Geofence;
+ 
+/**
+ * Fused Geofence Hardware interface.
+ *
+ * <p>This interface is the basic set of supported functionality by Fused Hardware modules that offer
+ * Geofencing capabilities.
+ *
+ * All operations are asynchronous and the status codes can be obtained via a set of callbacks.
+ *
+ * @hide
+ */
+interface IFusedGeofenceHardware {
+    /**
+     * Flags if the interface functionality is supported by the platform.
+     *
+     * @return true if the functionality is supported, false otherwise.
+     */
+    boolean isSupported();
+
+    /**
+     * 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.
+     */
+    // TODO: [GeofenceIntegration] GeofenceHardwareRequest is not a parcelable class exposed in aidl
+    void addGeofences(in int[] geofenceIdsArray, in Geofence[] geofencesArray);
+
+    /**
+     * Removes a give list of geofences from the system.
+     *
+     * @param geofences     The list of geofences to remove.
+     */
+    void removeGeofences(in int[] geofenceIds);
+
+    /**
+     * Pauses monitoring a particular geofence.
+     * 
+     * @param geofenceId    The geofence to pause monitoring.
+     */
+    void pauseMonitoringGeofence(in int geofenceId);
+
+    /**
+     * Resumes monitoring a particular geofence.
+     *
+     * @param geofenceid            The geofence to resume monitoring.
+     * @param transitionsToMonitor  The transitions to monitor upon resume.
+     *
+     * Remarks: keep naming of geofence request options consistent with the naming used in
+     *          GeofenceHardwareRequest
+     */
+    void resumeMonitoringGeofence(in int geofenceId, in int monitorTransitions);
+
+    /**
+     * Modifies the request options if a geofence that is already known by the
+     * system.
+     *  
+     * @param geofenceId                    The geofence to modify.
+     * @param lastTransition                The last known transition state of
+     *                                      the geofence.
+     * @param monitorTransitions            The set of transitions to monitor.
+     * @param notificationResponsiveness    The notification responsivness needed.
+     * @param unknownTimer                  The time span associated with the
+     *
+     * Remarks: keep the options as separate fields to be able to leverage the class
+     * GeofenceHardwareRequest without any changes
+     */
+    void modifyGeofenceOptions(
+            in int geofenceId,
+            in int lastTransition,
+            in int monitorTransitions,
+            in int notificationResponsiveness,
+            in int unknownTimer);
+}
diff --git a/location/java/android/location/IFusedProvider.aidl b/location/java/android/location/IFusedProvider.aidl
new file mode 100644
index 0000000..8870d2a
--- /dev/null
+++ b/location/java/android/location/IFusedProvider.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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.location;
+
+import android.hardware.location.IFusedLocationHardware;
+
+/**
+ * Interface definition for Location providers that require FLP services.
+ * @hide
+ */
+interface IFusedProvider {
+    /**
+     * Provides access to a FusedLocationHardware instance needed for the provider to work.
+     *
+     * @param instance      The FusedLocationHardware available for the provider to use.
+     */
+    void onFusedLocationHardwareChange(in IFusedLocationHardware instance);
+}
\ No newline at end of file
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 989178a..e5f1cf5 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -177,6 +177,17 @@
      */
     public static final String EXTRA_GPS_ENABLED = "enabled";
 
+    /**
+     * Broadcast intent action indicating that a high power location requests
+     * has either started or stopped being active.  The current state of
+     * active location requests should be read from AppOpsManager using
+     * {@code OP_MONITOR_HIGH_POWER_LOCATION}.
+     *
+     * @hide
+     */
+    public static final String HIGH_POWER_REQUEST_CHANGE_ACTION =
+        "android.location.HIGH_POWER_REQUEST_CHANGE";
+
     // Map from LocationListeners to their associated ListenerTransport objects
     private HashMap<LocationListener,ListenerTransport> mListeners =
         new HashMap<LocationListener,ListenerTransport>();
diff --git a/location/lib/java/com/android/location/provider/FusedLocationHardware.java b/location/lib/java/com/android/location/provider/FusedLocationHardware.java
new file mode 100644
index 0000000..abea9fb
--- /dev/null
+++ b/location/lib/java/com/android/location/provider/FusedLocationHardware.java
@@ -0,0 +1,202 @@
+/*
+ * 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 CONDITIOS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.location.provider;
+
+import android.hardware.location.IFusedLocationHardware;
+import android.hardware.location.IFusedLocationHardwareSink;
+
+import android.location.Location;
+
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.ArrayList;
+
+/**
+ * Class that exposes IFusedLocationHardware functionality to unbundled services.
+ * Namely this is used by GmsCore Fused Location Provider.
+ */
+public final class FusedLocationHardware {
+    private final String TAG = "FusedLocationHardware";
+
+    private IFusedLocationHardware mLocationHardware;
+    ArrayList<FusedLocationHardwareSink> mSinkList = new ArrayList<FusedLocationHardwareSink>();
+
+    private IFusedLocationHardwareSink mInternalSink = new IFusedLocationHardwareSink.Stub() {
+        @Override
+        public void onLocationAvailable(Location[] locations) {
+            dispatchLocations(locations);
+        }
+
+        @Override
+        public void onDiagnosticDataAvailable(String data) {
+            dispatchDiagnosticData(data);
+        }
+    };
+
+    public FusedLocationHardware(IFusedLocationHardware locationHardware) {
+        mLocationHardware = locationHardware;
+    }
+
+    /*
+     * Methods to provide a Facade for IFusedLocationHardware
+     */
+    public void registerSink(FusedLocationHardwareSink sink) {
+        if(sink == null) {
+            return;
+        }
+
+        boolean registerSink = false;
+        synchronized (mSinkList) {
+            // register only on first insertion
+            registerSink = mSinkList.size() == 0;
+            // guarantee uniqueness
+            if(!mSinkList.contains(sink)) {
+                mSinkList.add(sink);
+            }
+        }
+
+        if(registerSink) {
+            try {
+                mLocationHardware.registerSink(mInternalSink);
+            } catch(RemoteException e) {
+                Log.e(TAG, "RemoteException at registerSink");
+            }
+        }
+    }
+
+    public void unregisterSink(FusedLocationHardwareSink sink) {
+        if(sink == null) {
+            return;
+        }
+
+        boolean unregisterSink = false;
+        synchronized(mSinkList) {
+            mSinkList.remove(sink);
+            // unregister after the last sink
+            unregisterSink = mSinkList.size() == 0;
+        }
+
+        if(unregisterSink) {
+            try {
+                mLocationHardware.unregisterSink(mInternalSink);
+            } catch(RemoteException e) {
+                Log.e(TAG, "RemoteException at unregisterSink");
+            }
+        }
+    }
+
+    public int getSupportedBatchSize() {
+        try {
+            return mLocationHardware.getSupportedBatchSize();
+        } catch(RemoteException e) {
+            Log.e(TAG, "RemoteException at getSupportedBatchSize");
+            return 0;
+        }
+    }
+
+    public void startBatching(int id, GmsFusedBatchOptions batchOptions) {
+        try {
+            mLocationHardware.startBatching(id, batchOptions.getParcelableOptions());
+        } catch(RemoteException e) {
+            Log.e(TAG, "RemoteException at startBatching");
+        }
+    }
+
+    public void stopBatching(int id) {
+        try {
+            mLocationHardware.stopBatching(id);
+        } catch(RemoteException e) {
+            Log.e(TAG, "RemoteException at stopBatching");
+        }
+    }
+
+    public void updateBatchingOptions(int id, GmsFusedBatchOptions batchOptions) {
+        try {
+            mLocationHardware.updateBatchingOptions(id, batchOptions.getParcelableOptions());
+        } catch(RemoteException e) {
+            Log.e(TAG, "RemoteException at updateBatchingOptions");
+        }
+    }
+
+    public void requestBatchOfLocations(int batchSizeRequest) {
+        try {
+            mLocationHardware.requestBatchOfLocations(batchSizeRequest);
+        } catch(RemoteException e) {
+            Log.e(TAG, "RemoteException at requestBatchOfLocations");
+        }
+    }
+
+    public boolean supportsDiagnosticDataInjection() {
+        try {
+            return mLocationHardware.supportsDiagnosticDataInjection();
+        } catch(RemoteException e) {
+            Log.e(TAG, "RemoteException at supportsDiagnisticDataInjection");
+            return false;
+        }
+    }
+
+    public void injectDiagnosticData(String data) {
+        try {
+            mLocationHardware.injectDiagnosticData(data);
+        } catch(RemoteException e) {
+            Log.e(TAG, "RemoteException at injectDiagnosticData");
+        }
+    }
+
+    public boolean supportsDeviceContextInjection() {
+        try {
+            return mLocationHardware.supportsDeviceContextInjection();
+        } catch(RemoteException e) {
+            Log.e(TAG, "RemoteException at supportsDeviceContextInjection");
+            return false;
+        }
+    }
+
+    public void injectDeviceContext(int deviceEnabledContext) {
+        try {
+            mLocationHardware.injectDeviceContext(deviceEnabledContext);
+        } catch(RemoteException e) {
+            Log.e(TAG, "RemoteException at injectDeviceContext");
+        }
+    }
+
+    /*
+     * Helper methods
+     */
+    private void dispatchLocations(Location[] locations) {
+        ArrayList<FusedLocationHardwareSink> sinks = null;
+        synchronized (mSinkList) {
+            sinks = new ArrayList<FusedLocationHardwareSink>(mSinkList);
+        }
+
+        for(FusedLocationHardwareSink sink : sinks) {
+            sink.onLocationAvailable(locations);
+        }
+    }
+
+    private void dispatchDiagnosticData(String data) {
+        ArrayList<FusedLocationHardwareSink> sinks = null;
+        synchronized(mSinkList) {
+            sinks = new ArrayList<FusedLocationHardwareSink>(mSinkList);
+        }
+
+        for(FusedLocationHardwareSink sink : sinks) {
+            sink.onDiagnosticDataAvailable(data);
+        }
+    }
+}
diff --git a/location/lib/java/com/android/location/provider/FusedLocationHardwareSink.java b/location/lib/java/com/android/location/provider/FusedLocationHardwareSink.java
new file mode 100644
index 0000000..118b663
--- /dev/null
+++ b/location/lib/java/com/android/location/provider/FusedLocationHardwareSink.java
@@ -0,0 +1,31 @@
+/*
+ * 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 com.android.location.provider;
+
+import android.location.Location;
+
+/**
+ * Base class for sinks to interact with FusedLocationHardware.
+ * This is mainly used by GmsCore Fused Provider.
+ */
+public abstract class FusedLocationHardwareSink {
+    /*
+     * Methods to provide a facade for IFusedLocationHardware
+     */
+    public abstract void onLocationAvailable(Location[] locations);
+    public abstract void onDiagnosticDataAvailable(String data);
+}
\ No newline at end of file
diff --git a/location/lib/java/com/android/location/provider/FusedProvider.java b/location/lib/java/com/android/location/provider/FusedProvider.java
new file mode 100644
index 0000000..bc9feef
--- /dev/null
+++ b/location/lib/java/com/android/location/provider/FusedProvider.java
@@ -0,0 +1,59 @@
+/*
+ * 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 com.android.location.provider;
+
+import android.hardware.location.IFusedLocationHardware;
+import android.location.IFusedProvider;
+import android.os.IBinder;
+
+/**
+ * Base class for Fused providers implemented as unbundled services.
+ *
+ * <p>Fused providers can be implemented as services and return the result of
+ * {@link com.android.location.provider.FusedProvider#getBinder()} in its getBinder() method.
+ *
+ * <p>IMPORTANT: This class is effectively a public API for unbundled applications, and must remain
+ * API stable. See README.txt in the root of this package for more information.
+ *
+ * @hide
+ */
+public abstract class FusedProvider {
+    private IFusedProvider.Stub mProvider = new IFusedProvider.Stub() {
+        @Override
+        public void onFusedLocationHardwareChange(IFusedLocationHardware instance) {
+            setFusedLocationHardware(new FusedLocationHardware(instance));
+        }
+    };
+
+    /**
+     * Gets the Binder associated with the provider.
+     * This is intended to be used for the onBind() method of a service that implements a fused
+     * service.
+     *
+     * @return The IBinder instance associated with the provider.
+     */
+    public IBinder getBinder() {
+        return mProvider;
+    }
+
+    /**
+     * Sets the FusedLocationHardware instance in the provider..
+     * @param value     The instance to set. This can be null in cases where the service connection
+     *                  is disconnected.
+     */
+    public abstract void setFusedLocationHardware(FusedLocationHardware value);
+}
diff --git a/location/lib/java/com/android/location/provider/GmsFusedBatchOptions.java b/location/lib/java/com/android/location/provider/GmsFusedBatchOptions.java
new file mode 100644
index 0000000..fd3f402
--- /dev/null
+++ b/location/lib/java/com/android/location/provider/GmsFusedBatchOptions.java
@@ -0,0 +1,106 @@
+/*
+ * 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 com.android.location.provider;
+
+import android.location.FusedBatchOptions;
+
+/**
+ * Class that exposes FusedBatchOptions to the GmsCore .
+ */
+public class GmsFusedBatchOptions {
+    private FusedBatchOptions mOptions = new FusedBatchOptions();
+
+    /*
+     * Methods that provide a facade for properties in FusedBatchOptions.
+     */
+    public void setMaxPowerAllocationInMW(double value) {
+        mOptions.setMaxPowerAllocationInMW(value);
+    }
+
+    public double getMaxPowerAllocationInMW() {
+        return mOptions.getMaxPowerAllocationInMW();
+    }
+
+    public void setPeriodInNS(long value) {
+        mOptions.setPeriodInNS(value);
+    }
+
+    public long getPeriodInNS() {
+        return mOptions.getPeriodInNS();
+    }
+
+    public void setSourceToUse(int source) {
+        mOptions.setSourceToUse(source);
+    }
+
+    public void resetSourceToUse(int source) {
+        mOptions.resetSourceToUse(source);
+    }
+
+    public boolean isSourceToUseSet(int source) {
+        return mOptions.isSourceToUseSet(source);
+    }
+
+    public int getSourcesToUse() {
+        return mOptions.getSourcesToUse();
+    }
+
+    public void setFlag(int flag) {
+        mOptions.setFlag(flag);
+    }
+
+    public void resetFlag(int flag) {
+        mOptions.resetFlag(flag);
+    }
+
+    public boolean isFlagSet(int flag) {
+        return mOptions.isFlagSet(flag);
+    }
+
+    public int getFlags() {
+        return mOptions.getFlags();
+    }
+
+    /**
+     * Definition of enum flag sets needed by this class.
+     * Such values need to be kept in sync with the ones in fused_location.h
+     */
+
+    public static final class SourceTechnologies {
+        public static int GNSS = 1<<0;
+        public static int WIFI = 1<<1;
+        public static int SENSORS = 1<<2;
+        public static int CELL = 1<<3;
+        public static int BLUETOOTH = 1<<4;
+    }
+
+    public static final class BatchFlags {
+        public static int WAKEUP_ON_FIFO_FULL = 1<<0;
+        public static int CALLBACK_ON_LOCATION_FIX = 1<<1;
+    }
+
+    /*
+     * Method definitions for internal use.
+     */
+
+    /*
+     * @hide
+     */
+    public FusedBatchOptions getParcelableOptions() {
+        return mOptions;
+    }
+}
diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
old mode 100644
new mode 100755
index 632334b..cf1238a
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -221,7 +221,7 @@
                 if (c != null) c.close();
                 if (db != null) db.close();
             }
-            databaseFile.delete();
+            context.deleteDatabase(devicePropertiesName);
         }
     }
 
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 2267372..5e198a2 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -24,6 +24,7 @@
     <uses-permission android:name="android.permission.READ_CONTACTS" />
     <uses-permission android:name="android.permission.CONFIGURE_WIFI_DISPLAY" />
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+    <uses-permission android:name="android.permission.GET_APP_OPS_STATS" />
 
     <!-- Networking and telephony -->
     <uses-permission android:name="android.permission.BLUETOOTH" />
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_device_access_location_found.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_device_access_location_found.png
new file mode 100644
index 0000000..657a612
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_device_access_location_found.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_device_access_location_found.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_device_access_location_found.png
new file mode 100644
index 0000000..80fc24b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_device_access_location_found.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/stat_sys_device_access_location_found.png b/packages/SystemUI/res/drawable-xhdpi/stat_sys_device_access_location_found.png
new file mode 100644
index 0000000..fd8ad64
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/stat_sys_device_access_location_found.png
Binary files differ
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 0073e60..33a85c3a 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -418,6 +418,9 @@
     <!-- Notification text: when GPS has found a fix [CHAR LIMIT=50] -->
     <string name="gps_notification_found_text">Location set by GPS</string>
 
+    <!-- Accessibility text describing the presence of active location requests by one or more apps -->
+    <string name="accessibility_location_active">Location requests active</string>
+
     <!-- Content description of the clear button in the notification panel for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_clear_all">Clear all notifications.</string>
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java
index 3f8043d..91ddf0f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java
@@ -16,10 +16,8 @@
 
 package com.android.systemui.statusbar.policy;
 
-import android.app.INotificationManager;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
+import android.app.AppOpsManager;
+import android.app.StatusBarManager;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -28,36 +26,39 @@
 import android.database.ContentObserver;
 import android.location.LocationManager;
 import android.os.Handler;
-import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
 
 import com.android.systemui.R;
 
 import java.util.ArrayList;
+import java.util.List;
 
+/**
+ * A controller to manage changes of location related states and update the views accordingly.
+ */
 public class LocationController extends BroadcastReceiver {
-    private static final String TAG = "StatusBar.LocationController";
+    // The name of the placeholder corresponding to the location request status icon.
+    // This string corresponds to config_statusBarIcons in core/res/res/values/config.xml.
+    private static final String LOCATION_STATUS_ICON_PLACEHOLDER = "location";
+    private static final int LOCATION_STATUS_ICON_ID
+        = R.drawable.stat_sys_device_access_location_found;
 
-    private static final int GPS_NOTIFICATION_ID = 374203-122084;
+    private static final int[] mHighPowerRequestAppOpArray
+        = new int[] {AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION};
 
     private Context mContext;
 
-    private INotificationManager mNotificationService;
+    private AppOpsManager mAppOpsManager;
+    private StatusBarManager mStatusBarManager;
 
-    private ArrayList<LocationGpsStateChangeCallback> mChangeCallbacks =
-            new ArrayList<LocationGpsStateChangeCallback>();
+    private boolean mAreActiveLocationRequests;
+    private boolean mIsAirplaneMode;
+
     private ArrayList<LocationSettingsChangeCallback> mSettingsChangeCallbacks =
             new ArrayList<LocationSettingsChangeCallback>();
 
     /**
-     * A callback for change in gps status (enabled/disabled, have lock, etc).
-     */
-    public interface LocationGpsStateChangeCallback {
-        public void onLocationGpsStateChanged(boolean inUse, String description);
-    }
-
-    /**
      * A callback for change in location settings (the user has enabled/disabled location).
      */
     public interface LocationSettingsChangeCallback {
@@ -74,13 +75,15 @@
         mContext = context;
 
         IntentFilter filter = new IntentFilter();
-        filter.addAction(LocationManager.GPS_ENABLED_CHANGE_ACTION);
-        filter.addAction(LocationManager.GPS_FIX_CHANGE_ACTION);
+        filter.addAction(LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION);
+        // Listen for a change in the airplane mode setting so we can defensively turn off the
+        // high power location icon when radios are disabled.
+        filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
         context.registerReceiver(this, filter);
 
-        NotificationManager nm = (NotificationManager)context.getSystemService(
-                Context.NOTIFICATION_SERVICE);
-        mNotificationService = nm.getService();
+        mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+        mStatusBarManager
+                = (StatusBarManager) context.getSystemService(Context.STATUS_BAR_SERVICE);
 
         // Register to listen for changes to the location settings
         context.getContentResolver().registerContentObserver(
@@ -94,13 +97,11 @@
                         }
                     }
                 });
-    }
 
-    /**
-     * Add a callback to listen for changes in gps status.
-     */
-    public void addStateChangedCallback(LocationGpsStateChangeCallback cb) {
-        mChangeCallbacks.add(cb);
+        // Examine the current location state and initialize the status view.
+        updateActiveLocationRequests();
+        updateAirplaneMode();
+        refreshViews();
     }
 
     /**
@@ -145,76 +146,77 @@
        return isGpsEnabled || isNetworkEnabled;
     }
 
+    /**
+     * Returns true if there currently exist active high power location requests.
+     */
+    private boolean areActiveHighPowerLocationRequests() {
+        List<AppOpsManager.PackageOps> packages
+            = mAppOpsManager.getPackagesForOps(mHighPowerRequestAppOpArray);
+        // AppOpsManager can return null when there is no requested data.
+        if (packages != null) {
+            final int numPackages = packages.size();
+            for (int packageInd = 0; packageInd < numPackages; packageInd++) {
+                AppOpsManager.PackageOps packageOp = packages.get(packageInd);
+                List<AppOpsManager.OpEntry> opEntries = packageOp.getOps();
+                if (opEntries != null) {
+                    final int numOps = opEntries.size();
+                    for (int opInd = 0; opInd < numOps; opInd++) {
+                        AppOpsManager.OpEntry opEntry = opEntries.get(opInd);
+                        // AppOpsManager should only return OP_MONITOR_HIGH_POWER_LOCATION because
+                        // of the mHighPowerRequestAppOpArray filter, but checking defensively.
+                        if (opEntry.getOp() == AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION) {
+                            if (opEntry.isRunning()) {
+                                return true;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        return false;
+    }
+
+    // Updates the status view based on the current state of location requests and airplane mode.
+    private void refreshViews() {
+        // The airplane mode check is defensive - there shouldn't be any active high power
+        // location requests when airplane mode is on.
+        if (!mIsAirplaneMode && mAreActiveLocationRequests) {
+            mStatusBarManager.setIcon(LOCATION_STATUS_ICON_PLACEHOLDER, LOCATION_STATUS_ICON_ID, 0,
+                    mContext.getString(R.string.accessibility_location_active));
+        } else {
+            mStatusBarManager.removeIcon(LOCATION_STATUS_ICON_PLACEHOLDER);
+        }
+    }
+
+    // Reads the active location requests and updates the status view if necessary.
+    private void updateActiveLocationRequests() {
+        boolean hadActiveLocationRequests = mAreActiveLocationRequests;
+        mAreActiveLocationRequests = areActiveHighPowerLocationRequests();
+        if (mAreActiveLocationRequests != hadActiveLocationRequests) {
+            refreshViews();
+        }
+    }
+
+    // Reads the airplane mode setting and updates the status view if necessary.
+    private void updateAirplaneMode() {
+        boolean wasAirplaneMode = mIsAirplaneMode;
+        // TODO This probably warrants a utility method in Settings.java.
+        mIsAirplaneMode = (Settings.Global.getInt(
+                mContext.getContentResolver(),
+                Settings.Global.AIRPLANE_MODE_ON, 0) == 1);
+        if (mIsAirplaneMode != wasAirplaneMode) {
+            refreshViews();
+        }
+    }
+
     @Override
     public void onReceive(Context context, Intent intent) {
         final String action = intent.getAction();
-        final boolean enabled = intent.getBooleanExtra(LocationManager.EXTRA_GPS_ENABLED, false);
-
-        boolean visible;
-        int iconId, textResId;
-
-        if (action.equals(LocationManager.GPS_FIX_CHANGE_ACTION) && enabled) {
-            // GPS is getting fixes
-            iconId = com.android.internal.R.drawable.stat_sys_gps_on;
-            textResId = R.string.gps_notification_found_text;
-            visible = true;
-        } else if (action.equals(LocationManager.GPS_ENABLED_CHANGE_ACTION) && !enabled) {
-            // GPS is off
-            visible = false;
-            iconId = textResId = 0;
-        } else {
-            // GPS is on, but not receiving fixes
-            iconId = R.drawable.stat_sys_gps_acquiring_anim;
-            textResId = R.string.gps_notification_searching_text;
-            visible = true;
-        }
-
-        try {
-            if (visible) {
-                Intent gpsIntent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
-                gpsIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
-                PendingIntent pendingIntent = PendingIntent.getActivityAsUser(context, 0,
-                        gpsIntent, 0, null, UserHandle.CURRENT);
-                String text = mContext.getText(textResId).toString();
-
-                Notification n = new Notification.Builder(mContext)
-                    .setSmallIcon(iconId)
-                    .setContentTitle(text)
-                    .setOngoing(true)
-                    .setContentIntent(pendingIntent)
-                    .getNotification();
-
-                // Notification.Builder will helpfully fill these out for you no matter what you do
-                n.tickerView = null;
-                n.tickerText = null;
-
-                n.priority = Notification.PRIORITY_HIGH;
-
-                int[] idOut = new int[1];
-                mNotificationService.enqueueNotificationWithTag(
-                        mContext.getPackageName(), mContext.getBasePackageName(),
-                        null,
-                        GPS_NOTIFICATION_ID,
-                        n,
-                        idOut,
-                        UserHandle.USER_ALL);
-
-                for (LocationGpsStateChangeCallback cb : mChangeCallbacks) {
-                    cb.onLocationGpsStateChanged(true, text);
-                }
-            } else {
-                mNotificationService.cancelNotificationWithTag(
-                        mContext.getPackageName(), null,
-                        GPS_NOTIFICATION_ID, UserHandle.USER_ALL);
-
-                for (LocationGpsStateChangeCallback cb : mChangeCallbacks) {
-                    cb.onLocationGpsStateChanged(false, null);
-                }
-            }
-        } catch (android.os.RemoteException ex) {
-            // well, it was worth a shot
+        if (LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION.equals(action)) {
+            updateActiveLocationRequests();
+        } else if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(action)) {
+            updateAirplaneMode();
         }
     }
 }
-
diff --git a/services/java/com/android/server/AppOpsService.java b/services/java/com/android/server/AppOpsService.java
index 81572c5..7af95f3 100644
--- a/services/java/com/android/server/AppOpsService.java
+++ b/services/java/com/android/server/AppOpsService.java
@@ -426,12 +426,14 @@
         HashMap<Callback, ArrayList<Pair<String, Integer>>> callbacks = null;
         synchronized (this) {
             boolean changed = false;
-            for (int i=0; i<mUidOps.size(); i++) {
+            for (int i=mUidOps.size()-1; i>=0; i--) {
                 HashMap<String, Ops> packages = mUidOps.valueAt(i);
-                for (Map.Entry<String, Ops> ent : packages.entrySet()) {
+                Iterator<Map.Entry<String, Ops>> it = packages.entrySet().iterator();
+                while (it.hasNext()) {
+                    Map.Entry<String, Ops> ent = it.next();
                     String packageName = ent.getKey();
                     Ops pkgOps = ent.getValue();
-                    for (int j=0; j<pkgOps.size(); j++) {
+                    for (int j=pkgOps.size()-1; j>=0; j--) {
                         Op curOp = pkgOps.valueAt(j);
                         if (curOp.mode != AppOpsManager.MODE_ALLOWED) {
                             curOp.mode = AppOpsManager.MODE_ALLOWED;
@@ -440,9 +442,17 @@
                                     mOpModeWatchers.get(curOp.op));
                             callbacks = addCallbacks(callbacks, packageName, curOp.op,
                                     mPackageModeWatchers.get(packageName));
-                            pruneOp(curOp, mUidOps.keyAt(i), packageName);
+                            if (curOp.time == 0 && curOp.rejectTime == 0) {
+                                pkgOps.removeAt(j);
+                            }
                         }
                     }
+                    if (pkgOps.size() == 0) {
+                        it.remove();
+                    }
+                }
+                if (packages.size() == 0) {
+                    mUidOps.removeAt(i);
                 }
             }
             if (changed) {
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 3ae2eb5..1f71471 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -3916,11 +3916,26 @@
                 // hipri connection so the default connection stays active.
                 log("isMobileOk: start hipri url=" + params.mUrl);
                 mCs.setEnableFailFastMobileData(DctConstants.ENABLED);
-                mCs.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
-                        Phone.FEATURE_ENABLE_HIPRI, new Binder());
 
                 // Continue trying to connect until time has run out
                 long endTime = SystemClock.elapsedRealtime() + params.mTimeOutMs;
+
+                // First wait until we can start using hipri
+                Binder binder = new Binder();
+                while(SystemClock.elapsedRealtime() < endTime) {
+                    int ret = mCs.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
+                            Phone.FEATURE_ENABLE_HIPRI, binder);
+                    if ((ret == PhoneConstants.APN_ALREADY_ACTIVE)
+                        || (ret == PhoneConstants.APN_REQUEST_STARTED)) {
+                            log("isMobileOk: hipri started");
+                            break;
+                    }
+                    if (VDBG) log("isMobileOk: hipri not started yet");
+                    result = ConnectivityManager.CMP_RESULT_CODE_NO_CONNECTION;
+                    sleep(1);
+                }
+
+                // Continue trying to connect until time has run out
                 while(SystemClock.elapsedRealtime() < endTime) {
                     try {
                         // Wait for hipri to connect.
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index cde84dc..49746ff 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -63,6 +63,8 @@
 import com.android.internal.location.ProviderProperties;
 import com.android.internal.location.ProviderRequest;
 import com.android.internal.os.BackgroundThread;
+import com.android.server.location.FlpHardwareProvider;
+import com.android.server.location.FusedProxy;
 import com.android.server.location.GeocoderProxy;
 import com.android.server.location.GeofenceProxy;
 import com.android.server.location.GeofenceManager;
@@ -429,6 +431,17 @@
             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,
+                mLocationHandler,
+                flpHardwareProvider.getLocationHardware());
+
+        if(fusedProxy == null) {
+            Slog.e(TAG, "No FusedProvider found.");
+        }
     }
 
     /**
@@ -552,8 +565,14 @@
                     allowHighPower = false;
                 }
             }
+            boolean wasHighPowerMonitoring = mOpHighPowerMonitoring;
             mOpHighPowerMonitoring = updateMonitoring(allowHighPower, mOpHighPowerMonitoring,
                     AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION);
+            if (mOpHighPowerMonitoring != wasHighPowerMonitoring) {
+                // send an intent to notify that a high power request has been added/removed.
+                Intent intent = new Intent(LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION);
+                mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+            }
         }
 
         /**
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 3d8843e..9dba2ed 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -42,7 +42,7 @@
 import com.android.server.AppOpsService;
 import com.android.server.AttributeCache;
 import com.android.server.IntentResolver;
-import com.android.server.ProcessMap;
+import com.android.internal.app.ProcessMap;
 import com.android.server.SystemServer;
 import com.android.server.Watchdog;
 import com.android.server.am.ActivityStack.ActivityState;
@@ -1554,11 +1554,11 @@
         try {
             ActivityManagerService m = mSelf;
 
-            ServiceManager.addService("activity", m, true);
+            ServiceManager.addService(Context.ACTIVITY_SERVICE, m, true);
+            ServiceManager.addService(ProcessStats.SERVICE_NAME, m.mProcessStats);
             ServiceManager.addService("meminfo", new MemBinder(m));
             ServiceManager.addService("gfxinfo", new GraphicsBinder(m));
             ServiceManager.addService("dbinfo", new DbBinder(m));
-            ServiceManager.addService("procstats", new ProcBinder(m));
             if (MONITOR_CPU_USAGE) {
                 ServiceManager.addService("cpuinfo", new CpuBinder(m));
             }
@@ -1775,26 +1775,6 @@
         }
     }
 
-    static class ProcBinder extends Binder {
-        ActivityManagerService mActivityManagerService;
-        ProcBinder(ActivityManagerService activityManagerService) {
-            mActivityManagerService = activityManagerService;
-        }
-
-        @Override
-        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-            if (mActivityManagerService.checkCallingPermission(android.Manifest.permission.DUMP)
-                    != PackageManager.PERMISSION_GRANTED) {
-                pw.println("Permission Denial: can't dump procstats from from pid="
-                        + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
-                        + " without permission " + android.Manifest.permission.DUMP);
-                return;
-            }
-
-            mActivityManagerService.mProcessStats.dump(fd, pw, args);
-        }
-    }
-
     private ActivityManagerService() {
         Slog.i(TAG, "Memory class: " + ActivityManager.staticGetMemoryClass());
 
@@ -8453,9 +8433,17 @@
             resolver, Settings.Global.WAIT_FOR_DEBUGGER, 0) != 0;
         boolean alwaysFinishActivities = Settings.Global.getInt(
             resolver, Settings.Global.ALWAYS_FINISH_ACTIVITIES, 0) != 0;
+        boolean forceRtl = Settings.Global.getInt(
+                resolver, Settings.Global.DEVELOPMENT_FORCE_RTL, 0) != 0;
+        // Transfer any global setting for forcing RTL layout, into a System Property
+        SystemProperties.set(Settings.Global.DEVELOPMENT_FORCE_RTL, forceRtl ? "1":"0");
 
         Configuration configuration = new Configuration();
         Settings.System.getConfiguration(resolver, configuration);
+        if (forceRtl) {
+            // This will take care of setting the correct layout direction flags
+            configuration.setLayoutDirection(configuration.locale);
+        }
 
         synchronized (this) {
             mDebugApp = mOrigDebugApp = debugApp;
diff --git a/services/java/com/android/server/am/ProcessStatsService.java b/services/java/com/android/server/am/ProcessStatsService.java
index 8b4a5a0..81b5618 100644
--- a/services/java/com/android/server/am/ProcessStatsService.java
+++ b/services/java/com/android/server/am/ProcessStatsService.java
@@ -18,6 +18,8 @@
 
 import android.app.AppGlobals;
 import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.os.Binder;
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
@@ -59,7 +61,7 @@
     static long WRITE_PERIOD = 30*60*1000;      // Write file every 30 minutes or so.
     static long COMMIT_PERIOD = 24*60*60*1000;  // Commit current stats every day.
 
-    final Object mLock;
+    final ActivityManagerService mAm;
     final File mBaseDir;
     ProcessStats mProcessStats;
     AtomicFile mFile;
@@ -75,15 +77,15 @@
     boolean mPendingWriteCommitted;
     long mLastWriteTime;
 
-    public ProcessStatsService(Object lock, File file) {
-        mLock = lock;
+    public ProcessStatsService(ActivityManagerService am, File file) {
+        mAm = am;
         mBaseDir = file;
         mBaseDir.mkdirs();
         mProcessStats = new ProcessStats(true);
         updateFile();
         SystemProperties.addChangeCallback(new Runnable() {
             @Override public void run() {
-                synchronized (mLock) {
+                synchronized (mAm) {
                     if (mProcessStats.evaluateSystemProperties(false)) {
                         mProcessStats.mFlags |= ProcessStats.FLAG_SYSPROPS;
                         writeStateLocked(true, true);
@@ -449,7 +451,7 @@
         Parcel current = Parcel.obtain();
         mWriteLock.lock();
         try {
-            synchronized (mLock) {
+            synchronized (mAm) {
                 mProcessStats.writeToParcel(current, 0);
             }
             if (historic != null) {
@@ -493,7 +495,16 @@
         pw.println("  <package.name>: optional name of package to filter output by.");
     }
 
-    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (mAm.checkCallingPermission(android.Manifest.permission.DUMP)
+                != PackageManager.PERMISSION_GRANTED) {
+            pw.println("Permission Denial: can't dump procstats from from pid="
+                    + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
+                    + " without permission " + android.Manifest.permission.DUMP);
+            return;
+        }
+
         final long now = SystemClock.uptimeMillis();
 
         boolean isCheckin = false;
@@ -638,7 +649,7 @@
                 }
             }
             pw.println();
-            synchronized (mLock) {
+            synchronized (mAm) {
                 dumpFilteredProcessesCsvLocked(pw, null,
                         csvSepScreenStats, csvScreenStats, csvSepMemStats, csvMemStats,
                         csvSepProcStats, csvProcStats, now, reqPackage);
@@ -721,7 +732,7 @@
             }
         }
         if (!isCheckin) {
-            synchronized (mLock) {
+            synchronized (mAm) {
                 if (isCompact) {
                     mProcessStats.dumpCheckinLocked(pw, reqPackage);
                 } else {
diff --git a/services/java/com/android/server/location/FlpHardwareProvider.java b/services/java/com/android/server/location/FlpHardwareProvider.java
new file mode 100644
index 0000000..469f7ce
--- /dev/null
+++ b/services/java/com/android/server/location/FlpHardwareProvider.java
@@ -0,0 +1,344 @@
+/*
+ * 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 com.android.server.location;
+
+import android.hardware.location.GeofenceHardwareImpl;
+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;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.Log;
+import android.util.Slog;
+
+/**
+ * This class is an interop layer for JVM types and the JNI code that interacts
+ * with the FLP HAL implementation.
+ *
+ * {@hide}
+ */
+public class FlpHardwareProvider {
+    private GeofenceHardwareImpl mGeofenceHardwareSink = null;
+    private IFusedLocationHardwareSink mLocationSink = null;
+
+    private static FlpHardwareProvider sSingletonInstance = null;
+
+    private final static String TAG = "FlpHardwareProvider";
+    private final Context mContext;
+
+    public static FlpHardwareProvider getInstance(Context context) {
+        if (sSingletonInstance == null) {
+            sSingletonInstance = new FlpHardwareProvider(context);
+        }
+
+        return sSingletonInstance;
+    }
+
+    private FlpHardwareProvider(Context context) {
+        mContext = context;
+
+        // register for listening for passive provider data
+        Handler handler = new Handler();
+        LocationManager manager = (LocationManager) mContext.getSystemService(
+                Context.LOCATION_SERVICE);
+        manager.requestLocationUpdates(
+                LocationManager.PASSIVE_PROVIDER,
+                0 /* minTime */,
+                0 /* minDistance */,
+                new NetworkLocationListener(),
+                handler.getLooper());
+    }
+
+    public static boolean isSupported() {
+        return nativeIsSupported();
+    }
+
+    /**
+     * Private callback functions used by FLP HAL.
+     */
+    // FlpCallbacks members
+    private void onLocationReport(Location[] locations) {
+        for (Location location : locations) {
+            location.setProvider(LocationManager.FUSED_PROVIDER);
+            // set the elapsed time-stamp just as GPS provider does
+            location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
+        }
+
+        try {
+            if (mLocationSink != null) {
+                mLocationSink.onLocationAvailable(locations);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException calling onLocationAvailable");
+        }
+    }
+
+    // FlpDiagnosticCallbacks members
+    private void onDataReport(String data) {
+        try {
+            if (mLocationSink != null) {
+                mLocationSink.onDiagnosticDataAvailable(data);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException calling onDiagnosticDataAvailable");
+        }
+    }
+
+    // FlpGeofenceCallbacks members
+    private void onGeofenceTransition(
+            int geofenceId,
+            Location location,
+            int transition,
+            long timestamp,
+            int sourcesUsed
+            ) {
+        // TODO: [GeofenceIntegration] change GeofenceHardwareImpl to accept a location object
+    }
+
+    private void onGeofenceMonitorStatus(int status, int source, Location location) {
+        // TODO: [GeofenceIntegration]
+    }
+
+    private void onGeofenceAdd(int geofenceId, int result) {
+        // TODO: [GeofenceIntegration] map between GPS and FLP results to pass a consistent status
+    }
+
+    private void onGeofenceRemove(int geofenceId, int result) {
+        // TODO: [GeofenceIntegration] map between GPS and FLP results to pass a consistent status
+    }
+
+    private void onGeofencePause(int geofenceId, int result) {
+        // TODO; [GeofenceIntegration] map between GPS and FLP results
+    }
+
+    private void onGeofenceResume(int geofenceId, int result) {
+        // TODO: [GeofenceIntegration] map between GPS and FLP results
+    }
+
+    /**
+     * Private native methods accessing FLP HAL.
+     */
+    static { nativeClassInit(); }
+
+    // Core members
+    private static native void nativeClassInit();
+    private static native boolean nativeIsSupported();
+
+    // FlpLocationInterface members
+    private native void nativeInit();
+    private native int nativeGetBatchSize();
+    private native void nativeStartBatching(int requestId, FusedBatchOptions options);
+    private native void nativeUpdateBatchingOptions(int requestId, FusedBatchOptions optionsObject);
+    private native void nativeStopBatching(int id);
+    private native void nativeRequestBatchedLocation(int lastNLocations);
+    private native void nativeInjectLocation(Location location);
+    // TODO [Fix] sort out the lifetime of the instance
+    private native void nativeCleanup();
+
+    // FlpDiagnosticsInterface members
+    private native boolean nativeIsDiagnosticSupported();
+    private native void nativeInjectDiagnosticData(String data);
+
+    // FlpDeviceContextInterface members
+    private native boolean nativeIsDeviceContextSupported();
+    private native void nativeInjectDeviceContext(int deviceEnabledContext);
+
+    // FlpGeofencingInterface members
+    private native boolean nativeIsGeofencingSupported();
+    private native void nativeAddGeofences(int[] geofenceIdsArray, Geofence[] geofencesArray);
+    private native void nativePauseGeofence(int geofenceId);
+    private native void  nativeResumeGeofence(int geofenceId, int monitorTransitions);
+    private native void nativeModifyGeofenceOption(
+        int geofenceId,
+        int lastTransition,
+        int monitorTransitions,
+        int notificationResponsiveness,
+        int unknownTimer,
+        int sourcesToUse);
+    private native void nativeRemoveGeofences(int[] geofenceIdsArray);
+
+    /**
+     * Interface implementations for services built on top of this functionality.
+     */
+    public static final String LOCATION = "Location";
+    public static final String GEOFENCING = "Geofencing";
+
+    public IFusedLocationHardware getLocationHardware() {
+        nativeInit();
+        return mLocationHardware;
+    }
+
+    public IFusedGeofenceHardware getGeofenceHardware() {
+        nativeInit();
+        return mGeofenceHardwareService;
+    }
+
+    private final IFusedLocationHardware mLocationHardware = new IFusedLocationHardware.Stub() {
+        @Override
+        public void registerSink(IFusedLocationHardwareSink eventSink) {
+            // only one sink is allowed at the moment
+            if (mLocationSink != null) {
+                throw new RuntimeException("IFusedLocationHardware does not support multiple sinks");
+            }
+
+            mLocationSink = eventSink;
+        }
+
+        @Override
+        public void unregisterSink(IFusedLocationHardwareSink eventSink) {
+            // don't throw if the sink is not registered, simply make it a no-op
+            if (mLocationSink == eventSink) {
+                mLocationSink = null;
+            }
+        }
+
+        @Override
+        public int getSupportedBatchSize() {
+            return nativeGetBatchSize();
+        }
+
+        @Override
+        public void startBatching(int requestId, FusedBatchOptions options) {
+            nativeStartBatching(requestId, options);
+        }
+
+        @Override
+        public void stopBatching(int requestId) {
+            nativeStopBatching(requestId);
+        }
+
+        @Override
+        public void updateBatchingOptions(int requestId, FusedBatchOptions options) {
+            nativeUpdateBatchingOptions(requestId, options);
+        }
+
+        @Override
+        public void requestBatchOfLocations(int batchSizeRequested) {
+            nativeRequestBatchedLocation(batchSizeRequested);
+        }
+
+        @Override
+        public boolean supportsDiagnosticDataInjection() {
+            return nativeIsDiagnosticSupported();
+        }
+
+        @Override
+        public void injectDiagnosticData(String data) {
+            nativeInjectDiagnosticData(data);
+        }
+
+        @Override
+        public boolean supportsDeviceContextInjection() {
+            return nativeIsDeviceContextSupported();
+        }
+
+        @Override
+        public void injectDeviceContext(int deviceEnabledContext) {
+            nativeInjectDeviceContext(deviceEnabledContext);
+        }
+    };
+
+    private final IFusedGeofenceHardware mGeofenceHardwareService =
+            new IFusedGeofenceHardware.Stub() {
+        @Override
+        public boolean isSupported() {
+            return nativeIsGeofencingSupported();
+        }
+
+        @Override
+        public void addGeofences(int[] geofenceIdsArray, Geofence[] geofencesArray) {
+            nativeAddGeofences(geofenceIdsArray, geofencesArray);
+        }
+
+        @Override
+        public void removeGeofences(int[] geofenceIds) {
+            nativeRemoveGeofences(geofenceIds);
+        }
+
+        @Override
+        public void pauseMonitoringGeofence(int geofenceId) {
+            nativePauseGeofence(geofenceId);
+        }
+
+        @Override
+        public void resumeMonitoringGeofence(int geofenceId, int monitorTransitions) {
+            nativeResumeGeofence(geofenceId, monitorTransitions);
+        }
+
+        @Override
+        public void modifyGeofenceOptions(int geofenceId,
+                int lastTransition,
+                int monitorTransitions,
+                int notificationResponsiveness,
+                int unknownTimer
+                ) {
+            // TODO: [GeofenceIntegration] set sourcesToUse to the right value
+            // TODO: expose sourcesToUse externally when needed
+            nativeModifyGeofenceOption(
+                    geofenceId,
+                    lastTransition,
+                    monitorTransitions,
+                    notificationResponsiveness,
+                    unknownTimer,
+                    /* sourcesToUse */ 0xFFFF);
+        }
+    };
+
+    /**
+     * Internal classes and functions used by the provider.
+     */
+    private final class NetworkLocationListener implements LocationListener {
+        @Override
+        public void onLocationChanged(Location location) {
+            if (
+                !LocationManager.NETWORK_PROVIDER.equals(location.getProvider()) ||
+                !location.hasAccuracy()
+                ) {
+                return;
+            }
+
+            nativeInjectLocation(location);
+        }
+
+        @Override
+        public void onStatusChanged(String provider, int status, Bundle extras) { }
+
+        @Override
+        public void onProviderEnabled(String provider) { }
+
+        @Override
+        public void onProviderDisabled(String provider) { }
+    }
+
+    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
diff --git a/services/java/com/android/server/location/FusedLocationHardwareSecure.java b/services/java/com/android/server/location/FusedLocationHardwareSecure.java
new file mode 100644
index 0000000..389bd24
--- /dev/null
+++ b/services/java/com/android/server/location/FusedLocationHardwareSecure.java
@@ -0,0 +1,119 @@
+/*
+ * 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 com.android.server.location;
+
+import android.content.Context;
+import android.hardware.location.IFusedLocationHardware;
+import android.hardware.location.IFusedLocationHardwareSink;
+import android.location.FusedBatchOptions;
+import android.os.RemoteException;
+
+/**
+ * FusedLocationHardware decorator that adds permission checking.
+ * @hide
+ */
+public class FusedLocationHardwareSecure extends IFusedLocationHardware.Stub {
+    private final IFusedLocationHardware mLocationHardware;
+    private final Context mContext;
+    private final String mPermissionId;
+
+    public FusedLocationHardwareSecure(
+            IFusedLocationHardware locationHardware,
+            Context context,
+            String permissionId) {
+        mLocationHardware = locationHardware;
+        mContext = context;
+        mPermissionId = permissionId;
+    }
+
+    private void checkPermissions() {
+        mContext.enforceCallingPermission(
+                mPermissionId,
+                String.format(
+                        "Permission '%s' not granted to access FusedLocationHardware",
+                        mPermissionId));
+    }
+
+    @Override
+    public void registerSink(IFusedLocationHardwareSink eventSink) throws RemoteException {
+        checkPermissions();
+        mLocationHardware.registerSink(eventSink);
+    }
+
+    @Override
+    public void unregisterSink(IFusedLocationHardwareSink eventSink) throws RemoteException {
+        checkPermissions();
+        mLocationHardware.unregisterSink(eventSink);
+    }
+
+    @Override
+    public int getSupportedBatchSize() throws RemoteException {
+        checkPermissions();
+        return mLocationHardware.getSupportedBatchSize();
+    }
+
+    @Override
+    public void startBatching(int id, FusedBatchOptions batchOptions) throws RemoteException {
+        checkPermissions();
+        mLocationHardware.startBatching(id, batchOptions);
+    }
+
+    @Override
+    public void stopBatching(int id) throws RemoteException {
+        checkPermissions();
+        mLocationHardware.stopBatching(id);
+    }
+
+    @Override
+    public void updateBatchingOptions(
+            int id,
+            FusedBatchOptions batchoOptions
+            ) throws RemoteException {
+        checkPermissions();
+        mLocationHardware.updateBatchingOptions(id, batchoOptions);
+    }
+
+    @Override
+    public void requestBatchOfLocations(int batchSizeRequested) throws RemoteException {
+        checkPermissions();
+        mLocationHardware.requestBatchOfLocations(batchSizeRequested);
+    }
+
+    @Override
+    public boolean supportsDiagnosticDataInjection() throws RemoteException {
+        checkPermissions();
+        return mLocationHardware.supportsDiagnosticDataInjection();
+    }
+
+    @Override
+    public void injectDiagnosticData(String data) throws RemoteException {
+        checkPermissions();
+        mLocationHardware.injectDiagnosticData(data);
+    }
+
+    @Override
+    public boolean supportsDeviceContextInjection() throws RemoteException {
+        checkPermissions();
+        return mLocationHardware.supportsDeviceContextInjection();
+    }
+
+    @Override
+    public void injectDeviceContext(int deviceEnabledContext) throws RemoteException {
+        checkPermissions();
+        mLocationHardware.injectDeviceContext(deviceEnabledContext);
+    }
+}
diff --git a/services/java/com/android/server/location/FusedProxy.java b/services/java/com/android/server/location/FusedProxy.java
new file mode 100644
index 0000000..8278b96
--- /dev/null
+++ b/services/java/com/android/server/location/FusedProxy.java
@@ -0,0 +1,116 @@
+/*
+ * 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 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 com.android.server.location;
+
+import com.android.server.ServiceWatcher;
+
+import android.Manifest;
+import android.content.Context;
+import android.hardware.location.IFusedLocationHardware;
+import android.location.IFusedProvider;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * Proxy that helps bind GCore FusedProvider implementations to the Fused Hardware instances.
+ *
+ * @hide
+ */
+public final class FusedProxy {
+    private final String TAG = "FusedProxy";
+
+    private ServiceWatcher mServiceWatcher;
+
+    private FusedLocationHardwareSecure mLocationHardware;
+
+    /**
+     * Constructor of the class.
+     * This is private as the class follows a factory pattern for construction.
+     *
+     * @param context           The context needed for construction.
+     * @param handler           The handler needed for construction.
+     * @param locationHardware  The instance of the Fused location hardware assigned to the proxy.
+     */
+    private FusedProxy(Context context, Handler handler, IFusedLocationHardware locationHardware) {
+        mLocationHardware = new FusedLocationHardwareSecure(
+                locationHardware,
+                context,
+                Manifest.permission.LOCATION_HARDWARE);
+        Runnable newServiceWork = new Runnable() {
+            @Override
+            public void run() {
+                bindProvider(mLocationHardware);
+            }
+        };
+
+        // prepare the connection to the provider
+        mServiceWatcher = new ServiceWatcher(
+                context,
+                TAG,
+                "com.android.location.service.FusedProvider",
+                com.android.internal.R.bool.config_enableFusedLocationOverlay,
+                com.android.internal.R.string.config_fusedLocationProviderPackageName,
+                com.android.internal.R.array.config_locationProviderPackageNames,
+                newServiceWork,
+                handler);
+    }
+
+    /**
+     * Creates an instance of the proxy and binds it to the appropriate FusedProvider.
+     *
+     * @param context           The context needed for construction.
+     * @param handler           The handler needed for construction.
+     * @param locationHardware  The instance of the Fused location hardware assigned to the proxy.
+     *
+     * @return An instance of the proxy if it could be bound, null otherwise.
+     */
+    public static FusedProxy createAndBind(
+            Context context,
+            Handler handler,
+            IFusedLocationHardware locationHardware
+            ) {
+        FusedProxy fusedProxy = new FusedProxy(context, handler, locationHardware);
+
+        // try to bind the Fused provider
+        if (!fusedProxy.mServiceWatcher.start()) {
+            return null;
+        }
+
+        return fusedProxy;
+    }
+
+    /**
+     * Helper function to bind the FusedLocationHardware to the appropriate FusedProvider instance.
+     *
+     * @param locationHardware  The FusedLocationHardware instance to use for the binding operation.
+     */
+    private void bindProvider(IFusedLocationHardware locationHardware) {
+        IFusedProvider provider = IFusedProvider.Stub.asInterface(mServiceWatcher.getBinder());
+
+        if (provider == null) {
+            Log.e(TAG, "No instance of FusedProvider found on FusedLocationHardware connected.");
+            return;
+        }
+
+        try {
+            provider.onFusedLocationHardwareChange(locationHardware);
+        } catch (RemoteException e) {
+            Log.e(TAG, e.toString());
+        }
+    }
+}
diff --git a/services/jni/Android.mk b/services/jni/Android.mk
index 3946f15..93d8e07 100644
--- a/services/jni/Android.mk
+++ b/services/jni/Android.mk
@@ -15,6 +15,7 @@
     com_android_server_UsbHostManager.cpp \
     com_android_server_VibratorService.cpp \
     com_android_server_location_GpsLocationProvider.cpp \
+    com_android_server_location_FlpHardwareProvider.cpp \
     com_android_server_connectivity_Vpn.cpp \
     onload.cpp
 
diff --git a/services/jni/com_android_server_location_FlpHardwareProvider.cpp b/services/jni/com_android_server_location_FlpHardwareProvider.cpp
new file mode 100644
index 0000000..48b86db
--- /dev/null
+++ b/services/jni/com_android_server_location_FlpHardwareProvider.cpp
@@ -0,0 +1,901 @@
+/*
+ * 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/license/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.
+ */
+
+#define LOG_TAG "FuseLocationProvider"
+#define LOG_NDEBUG  0
+
+#define WAKE_LOCK_NAME  "FLP"
+#define LOCATION_CLASS_NAME "android/location/Location"
+
+#include "jni.h"
+#include "JNIHelp.h"
+#include "android_runtime/AndroidRuntime.h"
+#include "hardware/fused_location.h"
+#include "hardware_legacy/power.h"
+
+static jobject sCallbacksObj = NULL;
+static JNIEnv *sCallbackEnv = NULL;
+static hw_device_t* sHardwareDevice = NULL;
+
+static jmethodID sOnLocationReport = NULL;
+static jmethodID sOnDataReport = NULL;
+static jmethodID sOnGeofenceTransition = NULL;
+static jmethodID sOnGeofenceMonitorStatus = NULL;
+static jmethodID sOnGeofenceAdd = NULL;
+static jmethodID sOnGeofenceRemove = NULL;
+static jmethodID sOnGeofencePause = NULL;
+static jmethodID sOnGeofenceResume = NULL;
+
+static const FlpLocationInterface* sFlpInterface = NULL;
+static const FlpDiagnosticInterface* sFlpDiagnosticInterface = NULL;
+static const FlpGeofencingInterface* sFlpGeofencingInterface = NULL;
+static const FlpDeviceContextInterface* sFlpDeviceContextInterface = NULL;
+
+namespace android {
+
+static inline void CheckExceptions(JNIEnv* env, const char* methodName) {
+  if(!env->ExceptionCheck()) {
+    return;
+  }
+
+  ALOGE("An exception was thrown by '%s'.", methodName);
+  LOGE_EX(env);
+  env->ExceptionClear();
+}
+
+static inline void ThrowOnError(
+    JNIEnv* env,
+    int resultCode,
+    const char* methodName) {
+  if(resultCode == FLP_RESULT_SUCCESS) {
+    return;
+  }
+
+  ALOGE("Error %d in '%s'", resultCode, methodName);
+  jclass exceptionClass = env->FindClass("java/lang/RuntimeException");
+  env->ThrowNew(exceptionClass, methodName);
+}
+
+static bool IsValidCallbackThread() {
+  JNIEnv* env = AndroidRuntime::getJNIEnv();
+
+  if(sCallbackEnv == NULL || sCallbackEnv != env) {
+    ALOGE("CallbackThread check fail: env=%p, expected=%p", env, sCallbackEnv);
+    return false;
+  }
+
+  return true;
+}
+
+static int SetThreadEvent(ThreadEvent event) {
+  JavaVM* javaVm = AndroidRuntime::getJavaVM();
+
+  switch(event) {
+    case ASSOCIATE_JVM:
+    {
+      if(sCallbackEnv != NULL) {
+        ALOGE(
+            "Attempted to associate callback in '%s'. Callback already associated.",
+            __FUNCTION__
+            );
+        return FLP_RESULT_ERROR;
+      }
+
+      JavaVMAttachArgs args = {
+          JNI_VERSION_1_6,
+          "FLP Service Callback Thread",
+          /* group */ NULL
+      };
+
+      jint attachResult = javaVm->AttachCurrentThread(&sCallbackEnv, &args);
+      if (attachResult != 0) {
+        ALOGE("Callback thread attachment error: %d", attachResult);
+        return FLP_RESULT_ERROR;
+      }
+
+      ALOGV("Callback thread attached: %p", sCallbackEnv);
+      break;
+    }
+    case DISASSOCIATE_JVM:
+    {
+      if (!IsValidCallbackThread()) {
+        ALOGE(
+            "Attempted to dissasociate an unnownk callback thread : '%s'.",
+            __FUNCTION__
+            );
+        return FLP_RESULT_ERROR;
+      }
+
+      if (javaVm->DetachCurrentThread() != 0) {
+        return FLP_RESULT_ERROR;
+      }
+
+      sCallbackEnv = NULL;
+      break;
+    }
+    default:
+      ALOGE("Invalid ThreadEvent request %d", event);
+      return FLP_RESULT_ERROR;
+  }
+
+  return FLP_RESULT_SUCCESS;
+}
+
+/*
+ * Initializes the FlpHardwareProvider class from the native side by opening
+ * the HW module and obtaining the proper interfaces.
+ */
+static void ClassInit(JNIEnv* env, jclass clazz) {
+  // get references to the Java provider methods
+  sOnLocationReport = env->GetMethodID(
+      clazz,
+      "onLocationReport",
+      "([Landroid/location/Location;)V");
+  sOnDataReport = env->GetMethodID(
+      clazz,
+      "onDataReport",
+      "(Ljava/lang/String;)V"
+      );
+  sOnGeofenceTransition = env->GetMethodID(
+      clazz,
+      "onGeofenceTransition",
+      "(ILandroid/location/Location;IJI)V"
+      );
+  sOnGeofenceMonitorStatus = env->GetMethodID(
+      clazz,
+      "onGeofenceMonitorStatus",
+      "(IILandroid/location/Location;)V"
+      );
+  sOnGeofenceAdd = env->GetMethodID(clazz, "onGeofenceAdd", "(II)V");
+  sOnGeofenceRemove = env->GetMethodID(clazz, "onGeofenceRemove", "(II)V");
+  sOnGeofencePause = env->GetMethodID(clazz, "onGeofencePause", "(II)V");
+  sOnGeofenceResume = env->GetMethodID(clazz, "onGeofenceResume", "(II)V");
+}
+
+/*
+ * Helper function to unwrap a java object back into a FlpLocation structure.
+ */
+static void TranslateFromObject(
+    JNIEnv* env,
+    jobject locationObject,
+    FlpLocation& location) {
+  location.size = sizeof(FlpLocation);
+  location.flags = 0;
+
+  jclass locationClass = env->GetObjectClass(locationObject);
+
+  jmethodID getLatitude = env->GetMethodID(locationClass, "getLatitude", "()D");
+  location.latitude = env->CallDoubleMethod(locationObject, getLatitude);
+  jmethodID getLongitude = env->GetMethodID(locationClass, "getLongitude", "()D");
+  location.longitude = env->CallDoubleMethod(locationObject, getLongitude);
+  jmethodID getTime = env->GetMethodID(locationClass, "getTime", "()J");
+  location.timestamp = env->CallLongMethod(locationObject, getTime);
+  location.flags |= FLP_LOCATION_HAS_LAT_LONG;
+
+  jmethodID hasAltitude = env->GetMethodID(locationClass, "hasAltitude", "()Z");
+  if (env->CallBooleanMethod(locationObject, hasAltitude)) {
+    jmethodID getAltitude = env->GetMethodID(locationClass, "getAltitude", "()D");
+    location.altitude = env->CallDoubleMethod(locationObject, getAltitude);
+    location.flags |= FLP_LOCATION_HAS_ALTITUDE;
+  }
+
+  jmethodID hasSpeed = env->GetMethodID(locationClass, "hasSpeed", "()Z");
+  if (env->CallBooleanMethod(locationObject, hasSpeed)) {
+    jmethodID getSpeed = env->GetMethodID(locationClass, "getSpeed", "()F");
+    location.speed = env->CallFloatMethod(locationObject, getSpeed);
+    location.flags |= FLP_LOCATION_HAS_SPEED;
+  }
+
+  jmethodID hasBearing = env->GetMethodID(locationClass, "hasBearing", "()Z");
+  if (env->CallBooleanMethod(locationObject, hasBearing)) {
+    jmethodID getBearing = env->GetMethodID(locationClass, "getBearing", "()F");
+    location.bearing = env->CallFloatMethod(locationObject, getBearing);
+    location.flags |= FLP_LOCATION_HAS_BEARING;
+  }
+
+  jmethodID hasAccuracy = env->GetMethodID(locationClass, "hasAccuracy", "()Z");
+  if (env->CallBooleanMethod(locationObject, hasAccuracy)) {
+    jmethodID getAccuracy = env->GetMethodID(
+        locationClass,
+        "getAccuracy",
+        "()F"
+        );
+    location.accuracy = env->CallFloatMethod(locationObject, getAccuracy);
+    location.flags |= FLP_LOCATION_HAS_ACCURACY;
+  }
+
+  // TODO: wire sources_used if Location class exposes them
+}
+
+/*
+ * Helper function to unwrap FlpBatchOptions from the Java Runtime calls.
+ */
+static void TranslateFromObject(
+    JNIEnv* env,
+    jobject batchOptionsObject,
+    FlpBatchOptions& batchOptions) {
+  jclass batchOptionsClass = env->GetObjectClass(batchOptionsObject);
+
+  jmethodID getMaxPower = env->GetMethodID(
+      batchOptionsClass,
+      "getMaxPowerAllocationInMW",
+      "()D"
+      );
+  batchOptions.max_power_allocation_mW = env->CallDoubleMethod(
+      batchOptionsObject,
+      getMaxPower
+      );
+
+  jmethodID getPeriod = env->GetMethodID(
+      batchOptionsClass,
+      "getPeriodInNS",
+      "()J"
+      );
+  batchOptions.period_ns = env->CallLongMethod(batchOptionsObject, getPeriod);
+
+  jmethodID getSourcesToUse = env->GetMethodID(
+      batchOptionsClass,
+      "getSourcesToUse",
+      "()I"
+      );
+  batchOptions.sources_to_use = env->CallIntMethod(
+      batchOptionsObject,
+      getSourcesToUse
+      );
+
+  jmethodID getFlags = env->GetMethodID(batchOptionsClass, "getFlags", "()I");
+  batchOptions.flags = env->CallIntMethod(batchOptionsObject, getFlags);
+}
+
+/*
+ * Helper function to transform FlpLocation into a java object.
+ */
+static void TranslateToObject(const FlpLocation* location, jobject& locationObject) {
+  jclass locationClass = sCallbackEnv->FindClass(LOCATION_CLASS_NAME);
+  jmethodID locationCtor = sCallbackEnv->GetMethodID(
+      locationClass,
+      "<init>",
+      "(Ljava/lang/String;)V"
+      );
+
+  // the provider is set in the upper JVM layer
+  locationObject = sCallbackEnv->NewObject(locationClass, locationCtor, NULL);
+  jint flags = location->flags;
+
+  // set the valid information in the object
+  if (flags & FLP_LOCATION_HAS_LAT_LONG) {
+    jmethodID setLatitude = sCallbackEnv->GetMethodID(
+        locationClass,
+        "setLatitude",
+        "(D)V"
+        );
+    sCallbackEnv->CallVoidMethod(locationObject, setLatitude, location->latitude);
+
+    jmethodID setLongitude = sCallbackEnv->GetMethodID(
+        locationClass,
+        "setLongitude",
+        "(D)V"
+        );
+    sCallbackEnv->CallVoidMethod(
+        locationObject,
+        setLongitude,
+        location->longitude
+        );
+
+    jmethodID setTime = sCallbackEnv->GetMethodID(
+        locationClass,
+        "setTime",
+        "(J)V"
+        );
+    sCallbackEnv->CallVoidMethod(locationObject, setTime, location->timestamp);
+  }
+
+  if (flags & FLP_LOCATION_HAS_ALTITUDE) {
+    jmethodID setAltitude = sCallbackEnv->GetMethodID(
+        locationClass,
+        "setAltitude",
+        "(D)V"
+        );
+    sCallbackEnv->CallVoidMethod(locationObject, setAltitude, location->altitude);
+  }
+
+  if (flags & FLP_LOCATION_HAS_SPEED) {
+    jmethodID setSpeed = sCallbackEnv->GetMethodID(
+        locationClass,
+        "setSpeed",
+        "(F)V"
+        );
+    sCallbackEnv->CallVoidMethod(locationObject, setSpeed, location->speed);
+  }
+
+  if (flags & FLP_LOCATION_HAS_BEARING) {
+    jmethodID setBearing = sCallbackEnv->GetMethodID(
+        locationClass,
+        "setBearing",
+        "(F)V"
+        );
+    sCallbackEnv->CallVoidMethod(locationObject, setBearing, location->bearing);
+  }
+
+  if (flags & FLP_LOCATION_HAS_ACCURACY) {
+    jmethodID setAccuracy = sCallbackEnv->GetMethodID(
+        locationClass,
+        "setAccuracy",
+        "(F)V"
+        );
+    sCallbackEnv->CallVoidMethod(locationObject, setAccuracy, location->accuracy);
+  }
+
+  // TODO: wire FlpLocation::sources_used when needed
+}
+
+/*
+ * Helper function to serialize FlpLocation structures.
+ */
+static void TranslateToObjectArray(
+    int32_t locationsCount,
+    FlpLocation** locations,
+    jobjectArray& locationsArray) {
+  jclass locationClass = sCallbackEnv->FindClass(LOCATION_CLASS_NAME);
+  locationsArray = sCallbackEnv->NewObjectArray(
+      locationsCount,
+      locationClass,
+      /* initialElement */ NULL
+      );
+
+  for (int i = 0; i < locationsCount; ++i) {
+    jobject locationObject = NULL;
+    TranslateToObject(locations[i], locationObject);
+    sCallbackEnv->SetObjectArrayElement(locationsArray, i, locationObject);
+    sCallbackEnv->DeleteLocalRef(locationObject);
+  }
+}
+
+static void LocationCallback(int32_t locationsCount, FlpLocation** locations) {
+  if(!IsValidCallbackThread()) {
+    return;
+  }
+
+  if(locationsCount == 0 || locations == NULL) {
+    ALOGE(
+        "Invalid LocationCallback. Count: %d, Locations: %p",
+        locationsCount,
+        locations
+        );
+    return;
+  }
+
+  jobjectArray locationsArray = NULL;
+  TranslateToObjectArray(locationsCount, locations, locationsArray);
+
+  sCallbackEnv->CallVoidMethod(
+      sCallbacksObj,
+      sOnLocationReport,
+      locationsArray
+      );
+  CheckExceptions(sCallbackEnv, __FUNCTION__);
+}
+
+static void AcquireWakelock() {
+  acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME);
+}
+
+static void ReleaseWakelock() {
+  release_wake_lock(WAKE_LOCK_NAME);
+}
+
+FlpCallbacks sFlpCallbacks = {
+  sizeof(FlpCallbacks),
+  LocationCallback,
+  AcquireWakelock,
+  ReleaseWakelock,
+  SetThreadEvent
+};
+
+static void ReportData(char* data, int length) {
+  jstring stringData = NULL;
+
+  if(length != 0 && data != NULL) {
+    stringData = sCallbackEnv->NewString(reinterpret_cast<jchar*>(data), length);
+  } else {
+    ALOGE("Invalid ReportData callback. Length: %d, Data: %p", length, data);
+    return;
+  }
+
+  sCallbackEnv->CallVoidMethod(sCallbacksObj, sOnDataReport, stringData);
+  CheckExceptions(sCallbackEnv, __FUNCTION__);
+}
+
+FlpDiagnosticCallbacks sFlpDiagnosticCallbacks = {
+  sizeof(FlpDiagnosticCallbacks),
+  SetThreadEvent,
+  ReportData
+};
+
+static void GeofenceTransitionCallback(
+    int32_t geofenceId,
+    FlpLocation* location,
+    int32_t transition,
+    FlpUtcTime timestamp,
+    uint32_t sourcesUsed
+    ) {
+  if(!IsValidCallbackThread()) {
+    return;
+  }
+
+  if(location == NULL) {
+    ALOGE("GeofenceTransition received with invalid location: %p", location);
+    return;
+  }
+
+  jobject locationObject = NULL;
+  TranslateToObject(location, locationObject);
+
+  sCallbackEnv->CallVoidMethod(
+      sCallbacksObj,
+      sOnGeofenceTransition,
+      geofenceId,
+      locationObject,
+      transition,
+      timestamp,
+      sourcesUsed
+      );
+  CheckExceptions(sCallbackEnv, __FUNCTION__);
+}
+
+static void GeofenceMonitorStatusCallback(
+    int32_t status,
+    uint32_t source,
+    FlpLocation* lastLocation) {
+  if(!IsValidCallbackThread()) {
+    return;
+  }
+
+  jobject locationObject = NULL;
+  if(lastLocation != NULL) {
+    TranslateToObject(lastLocation, locationObject);
+  }
+
+  sCallbackEnv->CallVoidMethod(
+      sCallbacksObj,
+      sOnGeofenceMonitorStatus,
+      status,
+      source,
+      locationObject
+      );
+  CheckExceptions(sCallbackEnv, __FUNCTION__);
+}
+
+static void GeofenceAddCallback(int32_t geofenceId, int32_t result) {
+  if(!IsValidCallbackThread()) {
+    return;
+  }
+
+  sCallbackEnv->CallVoidMethod(sCallbacksObj, sOnGeofenceAdd, geofenceId, result);
+  CheckExceptions(sCallbackEnv, __FUNCTION__);
+}
+
+static void GeofenceRemoveCallback(int32_t geofenceId, int32_t result) {
+  if(!IsValidCallbackThread()) {
+    return;
+  }
+
+  sCallbackEnv->CallVoidMethod(
+      sCallbacksObj,
+      sOnGeofenceRemove,
+      geofenceId,
+      result
+      );
+  CheckExceptions(sCallbackEnv, __FUNCTION__);
+}
+
+static void GeofencePauseCallback(int32_t geofenceId, int32_t result) {
+  if(!IsValidCallbackThread()) {
+    return;
+  }
+
+  sCallbackEnv->CallVoidMethod(
+      sCallbacksObj,
+      sOnGeofencePause,
+      geofenceId,
+      result
+      );
+  CheckExceptions(sCallbackEnv, __FUNCTION__);
+}
+
+static void GeofenceResumeCallback(int32_t geofenceId, int32_t result) {
+  if(!IsValidCallbackThread()) {
+    return;
+  }
+
+  sCallbackEnv->CallVoidMethod(
+      sCallbacksObj,
+      sOnGeofenceResume,
+      geofenceId,
+      result
+      );
+  CheckExceptions(sCallbackEnv, __FUNCTION__);
+}
+
+FlpGeofenceCallbacks sFlpGeofenceCallbacks = {
+  sizeof(FlpGeofenceCallbacks),
+  GeofenceTransitionCallback,
+  GeofenceMonitorStatusCallback,
+  GeofenceAddCallback,
+  GeofenceRemoveCallback,
+  GeofencePauseCallback,
+  GeofenceResumeCallback,
+  SetThreadEvent
+};
+
+/*
+ * Initializes the Fused Location Provider in the native side. It ensures that
+ * the Flp interfaces are initialized properly.
+ */
+static void Init(JNIEnv* env, jobject obj) {
+  if(sHardwareDevice != NULL) {
+    ALOGD("Hardware Device already opened.");
+    return;
+  }
+
+  const hw_module_t* module = NULL;
+  int err = hw_get_module(FUSED_LOCATION_HARDWARE_MODULE_ID, &module);
+  if(err != 0) {
+    ALOGE("Error hw_get_module '%s': %d", FUSED_LOCATION_HARDWARE_MODULE_ID, err);
+    return;
+  }
+
+  err = module->methods->open(
+        module, 
+        FUSED_LOCATION_HARDWARE_MODULE_ID, &sHardwareDevice);
+  if(err != 0) {
+    ALOGE("Error opening device '%s': %d", FUSED_LOCATION_HARDWARE_MODULE_ID, err);
+    return;
+  }
+
+  sFlpInterface = NULL;
+  flp_device_t* flp_device = reinterpret_cast<flp_device_t*>(sHardwareDevice);
+  sFlpInterface = flp_device->get_flp_interface(flp_device);
+
+  if(sFlpInterface != NULL) {
+    sFlpDiagnosticInterface = reinterpret_cast<const FlpDiagnosticInterface*>(
+        sFlpInterface->get_extension(FLP_DIAGNOSTIC_INTERFACE)
+        );
+
+    sFlpGeofencingInterface = reinterpret_cast<const FlpGeofencingInterface*>(
+        sFlpInterface->get_extension(FLP_GEOFENCING_INTERFACE)
+        );
+
+    sFlpDeviceContextInterface = reinterpret_cast<const FlpDeviceContextInterface*>(
+        sFlpInterface->get_extension(FLP_DEVICE_CONTEXT_INTERFACE)
+        );
+  }
+
+  if(sCallbacksObj == NULL) {
+    sCallbacksObj = env->NewGlobalRef(obj);
+  }
+
+  // initialize the Flp interfaces
+  if(sFlpInterface == NULL || sFlpInterface->init(&sFlpCallbacks) != 0) {
+    ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
+  }
+
+  if(sFlpDiagnosticInterface != NULL) {
+    sFlpDiagnosticInterface->init(&sFlpDiagnosticCallbacks);
+  }
+
+  if(sFlpGeofencingInterface != NULL) {
+    sFlpGeofencingInterface->init(&sFlpGeofenceCallbacks);
+  }
+
+  // TODO: inject any device context if when needed
+}
+
+static jboolean IsSupported(JNIEnv* env, jclass clazz) {
+  return sFlpInterface != NULL;
+}
+
+static jint GetBatchSize(JNIEnv* env, jobject object) {
+  if(sFlpInterface == NULL) {
+    ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
+  }
+
+  return sFlpInterface->get_batch_size();
+}
+
+static void StartBatching(
+    JNIEnv* env,
+    jobject object,
+    jint id,
+    jobject optionsObject) {
+  if(sFlpInterface == NULL || optionsObject == NULL) {
+    ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
+  }
+
+  FlpBatchOptions options;
+  TranslateFromObject(env, optionsObject, options);
+  int result = sFlpInterface->start_batching(id, &options);
+  ThrowOnError(env, result, __FUNCTION__);
+}
+
+static void UpdateBatchingOptions(
+    JNIEnv* env,
+    jobject object,
+    jint id,
+    jobject optionsObject) {
+  if(sFlpInterface == NULL || optionsObject == NULL) {
+    ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
+  }
+
+  FlpBatchOptions options;
+  TranslateFromObject(env, optionsObject, options);
+  int result = sFlpInterface->update_batching_options(id, &options);
+  ThrowOnError(env, result, __FUNCTION__);
+}
+
+static void StopBatching(JNIEnv* env, jobject object, jint id) {
+  if(sFlpInterface == NULL) {
+    ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
+  }
+
+  sFlpInterface->stop_batching(id);
+}
+
+static void Cleanup(JNIEnv* env, jobject object) {
+  if(sFlpInterface == NULL) {
+    ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
+  }
+
+  sFlpInterface->cleanup();
+
+  if(sCallbacksObj != NULL) {
+    env->DeleteGlobalRef(sCallbacksObj);
+    sCallbacksObj = NULL;
+  }
+
+  sFlpInterface = NULL;
+  sFlpDiagnosticInterface = NULL;
+  sFlpDeviceContextInterface = NULL;
+  sFlpGeofencingInterface = NULL;
+
+  if(sHardwareDevice != NULL) {
+    sHardwareDevice->close(sHardwareDevice);
+    sHardwareDevice = NULL;
+  }
+}
+
+static void GetBatchedLocation(JNIEnv* env, jobject object, jint lastNLocations) {
+  if(sFlpInterface == NULL) {
+    ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
+  }
+
+  sFlpInterface->get_batched_location(lastNLocations);
+}
+
+static void InjectLocation(JNIEnv* env, jobject object, jobject locationObject) {
+  if(locationObject == NULL) {
+    ALOGE("Invalid location for injection: %p", locationObject);
+    ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
+  }
+
+  if(sFlpInterface == NULL) {
+    // there is no listener, bail
+    return;
+  }
+
+  FlpLocation location;
+  TranslateFromObject(env, locationObject, location);
+  int result = sFlpInterface->inject_location(&location);
+  if (result != FLP_RESULT_ERROR) {
+    // do not throw but log, this operation should be fire and forget
+    ALOGE("Error %d in '%s'", result, __FUNCTION__);
+  }
+}
+
+static jboolean IsDiagnosticSupported() {
+  return sFlpDiagnosticInterface != NULL;
+}
+
+static void InjectDiagnosticData(JNIEnv* env, jobject object, jstring stringData) {
+  if(stringData == NULL) {
+    ALOGE("Invalid diagnostic data for injection: %p", stringData);
+    ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
+  }
+
+  if(sFlpDiagnosticInterface == NULL) {
+    ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
+  }
+
+  int length = env->GetStringLength(stringData);
+  const jchar* data = env->GetStringChars(stringData, /* isCopy */ NULL);
+  if(data == NULL) {
+    ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
+  }
+
+  int result = sFlpDiagnosticInterface->inject_data((char*) data, length);
+  ThrowOnError(env, result, __FUNCTION__);
+}
+
+static jboolean IsDeviceContextSupported() {
+  return sFlpDeviceContextInterface != NULL;
+}
+
+static void InjectDeviceContext(JNIEnv* env, jobject object, jint enabledMask) {
+  if(sFlpDeviceContextInterface == NULL) {
+    ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
+  }
+
+  int result = sFlpDeviceContextInterface->inject_device_context(enabledMask);
+  ThrowOnError(env, result, __FUNCTION__);
+}
+
+static jboolean IsGeofencingSupported() {
+  return sFlpGeofencingInterface != NULL;
+}
+
+static void AddGeofences(
+    JNIEnv* env,
+    jobject object,
+    jintArray geofenceIdsArray,
+    jobjectArray geofencesArray) {
+  if(geofencesArray == NULL) {
+    ALOGE("Invalid Geofences to add: %p", geofencesArray);
+    ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
+  }
+
+  if (sFlpGeofencingInterface == NULL) {
+    ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
+  }
+
+  jint geofencesCount = env->GetArrayLength(geofenceIdsArray);
+  Geofence* geofences = new Geofence[geofencesCount];
+  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];
+
+    // TODO: fill in the GeofenceData
+
+    // TODO: fill in the GeofenceOptions
+  }
+
+  sFlpGeofencingInterface->add_geofences(geofencesCount, &geofences);
+  if (geofences != NULL) delete[] geofences;
+}
+
+static void PauseGeofence(JNIEnv* env, jobject object, jint geofenceId) {
+  if(sFlpGeofencingInterface == NULL) {
+    ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
+  }
+
+  sFlpGeofencingInterface->pause_geofence(geofenceId);
+}
+
+static void ResumeGeofence(
+    JNIEnv* env,
+    jobject object,
+    jint geofenceId,
+    jint monitorTransitions) {
+  if(sFlpGeofencingInterface == NULL) {
+    ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
+  }
+
+  sFlpGeofencingInterface->resume_geofence(geofenceId, monitorTransitions);
+}
+
+static void ModifyGeofenceOption(
+    JNIEnv* env,
+    jobject object,
+    jint geofenceId,
+    jint lastTransition,
+    jint monitorTransitions,
+    jint notificationResponsiveness,
+    jint unknownTimer,
+    jint sourcesToUse) {
+  if(sFlpGeofencingInterface == NULL) {
+    ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
+  }
+
+  GeofenceOptions options = {
+      lastTransition,
+      monitorTransitions,
+      notificationResponsiveness,
+      unknownTimer,
+      (uint32_t)sourcesToUse
+  };
+
+  sFlpGeofencingInterface->modify_geofence_option(geofenceId, &options);
+}
+
+static void RemoveGeofences(
+    JNIEnv* env,
+    jobject object,
+    jintArray geofenceIdsArray) {
+  if(sFlpGeofencingInterface == NULL) {
+    ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
+  }
+
+  jsize geofenceIdsCount = env->GetArrayLength(geofenceIdsArray);
+  jint* geofenceIds = env->GetIntArrayElements(geofenceIdsArray, /* isCopy */ NULL);
+  if(geofenceIds == NULL) {
+    ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
+  }
+
+  sFlpGeofencingInterface->remove_geofences(geofenceIdsCount, geofenceIds);
+}
+
+static JNINativeMethod sMethods[] = {
+  //{"name", "signature", functionPointer }
+  {"nativeClassInit", "()V", reinterpret_cast<void*>(ClassInit)},
+  {"nativeInit", "()V", reinterpret_cast<void*>(Init)},
+  {"nativeCleanup", "()V", reinterpret_cast<void*>(Cleanup)},
+  {"nativeIsSupported", "()Z", reinterpret_cast<void*>(IsSupported)},
+  {"nativeGetBatchSize", "()I", reinterpret_cast<void*>(GetBatchSize)},
+  {"nativeStartBatching", 
+        "(ILandroid/location/FusedBatchOptions;)V", 
+        reinterpret_cast<void*>(StartBatching)},
+  {"nativeUpdateBatchingOptions", 
+        "(ILandroid/location/FusedBatchOptions;)V", 
+        reinterpret_cast<void*>(UpdateBatchingOptions)},
+  {"nativeStopBatching", "(I)V", reinterpret_cast<void*>(StopBatching)},
+  {"nativeRequestBatchedLocation", 
+        "(I)V", 
+        reinterpret_cast<void*>(GetBatchedLocation)},
+  {"nativeInjectLocation", 
+        "(Landroid/location/Location;)V", 
+        reinterpret_cast<void*>(InjectLocation)},
+  {"nativeIsDiagnosticSupported", 
+        "()Z", 
+        reinterpret_cast<void*>(IsDiagnosticSupported)},
+  {"nativeInjectDiagnosticData", 
+        "(Ljava/lang/String;)V", 
+        reinterpret_cast<void*>(InjectDiagnosticData)},
+  {"nativeIsDeviceContextSupported", 
+        "()Z", 
+        reinterpret_cast<void*>(IsDeviceContextSupported)},
+  {"nativeInjectDeviceContext", 
+        "(I)V", 
+        reinterpret_cast<void*>(InjectDeviceContext)},
+  {"nativeIsGeofencingSupported", 
+        "()Z", 
+        reinterpret_cast<void*>(IsGeofencingSupported)},
+  {"nativeAddGeofences", 
+        "([I[Landroid/location/Geofence;)V", 
+        reinterpret_cast<void*>(AddGeofences)},
+  {"nativePauseGeofence", "(I)V", reinterpret_cast<void*>(PauseGeofence)},
+  {"nativeResumeGeofence", "(II)V", reinterpret_cast<void*>(ResumeGeofence)},
+  {"nativeModifyGeofenceOption", 
+        "(IIIIII)V", 
+        reinterpret_cast<void*>(ModifyGeofenceOption)},
+  {"nativeRemoveGeofences", "([I)V", reinterpret_cast<void*>(RemoveGeofences)}
+};
+
+/*
+ * Registration method invoked on JNI Load.
+ */
+int register_android_server_location_FlpHardwareProvider(JNIEnv* env) {
+  return jniRegisterNativeMethods(
+      env,
+      "com/android/server/location/FlpHardwareProvider",
+      sMethods,
+      NELEM(sMethods)
+      );
+}
+
+} /* name-space Android */
diff --git a/services/jni/onload.cpp b/services/jni/onload.cpp
index 736ef24..5427277 100644
--- a/services/jni/onload.cpp
+++ b/services/jni/onload.cpp
@@ -32,6 +32,7 @@
 int register_android_server_VibratorService(JNIEnv* env);
 int register_android_server_SystemServer(JNIEnv* env);
 int register_android_server_location_GpsLocationProvider(JNIEnv* env);
+int register_android_server_location_FlpHardwareProvider(JNIEnv* env);
 int register_android_server_connectivity_Vpn(JNIEnv* env);
 int register_android_server_AssetAtlasService(JNIEnv* env);
 };
@@ -61,6 +62,7 @@
     register_android_server_VibratorService(env);
     register_android_server_SystemServer(env);
     register_android_server_location_GpsLocationProvider(env);
+    register_android_server_location_FlpHardwareProvider(env);
     register_android_server_connectivity_Vpn(env);
     register_android_server_AssetAtlasService(env);
 
diff --git a/wifi/java/android/net/wifi/WifiMonitor.java b/wifi/java/android/net/wifi/WifiMonitor.java
index fe3c709..92b8e46 100644
--- a/wifi/java/android/net/wifi/WifiMonitor.java
+++ b/wifi/java/android/net/wifi/WifiMonitor.java
@@ -32,7 +32,10 @@
 import com.android.internal.util.Protocol;
 import com.android.internal.util.StateMachine;
 
+import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.regex.Pattern;
 import java.util.regex.Matcher;
 
@@ -44,6 +47,7 @@
  */
 public class WifiMonitor {
 
+    private static final boolean DBG = false;
     private static final String TAG = "WifiMonitor";
 
     /** Events we receive from the supplicant daemon */
@@ -279,9 +283,6 @@
     /* AP-STA-DISCONNECTED 42:fc:89:a8:96:09 */
     private static final String AP_STA_DISCONNECTED_STR = "AP-STA-DISCONNECTED";
 
-    private final StateMachine mStateMachine;
-    private final WifiNative mWifiNative;
-
     /* Supplicant events reported to a state machine */
     private static final int BASE = Protocol.BASE_WIFI_MONITOR;
 
@@ -347,164 +348,324 @@
     private static final String WPA_RECV_ERROR_STR = "recv error";
 
     /**
-     * Tracks consecutive receive errors
-     */
-    private int mRecvErrors = 0;
-
-    /**
      * Max errors before we close supplicant connection
      */
     private static final int MAX_RECV_ERRORS    = 10;
 
+    private final String mInterfaceName;
+    private final WifiNative mWifiNative;
+    private final StateMachine mWifiStateMachine;
+    private boolean mMonitoring;
+
     public WifiMonitor(StateMachine wifiStateMachine, WifiNative wifiNative) {
-        mStateMachine = wifiStateMachine;
+        if (DBG) Log.d(TAG, "Creating WifiMonitor");
         mWifiNative = wifiNative;
+        mInterfaceName = wifiNative.mInterfaceName;
+        mWifiStateMachine = wifiStateMachine;
+        mMonitoring = false;
+
+        WifiMonitorSingleton.getMonitor().registerInterfaceMonitor(mInterfaceName, this);
     }
 
     public void startMonitoring() {
-        new MonitorThread().start();
+        WifiMonitorSingleton.getMonitor().startMonitoring(mInterfaceName);
     }
 
-    class MonitorThread extends Thread {
-        public MonitorThread() {
-            super("WifiMonitor");
+    public void stopMonitoring() {
+        WifiMonitorSingleton.getMonitor().stopMonitoring(mInterfaceName);
+    }
+
+    public void stopSupplicant() {
+        WifiMonitorSingleton.getMonitor().stopSupplicant();
+    }
+
+    public void killSupplicant(boolean p2pSupported) {
+        WifiMonitorSingleton.getMonitor().killSupplicant(p2pSupported);
+    }
+
+    private static class WifiMonitorSingleton {
+        private static Object sSingletonLock = new Object();
+        private static WifiMonitorSingleton sWifiMonitorSingleton = null;
+        private HashMap<String, WifiMonitor> mIfaceMap = new HashMap<String, WifiMonitor>();
+        private boolean mConnected = false;
+        private WifiNative mWifiNative;
+
+        private WifiMonitorSingleton() {
         }
 
-        public void run() {
+        static WifiMonitorSingleton getMonitor() {
+            if (DBG) Log.d(TAG, "WifiMonitorSingleton gotten");
+            synchronized (sSingletonLock) {
+                if (sWifiMonitorSingleton == null) {
+                    if (DBG) Log.d(TAG, "WifiMonitorSingleton created");
+                    sWifiMonitorSingleton = new WifiMonitorSingleton();
+                }
+            }
+            return sWifiMonitorSingleton;
+        }
 
-            if (connectToSupplicant()) {
-                // Send a message indicating that it is now possible to send commands
-                // to the supplicant
-                mStateMachine.sendMessage(SUP_CONNECTION_EVENT);
-            } else {
-                mStateMachine.sendMessage(SUP_DISCONNECTION_EVENT);
+        public synchronized void startMonitoring(String iface) {
+            WifiMonitor m = mIfaceMap.get(iface);
+            if (m == null) {
+                Log.e(TAG, "startMonitor called with unknown iface=" + iface);
                 return;
             }
 
+            Log.d(TAG, "startMonitoring(" + iface + ") with mConnected = " + mConnected);
+
+            if (mConnected) {
+                m.mMonitoring = true;
+                m.mWifiStateMachine.sendMessage(SUP_CONNECTION_EVENT);
+            } else {
+                if (DBG) Log.d(TAG, "connecting to supplicant");
+                int connectTries = 0;
+                while (true) {
+                    if (mWifiNative.connectToSupplicant()) {
+                        m.mMonitoring = true;
+                        m.mWifiStateMachine.sendMessage(SUP_CONNECTION_EVENT);
+                        new MonitorThread(mWifiNative, this).start();
+                        mConnected = true;
+                        break;
+                    }
+                    if (connectTries++ < 5) {
+                        try {
+                            Thread.sleep(1000);
+                        } catch (InterruptedException ignore) {
+                        }
+                    } else {
+                        mIfaceMap.remove(iface);
+                        m.mWifiStateMachine.sendMessage(SUP_DISCONNECTION_EVENT);
+                        break;
+                    }
+                }
+            }
+        }
+
+        public synchronized void stopMonitoring(String iface) {
+            WifiMonitor m = mIfaceMap.get(iface);
+            if (DBG) Log.d(TAG, "stopMonitoring(" + iface + ") = " + m.mWifiStateMachine);
+            m.mMonitoring = false;
+            m.mWifiStateMachine.sendMessage(SUP_DISCONNECTION_EVENT);
+        }
+
+        public synchronized void registerInterfaceMonitor(String iface, WifiMonitor m) {
+            if (DBG) Log.d(TAG, "registerInterface(" + iface + "+" + m.mWifiStateMachine + ")");
+            mIfaceMap.put(iface, m);
+            if (mWifiNative == null) {
+                mWifiNative = m.mWifiNative;
+            }
+        }
+
+        public synchronized void unregisterInterfaceMonitor(String iface) {
+            // REVIEW: When should we call this? If this isn't called, then WifiMonitor
+            // objects will remain in the mIfaceMap; and won't ever get deleted
+
+            WifiMonitor m = mIfaceMap.remove(iface);
+            if (DBG) Log.d(TAG, "unregisterInterface(" + iface + "+" + m.mWifiStateMachine + ")");
+        }
+
+        public synchronized void stopSupplicant() {
+            mWifiNative.stopSupplicant();
+        }
+
+        public synchronized void killSupplicant(boolean p2pSupported) {
+            mWifiNative.killSupplicant(p2pSupported);
+            mConnected = false;
+            Iterator<Map.Entry<String, WifiMonitor>> it = mIfaceMap.entrySet().iterator();
+            while (it.hasNext()) {
+                Map.Entry<String, WifiMonitor> e = it.next();
+                WifiMonitor m = e.getValue();
+                m.mMonitoring = false;
+            }
+        }
+
+        private synchronized WifiMonitor getMonitor(String iface) {
+            return mIfaceMap.get(iface);
+        }
+    }
+
+    private static class MonitorThread extends Thread {
+        private final WifiNative mWifiNative;
+        private final WifiMonitorSingleton mWifiMonitorSingleton;
+        private int mRecvErrors = 0;
+        private StateMachine mStateMachine = null;
+
+        public MonitorThread(WifiNative wifiNative, WifiMonitorSingleton wifiMonitorSingleton) {
+            super("WifiMonitor");
+            mWifiNative = wifiNative;
+            mWifiMonitorSingleton = wifiMonitorSingleton;
+        }
+
+        public void run() {
             //noinspection InfiniteLoopStatement
             for (;;) {
                 String eventStr = mWifiNative.waitForEvent();
 
                 // Skip logging the common but mostly uninteresting scan-results event
-                if (false && eventStr.indexOf(SCAN_RESULTS_STR) == -1) {
+                if (DBG && eventStr.indexOf(SCAN_RESULTS_STR) == -1) {
                     Log.d(TAG, "Event [" + eventStr + "]");
                 }
-                if (!eventStr.startsWith(EVENT_PREFIX_STR)) {
-                    if (eventStr.startsWith(WPA_EVENT_PREFIX_STR) &&
-                            0 < eventStr.indexOf(PASSWORD_MAY_BE_INCORRECT_STR)) {
-                        mStateMachine.sendMessage(AUTHENTICATION_FAILURE_EVENT);
-                    } else if (eventStr.startsWith(WPS_SUCCESS_STR)) {
-                        mStateMachine.sendMessage(WPS_SUCCESS_EVENT);
-                    } else if (eventStr.startsWith(WPS_FAIL_STR)) {
-                        handleWpsFailEvent(eventStr);
-                    } else if (eventStr.startsWith(WPS_OVERLAP_STR)) {
-                        mStateMachine.sendMessage(WPS_OVERLAP_EVENT);
-                    } else if (eventStr.startsWith(WPS_TIMEOUT_STR)) {
-                        mStateMachine.sendMessage(WPS_TIMEOUT_EVENT);
-                    } else if (eventStr.startsWith(P2P_EVENT_PREFIX_STR)) {
-                        handleP2pEvents(eventStr);
-                    } else if (eventStr.startsWith(HOST_AP_EVENT_PREFIX_STR)) {
-                        handleHostApEvents(eventStr);
+
+                WifiMonitor m = null;
+                mStateMachine = null;
+
+                if (eventStr.startsWith("IFNAME=")) {
+                    int space = eventStr.indexOf(' ');
+                    if (space != -1) {
+                        String iface = eventStr.substring(7,space);
+                        m = mWifiMonitorSingleton.getMonitor(iface);
+                        if (m != null) {
+                            if (m.mMonitoring) {
+                                mStateMachine = m.mWifiStateMachine;
+                                eventStr = eventStr.substring(space + 1);
+                            }
+                            else {
+                                if (DBG) Log.d(TAG, "Dropping event because monitor (" + iface +
+                                        ") is stopped");
+                                continue;
+                            }
+                        }
+                        else {
+                            eventStr = eventStr.substring(space + 1);
+                        }
                     }
-                    continue;
                 }
 
-                String eventName = eventStr.substring(EVENT_PREFIX_LEN_STR);
-                int nameEnd = eventName.indexOf(' ');
-                if (nameEnd != -1)
-                    eventName = eventName.substring(0, nameEnd);
-                if (eventName.length() == 0) {
-                    if (false) Log.i(TAG, "Received wpa_supplicant event with empty event name");
-                    continue;
-                }
-                /*
-                 * Map event name into event enum
-                 */
-                int event;
-                if (eventName.equals(CONNECTED_STR))
-                    event = CONNECTED;
-                else if (eventName.equals(DISCONNECTED_STR))
-                    event = DISCONNECTED;
-                else if (eventName.equals(STATE_CHANGE_STR))
-                    event = STATE_CHANGE;
-                else if (eventName.equals(SCAN_RESULTS_STR))
-                    event = SCAN_RESULTS;
-                else if (eventName.equals(LINK_SPEED_STR))
-                    event = LINK_SPEED;
-                else if (eventName.equals(TERMINATING_STR))
-                    event = TERMINATING;
-                else if (eventName.equals(DRIVER_STATE_STR))
-                    event = DRIVER_STATE;
-                else if (eventName.equals(EAP_FAILURE_STR))
-                    event = EAP_FAILURE;
-                else if (eventName.equals(ASSOC_REJECT_STR))
-                    event = ASSOC_REJECT;
-                else
-                    event = UNKNOWN;
-
-                String eventData = eventStr;
-                if (event == DRIVER_STATE || event == LINK_SPEED)
-                    eventData = eventData.split(" ")[1];
-                else if (event == STATE_CHANGE || event == EAP_FAILURE) {
-                    int ind = eventStr.indexOf(" ");
-                    if (ind != -1) {
-                        eventData = eventStr.substring(ind + 1);
+                if (mStateMachine != null) {
+                    if (dispatchEvent(eventStr)) {
+                        break;
                     }
                 } else {
-                    int ind = eventStr.indexOf(" - ");
-                    if (ind != -1) {
-                        eventData = eventStr.substring(ind + 3);
-                    }
-                }
-
-                if (event == STATE_CHANGE) {
-                    handleSupplicantStateChange(eventData);
-                } else if (event == DRIVER_STATE) {
-                    handleDriverEvent(eventData);
-                } else if (event == TERMINATING) {
-                    /**
-                     * Close the supplicant connection if we see
-                     * too many recv errors
-                     */
-                    if (eventData.startsWith(WPA_RECV_ERROR_STR)) {
-                        if (++mRecvErrors > MAX_RECV_ERRORS) {
-                            if (false) {
-                                Log.d(TAG, "too many recv errors, closing connection");
-                            }
-                        } else {
-                            continue;
+                    if (DBG) Log.d(TAG, "Sending to all monitors because there's no interface id");
+                    boolean done = false;
+                    Iterator<Map.Entry<String, WifiMonitor>> it =
+                            mWifiMonitorSingleton.mIfaceMap.entrySet().iterator();
+                    while (it.hasNext()) {
+                        Map.Entry<String, WifiMonitor> e = it.next();
+                        m = e.getValue();
+                        mStateMachine = m.mWifiStateMachine;
+                        if (dispatchEvent(eventStr)) {
+                            done = true;
                         }
                     }
 
-                    // notify and exit
-                    mStateMachine.sendMessage(SUP_DISCONNECTION_EVENT);
-                    break;
-                } else if (event == EAP_FAILURE) {
-                    if (eventData.startsWith(EAP_AUTH_FAILURE_STR)) {
-                        mStateMachine.sendMessage(AUTHENTICATION_FAILURE_EVENT);
+                    if (done) {
+                        // After this thread terminates, we'll no longer
+                        // be connected to the supplicant
+                        if (DBG) Log.d(TAG, "Disconnecting from the supplicant, no more events");
+                        mWifiMonitorSingleton.mConnected = false;
+                        break;
                     }
-                } else if (event == ASSOC_REJECT) {
-                        mStateMachine.sendMessage(ASSOCIATION_REJECTION_EVENT);
-                } else {
-                    handleEvent(event, eventData);
                 }
-                mRecvErrors = 0;
             }
         }
 
-        private boolean connectToSupplicant() {
-            int connectTries = 0;
+        /* @return true if the event was supplicant disconnection */
+        private boolean dispatchEvent(String eventStr) {
 
-            while (true) {
-                if (mWifiNative.connectToSupplicant()) {
-                    return true;
+            if (!eventStr.startsWith(EVENT_PREFIX_STR)) {
+                if (eventStr.startsWith(WPA_EVENT_PREFIX_STR) &&
+                        0 < eventStr.indexOf(PASSWORD_MAY_BE_INCORRECT_STR)) {
+                    mStateMachine.sendMessage(AUTHENTICATION_FAILURE_EVENT);
+                } else if (eventStr.startsWith(WPS_SUCCESS_STR)) {
+                    mStateMachine.sendMessage(WPS_SUCCESS_EVENT);
+                } else if (eventStr.startsWith(WPS_FAIL_STR)) {
+                    handleWpsFailEvent(eventStr);
+                } else if (eventStr.startsWith(WPS_OVERLAP_STR)) {
+                    mStateMachine.sendMessage(WPS_OVERLAP_EVENT);
+                } else if (eventStr.startsWith(WPS_TIMEOUT_STR)) {
+                    mStateMachine.sendMessage(WPS_TIMEOUT_EVENT);
+                } else if (eventStr.startsWith(P2P_EVENT_PREFIX_STR)) {
+                    handleP2pEvents(eventStr);
+                } else if (eventStr.startsWith(HOST_AP_EVENT_PREFIX_STR)) {
+                    handleHostApEvents(eventStr);
                 }
-                if (connectTries++ < 5) {
-                    nap(1);
-                } else {
-                    break;
+                else {
+                    if (DBG) Log.w(TAG, "couldn't identify event type - " + eventStr);
+                }
+                return false;
+            }
+
+            String eventName = eventStr.substring(EVENT_PREFIX_LEN_STR);
+            int nameEnd = eventName.indexOf(' ');
+            if (nameEnd != -1)
+                eventName = eventName.substring(0, nameEnd);
+            if (eventName.length() == 0) {
+                if (DBG) Log.i(TAG, "Received wpa_supplicant event with empty event name");
+                return false;
+            }
+            /*
+             * Map event name into event enum
+             */
+            int event;
+            if (eventName.equals(CONNECTED_STR))
+                event = CONNECTED;
+            else if (eventName.equals(DISCONNECTED_STR))
+                event = DISCONNECTED;
+            else if (eventName.equals(STATE_CHANGE_STR))
+                event = STATE_CHANGE;
+            else if (eventName.equals(SCAN_RESULTS_STR))
+                event = SCAN_RESULTS;
+            else if (eventName.equals(LINK_SPEED_STR))
+                event = LINK_SPEED;
+            else if (eventName.equals(TERMINATING_STR))
+                event = TERMINATING;
+            else if (eventName.equals(DRIVER_STATE_STR))
+                event = DRIVER_STATE;
+            else if (eventName.equals(EAP_FAILURE_STR))
+                event = EAP_FAILURE;
+            else if (eventName.equals(ASSOC_REJECT_STR))
+                event = ASSOC_REJECT;
+            else
+                event = UNKNOWN;
+
+            String eventData = eventStr;
+            if (event == DRIVER_STATE || event == LINK_SPEED)
+                eventData = eventData.split(" ")[1];
+            else if (event == STATE_CHANGE || event == EAP_FAILURE) {
+                int ind = eventStr.indexOf(" ");
+                if (ind != -1) {
+                    eventData = eventStr.substring(ind + 1);
+                }
+            } else {
+                int ind = eventStr.indexOf(" - ");
+                if (ind != -1) {
+                    eventData = eventStr.substring(ind + 3);
                 }
             }
+
+            if (event == STATE_CHANGE) {
+                handleSupplicantStateChange(eventData);
+            } else if (event == DRIVER_STATE) {
+                handleDriverEvent(eventData);
+            } else if (event == TERMINATING) {
+                /**
+                 * Close the supplicant connection if we see
+                 * too many recv errors
+                 */
+                if (eventData.startsWith(WPA_RECV_ERROR_STR)) {
+                    if (++mRecvErrors > MAX_RECV_ERRORS) {
+                        if (DBG) {
+                            Log.d(TAG, "too many recv errors, closing connection");
+                        }
+                    } else {
+                        return false;
+                    }
+                }
+
+                // notify and exit
+                mStateMachine.sendMessage(SUP_DISCONNECTION_EVENT);
+                return true;
+            } else if (event == EAP_FAILURE) {
+                if (eventData.startsWith(EAP_AUTH_FAILURE_STR)) {
+                    mStateMachine.sendMessage(AUTHENTICATION_FAILURE_EVENT);
+                }
+            } else if (event == ASSOC_REJECT) {
+                mStateMachine.sendMessage(ASSOCIATION_REJECTION_EVENT);
+            } else {
+                handleEvent(event, eventData);
+            }
+            mRecvErrors = 0;
             return false;
         }
 
@@ -723,71 +884,60 @@
             }
             notifySupplicantStateChange(networkId, wifiSsid, BSSID, newSupplicantState);
         }
-    }
 
-    private void handleNetworkStateChange(NetworkInfo.DetailedState newState, String data) {
-        String BSSID = null;
-        int networkId = -1;
-        if (newState == NetworkInfo.DetailedState.CONNECTED) {
-            Matcher match = mConnectedEventPattern.matcher(data);
-            if (!match.find()) {
-                if (false) Log.d(TAG, "Could not find BSSID in CONNECTED event string");
-            } else {
-                BSSID = match.group(1);
-                try {
-                    networkId = Integer.parseInt(match.group(2));
-                } catch (NumberFormatException e) {
-                    networkId = -1;
+        private void handleNetworkStateChange(NetworkInfo.DetailedState newState, String data) {
+            String BSSID = null;
+            int networkId = -1;
+            if (newState == NetworkInfo.DetailedState.CONNECTED) {
+                Matcher match = mConnectedEventPattern.matcher(data);
+                if (!match.find()) {
+                    if (DBG) Log.d(TAG, "Could not find BSSID in CONNECTED event string");
+                } else {
+                    BSSID = match.group(1);
+                    try {
+                        networkId = Integer.parseInt(match.group(2));
+                    } catch (NumberFormatException e) {
+                        networkId = -1;
+                    }
                 }
+                notifyNetworkStateChange(newState, BSSID, networkId);
             }
         }
-        notifyNetworkStateChange(newState, BSSID, networkId);
-    }
 
-    /**
-     * Send the state machine a notification that the state of Wifi connectivity
-     * has changed.
-     * @param networkId the configured network on which the state change occurred
-     * @param newState the new network state
-     * @param BSSID when the new state is {@link DetailedState#CONNECTED
-     * NetworkInfo.DetailedState.CONNECTED},
-     * this is the MAC address of the access point. Otherwise, it
-     * is {@code null}.
-     */
-    void notifyNetworkStateChange(NetworkInfo.DetailedState newState, String BSSID, int netId) {
-        if (newState == NetworkInfo.DetailedState.CONNECTED) {
-            Message m = mStateMachine.obtainMessage(NETWORK_CONNECTION_EVENT,
-                    netId, 0, BSSID);
-            mStateMachine.sendMessage(m);
-        } else {
-            Message m = mStateMachine.obtainMessage(NETWORK_DISCONNECTION_EVENT,
-                    netId, 0, BSSID);
-            mStateMachine.sendMessage(m);
+        /**
+         * Send the state machine a notification that the state of Wifi connectivity
+         * has changed.
+         * @param networkId the configured network on which the state change occurred
+         * @param newState the new network state
+         * @param BSSID when the new state is {@link DetailedState#CONNECTED
+         * NetworkInfo.DetailedState.CONNECTED},
+         * this is the MAC address of the access point. Otherwise, it
+         * is {@code null}.
+         */
+        void notifyNetworkStateChange(NetworkInfo.DetailedState newState, String BSSID, int netId) {
+            if (newState == NetworkInfo.DetailedState.CONNECTED) {
+                Message m = mStateMachine.obtainMessage(NETWORK_CONNECTION_EVENT,
+                        netId, 0, BSSID);
+                mStateMachine.sendMessage(m);
+            } else {
+                Message m = mStateMachine.obtainMessage(NETWORK_DISCONNECTION_EVENT,
+                        netId, 0, BSSID);
+                mStateMachine.sendMessage(m);
+            }
         }
-    }
 
-    /**
-     * Send the state machine a notification that the state of the supplicant
-     * has changed.
-     * @param networkId the configured network on which the state change occurred
-     * @param wifiSsid network name
-     * @param BSSID network address
-     * @param newState the new {@code SupplicantState}
-     */
-    void notifySupplicantStateChange(int networkId, WifiSsid wifiSsid, String BSSID,
-            SupplicantState newState) {
-        mStateMachine.sendMessage(mStateMachine.obtainMessage(SUPPLICANT_STATE_CHANGE_EVENT,
-                new StateChangeResult(networkId, wifiSsid, BSSID, newState)));
-    }
-
-    /**
-     * Sleep for a period of time.
-     * @param secs the number of seconds to sleep
-     */
-    private static void nap(int secs) {
-        try {
-            Thread.sleep(secs * 1000);
-        } catch (InterruptedException ignore) {
+        /**
+         * Send the state machine a notification that the state of the supplicant
+         * has changed.
+         * @param networkId the configured network on which the state change occurred
+         * @param wifiSsid network name
+         * @param BSSID network address
+         * @param newState the new {@code SupplicantState}
+         */
+        void notifySupplicantStateChange(int networkId, WifiSsid wifiSsid, String BSSID,
+                SupplicantState newState) {
+            mStateMachine.sendMessage(mStateMachine.obtainMessage(SUPPLICANT_STATE_CHANGE_EVENT,
+                    new StateChangeResult(networkId, wifiSsid, BSSID, newState)));
         }
     }
 }
diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java
index b1dd2ce..d30c7cf 100644
--- a/wifi/java/android/net/wifi/WifiNative.java
+++ b/wifi/java/android/net/wifi/WifiNative.java
@@ -47,7 +47,9 @@
     static final int SCAN_WITHOUT_CONNECTION_SETUP          = 1;
     static final int SCAN_WITH_CONNECTION_SETUP             = 2;
 
-    String mInterface = "";
+    public final String mInterfaceName;
+    public final String mInterfacePrefix;
+
     private boolean mSuspendOptEnabled = false;
 
     public native static boolean loadDriver();
@@ -62,52 +64,53 @@
        or when the supplicant is hung */
     public native static boolean killSupplicant(boolean p2pSupported);
 
-    private native boolean connectToSupplicant(String iface);
+    private native boolean connectToSupplicantNative();
 
-    private native void closeSupplicantConnection(String iface);
+    private native void closeSupplicantConnectionNative();
 
     /**
      * Wait for the supplicant to send an event, returning the event string.
      * @return the event string sent by the supplicant.
      */
-    private native String waitForEvent(String iface);
+    private native String waitForEventNative();
 
-    private native boolean doBooleanCommand(String iface, String command);
+    private native boolean doBooleanCommandNative(String command);
 
-    private native int doIntCommand(String iface, String command);
+    private native int doIntCommandNative(String command);
 
-    private native String doStringCommand(String iface, String command);
+    private native String doStringCommandNative(String command);
 
-    public WifiNative(String iface) {
-        mInterface = iface;
-        mTAG = "WifiNative-" + iface;
+    public WifiNative(String interfaceName) {
+        mInterfaceName = interfaceName;
+        mInterfacePrefix = "IFNAME=" + interfaceName + " ";
+        mTAG = "WifiNative-" + interfaceName;
     }
 
     public boolean connectToSupplicant() {
-        return connectToSupplicant(mInterface);
+        return connectToSupplicantNative();
     }
 
     public void closeSupplicantConnection() {
-        closeSupplicantConnection(mInterface);
+        closeSupplicantConnectionNative();
     }
 
     public String waitForEvent() {
-        return waitForEvent(mInterface);
+        return waitForEventNative();
     }
 
     private boolean doBooleanCommand(String command) {
         if (DBG) Log.d(mTAG, "doBoolean: " + command);
-        return doBooleanCommand(mInterface, command);
+        return doBooleanCommandNative(mInterfacePrefix + command);
     }
 
     private int doIntCommand(String command) {
         if (DBG) Log.d(mTAG, "doInt: " + command);
-        return doIntCommand(mInterface, command);
+        return doIntCommandNative(mInterfacePrefix + command);
     }
 
     private String doStringCommand(String command) {
         if (DBG) Log.d(mTAG, "doString: " + command);
-        return doStringCommand(mInterface, command);
+        return doStringCommandNative(mInterfacePrefix + command);
     }
 
     public boolean ping() {
@@ -411,9 +414,9 @@
 
     public boolean startWpsPbc(String iface, String bssid) {
         if (TextUtils.isEmpty(bssid)) {
-            return doBooleanCommand("IFNAME=" + iface + " WPS_PBC");
+            return doBooleanCommandNative("IFNAME=" + iface + " WPS_PBC");
         } else {
-            return doBooleanCommand("IFNAME=" + iface + " WPS_PBC " + bssid);
+            return doBooleanCommandNative("IFNAME=" + iface + " WPS_PBC " + bssid);
         }
     }
 
@@ -424,7 +427,7 @@
 
     public boolean startWpsPinKeypad(String iface, String pin) {
         if (TextUtils.isEmpty(pin)) return false;
-        return doBooleanCommand("IFNAME=" + iface + " WPS_PIN any " + pin);
+        return doBooleanCommandNative("IFNAME=" + iface + " WPS_PIN any " + pin);
     }
 
 
@@ -438,9 +441,9 @@
 
     public String startWpsPinDisplay(String iface, String bssid) {
         if (TextUtils.isEmpty(bssid)) {
-            return doStringCommand("IFNAME=" + iface + " WPS_PIN any");
+            return doStringCommandNative("IFNAME=" + iface + " WPS_PIN any");
         } else {
-            return doStringCommand("IFNAME=" + iface + " WPS_PIN " + bssid);
+            return doStringCommandNative("IFNAME=" + iface + " WPS_PIN " + bssid);
         }
     }
 
@@ -492,7 +495,7 @@
     }
 
     public boolean setP2pGroupIdle(String iface, int time) {
-        return doBooleanCommand("IFNAME=" + iface + " SET p2p_group_idle " + time);
+        return doBooleanCommandNative("IFNAME=" + iface + " SET p2p_group_idle " + time);
     }
 
     public void setPowerSave(boolean enabled) {
@@ -505,9 +508,9 @@
 
     public boolean setP2pPowerSave(String iface, boolean enabled) {
         if (enabled) {
-            return doBooleanCommand("IFNAME=" + iface + " P2P_SET ps 1");
+            return doBooleanCommandNative("IFNAME=" + iface + " P2P_SET ps 1");
         } else {
-            return doBooleanCommand("IFNAME=" + iface + " P2P_SET ps 0");
+            return doBooleanCommandNative("IFNAME=" + iface + " P2P_SET ps 0");
         }
     }
 
@@ -645,7 +648,7 @@
 
     public boolean p2pGroupRemove(String iface) {
         if (TextUtils.isEmpty(iface)) return false;
-        return doBooleanCommand("P2P_GROUP_REMOVE " + iface);
+        return doBooleanCommandNative("IFNAME=" + iface + " P2P_GROUP_REMOVE " + iface);
     }
 
     public boolean p2pReject(String deviceAddress) {
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 4628c91..91702f9 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -68,6 +68,7 @@
 import android.os.UserHandle;
 import android.os.WorkSource;
 import android.provider.Settings;
+import android.util.Log;
 import android.util.LruCache;
 import android.text.TextUtils;
 
@@ -543,7 +544,6 @@
 
     public WifiStateMachine(Context context, String wlanInterface) {
         super("WifiStateMachine");
-
         mContext = context;
         mInterfaceName = wlanInterface;
 
@@ -888,6 +888,7 @@
      * TODO: doc
      */
     public void setOperationalMode(int mode) {
+        if (DBG) log("setting operational mode to " + String.valueOf(mode));
         sendMessage(CMD_SET_OPERATIONAL_MODE, mode, 0);
     }
 
@@ -1756,8 +1757,7 @@
         /* Socket connection can be lost when we do a graceful shutdown
         * or when the driver is hung. Ensure supplicant is stopped here.
         */
-        mWifiNative.killSupplicant(mP2pSupported);
-        mWifiNative.closeSupplicantConnection();
+        mWifiMonitor.killSupplicant(mP2pSupported);
         sendSupplicantConnectionChangedBroadcast(false);
         setWifiState(WIFI_STATE_DISABLED);
     }
@@ -2139,7 +2139,7 @@
                         * Avoids issues with drivers that do not handle interface down
                         * on a running supplicant properly.
                         */
-                        mWifiNative.killSupplicant(mP2pSupported);
+                        mWifiMonitor.killSupplicant(mP2pSupported);
                         if(mWifiNative.startSupplicant(mP2pSupported)) {
                             setWifiState(WIFI_STATE_ENABLING);
                             if (DBG) log("Supplicant start successful");
@@ -2222,7 +2222,7 @@
                 case WifiMonitor.SUP_DISCONNECTION_EVENT:
                     if (++mSupplicantRestartCount <= SUPPLICANT_RESTART_TRIES) {
                         loge("Failed to setup control channel, restart supplicant");
-                        mWifiNative.killSupplicant(mP2pSupported);
+                        mWifiMonitor.killSupplicant(mP2pSupported);
                         transitionTo(mInitialState);
                         sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
                     } else {
@@ -2329,9 +2329,7 @@
             }
 
             if (DBG) log("stopping supplicant");
-            if (!mWifiNative.stopSupplicant()) {
-                loge("Failed to stop supplicant");
-            }
+            mWifiMonitor.stopSupplicant();
 
             /* Send ourselves a delayed message to indicate failure after a wait time */
             sendMessageDelayed(obtainMessage(CMD_STOP_SUPPLICANT_FAILED,
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pService.java b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
index 68a082a..63b94a2 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pService.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
@@ -858,7 +858,7 @@
                     }
                     if (mGroups.clear()) sendP2pPersistentGroupsChangedBroadcast();
 
-                    mWifiNative.closeSupplicantConnection();
+                    mWifiMonitor.stopMonitoring();
                     transitionTo(mP2pDisablingState);
                     break;
                 case WifiP2pManager.SET_DEVICE_NAME: