Merge "MtpData bmp format file can't recognize when copy bmp into DUT"
diff --git a/api/current.txt b/api/current.txt
index 880170d..b5fd82b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -21526,9 +21526,9 @@
     method public static java.lang.String formatElapsedTime(long);
     method public static java.lang.String formatElapsedTime(java.lang.StringBuilder, long);
     method public static final java.lang.CharSequence formatSameDayTime(long, long, int, int);
-    method public static java.lang.String getAMPMString(int);
-    method public static java.lang.String getDayOfWeekString(int, int);
-    method public static java.lang.String getMonthString(int, int);
+    method public static deprecated java.lang.String getAMPMString(int);
+    method public static deprecated java.lang.String getDayOfWeekString(int, int);
+    method public static deprecated java.lang.String getMonthString(int, int);
     method public static java.lang.CharSequence getRelativeDateTimeString(android.content.Context, long, long, long, int);
     method public static java.lang.CharSequence getRelativeTimeSpanString(long);
     method public static java.lang.CharSequence getRelativeTimeSpanString(long, long, long);
@@ -21536,24 +21536,24 @@
     method public static java.lang.CharSequence getRelativeTimeSpanString(android.content.Context, long, boolean);
     method public static java.lang.CharSequence getRelativeTimeSpanString(android.content.Context, long);
     method public static boolean isToday(long);
-    field public static final java.lang.String ABBREV_MONTH_FORMAT = "%b";
+    field public static final deprecated java.lang.String ABBREV_MONTH_FORMAT = "%b";
     field public static final java.lang.String ABBREV_WEEKDAY_FORMAT = "%a";
     field public static final long DAY_IN_MILLIS = 86400000L; // 0x5265c00L
-    field public static final int FORMAT_12HOUR = 64; // 0x40
-    field public static final int FORMAT_24HOUR = 128; // 0x80
+    field public static final deprecated int FORMAT_12HOUR = 64; // 0x40
+    field public static final deprecated int FORMAT_24HOUR = 128; // 0x80
     field public static final int FORMAT_ABBREV_ALL = 524288; // 0x80000
     field public static final int FORMAT_ABBREV_MONTH = 65536; // 0x10000
     field public static final int FORMAT_ABBREV_RELATIVE = 262144; // 0x40000
     field public static final int FORMAT_ABBREV_TIME = 16384; // 0x4000
     field public static final int FORMAT_ABBREV_WEEKDAY = 32768; // 0x8000
-    field public static final int FORMAT_CAP_AMPM = 256; // 0x100
-    field public static final int FORMAT_CAP_MIDNIGHT = 4096; // 0x1000
-    field public static final int FORMAT_CAP_NOON = 1024; // 0x400
-    field public static final int FORMAT_CAP_NOON_MIDNIGHT = 5120; // 0x1400
+    field public static final deprecated int FORMAT_CAP_AMPM = 256; // 0x100
+    field public static final deprecated int FORMAT_CAP_MIDNIGHT = 4096; // 0x1000
+    field public static final deprecated int FORMAT_CAP_NOON = 1024; // 0x400
+    field public static final deprecated int FORMAT_CAP_NOON_MIDNIGHT = 5120; // 0x1400
     field public static final int FORMAT_NO_MIDNIGHT = 2048; // 0x800
     field public static final int FORMAT_NO_MONTH_DAY = 32; // 0x20
     field public static final int FORMAT_NO_NOON = 512; // 0x200
-    field public static final int FORMAT_NO_NOON_MIDNIGHT = 2560; // 0xa00
+    field public static final deprecated int FORMAT_NO_NOON_MIDNIGHT = 2560; // 0xa00
     field public static final int FORMAT_NO_YEAR = 8; // 0x8
     field public static final int FORMAT_NUMERIC_DATE = 131072; // 0x20000
     field public static final int FORMAT_SHOW_DATE = 16; // 0x10
@@ -21562,12 +21562,12 @@
     field public static final int FORMAT_SHOW_YEAR = 4; // 0x4
     field public static final deprecated int FORMAT_UTC = 8192; // 0x2000
     field public static final long HOUR_IN_MILLIS = 3600000L; // 0x36ee80L
-    field public static final java.lang.String HOUR_MINUTE_24 = "%H:%M";
-    field public static final int LENGTH_LONG = 10; // 0xa
-    field public static final int LENGTH_MEDIUM = 20; // 0x14
-    field public static final int LENGTH_SHORT = 30; // 0x1e
-    field public static final int LENGTH_SHORTER = 40; // 0x28
-    field public static final int LENGTH_SHORTEST = 50; // 0x32
+    field public static final deprecated java.lang.String HOUR_MINUTE_24 = "%H:%M";
+    field public static final deprecated int LENGTH_LONG = 10; // 0xa
+    field public static final deprecated int LENGTH_MEDIUM = 20; // 0x14
+    field public static final deprecated int LENGTH_SHORT = 30; // 0x1e
+    field public static final deprecated int LENGTH_SHORTER = 40; // 0x28
+    field public static final deprecated int LENGTH_SHORTEST = 50; // 0x32
     field public static final long MINUTE_IN_MILLIS = 60000L; // 0xea60L
     field public static final java.lang.String MONTH_DAY_FORMAT = "%-d";
     field public static final java.lang.String MONTH_FORMAT = "%B";
@@ -21578,8 +21578,8 @@
     field public static final java.lang.String YEAR_FORMAT = "%Y";
     field public static final java.lang.String YEAR_FORMAT_TWO_DIGITS = "%g";
     field public static final long YEAR_IN_MILLIS = 31449600000L; // 0x7528ad000L
-    field public static final int[] sameMonthTable;
-    field public static final int[] sameYearTable;
+    field public static final deprecated int[] sameMonthTable;
+    field public static final deprecated int[] sameYearTable;
   }
 
   public final class Formatter {
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 7242029..b7e0683a 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4728,13 +4728,14 @@
         Process.setArgV0("<pre-initialized>");
 
         Looper.prepareMainLooper();
-        if (sMainThreadHandler == null) {
-            sMainThreadHandler = new Handler();
-        }
 
         ActivityThread thread = new ActivityThread();
         thread.attach(false);
 
+        if (sMainThreadHandler == null) {
+            sMainThreadHandler = thread.getHandler();
+        }
+
         AsyncTask.init();
 
         if (false) {
diff --git a/core/java/android/content/SyncStorageEngine.java b/core/java/android/content/SyncStorageEngine.java
index 226e107..7a9fc65 100644
--- a/core/java/android/content/SyncStorageEngine.java
+++ b/core/java/android/content/SyncStorageEngine.java
@@ -26,6 +26,7 @@
 
 import android.accounts.Account;
 import android.accounts.AccountAndUser;
+import android.content.res.Resources;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteException;
@@ -337,6 +338,7 @@
 
     private int mNextHistoryId = 0;
     private SparseArray<Boolean> mMasterSyncAutomatically = new SparseArray<Boolean>();
+    private boolean mDefaultMasterSyncAutomatically;
 
     private OnSyncRequestListener mSyncRequestListener;
 
@@ -346,6 +348,9 @@
 
         mCal = Calendar.getInstance(TimeZone.getTimeZone("GMT+0"));
 
+        mDefaultMasterSyncAutomatically = mContext.getResources().getBoolean(
+               com.android.internal.R.bool.config_syncstorageengine_masterSyncAutomatically);
+
         File systemDir = new File(dataDir, "system");
         File syncDir = new File(systemDir, "sync");
         syncDir.mkdirs();
@@ -781,7 +786,7 @@
     public boolean getMasterSyncAutomatically(int userId) {
         synchronized (mAuthorities) {
             Boolean auto = mMasterSyncAutomatically.get(userId);
-            return auto == null ? true : auto;
+            return auto == null ? mDefaultMasterSyncAutomatically : auto;
         }
     }
 
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 4d9077f..829620b 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -26,6 +26,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.util.Log;
+import android.text.TextUtils;
 import android.view.Surface;
 import android.view.SurfaceHolder;
 
@@ -34,7 +35,6 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
-import java.util.StringTokenizer;
 import java.util.concurrent.locks.ReentrantLock;
 
 /**
@@ -1905,7 +1905,7 @@
         private HashMap<String, String> mMap;
 
         private Parameters() {
-            mMap = new HashMap<String, String>();
+            mMap = new HashMap<String, String>(64);
         }
 
         /**
@@ -1929,7 +1929,7 @@
          *         semi-colon delimited key-value pairs
          */
         public String flatten() {
-            StringBuilder flattened = new StringBuilder();
+            StringBuilder flattened = new StringBuilder(128);
             for (String k : mMap.keySet()) {
                 flattened.append(k);
                 flattened.append("=");
@@ -1952,9 +1952,9 @@
         public void unflatten(String flattened) {
             mMap.clear();
 
-            StringTokenizer tokenizer = new StringTokenizer(flattened, ";");
-            while (tokenizer.hasMoreElements()) {
-                String kv = tokenizer.nextToken();
+            TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(';');
+            splitter.setString(flattened);
+            for (String kv : splitter) {
                 int pos = kv.indexOf('=');
                 if (pos == -1) {
                     continue;
@@ -3488,11 +3488,11 @@
         private ArrayList<String> split(String str) {
             if (str == null) return null;
 
-            // Use StringTokenizer because it is faster than split.
-            StringTokenizer tokenizer = new StringTokenizer(str, ",");
+            TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(',');
+            splitter.setString(str);
             ArrayList<String> substrings = new ArrayList<String>();
-            while (tokenizer.hasMoreElements()) {
-                substrings.add(tokenizer.nextToken());
+            for (String s : splitter) {
+                substrings.add(s);
             }
             return substrings;
         }
@@ -3502,11 +3502,11 @@
         private ArrayList<Integer> splitInt(String str) {
             if (str == null) return null;
 
-            StringTokenizer tokenizer = new StringTokenizer(str, ",");
+            TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(',');
+            splitter.setString(str);
             ArrayList<Integer> substrings = new ArrayList<Integer>();
-            while (tokenizer.hasMoreElements()) {
-                String token = tokenizer.nextToken();
-                substrings.add(Integer.parseInt(token));
+            for (String s : splitter) {
+                substrings.add(Integer.parseInt(s));
             }
             if (substrings.size() == 0) return null;
             return substrings;
@@ -3515,11 +3515,11 @@
         private void splitInt(String str, int[] output) {
             if (str == null) return;
 
-            StringTokenizer tokenizer = new StringTokenizer(str, ",");
+            TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(',');
+            splitter.setString(str);
             int index = 0;
-            while (tokenizer.hasMoreElements()) {
-                String token = tokenizer.nextToken();
-                output[index++] = Integer.parseInt(token);
+            for (String s : splitter) {
+                output[index++] = Integer.parseInt(s);
             }
         }
 
@@ -3527,11 +3527,11 @@
         private void splitFloat(String str, float[] output) {
             if (str == null) return;
 
-            StringTokenizer tokenizer = new StringTokenizer(str, ",");
+            TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(',');
+            splitter.setString(str);
             int index = 0;
-            while (tokenizer.hasMoreElements()) {
-                String token = tokenizer.nextToken();
-                output[index++] = Float.parseFloat(token);
+            for (String s : splitter) {
+                output[index++] = Float.parseFloat(s);
             }
         }
 
@@ -3558,10 +3558,11 @@
         private ArrayList<Size> splitSize(String str) {
             if (str == null) return null;
 
-            StringTokenizer tokenizer = new StringTokenizer(str, ",");
+            TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(',');
+            splitter.setString(str);
             ArrayList<Size> sizeList = new ArrayList<Size>();
-            while (tokenizer.hasMoreElements()) {
-                Size size = strToSize(tokenizer.nextToken());
+            for (String s : splitter) {
+                Size size = strToSize(s);
                 if (size != null) sizeList.add(size);
             }
             if (sizeList.size() == 0) return null;
diff --git a/core/java/android/net/EthernetDataTracker.java b/core/java/android/net/EthernetDataTracker.java
index 28bd289..0cc78c9 100644
--- a/core/java/android/net/EthernetDataTracker.java
+++ b/core/java/android/net/EthernetDataTracker.java
@@ -223,7 +223,7 @@
                     mIface = iface;
                     mNMService.setInterfaceUp(iface);
                     InterfaceConfiguration config = mNMService.getInterfaceConfig(iface);
-                    mLinkUp = config.isActive();
+                    mLinkUp = config.hasFlag("up");
                     if (config != null && mHwAddr == null) {
                         mHwAddr = config.getHardwareAddress();
                         if (mHwAddr != null) {
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index 82cb74b..63c19ad 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -184,8 +184,17 @@
                 if (!TextUtils.equals(apnType, mApnType)) {
                     return;
                 }
-                mNetworkInfo.setSubtype(TelephonyManager.getDefault().getNetworkType(),
-                        TelephonyManager.getDefault().getNetworkTypeName());
+
+                int oldSubtype = mNetworkInfo.getSubtype();
+                int newSubType = TelephonyManager.getDefault().getNetworkType();
+                String subTypeName = TelephonyManager.getDefault().getNetworkTypeName();
+                mNetworkInfo.setSubtype(newSubType, subTypeName);
+                if (newSubType != oldSubtype && mNetworkInfo.isConnected()) {
+                    Message msg = mTarget.obtainMessage(EVENT_NETWORK_SUBTYPE_CHANGED,
+                                                        oldSubtype, 0, mNetworkInfo);
+                    msg.sendToTarget();
+                }
+
                 PhoneConstants.DataState state = Enum.valueOf(PhoneConstants.DataState.class,
                         intent.getStringExtra(PhoneConstants.STATE_KEY));
                 String reason = intent.getStringExtra(PhoneConstants.STATE_CHANGE_REASON_KEY);
diff --git a/core/java/android/net/NetworkStateTracker.java b/core/java/android/net/NetworkStateTracker.java
index 7df0193..0d6dcd6 100644
--- a/core/java/android/net/NetworkStateTracker.java
+++ b/core/java/android/net/NetworkStateTracker.java
@@ -69,6 +69,12 @@
     public static final int EVENT_RESTORE_DEFAULT_NETWORK = 6;
 
     /**
+     * msg.what = EVENT_NETWORK_SUBTYPE_CHANGED
+     * msg.obj = NetworkInfo object
+     */
+    public static final int EVENT_NETWORK_SUBTYPE_CHANGED = 7;
+
+    /**
      * -------------------------------------------------------------
      * Control Interface
      * -------------------------------------------------------------
diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java
index fb5263d..65d3f2b 100644
--- a/core/java/android/net/VpnService.java
+++ b/core/java/android/net/VpnService.java
@@ -329,7 +329,7 @@
                 throw new IllegalArgumentException("Bad address");
             }
 
-            mAddresses.append(String.format(" %s/%d", address.getHostAddress(), prefixLength));
+            mAddresses.append(' ' + address.getHostAddress() + '/' +  prefixLength);
             return this;
         }
 
diff --git a/core/java/android/net/arp/ArpPeer.java b/core/java/android/net/arp/ArpPeer.java
index 8e666bc..6ba1e7c 100644
--- a/core/java/android/net/arp/ArpPeer.java
+++ b/core/java/android/net/arp/ArpPeer.java
@@ -53,9 +53,11 @@
         mInterfaceName = interfaceName;
         mMyAddr = myAddr;
 
-        for (int i = 0; i < MAC_ADDR_LENGTH; i++) {
-            mMyMac[i] = (byte) Integer.parseInt(mac.substring(
-                        i*3, (i*3) + 2), 16);
+        if (mac != null) {
+            for (int i = 0; i < MAC_ADDR_LENGTH; i++) {
+                mMyMac[i] = (byte) Integer.parseInt(mac.substring(
+                            i*3, (i*3) + 2), 16);
+            }
         }
 
         if (myAddr instanceof Inet6Address || peer instanceof Inet6Address) {
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 7c103aa..0941d71 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -53,8 +53,6 @@
 
     public static native int setPermissions(String file, int mode, int uid, int gid);
 
-    public static native int setUMask(int mask);
-
     /** returns the FAT file system volume ID for the volume mounted 
      * at the given mount point, or -1 for failure
      * @param mountPoint point for FAT volume
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
old mode 100644
new mode 100755
index 8e123ac..e7b0579
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -8362,7 +8362,7 @@
                 // Line contains the query string - now search for it at the start of tokens.
                 List<String> lineTokens = new ArrayList<String>();
                 List<Integer> tokenOffsets = new ArrayList<Integer>();
-                split(contentLine.trim(), lineTokens, tokenOffsets);
+                split(contentLine, lineTokens, tokenOffsets);
 
                 // As we find matches against the query, we'll populate this list with the marked
                 // (or unchanged) tokens.
diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java
index da10311..2e962a0 100644
--- a/core/java/android/text/format/DateUtils.java
+++ b/core/java/android/text/format/DateUtils.java
@@ -161,12 +161,17 @@
     public static final int FORMAT_NO_YEAR = 0x00008;
     public static final int FORMAT_SHOW_DATE = 0x00010;
     public static final int FORMAT_NO_MONTH_DAY = 0x00020;
+    @Deprecated
     public static final int FORMAT_12HOUR = 0x00040;
+    @Deprecated
     public static final int FORMAT_24HOUR = 0x00080;
+    @Deprecated
     public static final int FORMAT_CAP_AMPM = 0x00100;
     public static final int FORMAT_NO_NOON = 0x00200;
+    @Deprecated
     public static final int FORMAT_CAP_NOON = 0x00400;
     public static final int FORMAT_NO_MIDNIGHT = 0x00800;
+    @Deprecated
     public static final int FORMAT_CAP_MIDNIGHT = 0x01000;
     /**
      * @deprecated Use
@@ -181,19 +186,25 @@
     public static final int FORMAT_NUMERIC_DATE = 0x20000;
     public static final int FORMAT_ABBREV_RELATIVE = 0x40000;
     public static final int FORMAT_ABBREV_ALL = 0x80000;
+    @Deprecated
     public static final int FORMAT_CAP_NOON_MIDNIGHT = (FORMAT_CAP_NOON | FORMAT_CAP_MIDNIGHT);
+    @Deprecated
     public static final int FORMAT_NO_NOON_MIDNIGHT = (FORMAT_NO_NOON | FORMAT_NO_MIDNIGHT);
 
     // Date and time format strings that are constant and don't need to be
     // translated.
     /**
      * This is not actually the preferred 24-hour date format in all locales.
+     * @deprecated use {@link java.text.SimpleDateFormat} instead.
      */
+    @Deprecated
     public static final String HOUR_MINUTE_24 = "%H:%M";
     public static final String MONTH_FORMAT = "%B";
     /**
      * This is not actually a useful month name in all locales.
+     * @deprecated use {@link java.text.SimpleDateFormat} instead.
      */
+    @Deprecated
     public static final String ABBREV_MONTH_FORMAT = "%b";
     public static final String NUMERIC_MONTH_FORMAT = "%m";
     public static final String MONTH_DAY_FORMAT = "%-d";
@@ -207,6 +218,7 @@
     // The index is constructed from a bit-wise OR of the boolean values:
     // {showTime, showYear, showWeekDay}.  For example, if showYear and
     // showWeekDay are both true, then the index would be 3.
+    /** @deprecated do not use. */
     public static final int sameYearTable[] = {
         com.android.internal.R.string.same_year_md1_md2,
         com.android.internal.R.string.same_year_wday1_md1_wday2_md2,
@@ -233,6 +245,7 @@
     // The index is constructed from a bit-wise OR of the boolean values:
     // {showTime, showYear, showWeekDay}.  For example, if showYear and
     // showWeekDay are both true, then the index would be 3.
+    /** @deprecated do not use. */
     public static final int sameMonthTable[] = {
         com.android.internal.R.string.same_month_md1_md2,
         com.android.internal.R.string.same_month_wday1_md1_wday2_md2,
@@ -259,7 +272,9 @@
      *
      * @more <p>
      *       e.g. "Sunday" or "January"
+     * @deprecated use {@link java.text.SimpleDateFormat} instead.
      */
+    @Deprecated
     public static final int LENGTH_LONG = 10;
 
     /**
@@ -268,7 +283,9 @@
      *
      * @more <p>
      *       e.g. "Sun" or "Jan"
+     * @deprecated use {@link java.text.SimpleDateFormat} instead.
      */
+    @Deprecated
     public static final int LENGTH_MEDIUM = 20;
 
     /**
@@ -278,14 +295,18 @@
      * <p>e.g. "Su" or "Jan"
      * <p>In most languages, the results returned for LENGTH_SHORT will be the same as
      * the results returned for {@link #LENGTH_MEDIUM}.
+     * @deprecated use {@link java.text.SimpleDateFormat} instead.
      */
+    @Deprecated
     public static final int LENGTH_SHORT = 30;
 
     /**
      * Request an even shorter abbreviated version of the name.
      * Do not use this.  Currently this will always return the same result
      * as {@link #LENGTH_SHORT}.
+     * @deprecated use {@link java.text.SimpleDateFormat} instead.
      */
+    @Deprecated
     public static final int LENGTH_SHORTER = 40;
 
     /**
@@ -295,7 +316,9 @@
      * <p>e.g. "S", "T", "T" or "J"
      * <p>In some languages, the results returned for LENGTH_SHORTEST will be the same as
      * the results returned for {@link #LENGTH_SHORT}.
+     * @deprecated use {@link java.text.SimpleDateFormat} instead.
      */
+    @Deprecated
     public static final int LENGTH_SHORTEST = 50;
 
     /**
@@ -309,7 +332,9 @@
      *               Undefined lengths will return {@link #LENGTH_MEDIUM}
      *               but may return something different in the future.
      * @throws IndexOutOfBoundsException if the dayOfWeek is out of bounds.
+     * @deprecated use {@link java.text.SimpleDateFormat} instead.
      */
+    @Deprecated
     public static String getDayOfWeekString(int dayOfWeek, int abbrev) {
         int[] list;
         switch (abbrev) {
@@ -330,7 +355,9 @@
      * @param ampm Either {@link Calendar#AM Calendar.AM} or {@link Calendar#PM Calendar.PM}.
      * @throws IndexOutOfBoundsException if the ampm is out of bounds.
      * @return Localized version of "AM" or "PM".
+     * @deprecated use {@link java.text.SimpleDateFormat} instead.
      */
+    @Deprecated
     public static String getAMPMString(int ampm) {
         Resources r = Resources.getSystem();
         return r.getString(sAmPm[ampm - Calendar.AM]);
@@ -345,7 +372,9 @@
      *               Undefined lengths will return {@link #LENGTH_MEDIUM}
      *               but may return something different in the future.
      * @return Localized month of the year.
+     * @deprecated use {@link java.text.SimpleDateFormat} instead.
      */
+    @Deprecated
     public static String getMonthString(int month, int abbrev) {
         // Note that here we use sMonthsMedium for MEDIUM, SHORT and SHORTER.
         // This is a shortcut to not spam the translators with too many variations
@@ -378,7 +407,9 @@
      *               but may return something different in the future.
      * @return Localized month of the year.
      * @hide Pending API council approval
+     * @deprecated use {@link java.text.SimpleDateFormat} instead.
      */
+    @Deprecated
     public static String getStandaloneMonthString(int month, int abbrev) {
         // Note that here we use sMonthsMedium for MEDIUM, SHORT and SHORTER.
         // This is a shortcut to not spam the translators with too many variations
@@ -1618,7 +1649,7 @@
 
         String result;
         long now = System.currentTimeMillis();
-        long span = now - millis;
+        long span = Math.abs(now - millis);
 
         synchronized (DateUtils.class) {
             if (sNowTime == null) {
diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java
index 84a6129..34c08ef 100644
--- a/core/java/android/webkit/WebViewClassic.java
+++ b/core/java/android/webkit/WebViewClassic.java
@@ -686,6 +686,10 @@
     // It's used to dismiss the dialog in destroy if not done before.
     private AlertDialog mListBoxDialog = null;
 
+    // Reference to the save password dialog so it can be dimissed in
+    // destroy if not done before.
+    private AlertDialog mSavePasswordDialog = null;
+
     static final String LOGTAG = "webview";
 
     private ZoomManager mZoomManager;
@@ -1811,7 +1815,7 @@
             neverRemember.getData().putString("password", password);
             neverRemember.obj = resumeMsg;
 
-            new AlertDialog.Builder(mContext)
+            mSavePasswordDialog = new AlertDialog.Builder(mContext)
                     .setTitle(com.android.internal.R.string.save_password_label)
                     .setMessage(com.android.internal.R.string.save_password_message)
                     .setPositiveButton(com.android.internal.R.string.save_password_notnow,
@@ -1822,6 +1826,7 @@
                                 resumeMsg.sendToTarget();
                                 mResumeMsg = null;
                             }
+                            mSavePasswordDialog = null;
                         }
                     })
                     .setNeutralButton(com.android.internal.R.string.save_password_remember,
@@ -1832,6 +1837,7 @@
                                 remember.sendToTarget();
                                 mResumeMsg = null;
                             }
+                            mSavePasswordDialog = null;
                         }
                     })
                     .setNegativeButton(com.android.internal.R.string.save_password_never,
@@ -1842,6 +1848,7 @@
                                 neverRemember.sendToTarget();
                                 mResumeMsg = null;
                             }
+                            mSavePasswordDialog = null;
                         }
                     })
                     .setOnCancelListener(new OnCancelListener() {
@@ -1851,6 +1858,7 @@
                                 resumeMsg.sendToTarget();
                                 mResumeMsg = null;
                             }
+                            mSavePasswordDialog = null;
                         }
                     }).show();
             // Return true so that WebViewCore will pause while the dialog is
@@ -2090,6 +2098,10 @@
             mListBoxDialog.dismiss();
             mListBoxDialog = null;
         }
+        if (mSavePasswordDialog != null) {
+            mSavePasswordDialog.dismiss();
+            mSavePasswordDialog = null;
+        }
         if (mWebViewCore != null) {
             // Tell WebViewCore to destroy itself
             synchronized (this) {
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index 18c4fe6..ff0579c 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -22,6 +22,7 @@
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.FocusFinder;
 import android.view.InputDevice;
 import android.view.KeyEvent;
@@ -62,6 +63,7 @@
 
     private static final float MAX_SCROLL_FACTOR = ScrollView.MAX_SCROLL_FACTOR;
 
+    private static final String TAG = "HorizontalScrollView";
 
     private long mLastScroll;
 
@@ -456,6 +458,12 @@
                 }
 
                 final int pointerIndex = ev.findPointerIndex(activePointerId);
+                if (pointerIndex == -1) {
+                    Log.e(TAG, "Invalid pointerId=" + activePointerId
+                            + " in onInterceptTouchEvent");
+                    break;
+                }
+
                 final int x = (int) ev.getX(pointerIndex);
                 final int xDiff = (int) Math.abs(x - mLastMotionX);
                 if (xDiff > mTouchSlop) {
@@ -557,6 +565,11 @@
             }
             case MotionEvent.ACTION_MOVE:
                 final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
+                if (activePointerIndex == -1) {
+                    Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent");
+                    break;
+                }
+
                 final int x = (int) ev.getX(activePointerIndex);
                 int deltaX = mLastMotionX - x;
                 if (!mIsBeingDragged && Math.abs(deltaX) > mTouchSlop) {
diff --git a/core/java/android/widget/MediaController.java b/core/java/android/widget/MediaController.java
index fc35f05..f76ab2b 100644
--- a/core/java/android/widget/MediaController.java
+++ b/core/java/android/widget/MediaController.java
@@ -477,7 +477,8 @@
             return true;
         } else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
                 || keyCode == KeyEvent.KEYCODE_VOLUME_UP
-                || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) {
+                || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE
+                || keyCode == KeyEvent.KEYCODE_CAMERA) {
             // don't show the controls for volume adjustment
             return super.dispatchKeyEvent(event);
         } else if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_MENU) {
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index ebc54f4..8747dc3 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -25,6 +25,7 @@
 import android.os.Bundle;
 import android.os.StrictMode;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.FocusFinder;
 import android.view.InputDevice;
 import android.view.KeyEvent;
@@ -69,6 +70,8 @@
 
     static final float MAX_SCROLL_FACTOR = 0.5f;
 
+    private static final String TAG = "ScrollView";
+
     private long mLastScroll;
 
     private final Rect mTempRect = new Rect();
@@ -478,6 +481,12 @@
                 }
 
                 final int pointerIndex = ev.findPointerIndex(activePointerId);
+                if (pointerIndex == -1) {
+                    Log.e(TAG, "Invalid pointerId=" + activePointerId
+                            + " in onInterceptTouchEvent");
+                    break;
+                }
+
                 final int y = (int) ev.getY(pointerIndex);
                 final int yDiff = Math.abs(y - mLastMotionY);
                 if (yDiff > mTouchSlop) {
@@ -585,6 +594,11 @@
             }
             case MotionEvent.ACTION_MOVE:
                 final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
+                if (activePointerIndex == -1) {
+                    Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent");
+                    break;
+                }
+
                 final int y = (int) ev.getY(activePointerIndex);
                 int deltaY = mLastMotionY - y;
                 if (!mIsBeingDragged && Math.abs(deltaY) > mTouchSlop) {
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index c44ce8a..86433d4 100644
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -1506,6 +1506,9 @@
         // because the voice search activity will always need to insert "QUERY" into
         // it anyway.
         Bundle queryExtras = new Bundle();
+        if (mAppSearchData != null) {
+            queryExtras.putParcelable(SearchManager.APP_DATA, mAppSearchData);
+        }
 
         // Now build the intent to launch the voice search.  Add all necessary
         // extras to launch the voice recognizer, and then all the necessary extras
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index 471f259..9ea1372 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -675,7 +675,7 @@
     @Override
     public void setChecked(boolean checked) {
         super.setChecked(checked);
-        mThumbPosition = checked ? getThumbScrollRange() : 0;
+        mThumbPosition = isChecked() ? getThumbScrollRange() : 0;
         invalidate();
     }
 
diff --git a/core/java/android/widget/TabHost.java b/core/java/android/widget/TabHost.java
index 8bb9348..238dc55 100644
--- a/core/java/android/widget/TabHost.java
+++ b/core/java/android/widget/TabHost.java
@@ -48,6 +48,10 @@
  */
 public class TabHost extends FrameLayout implements ViewTreeObserver.OnTouchModeChangeListener {
 
+    private static final int TABWIDGET_LOCATION_LEFT = 0;
+    private static final int TABWIDGET_LOCATION_TOP = 1;
+    private static final int TABWIDGET_LOCATION_RIGHT = 2;
+    private static final int TABWIDGET_LOCATION_BOTTOM = 3;
     private TabWidget mTabWidget;
     private FrameLayout mTabContent;
     private List<TabSpec> mTabSpecs = new ArrayList<TabSpec>(2);
@@ -293,22 +297,73 @@
         return mTabContent;
     }
 
+    /**
+     * Get the location of the TabWidget.
+     *
+     * @return The TabWidget location.
+     */
+    private int getTabWidgetLocation() {
+        int location = TABWIDGET_LOCATION_TOP;
+
+        switch (mTabWidget.getOrientation()) {
+            case LinearLayout.VERTICAL:
+                location = (mTabContent.getLeft() < mTabWidget.getLeft()) ? TABWIDGET_LOCATION_RIGHT
+                        : TABWIDGET_LOCATION_LEFT;
+                break;
+            case LinearLayout.HORIZONTAL:
+            default:
+                location = (mTabContent.getTop() < mTabWidget.getTop()) ? TABWIDGET_LOCATION_BOTTOM
+                        : TABWIDGET_LOCATION_TOP;
+                break;
+        }
+        return location;
+    }
+
     @Override
     public boolean dispatchKeyEvent(KeyEvent event) {
         final boolean handled = super.dispatchKeyEvent(event);
 
-        // unhandled key ups change focus to tab indicator for embedded activities
-        // when there is nothing that will take focus from default focus searching
+        // unhandled key events change focus to tab indicator for embedded
+        // activities when there is nothing that will take focus from default
+        // focus searching
         if (!handled
                 && (event.getAction() == KeyEvent.ACTION_DOWN)
-                && (event.getKeyCode() == KeyEvent.KEYCODE_DPAD_UP)
                 && (mCurrentView != null)
                 && (mCurrentView.isRootNamespace())
-                && (mCurrentView.hasFocus())
-                && (mCurrentView.findFocus().focusSearch(View.FOCUS_UP) == null)) {
-            mTabWidget.getChildTabViewAt(mCurrentTab).requestFocus();
-            playSoundEffect(SoundEffectConstants.NAVIGATION_UP);
-            return true;
+                && (mCurrentView.hasFocus())) {
+            int keyCodeShouldChangeFocus = KeyEvent.KEYCODE_DPAD_UP;
+            int directionShouldChangeFocus = View.FOCUS_UP;
+            int soundEffect = SoundEffectConstants.NAVIGATION_UP;
+
+            switch (getTabWidgetLocation()) {
+                case TABWIDGET_LOCATION_LEFT:
+                    keyCodeShouldChangeFocus = KeyEvent.KEYCODE_DPAD_LEFT;
+                    directionShouldChangeFocus = View.FOCUS_LEFT;
+                    soundEffect = SoundEffectConstants.NAVIGATION_LEFT;
+                    break;
+                case TABWIDGET_LOCATION_RIGHT:
+                    keyCodeShouldChangeFocus = KeyEvent.KEYCODE_DPAD_RIGHT;
+                    directionShouldChangeFocus = View.FOCUS_RIGHT;
+                    soundEffect = SoundEffectConstants.NAVIGATION_RIGHT;
+                    break;
+                case TABWIDGET_LOCATION_BOTTOM:
+                    keyCodeShouldChangeFocus = KeyEvent.KEYCODE_DPAD_DOWN;
+                    directionShouldChangeFocus = View.FOCUS_DOWN;
+                    soundEffect = SoundEffectConstants.NAVIGATION_DOWN;
+                    break;
+                case TABWIDGET_LOCATION_TOP:
+                default:
+                    keyCodeShouldChangeFocus = KeyEvent.KEYCODE_DPAD_UP;
+                    directionShouldChangeFocus = View.FOCUS_UP;
+                    soundEffect = SoundEffectConstants.NAVIGATION_UP;
+                    break;
+            }
+            if (event.getKeyCode() == keyCodeShouldChangeFocus
+                    && mCurrentView.findFocus().focusSearch(directionShouldChangeFocus) == null) {
+                mTabWidget.getChildTabViewAt(mCurrentTab).requestFocus();
+                playSoundEffect(soundEffect);
+                return true;
+            }
         }
         return handled;
     }
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 998c037..4924326 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -16,16 +16,17 @@
 
 package com.android.internal.os;
 
+import static libcore.io.OsConstants.S_IRWXG;
+import static libcore.io.OsConstants.S_IRWXO;
+
 import android.content.pm.ActivityInfo;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.drawable.Drawable;
 import android.net.LocalServerSocket;
 import android.os.Debug;
-import android.os.FileUtils;
 import android.os.Process;
 import android.os.SystemClock;
-import android.os.SystemProperties;
 import android.util.EventLog;
 import android.util.Log;
 
@@ -33,6 +34,7 @@
 import dalvik.system.Zygote;
 
 import libcore.io.IoUtils;
+import libcore.io.Libcore;
 
 import java.io.BufferedReader;
 import java.io.FileDescriptor;
@@ -447,7 +449,7 @@
         closeServerSocket();
 
         // set umask to 0077 so new files and directories will default to owner-only permissions.
-        FileUtils.setUMask(FileUtils.S_IRWXG | FileUtils.S_IRWXO);
+        Libcore.os.umask(S_IRWXG | S_IRWXO);
 
         if (parsedArgs.niceName != null) {
             Process.setArgV0(parsedArgs.niceName);
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 3c27caf..1bba5b4 100644
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -298,8 +298,18 @@
     }

 

     bool success = false;

-    SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage);

-    if (NULL != strm) {

+    if (NULL != bitmap) {

+        SkAutoLockPixels alp(*bitmap);

+

+        if (NULL == bitmap->getPixels()) {

+            return false;

+        }

+

+        SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage);

+        if (NULL == strm) {

+            return false;

+        }

+

         SkImageEncoder* encoder = SkImageEncoder::Create(fm);

         if (NULL != encoder) {

             success = encoder->encodeStream(strm, *bitmap, quality);

diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index a65262c..a4d5477 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -750,7 +750,7 @@
     static void doTextBounds(JNIEnv* env, const jchar* text, int count,
                              jobject bounds, const SkPaint& paint)
     {
-        SkRect  r;
+        SkRect  r{0,0,0,0};
         SkIRect ir;
 
         sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(&paint,
diff --git a/core/jni/android_os_FileUtils.cpp b/core/jni/android_os_FileUtils.cpp
index 82bfc36..a07f5b7 100644
--- a/core/jni/android_os_FileUtils.cpp
+++ b/core/jni/android_os_FileUtils.cpp
@@ -55,11 +55,6 @@
     return chmod(file8.string(), mode) == 0 ? 0 : errno;
 }
 
-jint android_os_FileUtils_setUMask(JNIEnv* env, jobject clazz, jint mask)
-{
-    return umask(mask);
-}
-
 jint android_os_FileUtils_getFatVolumeId(JNIEnv* env, jobject clazz, jstring path)
 {
     if (path == NULL) {
@@ -83,7 +78,6 @@
 
 static const JNINativeMethod methods[] = {
     {"setPermissions",  "(Ljava/lang/String;III)I", (void*)android_os_FileUtils_setPermissions},
-    {"setUMask",        "(I)I",                    (void*)android_os_FileUtils_setUMask},
     {"getFatVolumeId",  "(Ljava/lang/String;)I", (void*)android_os_FileUtils_getFatVolumeId},
 };
 
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index ccc85a3..0a0c81c 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -865,4 +865,7 @@
 
     <!-- Set to true to add links to Cell Broadcast app from Settings and MMS app. -->
     <bool name="config_cellBroadcastAppLinks">false</bool>
+
+    <!-- The default value if the SyncStorageEngine should sync automatically or not -->
+    <bool name="config_syncstorageengine_masterSyncAutomatically">true</bool>
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 40768a0..6334f88 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -253,6 +253,7 @@
   <java-symbol type="bool" name="config_sms_capable" />
   <java-symbol type="bool" name="config_sms_utf8_support" />
   <java-symbol type="bool" name="config_swipeDisambiguation" />
+  <java-symbol type="bool" name="config_syncstorageengine_masterSyncAutomatically" />
   <java-symbol type="bool" name="config_telephony_use_own_number_for_voicemail" />
   <java-symbol type="bool" name="config_ui_enableFadingMarquee" />
   <java-symbol type="bool" name="config_use_strict_phone_number_comparation" />
diff --git a/data/etc/Android.mk b/data/etc/Android.mk
index 71a9a15..134ac0c 100644
--- a/data/etc/Android.mk
+++ b/data/etc/Android.mk
@@ -21,8 +21,6 @@
 
 LOCAL_MODULE := platform.xml
 
-LOCAL_MODULE_TAGS := user
-
 LOCAL_MODULE_CLASS := ETC
 
 # This will install the file in /system/etc/permissions
@@ -38,8 +36,6 @@
 
 #LOCAL_MODULE := required_hardware.xml
 
-#LOCAL_MODULE_TAGS := user
-
 #LOCAL_MODULE_CLASS := ETC
 
 # This will install the file in /system/etc/permissions
diff --git a/drm/jni/Android.mk b/drm/jni/Android.mk
index f8ecc8c..fff7eee 100644
--- a/drm/jni/Android.mk
+++ b/drm/jni/Android.mk
@@ -35,7 +35,8 @@
     $(JNI_H_INCLUDE) \
     $(TOP)/frameworks/av/drm/libdrmframework/include \
     $(TOP)/frameworks/av/drm/libdrmframework/plugins/common/include \
-    $(TOP)/frameworks/av/include
+    $(TOP)/frameworks/av/include \
+    $(TOP)/libcore/include
 
 
 
diff --git a/drm/jni/android_drm_DrmManagerClient.cpp b/drm/jni/android_drm_DrmManagerClient.cpp
index 14ec4d6..fb685a2 100644
--- a/drm/jni/android_drm_DrmManagerClient.cpp
+++ b/drm/jni/android_drm_DrmManagerClient.cpp
@@ -20,6 +20,7 @@
 
 #include <jni.h>
 #include <JNIHelp.h>
+#include <ScopedLocalRef.h>
 #include <android_runtime/AndroidRuntime.h>
 
 #include <drm/DrmInfo.h>
@@ -250,16 +251,18 @@
         = getDrmManagerClientImpl(env, thiz)->getConstraints(uniqueId, &pathString, usage);
 
     jclass localRef = env->FindClass("android/content/ContentValues");
+    jmethodID ContentValues_putByteArray =
+            env->GetMethodID(localRef, "put", "(Ljava/lang/String;[B)V");
+    jmethodID ContentValues_putString =
+            env->GetMethodID(localRef, "put", "(Ljava/lang/String;Ljava/lang/String;)V");
+    jmethodID ContentValues_constructor = env->GetMethodID(localRef, "<init>", "()V");
     jobject constraints = NULL;
 
     if (NULL != localRef && NULL != pConstraints) {
-        // Get the constructor id
-        jmethodID constructorId = env->GetMethodID(localRef, "<init>", "()V");
         // create the java DrmConstraints object
-        constraints = env->NewObject(localRef, constructorId);
+        constraints = env->NewObject(localRef, ContentValues_constructor);
 
         DrmConstraints::KeyIterator keyIt = pConstraints->keyIterator();
-
         while (keyIt.hasNext()) {
             String8 key = keyIt.next();
 
@@ -267,18 +270,18 @@
             if (DrmConstraints::EXTENDED_METADATA == key) {
                 const char* value = pConstraints->getAsByteArray(&key);
                 if (NULL != value) {
-                    jbyteArray dataArray = env->NewByteArray(strlen(value));
-                    env->SetByteArrayRegion(dataArray, 0, strlen(value), (jbyte*)value);
-                    env->CallVoidMethod(
-                        constraints, env->GetMethodID(localRef, "put", "(Ljava/lang/String;[B)V"),
-                                     env->NewStringUTF(key.string()), dataArray);
+                    ScopedLocalRef<jbyteArray> dataArray(env, env->NewByteArray(strlen(value)));
+                    ScopedLocalRef<jstring> keyString(env, env->NewStringUTF(key.string()));
+                    env->SetByteArrayRegion(dataArray.get(), 0, strlen(value), (jbyte*)value);
+                    env->CallVoidMethod(constraints, ContentValues_putByteArray,
+                                        keyString.get(), dataArray.get());
                 }
             } else {
                 String8 value = pConstraints->get(key);
-                env->CallVoidMethod(
-                    constraints,
-                    env->GetMethodID(localRef, "put", "(Ljava/lang/String;Ljava/lang/String;)V"),
-                env->NewStringUTF(key.string()), env->NewStringUTF(value.string()));
+                ScopedLocalRef<jstring> keyString(env, env->NewStringUTF(key.string()));
+                ScopedLocalRef<jstring> valueString(env, env->NewStringUTF(value.string()));
+                env->CallVoidMethod(constraints, ContentValues_putString,
+                                    keyString.get(), valueString.get());
             }
         }
     }
@@ -297,8 +300,10 @@
 
     jobject metadata = NULL;
 
-    jclass localRef = NULL;
-    localRef = env->FindClass("android/content/ContentValues");
+    jclass localRef = env->FindClass("android/content/ContentValues");
+    jmethodID ContentValues_putString =
+            env->GetMethodID(localRef, "put", "(Ljava/lang/String;Ljava/lang/String;)V");
+
     if (NULL != localRef && NULL != pMetadata) {
         // Get the constructor id
         jmethodID constructorId = NULL;
@@ -313,9 +318,10 @@
                     // insert the entry<constraintKey, constraintValue>
                     // to newly created java object
                     String8 value = pMetadata->get(key);
-                    env->CallVoidMethod(metadata, env->GetMethodID(localRef, "put",
-                            "(Ljava/lang/String;Ljava/lang/String;)V"),
-                    env->NewStringUTF(key.string()), env->NewStringUTF(value.string()));
+                    ScopedLocalRef<jstring> keyString(env, env->NewStringUTF(key.string()));
+                    ScopedLocalRef<jstring> valueString(env, env->NewStringUTF(value.string()));
+                    env->CallVoidMethod(metadata, ContentValues_putString,
+                                        keyString.get(), valueString.get());
                 }
             }
         }
@@ -426,29 +432,30 @@
     DrmInfo drmInfo(mInfoType, buffer, mMimeType);
 
     jclass clazz = env->FindClass("android/drm/DrmInfo");
+    jmethodID DrmInfo_get = env->GetMethodID(clazz, "get", "(Ljava/lang/String;)Ljava/lang/Object;");
     jobject keyIterator
         = env->CallObjectMethod(drmInfoObject,
                 env->GetMethodID(clazz, "keyIterator", "()Ljava/util/Iterator;"));
 
-    jmethodID hasNextId = env->GetMethodID(env->FindClass("java/util/Iterator"), "hasNext", "()Z");
+    jclass Iterator_class = env->FindClass("java/util/Iterator");
+    jmethodID Iterator_hasNext = env->GetMethodID(Iterator_class, "hasNext", "()Z");
+    jmethodID Iterator_next = env->GetMethodID(Iterator_class, "next", "()Ljava/lang/Object;");
 
-    while (env->CallBooleanMethod(keyIterator, hasNextId)) {
-        jstring key = (jstring) env->CallObjectMethod(keyIterator,
-                env->GetMethodID(env->FindClass("java/util/Iterator"),
-                "next", "()Ljava/lang/Object;"));
+    jclass Object_class = env->FindClass("java/lang/Object");
+    jmethodID Object_toString = env->GetMethodID(Object_class, "toString", "()Ljava/lang/String;");
 
-        jobject valueObject = env->CallObjectMethod(drmInfoObject,
-                env->GetMethodID(clazz, "get", "(Ljava/lang/String;)Ljava/lang/Object;"), key);
-
-        jstring valString = NULL;
-        if (NULL != valueObject) {
-            valString = (jstring) env->CallObjectMethod(valueObject,
-                env->GetMethodID(env->FindClass("java/lang/Object"),
-                "toString", "()Ljava/lang/String;"));
+    while (env->CallBooleanMethod(keyIterator, Iterator_hasNext)) {
+        ScopedLocalRef<jstring> key(env,
+                (jstring) env->CallObjectMethod(keyIterator, Iterator_next));
+        ScopedLocalRef<jobject> valueObject(env,
+                env->CallObjectMethod(drmInfoObject, DrmInfo_get, key.get()));
+        ScopedLocalRef<jstring> valString(env, NULL);
+        if (NULL != valueObject.get()) {
+            valString.reset((jstring) env->CallObjectMethod(valueObject.get(), Object_toString));
         }
 
-        String8 keyString = Utility::getStringValue(env, key);
-        String8 valueString = Utility::getStringValue(env, valString);
+        String8 keyString = Utility::getStringValue(env, key.get());
+        String8 valueString = Utility::getStringValue(env, valString.get());
         ALOGV("Key: %s | Value: %s", keyString.string(), valueString.string());
 
         drmInfo.put(keyString, valueString);
@@ -508,20 +515,21 @@
     jobject keyIterator
         = env->CallObjectMethod(drmInfoRequest,
                 env->GetMethodID(clazz, "keyIterator", "()Ljava/util/Iterator;"));
+    jmethodID DrmInfoRequest_get = env->GetMethodID(clazz,
+            "get", "(Ljava/lang/String;)Ljava/lang/Object;");
 
-    jmethodID hasNextId = env->GetMethodID(env->FindClass("java/util/Iterator"), "hasNext", "()Z");
+    jclass Iterator_class = env->FindClass("java/util/Iterator");
+    jmethodID Iterator_hasNext = env->GetMethodID(Iterator_class, "hasNext", "()Z");
+    jmethodID Iterator_next = env->GetMethodID(Iterator_class, "next", "()Ljava/lang/Object;");
 
-    while (env->CallBooleanMethod(keyIterator, hasNextId)) {
-        jstring key
-            = (jstring) env->CallObjectMethod(keyIterator,
-                env->GetMethodID(env->FindClass("java/util/Iterator"),
-                                "next", "()Ljava/lang/Object;"));
+    while (env->CallBooleanMethod(keyIterator, Iterator_hasNext)) {
+        ScopedLocalRef<jstring> key(env,
+                (jstring) env->CallObjectMethod(keyIterator, Iterator_next));
+        ScopedLocalRef<jstring> value(env,
+                (jstring) env->CallObjectMethod(drmInfoRequest, DrmInfoRequest_get, key.get()));
 
-        jstring value = (jstring) env->CallObjectMethod(drmInfoRequest,
-                env->GetMethodID(clazz, "get", "(Ljava/lang/String;)Ljava/lang/Object;"), key);
-
-        String8 keyString = Utility::getStringValue(env, key);
-        String8 valueString = Utility::getStringValue(env, value);
+        String8 keyString = Utility::getStringValue(env, key.get());
+        String8 valueString = Utility::getStringValue(env, value.get());
         ALOGV("Key: %s | Value: %s", keyString.string(), valueString.string());
 
         drmInfoReq.put(keyString, valueString);
@@ -552,9 +560,10 @@
             while (it.hasNext()) {
                 String8 key = it.next();
                 String8 value = pDrmInfo->get(key);
-
+                ScopedLocalRef<jstring> keyString(env, env->NewStringUTF(key.string()));
+                ScopedLocalRef<jstring> valueString(env, env->NewStringUTF(value.string()));
                 env->CallVoidMethod(drmInfoObject, putMethodId,
-                    env->NewStringUTF(key.string()), env->NewStringUTF(value.string()));
+                    keyString.get(), valueString.get());
             }
         }
         delete [] pDrmInfo->getData().data;
diff --git a/graphics/jni/android_renderscript_RenderScript.cpp b/graphics/jni/android_renderscript_RenderScript.cpp
index 3d5d1a9..b205a77 100644
--- a/graphics/jni/android_renderscript_RenderScript.cpp
+++ b/graphics/jni/android_renderscript_RenderScript.cpp
@@ -718,7 +718,7 @@
     LOG_API("nAllocationRead_i, con(%p), alloc(%p), len(%i)", con, (RsAllocation)alloc, len);
     jint *ptr = _env->GetIntArrayElements(data, NULL);
     jsize length = _env->GetArrayLength(data);
-    rsAllocationRead(con, (RsAllocation)alloc, ptr, length);
+    rsAllocationRead(con, (RsAllocation)alloc, ptr, length * sizeof(int));
     _env->ReleaseIntArrayElements(data, ptr, 0);
 }
 
@@ -729,7 +729,7 @@
     LOG_API("nAllocationRead_i, con(%p), alloc(%p), len(%i)", con, (RsAllocation)alloc, len);
     jshort *ptr = _env->GetShortArrayElements(data, NULL);
     jsize length = _env->GetArrayLength(data);
-    rsAllocationRead(con, (RsAllocation)alloc, ptr, length);
+    rsAllocationRead(con, (RsAllocation)alloc, ptr, length * sizeof(short));
     _env->ReleaseShortArrayElements(data, ptr, 0);
 }
 
@@ -740,7 +740,7 @@
     LOG_API("nAllocationRead_i, con(%p), alloc(%p), len(%i)", con, (RsAllocation)alloc, len);
     jbyte *ptr = _env->GetByteArrayElements(data, NULL);
     jsize length = _env->GetArrayLength(data);
-    rsAllocationRead(con, (RsAllocation)alloc, ptr, length);
+    rsAllocationRead(con, (RsAllocation)alloc, ptr, length * sizeof(char));
     _env->ReleaseByteArrayElements(data, ptr, 0);
 }
 
@@ -751,7 +751,7 @@
     LOG_API("nAllocationRead_f, con(%p), alloc(%p), len(%i)", con, (RsAllocation)alloc, len);
     jfloat *ptr = _env->GetFloatArrayElements(data, NULL);
     jsize length = _env->GetArrayLength(data);
-    rsAllocationRead(con, (RsAllocation)alloc, ptr, length);
+    rsAllocationRead(con, (RsAllocation)alloc, ptr, length * sizeof(float));
     _env->ReleaseFloatArrayElements(data, ptr, 0);
 }
 
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 5fdbc11..b51b1e1 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -617,7 +617,7 @@
         uint32_t* retOriginX, uint32_t* retOriginY) {
     cachedGlyph->mIsValid = false;
     // If the glyph is too tall, don't cache it
-    if (glyph.fHeight + TEXTURE_BORDER_SIZE > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
+    if (mCacheLines.size() != 0  && (glyph.fHeight + TEXTURE_BORDER_SIZE > mCacheLines[mCacheLines.size() - 1]->mMaxHeight)) {
         ALOGE("Font size to large to fit in cache. width, height = %i, %i",
                 (int) glyph.fWidth, (int) glyph.fHeight);
         return;
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 9af201d..613354f 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -351,12 +351,11 @@
      */
     public void setCaptureRate(double fps) {
         // Make sure that time lapse is enabled when this method is called.
-        setParameter(String.format("time-lapse-enable=1"));
+        setParameter("time-lapse-enable=1");
 
         double timeBetweenFrameCapture = 1 / fps;
         int timeBetweenFrameCaptureMs = (int) (1000 * timeBetweenFrameCapture);
-        setParameter(String.format("time-between-time-lapse-frame-capture=%d",
-                    timeBetweenFrameCaptureMs));
+        setParameter("time-between-time-lapse-frame-capture=" + timeBetweenFrameCaptureMs);
     }
 
     /**
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 6f8b809..fd37bcf 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -1399,7 +1399,8 @@
         long lastModifiedSeconds = file.lastModified() / 1000;
 
         if (!MediaFile.isAudioFileType(fileType) && !MediaFile.isVideoFileType(fileType) &&
-            !MediaFile.isImageFileType(fileType) && !MediaFile.isPlayListFileType(fileType)) {
+            !MediaFile.isImageFileType(fileType) && !MediaFile.isPlayListFileType(fileType) &&
+            !MediaFile.isDrmFileType(fileType)) {
 
             // no need to use the media scanner, but we need to update last modified and file size
             ContentValues values = new ContentValues();
diff --git a/media/jni/mediaeditor/VideoEditorClasses.cpp b/media/jni/mediaeditor/VideoEditorClasses.cpp
index 4e0e0f2..4982a47 100755
--- a/media/jni/mediaeditor/VideoEditorClasses.cpp
+++ b/media/jni/mediaeditor/VideoEditorClasses.cpp
@@ -1853,6 +1853,9 @@
                             // Get the clip settings.
                             videoEditClasses_getClipSettings(pResult, pEnv, clipSettings,
                                 &pSettings->pClipList[i]);
+
+                            // Free the local references to avoid memory leaks
+                            pEnv->DeleteLocalRef(clipSettings);
                         }
                     }
                 }
@@ -1877,6 +1880,9 @@
                             // Get the transition settings.
                             videoEditClasses_getTransitionSettings(pResult, pEnv,
                                     transitionSettings, &pSettings->pTransitionList[i]);
+
+                            // Free the local references to avoid memory leaks
+                            pEnv->DeleteLocalRef(transitionSettings);
                         }
                     }
                 }
@@ -1900,6 +1906,9 @@
                             // Get the effect settings.
                             videoEditClasses_getEffectSettings(pResult, pEnv, effectSettings,
                                     &pSettings->Effects[i]);
+
+                            // Free the local references to avoid memory leaks
+                            pEnv->DeleteLocalRef(effectSettings);
                         }
                     }
                 }
diff --git a/media/jni/mediaeditor/VideoEditorJava.cpp b/media/jni/mediaeditor/VideoEditorJava.cpp
index ec8050f..bcf9099 100755
--- a/media/jni/mediaeditor/VideoEditorJava.cpp
+++ b/media/jni/mediaeditor/VideoEditorJava.cpp
@@ -387,6 +387,9 @@
                 (*pLength) = length;
             }
         }
+
+        // Delete local references to avoid memory leaks
+        pEnv->DeleteLocalRef(string);
     }
 
     // Return the string.
diff --git a/media/jni/mediaeditor/VideoEditorMain.cpp b/media/jni/mediaeditor/VideoEditorMain.cpp
index 41ec120..41c28c0 100755
--- a/media/jni/mediaeditor/VideoEditorMain.cpp
+++ b/media/jni/mediaeditor/VideoEditorMain.cpp
@@ -380,6 +380,9 @@
                     pEnv->GetIntField(object,fid);
    M4OSA_TRACE1_1("videoRotation = %d",
                     pSettings->ClipProperties.videoRotationDegrees);
+
+   // Free the local references to avoid memory leaks
+   pEnv->DeleteLocalRef(clazz);
 }
 
 static void jniPreviewProgressCallback (void* cookie, M4OSA_UInt32 msgType,
@@ -1849,7 +1852,9 @@
                 "not initialized");
             if (needToBeLoaded) {
                 getClipSetting(pEnv,properties, pContext->pEditSettings->pClipList[i]);
+                pEnv->DeleteLocalRef(properties);
             } else {
+                pEnv->DeleteLocalRef(properties);
                 goto videoEditor_populateSettings_cleanup;
             }
         }
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index cd0da5a..2c9a6fe 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -46,6 +46,8 @@
     <bool name="def_netstats_enabled">true</bool>
     <bool name="def_usb_mass_storage_enabled">true</bool>
     <bool name="def_wifi_on">false</bool>
+    <!-- 0 == default, 1 == never while plugged, 2 == never -->
+    <integer name="def_wifi_sleep_policy">0</integer>
     <bool name="def_networks_available_notification_on">true</bool>
 
     <bool name="def_backup_enabled">false</bool>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index de078d20..aa2164e 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -1496,6 +1496,8 @@
             loadIntegerSetting(stmt, Settings.System.POINTER_SPEED,
                     R.integer.def_pointer_speed);
 
+            loadIntegerSetting(stmt, Settings.System.WIFI_SLEEP_POLICY,
+                    R.integer.def_wifi_sleep_policy);
         } finally {
             if (stmt != null) stmt.close();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index 751764c..22a8443 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -976,6 +976,8 @@
                 combinedActivityIconId = mMobileActivityIconId;
                 combinedSignalIconId = mDataSignalIconId; // set by updateDataIcon()
                 mContentDescriptionCombinedSignal = mContentDescriptionDataType;
+            } else {
+                mMobileActivityIconId = 0;
             }
         }
 
diff --git a/services/common_time/common_clock_service.h b/services/common_time/common_clock_service.h
index a65e398..bd663f0 100644
--- a/services/common_time/common_clock_service.h
+++ b/services/common_time/common_clock_service.h
@@ -14,11 +14,12 @@
  * limitations under the License.
  */
 
-#include <common_time/ICommonClock.h>
-
 #ifndef ANDROID_COMMON_CLOCK_SERVICE_H
 #define ANDROID_COMMON_CLOCK_SERVICE_H
 
+#include <sys/socket.h>
+#include <common_time/ICommonClock.h>
+
 namespace android {
 
 class CommonTimeServer;
diff --git a/services/common_time/common_time_config_service.h b/services/common_time/common_time_config_service.h
index 8283c24..89806dd 100644
--- a/services/common_time/common_time_config_service.h
+++ b/services/common_time/common_time_config_service.h
@@ -13,11 +13,12 @@
  * limitations under the License.
  */
 
-#include <common_time/ICommonTimeConfig.h>
-
 #ifndef ANDROID_COMMON_TIME_CONFIG_SERVICE_H
 #define ANDROID_COMMON_TIME_CONFIG_SERVICE_H
 
+#include <sys/socket.h>
+#include <common_time/ICommonTimeConfig.h>
+
 namespace android {
 
 class String16;
diff --git a/services/common_time/common_time_server.h b/services/common_time/common_time_server.h
index a0f549f..89bca64 100644
--- a/services/common_time/common_time_server.h
+++ b/services/common_time/common_time_server.h
@@ -19,7 +19,7 @@
 
 #include <arpa/inet.h>
 #include <stdint.h>
-#include <linux/socket.h>
+#include <sys/socket.h>
 
 #include <common_time/ICommonClock.h>
 #include <common_time/local_clock.h>
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 230f07b..86ada40 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -2490,6 +2490,11 @@
                     //       @see bug/4455071
                     handleConnectivityChange(info.getType(), false);
                     break;
+                case NetworkStateTracker.EVENT_NETWORK_SUBTYPE_CHANGED:
+                    info = (NetworkInfo) msg.obj;
+                    type = info.getType();
+                    updateNetworkSettings(mNetTrackers[type]);
+                    break;
                 case EVENT_CLEAR_NET_TRANSITION_WAKELOCK:
                     String causedBy = null;
                     synchronized (ConnectivityService.this) {
diff --git a/services/java/com/android/server/VibratorService.java b/services/java/com/android/server/VibratorService.java
index b609867..72fde11 100755
--- a/services/java/com/android/server/VibratorService.java
+++ b/services/java/com/android/server/VibratorService.java
@@ -441,7 +441,7 @@
 
         private void delay(long duration) {
             if (duration > 0) {
-                long bedtime = SystemClock.uptimeMillis();
+                long bedtime = duration + SystemClock.uptimeMillis();
                 do {
                     try {
                         this.wait(duration);
@@ -451,8 +451,7 @@
                     if (mDone) {
                         break;
                     }
-                    duration = duration
-                            - SystemClock.uptimeMillis() - bedtime;
+                    duration = bedtime - SystemClock.uptimeMillis();
                 } while (duration > 0);
             }
         }
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 60085f4..5d74cf3 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -106,6 +106,7 @@
 import android.os.Process;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
+import android.os.SELinux;
 import android.os.ServiceManager;
 import android.os.StrictMode;
 import android.os.SystemClock;
@@ -3032,7 +3033,12 @@
         File tracesFile = new File(tracesPath);
         try {
             File tracesDir = tracesFile.getParentFile();
-            if (!tracesDir.exists()) tracesFile.mkdirs();
+            if (!tracesDir.exists()) {
+                tracesFile.mkdirs();
+                if (!SELinux.restorecon(tracesDir)) {
+                    return null;
+                }
+            }
             FileUtils.setPermissions(tracesDir.getPath(), 0775, -1, -1);  // drwxrwxr-x
 
             if (clearTraces && tracesFile.exists()) tracesFile.delete();
@@ -3136,7 +3142,12 @@
             final File tracesDir = tracesFile.getParentFile();
             final File tracesTmp = new File(tracesDir, "__tmp__");
             try {
-                if (!tracesDir.exists()) tracesFile.mkdirs();
+                if (!tracesDir.exists()) {
+                    tracesFile.mkdirs();
+                    if (!SELinux.restorecon(tracesDir.getPath())) {
+                        return;
+                    }
+                }
                 FileUtils.setPermissions(tracesDir.getPath(), 0775, -1, -1);  // drwxrwxr-x
 
                 if (tracesFile.exists()) {
@@ -10950,7 +10961,9 @@
                     restart = true;
                 } else {
                     removeDyingProviderLocked(app, cpr, true);
+                    // cpr should have been removed from mLaunchingProviders
                     NL = mLaunchingProviders.size();
+                    i--;
                 }
             }
         }
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 9171e47..b9e63b7 100755
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -2762,7 +2762,8 @@
                         // If the top activity in the task is the root
                         // activity, deliver this new intent to it if it
                         // desires.
-                        if ((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
+                        if (((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
+                                || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP)
                                 && taskTop.realActivity.equals(r.realActivity)) {
                             logStartActivity(EventLogTags.AM_NEW_INTENT, r, taskTop.task);
                             if (taskTop.frontOfTask) {
diff --git a/services/java/com/android/server/am/DeviceMonitor.java b/services/java/com/android/server/am/DeviceMonitor.java
index 5f3b0ce..21e7252 100644
--- a/services/java/com/android/server/am/DeviceMonitor.java
+++ b/services/java/com/android/server/am/DeviceMonitor.java
@@ -16,6 +16,7 @@
 
 package com.android.server.am;
 
+import android.os.SELinux;
 import android.util.Slog;
 
 import java.io.*;
@@ -80,6 +81,9 @@
         if (!BASE.isDirectory() && !BASE.mkdirs()) {
             throw new AssertionError("Couldn't create " + BASE + ".");
         }
+        if (!SELinux.restorecon(BASE)) {
+            throw new AssertionError("Couldn't restorecon " + BASE + ".");
+        }
     }
 
     private static final File[] PATHS = {
diff --git a/services/java/com/android/server/am/ProviderMap.java b/services/java/com/android/server/am/ProviderMap.java
index d148ec3..e4608a2 100644
--- a/services/java/com/android/server/am/ProviderMap.java
+++ b/services/java/com/android/server/am/ProviderMap.java
@@ -127,7 +127,12 @@
                 Slog.i(TAG,
                         "Removing from providersByName name=" + name + " user="
                         + (optionalUserId == -1 ? Binder.getOrigCallingUser() : optionalUserId));
-            getProvidersByName(optionalUserId).remove(name);
+            HashMap<String, ContentProviderRecord> map = getProvidersByName(optionalUserId);
+            // map returned by getProvidersByName wouldn't be null
+            map.remove(name);
+            if (map.size() == 0) {
+                mProvidersByNamePerUser.remove(optionalUserId);
+            }
         }
     }
 
@@ -141,7 +146,12 @@
                 Slog.i(TAG,
                         "Removing from providersByClass name=" + name + " user="
                         + (optionalUserId == -1 ? Binder.getOrigCallingUser() : optionalUserId));
-            getProvidersByClass(optionalUserId).remove(name);
+            HashMap<ComponentName, ContentProviderRecord> map = getProvidersByClass(optionalUserId);
+            // map returned by getProvidersByClass wouldn't be null
+            map.remove(name);
+            if (map.size() == 0) {
+                mProvidersByClassPerUser.remove(optionalUserId);
+            }
         }
     }
 
diff --git a/telephony/java/com/android/internal/telephony/IccCardConstants.java b/telephony/java/com/android/internal/telephony/IccCardConstants.java
index 20439bc..236bb2f 100644
--- a/telephony/java/com/android/internal/telephony/IccCardConstants.java
+++ b/telephony/java/com/android/internal/telephony/IccCardConstants.java
@@ -22,6 +22,8 @@
 
     /* The extra data for broacasting intent INTENT_ICC_STATE_CHANGE */
     public static final String INTENT_KEY_ICC_STATE = "ss";
+    /* UNKNOWN means the ICC state is unknown */
+    public static final String INTENT_VALUE_ICC_UNKNOWN = "UNKNOWN";
     /* NOT_READY means the ICC interface is not ready (eg, radio is off or powering on) */
     public static final String INTENT_VALUE_ICC_NOT_READY = "NOT_READY";
     /* ABSENT means ICC is missing */
@@ -45,10 +47,13 @@
     /* PERM_DISABLED means ICC is permanently disabled due to puk fails */
     public static final String INTENT_VALUE_ABSENT_ON_PERM_DISABLED = "PERM_DISABLED";
 
-    /*
-      UNKNOWN is a transient state, for example, after uesr inputs ICC pin under
-      PIN_REQUIRED state, the query for ICC status returns UNKNOWN before it
-      turns to READY
+    /**
+     * This is combination of IccCardStatus.CardState and IccCardApplicationStatus.AppState
+     * for external apps (like PhoneApp) to use
+     *
+     * UNKNOWN is a transient state, for example, after user inputs ICC pin under
+     * PIN_REQUIRED state, the query for ICC status returns UNKNOWN before it
+     * turns to READY
      */
     public enum State {
         UNKNOWN,
diff --git a/tests/RenderScriptTests/Balls/src/com/example/android/rs/balls/BallsView.java b/tests/RenderScriptTests/Balls/src/com/example/android/rs/balls/BallsView.java
index b3b3756..041782d 100644
--- a/tests/RenderScriptTests/Balls/src/com/example/android/rs/balls/BallsView.java
+++ b/tests/RenderScriptTests/Balls/src/com/example/android/rs/balls/BallsView.java
@@ -105,7 +105,7 @@
     }
 
     void setAccel(float x, float y, float z) {
-        if (mRender == null) {
+        if ((mRender == null) || (mRS == null)) {
             return;
         }
         mRender.setAccel(x, -y);
diff --git a/tools/aapt/Android.mk b/tools/aapt/Android.mk
index 482f43e..d9b0681 100644
--- a/tools/aapt/Android.mk
+++ b/tools/aapt/Android.mk
@@ -34,7 +34,6 @@
 endif
 
 
-LOCAL_C_INCLUDES += external/expat/lib
 LOCAL_C_INCLUDES += external/libpng
 LOCAL_C_INCLUDES += external/zlib
 LOCAL_C_INCLUDES += build/libs/host/include
diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h
index a5aa0b5..fde3bd6 100644
--- a/tools/aapt/Bundle.h
+++ b/tools/aapt/Bundle.h
@@ -61,7 +61,8 @@
           mMinSdkVersion(NULL), mTargetSdkVersion(NULL), mMaxSdkVersion(NULL),
           mVersionCode(NULL), mVersionName(NULL), mCustomPackage(NULL), mExtraPackages(NULL),
           mMaxResVersion(NULL), mDebugMode(false), mNonConstantId(false), mProduct(NULL),
-          mUseCrunchCache(false), mArgc(0), mArgv(NULL)
+          mUseCrunchCache(false), mErrorOnFailedInsert(false), mOutputTextSymbols(NULL),
+          mArgc(0), mArgv(NULL)
         {}
     ~Bundle(void) {}
 
@@ -110,6 +111,8 @@
     void setAutoAddOverlay(bool val) { mAutoAddOverlay = val; }
     bool getGenDependencies() { return mGenDependencies; }
     void setGenDependencies(bool val) { mGenDependencies = val; }
+    bool getErrorOnFailedInsert() { return mErrorOnFailedInsert; }
+    void setErrorOnFailedInsert(bool val) { mErrorOnFailedInsert = val; }
 
     bool getUTF16StringsOption() {
         return mWantUTF16 || !isMinSdkAtLeast(SDK_FROYO);
@@ -171,6 +174,8 @@
     void setProduct(const char * val) { mProduct = val; }
     void setUseCrunchCache(bool val) { mUseCrunchCache = val; }
     bool getUseCrunchCache() const { return mUseCrunchCache; }
+    const char* getOutputTextSymbols() const { return mOutputTextSymbols; }
+    void setOutputTextSymbols(const char* val) { mOutputTextSymbols = val; }
 
     /*
      * Set and get the file specification.
@@ -276,6 +281,8 @@
     bool        mNonConstantId;
     const char* mProduct;
     bool        mUseCrunchCache;
+    bool        mErrorOnFailedInsert;
+    const char* mOutputTextSymbols;
 
     /* file specification */
     int         mArgc;
diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp
index 9570c66..f398de0 100644
--- a/tools/aapt/Main.cpp
+++ b/tools/aapt/Main.cpp
@@ -69,7 +69,8 @@
         "        [-F apk-file] [-J R-file-dir] \\\n"
         "        [--product product1,product2,...] \\\n"
         "        [-c CONFIGS] [--preferred-configurations CONFIGS] \\\n"
-        "        [raw-files-dir [raw-files-dir] ...]\n"
+        "        [raw-files-dir [raw-files-dir] ...] \\\n"
+        "        [--output-text-symbols DIR]\n"
         "\n"
         "   Package the android resources.  It will read assets and resources that are\n"
         "   supplied with the -M -A -S or raw-files-dir arguments.  The -J -P -F and -R\n"
@@ -177,6 +178,14 @@
         "       Make the resources ID non constant. This is required to make an R java class\n"
         "       that does not contain the final value but is used to make reusable compiled\n"
         "       libraries that need to access resources.\n"
+        "   --error-on-failed-insert\n"
+        "       Forces aapt to return an error if it fails to insert values into the manifest\n"
+        "       with --debug-mode, --min-sdk-version, --target-sdk-version --version-code\n"
+        "       and --version-name.\n"
+        "       Insertion typically fails if the manifest already defines the attribute.\n"
+        "   --output-text-symbols\n"
+        "       Generates a text file containing the resource symbols of the R class in the\n"
+        "       specified folder.\n"
         "   --ignore-assets\n"
         "       Assets to be ignored. Default pattern is:\n"
         "       %s\n",
@@ -542,6 +551,17 @@
                     bundle.setInstrumentationPackageNameOverride(argv[0]);
                 } else if (strcmp(cp, "-auto-add-overlay") == 0) {
                     bundle.setAutoAddOverlay(true);
+                } else if (strcmp(cp, "-error-on-failed-insert") == 0) {
+                    bundle.setErrorOnFailedInsert(true);
+                } else if (strcmp(cp, "-output-text-symbols") == 0) {
+                    argc--;
+                    argv++;
+                    if (!argc) {
+                        fprintf(stderr, "ERROR: No argument supplied for '-output-text-symbols' option\n");
+                        wantUsage = true;
+                        goto bail;
+                    }
+                    bundle.setOutputTextSymbols(argv[0]);
                 } else if (strcmp(cp, "-product") == 0) {
                     argc--;
                     argv++;
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index a69adc1..5033418 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -673,24 +673,40 @@
     return true;
 }
 
-void addTagAttribute(const sp<XMLNode>& node, const char* ns8,
-        const char* attr8, const char* value)
+/*
+ * Inserts an attribute in a given node, only if the attribute does not
+ * exist.
+ * If errorOnFailedInsert is true, and the attribute already exists, returns false.
+ * Returns true otherwise, even if the attribute already exists.
+ */
+bool addTagAttribute(const sp<XMLNode>& node, const char* ns8,
+        const char* attr8, const char* value, bool errorOnFailedInsert)
 {
     if (value == NULL) {
-        return;
+        return true;
     }
-    
+
     const String16 ns(ns8);
     const String16 attr(attr8);
-    
+
     if (node->getAttribute(ns, attr) != NULL) {
+        if (errorOnFailedInsert) {
+            fprintf(stderr, "Error: AndroidManifest.xml already defines %s (in %s);"
+                            " cannot insert new value %s.\n",
+                    String8(attr).string(), String8(ns).string(), value);
+            return false;
+        }
+
         fprintf(stderr, "Warning: AndroidManifest.xml already defines %s (in %s);"
                         " using existing value in manifest.\n",
                 String8(attr).string(), String8(ns).string());
-        return;
+
+        // don't stop the build.
+        return true;
     }
     
     node->addAttribute(ns, attr, String16(value));
+    return true;
 }
 
 static void fullyQualifyClassName(const String8& package, sp<XMLNode> node,
@@ -728,11 +744,17 @@
         fprintf(stderr, "No <manifest> tag.\n");
         return UNKNOWN_ERROR;
     }
-    
-    addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionCode",
-            bundle->getVersionCode());
-    addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionName",
-            bundle->getVersionName());
+
+    bool errorOnFailedInsert = bundle->getErrorOnFailedInsert();
+
+    if (!addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionCode",
+            bundle->getVersionCode(), errorOnFailedInsert)) {
+        return UNKNOWN_ERROR;
+    }
+    if (!addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionName",
+            bundle->getVersionName(), errorOnFailedInsert)) {
+        return UNKNOWN_ERROR;
+    }
     
     if (bundle->getMinSdkVersion() != NULL
             || bundle->getTargetSdkVersion() != NULL
@@ -743,18 +765,27 @@
             root->insertChildAt(vers, 0);
         }
         
-        addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "minSdkVersion",
-                bundle->getMinSdkVersion());
-        addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "targetSdkVersion",
-                bundle->getTargetSdkVersion());
-        addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "maxSdkVersion",
-                bundle->getMaxSdkVersion());
+        if (!addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "minSdkVersion",
+                bundle->getMinSdkVersion(), errorOnFailedInsert)) {
+            return UNKNOWN_ERROR;
+        }
+        if (!addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "targetSdkVersion",
+                bundle->getTargetSdkVersion(), errorOnFailedInsert)) {
+            return UNKNOWN_ERROR;
+        }
+        if (!addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "maxSdkVersion",
+                bundle->getMaxSdkVersion(), errorOnFailedInsert)) {
+            return UNKNOWN_ERROR;
+        }
     }
 
     if (bundle->getDebugMode()) {
         sp<XMLNode> application = root->getChildElement(String16(), String16("application"));
         if (application != NULL) {
-            addTagAttribute(application, RESOURCES_ANDROID_NAMESPACE, "debuggable", "true");
+            if (!addTagAttribute(application, RESOURCES_ANDROID_NAMESPACE, "debuggable", "true",
+                    errorOnFailedInsert)) {
+                return UNKNOWN_ERROR;
+            }
         }
     }
 
@@ -1821,6 +1852,110 @@
     return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
 }
 
+static status_t writeTextLayoutClasses(
+    FILE* fp, const sp<AaptAssets>& assets,
+    const sp<AaptSymbols>& symbols, bool includePrivate)
+{
+    String16 attr16("attr");
+    String16 package16(assets->getPackage());
+
+    bool hasErrors = false;
+
+    size_t i;
+    size_t N = symbols->getNestedSymbols().size();
+    for (i=0; i<N; i++) {
+        sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i);
+        String16 nclassName16(symbols->getNestedSymbols().keyAt(i));
+        String8 realClassName(nclassName16);
+        if (fixupSymbol(&nclassName16) != NO_ERROR) {
+            hasErrors = true;
+        }
+        String8 nclassName(nclassName16);
+
+        SortedVector<uint32_t> idents;
+        Vector<uint32_t> origOrder;
+        Vector<bool> publicFlags;
+
+        size_t a;
+        size_t NA = nsymbols->getSymbols().size();
+        for (a=0; a<NA; a++) {
+            const AaptSymbolEntry& sym(nsymbols->getSymbols().valueAt(a));
+            int32_t code = sym.typeCode == AaptSymbolEntry::TYPE_INT32
+                    ? sym.int32Val : 0;
+            bool isPublic = true;
+            if (code == 0) {
+                String16 name16(sym.name);
+                uint32_t typeSpecFlags;
+                code = assets->getIncludedResources().identifierForName(
+                    name16.string(), name16.size(),
+                    attr16.string(), attr16.size(),
+                    package16.string(), package16.size(), &typeSpecFlags);
+                if (code == 0) {
+                    fprintf(stderr, "ERROR: In <declare-styleable> %s, unable to find attribute %s\n",
+                            nclassName.string(), sym.name.string());
+                    hasErrors = true;
+                }
+                isPublic = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0;
+            }
+            idents.add(code);
+            origOrder.add(code);
+            publicFlags.add(isPublic);
+        }
+
+        NA = idents.size();
+
+        fprintf(fp, "int[] styleable %s {", nclassName.string());
+
+        for (a=0; a<NA; a++) {
+            if (a != 0) {
+                fprintf(fp, ",");
+            }
+            fprintf(fp, " 0x%08x", idents[a]);
+        }
+
+        fprintf(fp, " }\n");
+
+        for (a=0; a<NA; a++) {
+            ssize_t pos = idents.indexOf(origOrder.itemAt(a));
+            if (pos >= 0) {
+                const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a);
+                if (!publicFlags.itemAt(a) && !includePrivate) {
+                    continue;
+                }
+                String8 name8(sym.name);
+                String16 comment(sym.comment);
+                String16 typeComment;
+                if (comment.size() <= 0) {
+                    comment = getAttributeComment(assets, name8, &typeComment);
+                } else {
+                    getAttributeComment(assets, name8, &typeComment);
+                }
+                String16 name(name8);
+                if (fixupSymbol(&name) != NO_ERROR) {
+                    hasErrors = true;
+                }
+
+                uint32_t typeSpecFlags = 0;
+                String16 name16(sym.name);
+                assets->getIncludedResources().identifierForName(
+                    name16.string(), name16.size(),
+                    attr16.string(), attr16.size(),
+                    package16.string(), package16.size(), &typeSpecFlags);
+                //printf("%s:%s/%s: 0x%08x\n", String8(package16).string(),
+                //    String8(attr16).string(), String8(name16).string(), typeSpecFlags);
+                const bool pub = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0;
+
+                fprintf(fp,
+                        "int styleable.%s_%s %d\n",
+                        nclassName.string(),
+                        String8(name).string(), (int)pos);
+            }
+        }
+    }
+
+    return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
+}
+
 static status_t writeSymbolClass(
     FILE* fp, const sp<AaptAssets>& assets, bool includePrivate,
     const sp<AaptSymbols>& symbols, const String8& className, int indent,
@@ -1848,7 +1983,6 @@
             continue;
         }
         String16 name(sym.name);
-        String8 realName(name);
         if (fixupSymbol(&name) != NO_ERROR) {
             return UNKNOWN_ERROR;
         }
@@ -1960,6 +2094,51 @@
     return NO_ERROR;
 }
 
+static status_t writeTextSymbolClass(
+    FILE* fp, const sp<AaptAssets>& assets, bool includePrivate,
+    const sp<AaptSymbols>& symbols, const String8& className)
+{
+    size_t i;
+    status_t err = NO_ERROR;
+
+    size_t N = symbols->getSymbols().size();
+    for (i=0; i<N; i++) {
+        const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i);
+        if (sym.typeCode != AaptSymbolEntry::TYPE_INT32) {
+            continue;
+        }
+
+        if (!assets->isJavaSymbol(sym, includePrivate)) {
+            continue;
+        }
+
+        String16 name(sym.name);
+        if (fixupSymbol(&name) != NO_ERROR) {
+            return UNKNOWN_ERROR;
+        }
+
+        fprintf(fp, "int %s %s 0x%08x\n",
+                className.string(),
+                String8(name).string(), (int)sym.int32Val);
+    }
+
+    N = symbols->getNestedSymbols().size();
+    for (i=0; i<N; i++) {
+        sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i);
+        String8 nclassName(symbols->getNestedSymbols().keyAt(i));
+        if (nclassName == "styleable") {
+            err = writeTextLayoutClasses(fp, assets, nsymbols, includePrivate);
+        } else {
+            err = writeTextSymbolClass(fp, assets, includePrivate, nsymbols, nclassName);
+        }
+        if (err != NO_ERROR) {
+            return err;
+        }
+    }
+
+    return NO_ERROR;
+}
+
 status_t writeResourceSymbols(Bundle* bundle, const sp<AaptAssets>& assets,
     const String8& package, bool includePrivate)
 {
@@ -1967,11 +2146,15 @@
         return NO_ERROR;
     }
 
+    const char* textSymbolsDest = bundle->getOutputTextSymbols();
+
+    String8 R("R");
     const size_t N = assets->getSymbols().size();
     for (size_t i=0; i<N; i++) {
         sp<AaptSymbols> symbols = assets->getSymbols().valueAt(i);
         String8 className(assets->getSymbols().keyAt(i));
         String8 dest(bundle->getRClassDir());
+
         if (bundle->getMakePackageDirs()) {
             String8 pkg(package);
             const char* last = pkg.string();
@@ -2003,14 +2186,14 @@
         }
 
         fprintf(fp,
-        "/* AUTO-GENERATED FILE.  DO NOT MODIFY.\n"
-        " *\n"
-        " * This class was automatically generated by the\n"
-        " * aapt tool from the resource data it found.  It\n"
-        " * should not be modified by hand.\n"
-        " */\n"
-        "\n"
-        "package %s;\n\n", package.string());
+            "/* AUTO-GENERATED FILE.  DO NOT MODIFY.\n"
+            " *\n"
+            " * This class was automatically generated by the\n"
+            " * aapt tool from the resource data it found.  It\n"
+            " * should not be modified by hand.\n"
+            " */\n"
+            "\n"
+            "package %s;\n\n", package.string());
 
         status_t err = writeSymbolClass(fp, assets, includePrivate, symbols,
                 className, 0, bundle->getNonConstantId());
@@ -2019,14 +2202,37 @@
         }
         fclose(fp);
 
+        if (textSymbolsDest != NULL && R == className) {
+            String8 textDest(textSymbolsDest);
+            textDest.appendPath(className);
+            textDest.append(".txt");
+
+            FILE* fp = fopen(textDest.string(), "w+");
+            if (fp == NULL) {
+                fprintf(stderr, "ERROR: Unable to open text symbol file %s: %s\n",
+                        textDest.string(), strerror(errno));
+                return UNKNOWN_ERROR;
+            }
+            if (bundle->getVerbose()) {
+                printf("  Writing text symbols for class %s.\n", className.string());
+            }
+
+            status_t err = writeTextSymbolClass(fp, assets, includePrivate, symbols,
+                    className);
+            if (err != NO_ERROR) {
+                return err;
+            }
+            fclose(fp);
+        }
+
         // If we were asked to generate a dependency file, we'll go ahead and add this R.java
         // as a target in the dependency file right next to it.
-        if (bundle->getGenDependencies()) {
+        if (bundle->getGenDependencies() && R == className) {
             // Add this R.java to the dependency file
             String8 dependencyFile(bundle->getRClassDir());
             dependencyFile.appendPath("R.java.d");
 
-            fp = fopen(dependencyFile.string(), "a");
+            FILE *fp = fopen(dependencyFile.string(), "a");
             fprintf(fp,"%s \\\n", dest.string());
             fclose(fp);
         }
@@ -2036,7 +2242,6 @@
 }
 
 
-
 class ProguardKeepSet
 {
 public:
diff --git a/tools/aapt/StringPool.h b/tools/aapt/StringPool.h
index d501008..16050b2 100644
--- a/tools/aapt/StringPool.h
+++ b/tools/aapt/StringPool.h
@@ -21,7 +21,7 @@
 #include <ctype.h>
 #include <errno.h>
 
-#include <expat.h>
+#include <libexpat/expat.h>
 
 using namespace android;
 
diff --git a/voip/java/android/net/sip/SimpleSessionDescription.java b/voip/java/android/net/sip/SimpleSessionDescription.java
index 29166dc..9fcd21d 100644
--- a/voip/java/android/net/sip/SimpleSessionDescription.java
+++ b/voip/java/android/net/sip/SimpleSessionDescription.java
@@ -18,6 +18,7 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Locale;
 
 /**
  * An object used to manipulate messages of Session Description Protocol (SDP).
@@ -66,7 +67,7 @@
     public SimpleSessionDescription(long sessionId, String address) {
         address = (address.indexOf(':') < 0 ? "IN IP4 " : "IN IP6 ") + address;
         mFields.parse("v=0");
-        mFields.parse(String.format("o=- %d %d %s", sessionId,
+        mFields.parse(String.format(Locale.US, "o=- %d %d %s", sessionId,
                 System.currentTimeMillis(), address));
         mFields.parse("s=-");
         mFields.parse("t=0 0");
diff --git a/wifi/java/android/net/wifi/WifiMonitor.java b/wifi/java/android/net/wifi/WifiMonitor.java
index a447c86..17c930b 100644
--- a/wifi/java/android/net/wifi/WifiMonitor.java
+++ b/wifi/java/android/net/wifi/WifiMonitor.java
@@ -20,6 +20,8 @@
 import android.net.wifi.p2p.WifiP2pConfig;
 import android.net.wifi.p2p.WifiP2pDevice;
 import android.net.wifi.p2p.WifiP2pGroup;
+import android.net.wifi.p2p.WifiP2pService;
+import android.net.wifi.p2p.WifiP2pService.P2pStatus;
 import android.net.wifi.p2p.WifiP2pProvDiscEvent;
 import android.net.wifi.p2p.nsd.WifiP2pServiceResponse;
 import android.net.wifi.StateChangeResult;
@@ -186,7 +188,7 @@
 
     /* P2P-GROUP-STARTED p2p-wlan0-0 [client|GO] ssid="DIRECT-W8" freq=2437
        [psk=2182b2e50e53f260d04f3c7b25ef33c965a3291b9b36b455a82d77fd82ca15bc|passphrase="fKG4jMe3"]
-       go_dev_addr=fa:7b:7a:42:02:13 */
+       go_dev_addr=fa:7b:7a:42:02:13 [PERSISTENT] */
     private static final String P2P_GROUP_STARTED_STR = "P2P-GROUP-STARTED";
 
     /* P2P-GROUP-REMOVED p2p-wlan0-0 [client|GO] reason=REQUESTED */
@@ -594,7 +596,13 @@
                 if (tokens.length != 2) return;
                 String[] nameValue = tokens[1].split("=");
                 if (nameValue.length != 2) return;
-                mStateMachine.sendMessage(P2P_INVITATION_RESULT_EVENT, nameValue[1]);
+                P2pStatus err = P2pStatus.UNKNOWN;
+                try {
+                    err = P2pStatus.valueOf(Integer.parseInt(nameValue[1]));
+                } catch (NumberFormatException e) {
+                    e.printStackTrace();
+                }
+                mStateMachine.sendMessage(P2P_INVITATION_RESULT_EVENT, err);
             } else if (dataString.startsWith(P2P_PROV_DISC_PBC_REQ_STR)) {
                 mStateMachine.sendMessage(P2P_PROV_DISC_PBC_REQ_EVENT,
                         new WifiP2pProvDiscEvent(dataString));
diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java
index 4bf1ca3..e520185 100644
--- a/wifi/java/android/net/wifi/WifiNative.java
+++ b/wifi/java/android/net/wifi/WifiNative.java
@@ -547,10 +547,9 @@
                 break;
         }
 
-        //TODO: Add persist behavior once the supplicant interaction is fixed for both
-        // group and client scenarios
-        /* Persist unless there is an explicit request to not do so*/
-        //if (config.persist != WifiP2pConfig.Persist.NO) args.add("persistent");
+        if (config.netId == WifiP2pGroup.PERSISTENT_NET_ID) {
+            args.add("persistent");
+        }
 
         if (joinExistingGroup) {
             args.add("join");
@@ -592,10 +591,17 @@
         return false;
     }
 
-    public boolean p2pGroupAdd() {
+    public boolean p2pGroupAdd(boolean persistent) {
+        if (persistent) {
+            return doBooleanCommand("P2P_GROUP_ADD persistent");
+        }
         return doBooleanCommand("P2P_GROUP_ADD");
     }
 
+    public boolean p2pGroupAdd(int netId) {
+        return doBooleanCommand("P2P_GROUP_ADD persistent=" + netId);
+    }
+
     public boolean p2pGroupRemove(String iface) {
         if (TextUtils.isEmpty(iface)) return false;
         return doBooleanCommand("P2P_GROUP_REMOVE " + iface);
@@ -624,6 +630,9 @@
         return doBooleanCommand("P2P_INVITE persistent=" + netId + " peer=" + deviceAddress);
     }
 
+    public String p2pGetSsid(String deviceAddress) {
+        return p2pGetParam(deviceAddress, "oper_ssid");
+    }
 
     public String p2pGetDeviceAddress() {
         String status = status();
@@ -665,6 +674,24 @@
         return doStringCommand("P2P_PEER " + deviceAddress);
     }
 
+    private String p2pGetParam(String deviceAddress, String key) {
+        if (deviceAddress == null) return null;
+
+        String peerInfo = p2pPeer(deviceAddress);
+        if (peerInfo == null) return null;
+        String[] tokens= peerInfo.split("\n");
+
+        key += "=";
+        for (String token : tokens) {
+            if (token.startsWith(key)) {
+                String[] nameValue = token.split("=");
+                if (nameValue.length != 2) break;
+                return nameValue[1];
+            }
+        }
+        return null;
+    }
+
     public boolean p2pServiceAdd(WifiP2pServiceInfo servInfo) {
         /*
          * P2P_SERVICE_ADD bonjour <query hexdump> <RDATA hexdump>
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index bb09704..78c4b6c 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -3535,6 +3535,13 @@
                     if (DBG) log("Network connection lost");
                     handleNetworkDisconnect();
                     break;
+                case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
+                    // Disregard auth failure events during WPS connection. The
+                    // EAP sequence is retried several times, and there might be
+                    // failures (especially for wps pin). We will get a WPS_XXX
+                    // event at the end of the sequence anyway.
+                    if (DBG) log("Ignore auth failure during WPS connection");
+                    break;
                 case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
                     //Throw away supplicant state changes when WPS is running.
                     //We will start getting supplicant state changes once we get
diff --git a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
index 1a42f93..c6d3eae 100644
--- a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
@@ -881,6 +881,9 @@
             //test to avoid any wifi connectivity issues
             loge("ARP test initiation failure: " + se);
             success = true;
+        } catch (IllegalArgumentException ae) {
+            log("ARP test initiation failure: " + ae);
+            success = true;
         }
 
         return success;
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
index 6aea090..100e062 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
@@ -46,18 +46,8 @@
      */
     public int groupOwnerIntent = -1;
 
-    /**
-     * Indicates whether the configuration is saved
-     * @hide
-     */
-    public enum Persist {
-        SYSTEM_DEFAULT,
-        YES,
-        NO
-    }
-
     /** @hide */
-    public Persist persist = Persist.SYSTEM_DEFAULT;
+    public int netId = WifiP2pGroup.PERSISTENT_NET_ID;
 
     public WifiP2pConfig() {
         //set defaults
@@ -110,7 +100,7 @@
         sbuf.append("\n address: ").append(deviceAddress);
         sbuf.append("\n wps: ").append(wps);
         sbuf.append("\n groupOwnerIntent: ").append(groupOwnerIntent);
-        sbuf.append("\n persist: ").append(persist.toString());
+        sbuf.append("\n persist: ").append(netId);
         return sbuf.toString();
     }
 
@@ -125,7 +115,7 @@
             deviceAddress = source.deviceAddress;
             wps = new WpsInfo(source.wps);
             groupOwnerIntent = source.groupOwnerIntent;
-            persist = source.persist;
+            netId = source.netId;
         }
     }
 
@@ -134,7 +124,7 @@
         dest.writeString(deviceAddress);
         dest.writeParcelable(wps, flags);
         dest.writeInt(groupOwnerIntent);
-        dest.writeString(persist.name());
+        dest.writeInt(netId);
     }
 
     /** Implement the Parcelable interface */
@@ -145,7 +135,7 @@
                 config.deviceAddress = in.readString();
                 config.wps = (WpsInfo) in.readParcelable(null);
                 config.groupOwnerIntent = in.readInt();
-                config.persist = Persist.valueOf(in.readString());
+                config.netId = in.readInt();
                 return config;
             }
 
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java b/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java
index afdc9be..c86ec8b 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java
@@ -231,11 +231,26 @@
         return (deviceCapability & DEVICE_CAPAB_SERVICE_DISCOVERY) != 0;
     }
 
+    /** Returns true if the device is capable of invitation {@hide}*/
+    public boolean isInvitationCapable() {
+        return (deviceCapability & DEVICE_CAPAB_INVITATION_PROCEDURE) != 0;
+    }
+
+    /** Returns true if the device reaches the limit. {@hide}*/
+    public boolean isDeviceLimit() {
+        return (deviceCapability & DEVICE_CAPAB_DEVICE_LIMIT) != 0;
+    }
+
     /** Returns true if the device is a group owner */
     public boolean isGroupOwner() {
         return (groupCapability & GROUP_CAPAB_GROUP_OWNER) != 0;
     }
 
+    /** Returns true if the group reaches the limit. {@hide}*/
+    public boolean isGroupLimit() {
+        return (groupCapability & GROUP_CAPAB_GROUP_LIMIT) != 0;
+    }
+
     @Override
     public boolean equals(Object obj) {
         if (this == obj) return true;
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
index c30cc73..bc492b3 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
@@ -33,6 +33,16 @@
  */
 public class WifiP2pGroup implements Parcelable {
 
+    /** The temporary network id.
+     * {@hide} */
+    public static final int TEMPORARY_NET_ID = -1;
+
+    /** The persistent network id.
+     * If a matching persistent profile is found, use it.
+     * Otherwise, create a new persistent profile.
+     * {@hide} */
+    public static final int PERSISTENT_NET_ID = -2;
+
     /** The network name */
     private String mNetworkName;
 
@@ -50,13 +60,17 @@
 
     private String mInterface;
 
+    /** The network id in the wpa_supplicant */
+    private int mNetId;
+
     /** P2P group started string pattern */
     private static final Pattern groupStartedPattern = Pattern.compile(
         "ssid=\"(.+)\" " +
         "freq=(\\d+) " +
         "(?:psk=)?([0-9a-fA-F]{64})?" +
         "(?:passphrase=)?(?:\"(.{8,63})\")? " +
-        "go_dev_addr=((?:[0-9a-f]{2}:){5}[0-9a-f]{2})"
+        "go_dev_addr=((?:[0-9a-f]{2}:){5}[0-9a-f]{2})" +
+        " ?(\\[PERSISTENT\\])?"
     );
 
     public WifiP2pGroup() {
@@ -67,13 +81,15 @@
      *
      *  P2P-GROUP-STARTED p2p-wlan0-0 [client|GO] ssid="DIRECT-W8" freq=2437
      *  [psk=2182b2e50e53f260d04f3c7b25ef33c965a3291b9b36b455a82d77fd82ca15bc|
-     *  passphrase="fKG4jMe3"] go_dev_addr=fa:7b:7a:42:02:13
+     *  passphrase="fKG4jMe3"] go_dev_addr=fa:7b:7a:42:02:13 [PERSISTENT]
      *
      *  P2P-GROUP-REMOVED p2p-wlan0-0 [client|GO] reason=REQUESTED
      *
      *  P2P-INVITATION-RECEIVED sa=fa:7b:7a:42:02:13 go_dev_addr=f8:7b:7a:42:02:13
      *  bssid=fa:7b:7a:42:82:13 unknown-network
      *
+     *  P2P-INVITATION-RECEIVED sa=b8:f9:34:2a:c7:9d persistent=0
+     *
      *  Note: The events formats can be looked up in the wpa_supplicant code
      *  @hide
      */
@@ -100,16 +116,38 @@
             //String psk = match.group(3);
             mPassphrase = match.group(4);
             mOwner = new WifiP2pDevice(match.group(5));
-
+            if (match.group(6) != null) {
+                mNetId = PERSISTENT_NET_ID;
+            } else {
+                mNetId = TEMPORARY_NET_ID;
+            }
         } else if (tokens[0].equals("P2P-INVITATION-RECEIVED")) {
+            String sa = null;
+            mNetId = PERSISTENT_NET_ID;
             for (String token : tokens) {
                 String[] nameValue = token.split("=");
                 if (nameValue.length != 2) continue;
 
+                if (nameValue[0].equals("sa")) {
+                    sa = nameValue[1];
+
+                    // set source address into the client list.
+                    WifiP2pDevice dev = new WifiP2pDevice();
+                    dev.deviceAddress = nameValue[1];
+                    mClients.add(dev);
+                    continue;
+                }
+
                 if (nameValue[0].equals("go_dev_addr")) {
                     mOwner = new WifiP2pDevice(nameValue[1]);
                     continue;
                 }
+
+                if (nameValue[0].equals("persistent")) {
+                    mOwner = new WifiP2pDevice(sa);
+                    mNetId = Integer.parseInt(nameValue[1]);
+                    continue;
+                }
             }
         } else {
             throw new IllegalArgumentException("Malformed supplicant event");
@@ -212,6 +250,16 @@
         return mInterface;
     }
 
+    /** @hide */
+    public int getNetworkId() {
+        return mNetId;
+    }
+
+    /** @hide */
+    public void setNetworkId(int netId) {
+        this.mNetId = netId;
+    }
+
     public String toString() {
         StringBuffer sbuf = new StringBuffer();
         sbuf.append("network: ").append(mNetworkName);
@@ -221,6 +269,7 @@
             sbuf.append("\n Client: ").append(client);
         }
         sbuf.append("\n interface: ").append(mInterface);
+        sbuf.append("\n networkId: ").append(mNetId);
         return sbuf.toString();
     }
 
@@ -238,6 +287,7 @@
             for (WifiP2pDevice d : source.getClientList()) mClients.add(d);
             mPassphrase = source.getPassphrase();
             mInterface = source.getInterface();
+            mNetId = source.getNetworkId();
         }
     }
 
@@ -252,6 +302,7 @@
         }
         dest.writeString(mPassphrase);
         dest.writeString(mInterface);
+        dest.writeInt(mNetId);
     }
 
     /** Implement the Parcelable interface */
@@ -268,6 +319,7 @@
                 }
                 group.setPassphrase(in.readString());
                 group.setInterface(in.readString());
+                group.setNetworkId(in.readInt());
                 return group;
             }
 
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pGroupList.aidl b/wifi/java/android/net/wifi/p2p/WifiP2pGroupList.aidl
new file mode 100644
index 0000000..3d8a476
--- /dev/null
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pGroupList.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2012, 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.net.wifi.p2p;
+
+parcelable WifiP2pGroupList;
\ No newline at end of file
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pGroupList.java b/wifi/java/android/net/wifi/p2p/WifiP2pGroupList.java
new file mode 100644
index 0000000..3459a5a
--- /dev/null
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pGroupList.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2012 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.net.wifi.p2p;
+
+import java.util.Collection;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.LruCache;
+
+
+/**
+ * A class representing a Wi-Fi P2p group list
+ *
+ * {@see WifiP2pManager}
+ * @hide
+ */
+public class WifiP2pGroupList implements Parcelable {
+
+    private static final int CREDENTIAL_MAX_NUM             =   32;
+
+    private LruCache<Integer, WifiP2pGroup> mGroups;
+    private GroupDeleteListener mListener;
+    private boolean isClearCalled = false;
+
+    public interface GroupDeleteListener {
+        public void onDeleteGroup(int netId);
+    }
+
+    WifiP2pGroupList() {
+        this(null);
+    }
+
+    WifiP2pGroupList(GroupDeleteListener listener) {
+        mListener = listener;
+        mGroups = new LruCache<Integer, WifiP2pGroup>(CREDENTIAL_MAX_NUM) {
+            @Override
+            protected void entryRemoved(boolean evicted, Integer netId,
+                    WifiP2pGroup oldValue, WifiP2pGroup newValue) {
+                if (mListener != null && !isClearCalled) {
+                    mListener.onDeleteGroup(oldValue.getNetworkId());
+                }
+            }
+        };
+    }
+
+    /**
+     * Return the list of p2p group.
+     *
+     * @return the list of p2p group.
+     */
+    public Collection<WifiP2pGroup> getGroupList() {
+        return mGroups.snapshot().values();
+    }
+
+    /**
+     * Add the specified group to this group list.
+     *
+     * @param group
+     */
+    void add(WifiP2pGroup group) {
+        mGroups.put(group.getNetworkId(), group);
+    }
+
+    /**
+     * Remove the group with the specified network id from this group list.
+     *
+     * @param netId
+     */
+    void remove(int netId) {
+        mGroups.remove(netId);
+    }
+
+    /**
+     * Remove the group with the specified device address from this group list.
+     *
+     * @param deviceAddress
+     */
+    void remove(String deviceAddress) {
+        remove(getNetworkId(deviceAddress));
+    }
+
+    /**
+     * Clear the group.
+     */
+    boolean clear() {
+        if (mGroups.size() == 0) return false;
+        isClearCalled = true;
+        mGroups.evictAll();
+        isClearCalled = false;
+        return true;
+    }
+
+    /**
+     * Return the network id of the group owner profile with the specified p2p device
+     * address.
+     * If more than one persistent group of the same address is present in the list,
+     * return the first one.
+     *
+     * @param deviceAddress p2p device address.
+     * @return the network id. if not found, return -1.
+     */
+    int getNetworkId(String deviceAddress) {
+        if (deviceAddress == null) return -1;
+
+        final Collection<WifiP2pGroup> groups = mGroups.snapshot().values();
+        for (WifiP2pGroup grp: groups) {
+            if (deviceAddress.equalsIgnoreCase(grp.getOwner().deviceAddress)) {
+                // update cache ordered.
+                mGroups.get(grp.getNetworkId());
+                return grp.getNetworkId();
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * Return the network id of the group with the specified p2p device address
+     * and the ssid.
+     *
+     * @param deviceAddress p2p device address.
+     * @param ssid ssid.
+     * @return the network id. if not found, return -1.
+     */
+    int getNetworkId(String deviceAddress, String ssid) {
+        if (deviceAddress == null || ssid == null) {
+            return -1;
+        }
+
+        final Collection<WifiP2pGroup> groups = mGroups.snapshot().values();
+        for (WifiP2pGroup grp: groups) {
+            if (deviceAddress.equalsIgnoreCase(grp.getOwner().deviceAddress) &&
+                    ssid.equals(grp.getNetworkName())) {
+                // update cache ordered.
+                mGroups.get(grp.getNetworkId());
+                return grp.getNetworkId();
+            }
+        }
+
+        return -1;
+    }
+
+    /**
+     * Return the group owner address of the group with the specified network id
+     *
+     * @param netId network id.
+     * @return the address. if not found, return null.
+     */
+    String getOwnerAddr(int netId) {
+        WifiP2pGroup grp = mGroups.get(netId);
+        if (grp != null) {
+            return grp.getOwner().deviceAddress;
+        }
+        return null;
+    }
+
+    /**
+     * Return true if this group list contains the specified network id.
+     * This function does NOT update LRU information.
+     * It means the internal queue is NOT reordered.
+     *
+     * @param netId network id.
+     * @return true if the specified network id is present in this group list.
+     */
+    boolean contains(int netId) {
+        final Collection<WifiP2pGroup> groups = mGroups.snapshot().values();
+        for (WifiP2pGroup grp: groups) {
+            if (netId == grp.getNetworkId()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public String toString() {
+        StringBuffer sbuf = new StringBuffer();
+
+        final Collection<WifiP2pGroup> groups = mGroups.snapshot().values();
+        for (WifiP2pGroup grp: groups) {
+            sbuf.append(grp).append("\n");
+        }
+        return sbuf.toString();
+    }
+
+    /** Implement the Parcelable interface */
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Implement the Parcelable interface */
+    public void writeToParcel(Parcel dest, int flags) {
+        final Collection<WifiP2pGroup> groups = mGroups.snapshot().values();
+        dest.writeInt(groups.size());
+        for(WifiP2pGroup group : groups) {
+            dest.writeParcelable(group, flags);
+        }
+    }
+
+    /** Implement the Parcelable interface */
+    public static final Creator<WifiP2pGroupList> CREATOR =
+        new Creator<WifiP2pGroupList>() {
+            public WifiP2pGroupList createFromParcel(Parcel in) {
+                WifiP2pGroupList grpList = new WifiP2pGroupList();
+
+                int deviceCount = in.readInt();
+                for (int i = 0; i < deviceCount; i++) {
+                    grpList.add((WifiP2pGroup)in.readParcelable(null));
+                }
+                return grpList;
+            }
+
+            public WifiP2pGroupList[] newArray(int size) {
+                return new WifiP2pGroupList[size];
+            }
+        };
+}
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index 2c25e9d..96d3a7f 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -34,10 +34,10 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.Messenger;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.WorkSource;
-import android.os.Messenger;
 import android.util.Log;
 
 import com.android.internal.util.AsyncChannel;
@@ -267,6 +267,13 @@
     public static final String EXTRA_WIFI_P2P_DEVICE = "wifiP2pDevice";
 
     /**
+     * Broadcast intent action indicating that remembered persistent groups have changed.
+     * @hide
+     */
+    public static final String WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION =
+        "android.net.wifi.p2p.PERSISTENT_GROUPS_CHANGED";
+
+    /**
      * The lookup key for a {@link #String} object.
      * Retrieve with {@link android.os.Bundle#getString(String)}.
      * @hide
@@ -436,6 +443,18 @@
     /** @hide */
     public static final int SHOW_PIN_REQUESTED                      = BASE + 58;
 
+    /** @hide */
+    public static final int DELETE_PERSISTENT_GROUP                 = BASE + 59;
+    /** @hide */
+    public static final int DELETE_PERSISTENT_GROUP_FAILED          = BASE + 60;
+    /** @hide */
+    public static final int DELETE_PERSISTENT_GROUP_SUCCEEDED       = BASE + 61;
+
+    /** @hide */
+    public static final int REQUEST_PERSISTENT_GROUP_INFO           = BASE + 62;
+    /** @hide */
+    public static final int RESPONSE_PERSISTENT_GROUP_INFO          = BASE + 63;
+
     /**
      * Create a new WifiP2pManager instance. Applications use
      * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
@@ -657,6 +676,15 @@
         public void onDetached(int reason);
     }
 
+    /** Interface for callback invocation when stored group info list is available {@hide}*/
+    public interface PersistentGroupInfoListener {
+        /**
+         * The requested stored p2p group info list is available
+         * @param groups Wi-Fi p2p group info list
+         */
+        public void onPersistentGroupInfoAvailable(WifiP2pGroupList groups);
+    }
+
     /**
      * A channel that connects the application to the Wifi p2p framework.
      * Most p2p operations require a Channel as an argument. An instance of Channel is obtained
@@ -713,6 +741,7 @@
                     case WifiP2pManager.REMOVE_SERVICE_REQUEST_FAILED:
                     case WifiP2pManager.CLEAR_SERVICE_REQUESTS_FAILED:
                     case WifiP2pManager.SET_DEVICE_NAME_FAILED:
+                    case WifiP2pManager.DELETE_PERSISTENT_GROUP_FAILED:
                         if (listener != null) {
                             ((ActionListener) listener).onFailure(message.arg1);
                         }
@@ -732,6 +761,7 @@
                     case WifiP2pManager.REMOVE_SERVICE_REQUEST_SUCCEEDED:
                     case WifiP2pManager.CLEAR_SERVICE_REQUESTS_SUCCEEDED:
                     case WifiP2pManager.SET_DEVICE_NAME_SUCCEEDED:
+                    case WifiP2pManager.DELETE_PERSISTENT_GROUP_SUCCEEDED:
                         if (listener != null) {
                             ((ActionListener) listener).onSuccess();
                         }
@@ -786,6 +816,13 @@
                             mDialogListener = null;
                         }
                         break;
+                    case WifiP2pManager.RESPONSE_PERSISTENT_GROUP_INFO:
+                        WifiP2pGroupList groups = (WifiP2pGroupList) message.obj;
+                        if (listener != null) {
+                            ((PersistentGroupInfoListener) listener).
+                                onPersistentGroupInfoAvailable(groups);
+                        }
+                        break;
                    default:
                         Log.d(TAG, "Ignored " + message);
                         break;
@@ -995,7 +1032,8 @@
      */
     public void createGroup(Channel c, ActionListener listener) {
         checkChannel(c);
-        c.mAsyncChannel.sendMessage(CREATE_GROUP, 0, c.putListener(listener));
+        c.mAsyncChannel.sendMessage(CREATE_GROUP, WifiP2pGroup.PERSISTENT_NET_ID,
+                c.putListener(listener));
     }
 
     /**
@@ -1297,6 +1335,40 @@
     }
 
     /**
+     * Delete a stored persistent group from the system settings.
+     *
+     * <p> The function call immediately returns after sending a persistent group removal request
+     * to the framework. The application is notified of a success or failure to initiate
+     * group removal through listener callbacks {@link ActionListener#onSuccess} or
+     * {@link ActionListener#onFailure}.
+     *
+     * <p>The persistent p2p group list stored in the system can be obtained by
+     * {@link #requestPersistentGroupInfo(Channel, PersistentGroupInfoListener)} and
+     *  a network id can be obtained by {@link WifiP2pGroup#getNetworkId()}.
+     *
+     * @param c is the channel created at {@link #initialize}
+     * @param netId he network id of the p2p group.
+     * @param listener for callbacks on success or failure. Can be null.
+     * @hide
+     */
+    public void deletePersistentGroup(Channel c, int netId, ActionListener listener) {
+        checkChannel(c);
+        c.mAsyncChannel.sendMessage(DELETE_PERSISTENT_GROUP, netId, c.putListener(listener));
+    }
+
+    /**
+     * Request a list of all the persistent p2p groups stored in system.
+     *
+     * @param c is the channel created at {@link #initialize}
+     * @param listener for callback when persistent group info list is available. Can be null.
+     * @hide
+     */
+    public void requestPersistentGroupInfo(Channel c, PersistentGroupInfoListener listener) {
+        checkChannel(c);
+        c.mAsyncChannel.sendMessage(REQUEST_PERSISTENT_GROUP_INFO, 0, c.putListener(listener));
+    }
+
+    /**
      * Get a reference to WifiP2pService handler. This is used to establish
      * an AsyncChannel communication with WifiService
      *
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pService.java b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
index 5759074..0be2b27 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pService.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
@@ -45,6 +45,7 @@
 import android.net.wifi.WifiNative;
 import android.net.wifi.WifiStateMachine;
 import android.net.wifi.WpsInfo;
+import android.net.wifi.p2p.WifiP2pGroupList.GroupDeleteListener;
 import android.net.wifi.p2p.nsd.WifiP2pServiceInfo;
 import android.net.wifi.p2p.nsd.WifiP2pServiceRequest;
 import android.net.wifi.p2p.nsd.WifiP2pServiceResponse;
@@ -118,6 +119,13 @@
     private static final Boolean JOIN_GROUP = true;
     private static final Boolean FORM_GROUP = false;
 
+    private static final Boolean TRY_REINVOCATION = true;;
+    private static final Boolean NO_REINVOCATION = false;
+
+    private static final int CONNECT_FAILURE = -1;
+    private static final int CONNECT_SUCCESS = 0;
+    private static final int NEEDS_PROVISION_REQ = 1;
+
     /* Two minutes comes from the wpa_supplicant setting */
     private static final int GROUP_CREATING_WAIT_TIME_MS = 120 * 1000;
     private static int mGroupCreatingTimeoutIndex = 0;
@@ -191,6 +199,84 @@
     private static final String[] DHCP_RANGE = {"192.168.49.2", "192.168.49.254"};
     private static final String SERVER_ADDRESS = "192.168.49.1";
 
+    /**
+     * Error code definition.
+     * see the Table.8 in the WiFi Direct specification for the detail.
+     */
+    public static enum P2pStatus {
+        /* Success. */
+        SUCCESS,
+
+        /* The target device is currently unavailable. */
+        INFORMATION_IS_CURRENTLY_UNAVAILABLE,
+
+        /* Protocol error. */
+        INCOMPATIBLE_PARAMETERS,
+
+        /* The target device reached the limit of the number of the connectable device.
+         * For example, device limit or group limit is set. */
+        LIMIT_REACHED,
+
+        /* Protocol error. */
+        INVALID_PARAMETER,
+
+        /* Unable to accommodate request. */
+        UNABLE_TO_ACCOMMODATE_REQUEST,
+
+        /* Previous protocol error, or disruptive behavior. */
+        PREVIOUS_PROTOCOL_ERROR,
+
+        /* There is no common channels the both devices can use. */
+        NO_COMMON_CHANNE,
+
+        /* Unknown p2p group. For example, Device A tries to invoke the previous persistent group,
+         *  but device B has removed the specified credential already. */
+        UNKNOWN_P2P_GROUP,
+
+        /* Both p2p devices indicated an intent of 15 in group owner negotiation. */
+        BOTH_GO_INTENT_15,
+
+        /* Incompatible provisioning method. */
+        INCOMPATIBLE_PROVISIONING_METHOD,
+
+        /* Rejected by user */
+        REJECTED_BY_USER,
+
+        /* Unknown error */
+        UNKNOWN;
+
+        public static P2pStatus valueOf(int error) {
+            switch(error) {
+            case 0 :
+                return SUCCESS;
+            case 1:
+                return INFORMATION_IS_CURRENTLY_UNAVAILABLE;
+            case 2:
+                return INCOMPATIBLE_PARAMETERS;
+            case 3:
+                return LIMIT_REACHED;
+            case 4:
+                return INVALID_PARAMETER;
+            case 5:
+                return UNABLE_TO_ACCOMMODATE_REQUEST;
+            case 6:
+                return PREVIOUS_PROTOCOL_ERROR;
+            case 7:
+                return NO_COMMON_CHANNE;
+            case 8:
+                return UNKNOWN_P2P_GROUP;
+            case 9:
+                return BOTH_GO_INTENT_15;
+            case 10:
+                return INCOMPATIBLE_PROVISIONING_METHOD;
+            case 11:
+                return REJECTED_BY_USER;
+            default:
+                return UNKNOWN;
+            }
+        }
+    }
+
     public WifiP2pService(Context context) {
         mContext = context;
 
@@ -273,6 +359,16 @@
         private WifiMonitor mWifiMonitor = new WifiMonitor(this, mWifiNative);
 
         private WifiP2pDeviceList mPeers = new WifiP2pDeviceList();
+        private WifiP2pGroupList mGroups = new WifiP2pGroupList(
+                new GroupDeleteListener() {
+            @Override
+            public void onDeleteGroup(int netId) {
+                if (DBG) logd("called onDeleteGroup() netId=" + netId);
+                mWifiNative.removeNetwork(netId);
+                mWifiNative.saveConfig();
+                sendP2pPersistentGroupsChangedBroadcast();
+            }
+        });
         private WifiP2pInfo mWifiP2pInfo = new WifiP2pInfo();
         private WifiP2pGroup mGroup;
 
@@ -395,6 +491,10 @@
                     replyToMessage(message, WifiP2pManager.SET_DEVICE_NAME_FAILED,
                             WifiP2pManager.BUSY);
                     break;
+                case WifiP2pManager.DELETE_PERSISTENT_GROUP:
+                    replyToMessage(message, WifiP2pManager.DELETE_PERSISTENT_GROUP,
+                            WifiP2pManager.BUSY);
+                    break;
                 case WifiP2pManager.REQUEST_PEERS:
                     replyToMessage(message, WifiP2pManager.RESPONSE_PEERS, mPeers);
                     break;
@@ -404,6 +504,10 @@
                 case WifiP2pManager.REQUEST_GROUP_INFO:
                     replyToMessage(message, WifiP2pManager.RESPONSE_GROUP_INFO, mGroup);
                     break;
+                case WifiP2pManager.REQUEST_PERSISTENT_GROUP_INFO:
+                    replyToMessage(message, WifiP2pManager.RESPONSE_PERSISTENT_GROUP_INFO,
+                            mGroups);
+                    break;
                 case WifiP2pManager.SET_DIALOG_LISTENER:
                     String appPkgName = (String)message.getData().getString(
                             WifiP2pManager.APP_PKG_BUNDLE_KEY);
@@ -520,6 +624,10 @@
                     replyToMessage(message, WifiP2pManager.SET_DEVICE_NAME_FAILED,
                             WifiP2pManager.P2P_UNSUPPORTED);
                     break;
+                case WifiP2pManager.DELETE_PERSISTENT_GROUP:
+                    replyToMessage(message, WifiP2pManager.DELETE_PERSISTENT_GROUP,
+                            WifiP2pManager.P2P_UNSUPPORTED);
+                    break;
                default:
                     return NOT_HANDLED;
             }
@@ -626,6 +734,8 @@
                     break;
                 case WifiStateMachine.CMD_DISABLE_P2P:
                     if (mPeers.clear()) sendP2pPeersChangedBroadcast();
+                    if (mGroups.clear()) sendP2pPersistentGroupsChangedBroadcast();
+
                     mWifiNative.closeSupplicantConnection();
                     transitionTo(mP2pDisablingState);
                     break;
@@ -734,6 +844,11 @@
                         sendServiceResponse(resp);
                     }
                     break;
+               case WifiP2pManager.DELETE_PERSISTENT_GROUP:
+                   if (DBG) logd(getName() + " delete persistent group");
+                   mGroups.remove(message.arg1);
+                   replyToMessage(message, WifiP2pManager.DELETE_PERSISTENT_GROUP_SUCCEEDED);
+                   break;
                 default:
                     return NOT_HANDLED;
             }
@@ -768,47 +883,35 @@
                     /* Update group capability before connect */
                     int gc = mWifiNative.getGroupCapability(config.deviceAddress);
                     mPeers.updateGroupCapability(config.deviceAddress, gc);
-
-                    if (mSavedPeerConfig != null && config.deviceAddress.equals(
-                                    mSavedPeerConfig.deviceAddress)) {
-                        mSavedPeerConfig = config;
-
-                        //Stop discovery before issuing connect
-                        mWifiNative.p2pStopFind();
-                        if (mPeers.isGroupOwner(mSavedPeerConfig.deviceAddress)) {
-                            p2pConnectWithPinDisplay(mSavedPeerConfig, JOIN_GROUP);
-                        } else {
-                            p2pConnectWithPinDisplay(mSavedPeerConfig, FORM_GROUP);
-                        }
-                        transitionTo(mGroupNegotiationState);
-                    } else {
-                        mSavedPeerConfig = config;
-                        int netId = configuredNetworkId(mSavedPeerConfig.deviceAddress);
-                        if (netId >= 0) {
-                            //TODO: if failure, remove config and do a regular p2pConnect()
-                            mWifiNative.p2pReinvoke(netId, mSavedPeerConfig.deviceAddress);
-                        } else {
-                            //Stop discovery before issuing connect
-                            mWifiNative.p2pStopFind();
-                            //If peer is a GO, we do not need to send provisional discovery,
-                            //the supplicant takes care of it.
-                            if (mPeers.isGroupOwner(mSavedPeerConfig.deviceAddress)) {
-                                if (DBG) logd("Sending join to GO");
-                                p2pConnectWithPinDisplay(mSavedPeerConfig, JOIN_GROUP);
-                                transitionTo(mGroupNegotiationState);
-                            } else {
-                                if (DBG) logd("Sending prov disc");
-                                transitionTo(mProvisionDiscoveryState);
-                            }
-                        }
+                    int connectRet = connect(config, TRY_REINVOCATION);
+                    if (connectRet == CONNECT_FAILURE) {
+                        replyToMessage(message, WifiP2pManager.CONNECT_FAILED);
+                        break;
                     }
                     mPeers.updateStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.INVITED);
                     sendP2pPeersChangedBroadcast();
                     replyToMessage(message, WifiP2pManager.CONNECT_SUCCEEDED);
+                    if (connectRet == NEEDS_PROVISION_REQ) {
+                        if (DBG) logd("Sending prov disc");
+                        transitionTo(mProvisionDiscoveryState);
+                        break;
+                    }
+                    transitionTo(mGroupNegotiationState);
+                    break;
+                case WifiP2pManager.STOP_DISCOVERY:
+                    if (mWifiNative.p2pStopFind()) {
+                        // When discovery stops in inactive state, flush to clear
+                        // state peer data
+                        mWifiNative.p2pFlush();
+                        mServiceDiscReqId = null;
+                        replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_SUCCEEDED);
+                    } else {
+                        replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_FAILED,
+                                WifiP2pManager.ERROR);
+                    }
                     break;
                 case WifiMonitor.P2P_GO_NEGOTIATION_REQUEST_EVENT:
                     mSavedPeerConfig = (WifiP2pConfig) message.obj;
-
                     mAutonomousGroup = false;
                     mJoinExistingGroup = false;
                     if (!sendConnectNoticeToApp(mPeers.get(mSavedPeerConfig.deviceAddress),
@@ -848,13 +951,6 @@
                         transitionTo(mUserAuthorizingInvitationState);
                     }
                     break;
-                case WifiMonitor.P2P_FIND_STOPPED_EVENT:
-                    // When discovery stops in inactive state, flush to clear
-                    // state peer data
-                    mWifiNative.p2pFlush();
-                    mServiceDiscReqId = null;
-                    sendP2pDiscoveryChangedBroadcast(false);
-                    break;
                 case WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT:
                 case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:
                 case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT:
@@ -865,16 +961,44 @@
                    break;
                 case WifiP2pManager.CREATE_GROUP:
                    mAutonomousGroup = true;
-                   if (mWifiNative.p2pGroupAdd()) {
-                        replyToMessage(message, WifiP2pManager.CREATE_GROUP_SUCCEEDED);
+                   int netId = message.arg1;
+                   boolean ret = false;
+                   if (netId == WifiP2pGroup.PERSISTENT_NET_ID) {
+                       // check if the go persistent group is present.
+                       netId = mGroups.getNetworkId(mThisDevice.deviceAddress);
+                       if (netId != -1) {
+                           ret = mWifiNative.p2pGroupAdd(netId);
+                       } else {
+                           ret = mWifiNative.p2pGroupAdd(true);
+                       }
+                   } else {
+                       ret = mWifiNative.p2pGroupAdd(false);
+                   }
+
+                   if (ret) {
+                       replyToMessage(message, WifiP2pManager.CREATE_GROUP_SUCCEEDED);
+                       transitionTo(mGroupNegotiationState);
+                   } else {
+                       replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED,
+                               WifiP2pManager.ERROR);
+                       // remain at this state.
+                   }
+                   break;
+                case WifiMonitor.P2P_GROUP_STARTED_EVENT:
+                    mGroup = (WifiP2pGroup) message.obj;
+                    if (DBG) logd(getName() + " group started");
+
+                    if (mGroup.getNetworkId() == WifiP2pGroup.PERSISTENT_NET_ID) {
+                        // This is an invocation case.
+                        mAutonomousGroup = false;
+                        deferMessage(message);
+                        transitionTo(mGroupNegotiationState);
                     } else {
-                        replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED,
-                                WifiP2pManager.ERROR);
+                        return NOT_HANDLED;
                     }
-                    transitionTo(mGroupNegotiationState);
                     break;
                default:
-                    return NOT_HANDLED;
+                   return NOT_HANDLED;
             }
             return HANDLED;
         }
@@ -941,10 +1065,7 @@
         @Override
         public void enter() {
             if (DBG) logd(getName());
-            if (!sendConnectNoticeToApp(mPeers.get(mSavedPeerConfig.deviceAddress),
-                    mSavedPeerConfig)) {
-                notifyInvitationReceived();
-            }
+            notifyInvitationReceived();
         }
 
         @Override
@@ -953,11 +1074,10 @@
             boolean ret = HANDLED;
             switch (message.what) {
                 case PEER_CONNECTION_USER_ACCEPT:
-                    //TODO: handle persistence
-                    if (mJoinExistingGroup) {
-                        p2pConnectWithPinDisplay(mSavedPeerConfig, JOIN_GROUP);
-                    } else {
-                        p2pConnectWithPinDisplay(mSavedPeerConfig, FORM_GROUP);
+                    if (connect(mSavedPeerConfig, TRY_REINVOCATION) == CONNECT_FAILURE) {
+                        handleGroupCreationFailure();
+                        transitionTo(mInactiveState);
+                        break;
                     }
                     mPeers.updateStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.INVITED);
                     sendP2pPeersChangedBroadcast();
@@ -1000,7 +1120,7 @@
 
                     if (mSavedPeerConfig.wps.setup == WpsInfo.PBC) {
                         if (DBG) logd("Found a match " + mSavedPeerConfig);
-                        mWifiNative.p2pConnect(mSavedPeerConfig, FORM_GROUP);
+                        p2pConnectWithPinDisplay(mSavedPeerConfig);
                         transitionTo(mGroupNegotiationState);
                     }
                     break;
@@ -1013,11 +1133,14 @@
                         if (DBG) logd("Found a match " + mSavedPeerConfig);
                         /* we already have the pin */
                         if (!TextUtils.isEmpty(mSavedPeerConfig.wps.pin)) {
-                            mWifiNative.p2pConnect(mSavedPeerConfig, FORM_GROUP);
+                            p2pConnectWithPinDisplay(mSavedPeerConfig);
                             transitionTo(mGroupNegotiationState);
                         } else {
                             mJoinExistingGroup = false;
-                            transitionTo(mUserAuthorizingInvitationState);
+                            if (!sendConnectNoticeToApp(mPeers.get(mSavedPeerConfig.deviceAddress),
+                                    mSavedPeerConfig)) {
+                                transitionTo(mUserAuthorizingInvitationState);
+                            }
                         }
                     }
                     break;
@@ -1029,7 +1152,7 @@
                     if (mSavedPeerConfig.wps.setup == WpsInfo.DISPLAY) {
                         if (DBG) logd("Found a match " + mSavedPeerConfig);
                         mSavedPeerConfig.wps.pin = provDisc.pin;
-                        mWifiNative.p2pConnect(mSavedPeerConfig, FORM_GROUP);
+                        p2pConnectWithPinDisplay(mSavedPeerConfig);
                         if (!sendShowPinReqToFrontApp(provDisc.pin)) {
                             notifyInvitationSent(provDisc.pin, device.deviceAddress);
                         }
@@ -1062,6 +1185,17 @@
                 case WifiMonitor.P2P_GROUP_STARTED_EVENT:
                     mGroup = (WifiP2pGroup) message.obj;
                     if (DBG) logd(getName() + " group started");
+
+                    if (mGroup.getNetworkId() == WifiP2pGroup.PERSISTENT_NET_ID) {
+                        /*
+                         * update cache information and set network id to mGroup.
+                         */
+                        updatePersistentNetworks();
+                        String devAddr = mGroup.getOwner().deviceAddress;
+                        mGroup.setNetworkId(mGroups.getNetworkId(devAddr,
+                                mGroup.getNetworkName()));
+                    }
+
                     if (mGroup.isGroupOwner()) {
                         startDhcpServer(mGroup.getInterface());
                     } else {
@@ -1090,6 +1224,29 @@
                 // failure causes supplicant issues. Ignore right now.
                 case WifiMonitor.P2P_GROUP_FORMATION_FAILURE_EVENT:
                     break;
+                case WifiMonitor.P2P_INVITATION_RESULT_EVENT:
+                    P2pStatus status = (P2pStatus)message.obj;
+                    if (status == P2pStatus.SUCCESS) {
+                        // invocation was succeeded.
+                        // wait P2P_GROUP_STARTED_EVENT.
+                        break;
+                    } else if (status == P2pStatus.UNKNOWN_P2P_GROUP) {
+                        // target device has already removed the credential.
+                        // So, remove this credential accordingly.
+                        int netId = mSavedPeerConfig.netId;
+                        if (netId >= 0) {
+                            if (DBG) logd("Remove unknown client from the list");
+                            removeClientFromList(netId, mSavedPeerConfig.deviceAddress, true);
+                        }
+                    }
+
+                    // invocation is failed or deferred. Try another way to connect.
+                    mSavedPeerConfig.netId = WifiP2pGroup.PERSISTENT_NET_ID;
+                    if (connect(mSavedPeerConfig, NO_REINVOCATION) == CONNECT_FAILURE) {
+                        handleGroupCreationFailure();
+                        transitionTo(mInactiveState);
+                    }
+                    break;
                 default:
                     return NOT_HANDLED;
             }
@@ -1152,7 +1309,7 @@
                             }
                         }
                         sendP2pPeersChangedBroadcast();
-                        if (DBG) loge(getName() + " ap sta disconnected");
+                        if (DBG) logd(getName() + " ap sta disconnected");
                     } else {
                         loge("Disconnect on unknown device: " + device);
                     }
@@ -1172,7 +1329,7 @@
                     }
                     break;
                 case WifiP2pManager.REMOVE_GROUP:
-                    if (DBG) loge(getName() + " remove group");
+                    if (DBG) logd(getName() + " remove group");
                     if (mWifiNative.p2pGroupRemove(mGroup.getInterface())) {
                         replyToMessage(message, WifiP2pManager.REMOVE_GROUP_SUCCEEDED);
                     } else {
@@ -1181,7 +1338,7 @@
                     }
                     break;
                 case WifiMonitor.P2P_GROUP_REMOVED_EVENT:
-                    if (DBG) loge(getName() + " group removed");
+                    if (DBG) logd(getName() + " group removed");
                     Collection <WifiP2pDevice> devices = mGroup.getClientList();
                     boolean changed = false;
                     for (WifiP2pDevice d : mPeers.getDeviceList()) {
@@ -1252,6 +1409,7 @@
                         replyToMessage(message, WifiP2pManager.CONNECT_SUCCEEDED);
                     } else {
                         logd("Inviting device : " + config.deviceAddress);
+                        mSavedPeerConfig = config;
                         if (mWifiNative.p2pInvite(mGroup, config.deviceAddress)) {
                             mPeers.updateStatus(config.deviceAddress, WifiP2pDevice.INVITED);
                             sendP2pPeersChangedBroadcast();
@@ -1263,6 +1421,29 @@
                     }
                     // TODO: figure out updating the status to declined when invitation is rejected
                     break;
+                case WifiMonitor.P2P_INVITATION_RESULT_EVENT:
+                    P2pStatus status = (P2pStatus)message.obj;
+                    logd("===> INVITATION RESULT EVENT : " + status);
+                    if (status == P2pStatus.SUCCESS) {
+                        // invocation was succeeded.
+                        break;
+                    } else if (status == P2pStatus.UNKNOWN_P2P_GROUP) {
+                        // target device has already removed the credential.
+                        // So, remove this credential accordingly.
+                        int netId = mGroup.getNetworkId();
+                        if (netId >= 0) {
+                            if (DBG) logd("Remove unknown client from the list");
+                            if (!removeClientFromList(netId,
+                                    mSavedPeerConfig.deviceAddress, false)) {
+                                // not found the client on the list
+                                Slog.e(TAG, "Already removed the client, ignore");
+                                break;
+                            }
+                            // try invitation.
+                            sendMessage(WifiP2pManager.CONNECT, mSavedPeerConfig);
+                        }
+                    }
+                    break;
                 case WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT:
                 case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:
                 case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT:
@@ -1304,7 +1485,6 @@
         @Override
         public void enter() {
             if (DBG) logd(getName());
-
             notifyInvitationReceived();
         }
 
@@ -1394,6 +1574,13 @@
         mContext.sendStickyBroadcast(intent);
     }
 
+    private void sendP2pPersistentGroupsChangedBroadcast() {
+        if (DBG) logd("sending p2p persistent groups changed broadcast");
+        Intent intent = new Intent(WifiP2pManager.WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION);
+        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+        mContext.sendStickyBroadcast(intent);   
+    }
+
     private void startDhcpServer(String intf) {
         InterfaceConfiguration ifcg = null;
         try {
@@ -1520,11 +1707,246 @@
         dialog.show();
     }
 
-    //TODO: implement when wpa_supplicant is fixed
-    private int configuredNetworkId(String deviceAddress) {
+    /**
+     * Synchronize the persistent group list between
+     * wpa_supplicant and mGroups.
+     */
+    private void updatePersistentNetworks() {
+        String listStr = mWifiNative.listNetworks();
+
+        boolean isSaveRequired = false;
+        String[] lines = listStr.split("\n");
+        // Skip the first line, which is a header
+        for (int i = 1; i < lines.length; i++) {
+            String[] result = lines[i].split("\t");
+            if (result == null || result.length < 4) {
+                continue;
+            }
+            // network-id | ssid | bssid | flags
+            int netId = -1;
+            String ssid = result[1];
+            String bssid = result[2];
+            String flags = result[3];
+            try {
+                netId = Integer.parseInt(result[0]);
+            } catch(NumberFormatException e) {
+                e.printStackTrace();
+                continue;
+            }
+
+            if (flags.indexOf("[CURRENT]") != -1) {
+                continue;
+            }
+            if (flags.indexOf("[P2P-PERSISTENT]") == -1) {
+                /*
+                 * The unused profile is sometimes remained when the p2p group formation is failed.
+                 * So, we clean up the p2p group here.
+                 */
+                if (DBG) logd("clean up the unused persistent group. netId=" + netId);
+                mWifiNative.removeNetwork(netId);
+                isSaveRequired = true;
+                continue;
+            }
+
+            if (mGroups.contains(netId)) {
+                continue;
+            }
+
+            WifiP2pGroup group = new WifiP2pGroup();
+            group.setNetworkId(netId);
+            group.setNetworkName(ssid);
+            String mode = mWifiNative.getNetworkVariable(netId, "mode");
+            if (mode != null && mode.equals("3")) {
+                group.setIsGroupOwner(true);
+            }
+            if (bssid.equalsIgnoreCase(mThisDevice.deviceAddress)) {
+                group.setOwner(mThisDevice);
+            } else {
+                WifiP2pDevice device = new WifiP2pDevice();
+                device.deviceAddress = bssid;
+                group.setOwner(device);
+            }
+            mGroups.add(group);
+            isSaveRequired = true;
+        }
+
+        if (isSaveRequired) {
+            sendP2pPersistentGroupsChangedBroadcast();
+            mWifiNative.saveConfig();
+        }
+    }
+
+    /**
+     * Try to connect to the target device.
+     *
+     * Use the persistent credential if it has been stored.
+     *
+     * @param config
+     * @param tryInvocation if true, try to invoke. Otherwise, never try to invoke.
+     * @return
+     */
+    private int connect(WifiP2pConfig config, boolean tryInvocation) {
+
+        if (config == null) {
+            loge("invalid argument.");
+            return CONNECT_FAILURE;
+        }
+
+        boolean isResp = (mSavedPeerConfig != null &&
+                config.deviceAddress.equals(mSavedPeerConfig.deviceAddress));
+        mSavedPeerConfig = config;
+
+        WifiP2pDevice dev = mPeers.get(config.deviceAddress);
+        if (dev == null) {
+            loge("target device is not found.");
+            return CONNECT_FAILURE;
+        }
+
+        boolean join = dev.isGroupOwner();
+        String ssid = mWifiNative.p2pGetSsid(dev.deviceAddress);
+        if (DBG) logd("target ssid is " + ssid + " join:" + join);
+
+        if (join && dev.isGroupLimit()) {
+            if (DBG) logd("target device reaches group limit.");
+
+            // if the target group has reached the limit,
+            // try group formation.
+            join = false;
+        } else if (join) {
+            int netId = mGroups.getNetworkId(dev.deviceAddress, ssid);
+            if (netId >= 0) {
+                // Skip WPS and start 4way handshake immediately.
+                if (!mWifiNative.p2pGroupAdd(netId)) {
+                    return CONNECT_FAILURE;
+                }
+                return CONNECT_SUCCESS;
+            }
+        }
+
+        if (!join && dev.isDeviceLimit()) {
+            loge("target device reaches the device limit.");
+            return CONNECT_FAILURE;
+        }
+
+        if (!join && tryInvocation && dev.isInvitationCapable()) {
+            int netId = WifiP2pGroup.PERSISTENT_NET_ID;
+            if (config.netId >= 0) {
+                if (config.deviceAddress.equals(mGroups.getOwnerAddr(config.netId))) {
+                    netId = config.netId;
+                }
+            } else {
+                netId = mGroups.getNetworkId(dev.deviceAddress);
+            }
+            if (netId < 0) {
+                netId = getNetworkIdFromClientList(dev.deviceAddress);
+            }
+            if (DBG) logd("netId related with " + dev.deviceAddress + " = " + netId);
+            if (netId >= 0) {
+
+                // Invoke the persistent group.
+                if (!mWifiNative.p2pReinvoke(netId, dev.deviceAddress)) {
+                    loge("p2pReinvoke() failed");
+                    return CONNECT_FAILURE;
+                }
+                // Save network id. It'll be used when an invitation result event is received.
+                mSavedPeerConfig.netId = netId;
+                return CONNECT_SUCCESS;
+            }
+        }
+
+        //Stop discovery before issuing connect
+        mWifiNative.p2pStopFind();
+
+        if (!isResp) {
+            return NEEDS_PROVISION_REQ;
+        }
+
+        p2pConnectWithPinDisplay(config);
+        return CONNECT_SUCCESS;
+    }
+
+    /**
+     * Return the network id of the group owner profile which has the p2p client with
+     * the specified device address in it's client list.
+     * If more than one persistent group of the same address is present in its client
+     * lists, return the first one.
+     *
+     * @param deviceAddress p2p device address.
+     * @return the network id. if not found, return -1.
+     */
+    private int getNetworkIdFromClientList(String deviceAddress) {
+        if (deviceAddress == null) return -1;
+
+        Collection<WifiP2pGroup> groups = mGroups.getGroupList();
+        for (WifiP2pGroup group : groups) {
+            int netId = group.getNetworkId();
+            String[] p2pClientList = getClientList(netId);
+            if (p2pClientList == null) continue;
+            for (String client : p2pClientList) {
+                if (deviceAddress.equalsIgnoreCase(client)) {
+                    return netId;
+                }
+            }
+        }
         return -1;
     }
 
+    /**
+     * Return p2p client list associated with the specified network id.
+     * @param netId network id.
+     * @return p2p client list. if not found, return null.
+     */
+    private String[] getClientList(int netId) {
+        String p2pClients = mWifiNative.getNetworkVariable(netId, "p2p_client_list");
+        if (p2pClients == null) {
+            return null;
+        }
+        return p2pClients.split(" ");
+    }
+
+    /**
+     * Remove the specified p2p client from the specified profile.
+     * @param netId network id of the profile.
+     * @param addr p2p client address to be removed.
+     * @param isRemovable if true, remove the specified profile if its client list becomes empty.
+     * @return whether removing the specified p2p client is successful or not.
+     */
+    private boolean removeClientFromList(int netId, String addr, boolean isRemovable) {
+        StringBuilder modifiedClientList =  new StringBuilder();
+        String[] currentClientList = getClientList(netId);
+        boolean isClientRemoved = false;
+        if (currentClientList != null) {
+            for (String client : currentClientList) {
+                if (!client.equalsIgnoreCase(addr)) {
+                    modifiedClientList.append(" ");
+                    modifiedClientList.append(client);
+                } else {
+                    isClientRemoved = true;
+                }
+            }
+        }
+        if (modifiedClientList.length() == 0 && isRemovable) {
+            // the client list is empty. so remove it.
+            if (DBG) logd("Remove unknown network");
+            mGroups.remove(netId);
+            return true;
+        }
+
+        if (!isClientRemoved) {
+            // specified p2p client is not found. already removed.
+            return false;
+        }
+
+        if (DBG) logd("Modified client list: " + modifiedClientList);
+        if (modifiedClientList.length() == 0) {
+            modifiedClientList.append("\"\"");
+        }
+        mWifiNative.setNetworkVariable(netId,
+                "p2p_client_list", modifiedClientList.toString());
+        mWifiNative.saveConfig();
+        return true;
+    }
+
     private void setWifiP2pInfoOnGroupFormation(String serverAddress) {
         mWifiP2pInfo.groupFormed = true;
         mWifiP2pInfo.isGroupOwner = mGroup.isGroupOwner();
@@ -1546,8 +1968,14 @@
         return deviceAddress;
     }
 
-    private void p2pConnectWithPinDisplay(WifiP2pConfig config, boolean join) {
-        String pin = mWifiNative.p2pConnect(config, join);
+    private void p2pConnectWithPinDisplay(WifiP2pConfig config) {
+        WifiP2pDevice dev = mPeers.get(config.deviceAddress);
+        if (dev == null) {
+            loge("target device is not found " + config.deviceAddress);
+            return;
+        }
+
+        String pin = mWifiNative.p2pConnect(config, dev.isGroupOwner());
         try {
             Integer.parseInt(pin);
             if (!sendShowPinReqToFrontApp(pin)) {
@@ -1610,6 +2038,8 @@
         mWifiNative.p2pServiceFlush();
         mServiceTransactionId = 0;
         mServiceDiscReqId = null;
+
+        updatePersistentNetworks();
     }
 
     private void updateThisDevice(int status) {
@@ -1738,7 +2168,7 @@
         //Application does not have transaction id information
         //go through stored requests to remove
         boolean removed = false;
-        for (int i=0; i < clientInfo.mReqList.size(); i++) {
+        for (int i=0; i<clientInfo.mReqList.size(); i++) {
             if (req.equals(clientInfo.mReqList.valueAt(i))) {
                 removed = true;
                 clientInfo.mReqList.removeAt(i);
@@ -2077,5 +2507,4 @@
             mServList = new ArrayList<WifiP2pServiceInfo>();
         }
     }
-
 }