Merge "Refactor TextUtils.join() and add documentation"
diff --git a/Android.mk b/Android.mk
index 8fd8a2e..69c8c2c 100644
--- a/Android.mk
+++ b/Android.mk
@@ -246,6 +246,7 @@
 	core/java/android/nfc/INfcCardEmulation.aidl \
 	core/java/android/nfc/INfcFCardEmulation.aidl \
 	core/java/android/nfc/INfcUnlockHandler.aidl \
+	core/java/android/nfc/INfcDta.aidl \
 	core/java/android/nfc/ITagRemovedCallback.aidl \
 	core/java/android/os/IBatteryPropertiesListener.aidl \
 	core/java/android/os/IBatteryPropertiesRegistrar.aidl \
diff --git a/api/current.txt b/api/current.txt
index e149797..1535435 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -27070,7 +27070,8 @@
     method public abstract void onSuccess();
   }
 
-  public static class WifiP2pManager.Channel {
+  public static class WifiP2pManager.Channel implements java.lang.AutoCloseable {
+    method public void close();
   }
 
   public static abstract interface WifiP2pManager.ChannelListener {
diff --git a/api/system-current.txt b/api/system-current.txt
index ff3069a..cf2c3b8 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -29868,7 +29868,8 @@
     method public abstract void onSuccess();
   }
 
-  public static class WifiP2pManager.Channel {
+  public static class WifiP2pManager.Channel implements java.lang.AutoCloseable {
+    method public void close();
   }
 
   public static abstract interface WifiP2pManager.ChannelListener {
diff --git a/api/test-current.txt b/api/test-current.txt
index 08df809..98fa305 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -27181,7 +27181,8 @@
     method public abstract void onSuccess();
   }
 
-  public static class WifiP2pManager.Channel {
+  public static class WifiP2pManager.Channel implements java.lang.AutoCloseable {
+    method public void close();
   }
 
   public static abstract interface WifiP2pManager.ChannelListener {
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index f991efe..6801618 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -29,6 +29,7 @@
 import android.nfc.INfcFCardEmulation;
 import android.nfc.INfcUnlockHandler;
 import android.nfc.ITagRemovedCallback;
+import android.nfc.INfcDta;
 import android.os.Bundle;
 
 /**
@@ -40,7 +41,7 @@
     INfcCardEmulation getNfcCardEmulationInterface();
     INfcFCardEmulation getNfcFCardEmulationInterface();
     INfcAdapterExtras getNfcAdapterExtrasInterface(in String pkg);
-
+    INfcDta getNfcDtaInterface(in String pkg);
     int getState();
     boolean disable(boolean saveState);
     boolean enable();
diff --git a/core/java/android/nfc/INfcDta.aidl b/core/java/android/nfc/INfcDta.aidl
new file mode 100644
index 0000000..4cc5927
--- /dev/null
+++ b/core/java/android/nfc/INfcDta.aidl
@@ -0,0 +1,34 @@
+ /*
+  * Copyright (C) 2017 NXP Semiconductors
+  *
+  * 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.nfc;
+
+import android.os.Bundle;
+
+/**
+ * {@hide}
+ */
+interface INfcDta {
+
+    void enableDta();
+    void disableDta();
+    boolean enableServer(String serviceName, int serviceSap, int miu,
+            int rwSize,int testCaseId);
+    void disableServer();
+    boolean enableClient(String serviceName, int miu, int rwSize,
+            int testCaseId);
+    void disableClient();
+    boolean registerMessageService(String msgServiceName);
+}
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index 48869c7..debef63 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -16,8 +16,6 @@
 
 package android.nfc;
 
-import java.util.HashMap;
-
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
@@ -43,6 +41,7 @@
 import android.util.Log;
 
 import java.io.IOException;
+import java.util.HashMap;
 
 /**
  * Represents the local NFC adapter.
@@ -627,6 +626,23 @@
     }
 
     /**
+     * Returns the binder interface to the NFC-DTA test interface.
+     * @hide
+     */
+    public INfcDta getNfcDtaInterface() {
+        if (mContext == null) {
+            throw new UnsupportedOperationException("You need a context on NfcAdapter to use the "
+                    + " NFC extras APIs");
+        }
+        try {
+            return sService.getNfcDtaInterface(mContext.getPackageName());
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+            return null;
+        }
+    }
+
+    /**
      * NFC service dead - attempt best effort recovery
      * @hide
      */
diff --git a/core/java/android/nfc/dta/NfcDta.java b/core/java/android/nfc/dta/NfcDta.java
new file mode 100644
index 0000000..8801662
--- /dev/null
+++ b/core/java/android/nfc/dta/NfcDta.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2017 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.nfc.dta;
+
+import android.content.Context;
+import android.nfc.INfcDta;
+import android.nfc.NfcAdapter;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.HashMap;
+
+/**
+ * This class provides the primary API for DTA operations.
+ * @hide
+ */
+public final class NfcDta {
+    private static final String TAG = "NfcDta";
+
+    private static INfcDta sService;
+    private static HashMap<Context, NfcDta> sNfcDtas = new HashMap<Context, NfcDta>();
+
+    private final Context mContext;
+
+    private NfcDta(Context context, INfcDta service) {
+        mContext = context.getApplicationContext();
+        sService = service;
+    }
+
+    /**
+     * Helper to get an instance of this class.
+     *
+     * @param adapter A reference to an NfcAdapter object.
+     * @return
+     */
+    public static synchronized NfcDta getInstance(NfcAdapter adapter) {
+        if (adapter == null) throw new NullPointerException("NfcAdapter is null");
+        Context context = adapter.getContext();
+        if (context == null) {
+            Log.e(TAG, "NfcAdapter context is null.");
+            throw new UnsupportedOperationException();
+        }
+
+        NfcDta manager = sNfcDtas.get(context);
+        if (manager == null) {
+            INfcDta service = adapter.getNfcDtaInterface();
+            if (service == null) {
+                Log.e(TAG, "This device does not implement the INfcDta interface.");
+                throw new UnsupportedOperationException();
+            }
+            manager = new NfcDta(context, service);
+            sNfcDtas.put(context, manager);
+        }
+        return manager;
+    }
+
+    /**
+     * Enables DTA mode
+     *
+     * @return true/false if enabling was successful
+     */
+    public boolean enableDta() {
+        try {
+            sService.enableDta();
+        } catch (RemoteException e) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Disables DTA mode
+     *
+     * @return true/false if disabling was successful
+     */
+    public boolean disableDta() {
+        try {
+            sService.disableDta();
+        } catch (RemoteException e) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Enables Server
+     *
+     * @return true/false if enabling was successful
+     */
+    public boolean enableServer(String serviceName, int serviceSap, int miu,
+            int rwSize, int testCaseId) {
+        try {
+            return sService.enableServer(serviceName, serviceSap, miu, rwSize, testCaseId);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Disables Server
+     *
+     * @return true/false if disabling was successful
+     */
+    public boolean disableServer() {
+        try {
+            sService.disableServer();
+        } catch (RemoteException e) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Enables Client
+     *
+     * @return true/false if enabling was successful
+     */
+    public boolean enableClient(String serviceName, int miu, int rwSize,
+            int testCaseId) {
+        try {
+            return sService.enableClient(serviceName, miu, rwSize, testCaseId);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Disables client
+     *
+     * @return true/false if disabling was successful
+     */
+    public boolean disableClient() {
+        try {
+            sService.disableClient();
+        } catch (RemoteException e) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Registers Message Service
+     *
+     * @return true/false if registration was successful
+     */
+    public boolean registerMessageService(String msgServiceName) {
+        try {
+            return sService.registerMessageService(msgServiceName);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+}
diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java
index b5a8aca..34fe07b 100755
--- a/core/java/android/text/format/DateFormat.java
+++ b/core/java/android/text/format/DateFormat.java
@@ -177,43 +177,38 @@
      * @hide
      */
     public static boolean is24HourFormat(Context context, int userHandle) {
-        String value = Settings.System.getStringForUser(context.getContentResolver(),
+        final String value = Settings.System.getStringForUser(context.getContentResolver(),
                 Settings.System.TIME_12_24, userHandle);
-
-        if (value == null) {
-            Locale locale = context.getResources().getConfiguration().locale;
-
-            synchronized (sLocaleLock) {
-                if (sIs24HourLocale != null && sIs24HourLocale.equals(locale)) {
-                    return sIs24Hour;
-                }
-            }
-
-            java.text.DateFormat natural =
-                    java.text.DateFormat.getTimeInstance(java.text.DateFormat.LONG, locale);
-
-            if (natural instanceof SimpleDateFormat) {
-                SimpleDateFormat sdf = (SimpleDateFormat) natural;
-                String pattern = sdf.toPattern();
-
-                if (pattern.indexOf('H') >= 0) {
-                    value = "24";
-                } else {
-                    value = "12";
-                }
-            } else {
-                value = "12";
-            }
-
-            synchronized (sLocaleLock) {
-                sIs24HourLocale = locale;
-                sIs24Hour = value.equals("24");
-            }
-
-            return sIs24Hour;
+        if (value != null) {
+            return value.equals("24");
         }
 
-        return value.equals("24");
+        final Locale locale = context.getResources().getConfiguration().locale;
+
+        synchronized (sLocaleLock) {
+            if (sIs24HourLocale != null && sIs24HourLocale.equals(locale)) {
+                return sIs24Hour;
+            }
+        }
+
+        final java.text.DateFormat natural =
+                java.text.DateFormat.getTimeInstance(java.text.DateFormat.LONG, locale);
+
+        final boolean is24Hour;
+        if (natural instanceof SimpleDateFormat) {
+            final SimpleDateFormat sdf = (SimpleDateFormat) natural;
+            final String pattern = sdf.toPattern();
+            is24Hour = hasDesignator(pattern, 'H');
+        } else {
+            is24Hour = false;
+        }
+
+        synchronized (sLocaleLock) {
+            sIs24HourLocale = locale;
+            sIs24Hour = is24Hour;
+        }
+
+        return is24Hour;
     }
 
     /**
@@ -249,17 +244,18 @@
 
     /**
      * Returns a {@link java.text.DateFormat} object that can format the time according
-     * to the current locale and the user's 12-/24-hour clock preference.
+     * to the context's locale and the user's 12-/24-hour clock preference.
      * @param context the application context
      * @return the {@link java.text.DateFormat} object that properly formats the time.
      */
     public static java.text.DateFormat getTimeFormat(Context context) {
-        return new java.text.SimpleDateFormat(getTimeFormatString(context));
+        final Locale locale = context.getResources().getConfiguration().locale;
+        return new java.text.SimpleDateFormat(getTimeFormatString(context), locale);
     }
 
     /**
      * Returns a String pattern that can be used to format the time according
-     * to the current locale and the user's 12-/24-hour clock preference.
+     * to the context's locale and the user's 12-/24-hour clock preference.
      * @param context the application context
      * @hide
      */
@@ -269,45 +265,48 @@
 
     /**
      * Returns a String pattern that can be used to format the time according
-     * to the current locale and the user's 12-/24-hour clock preference.
+     * to the context's locale and the user's 12-/24-hour clock preference.
      * @param context the application context
      * @param userHandle the user handle of the user to query the format for
      * @hide
      */
     public static String getTimeFormatString(Context context, int userHandle) {
-        LocaleData d = LocaleData.get(context.getResources().getConfiguration().locale);
+        final LocaleData d = LocaleData.get(context.getResources().getConfiguration().locale);
         return is24HourFormat(context, userHandle) ? d.timeFormat_Hm : d.timeFormat_hm;
     }
 
     /**
      * Returns a {@link java.text.DateFormat} object that can format the date
-     * in short form according to the current locale.
+     * in short form according to the context's locale.
      *
      * @param context the application context
      * @return the {@link java.text.DateFormat} object that properly formats the date.
      */
     public static java.text.DateFormat getDateFormat(Context context) {
-        return java.text.DateFormat.getDateInstance(java.text.DateFormat.SHORT);
+        final Locale locale = context.getResources().getConfiguration().locale;
+        return java.text.DateFormat.getDateInstance(java.text.DateFormat.SHORT, locale);
     }
 
     /**
      * Returns a {@link java.text.DateFormat} object that can format the date
-     * in long form (such as {@code Monday, January 3, 2000}) for the current locale.
+     * in long form (such as {@code Monday, January 3, 2000}) for the context's locale.
      * @param context the application context
      * @return the {@link java.text.DateFormat} object that formats the date in long form.
      */
     public static java.text.DateFormat getLongDateFormat(Context context) {
-        return java.text.DateFormat.getDateInstance(java.text.DateFormat.LONG);
+        final Locale locale = context.getResources().getConfiguration().locale;
+        return java.text.DateFormat.getDateInstance(java.text.DateFormat.LONG, locale);
     }
 
     /**
      * Returns a {@link java.text.DateFormat} object that can format the date
-     * in medium form (such as {@code Jan 3, 2000}) for the current locale.
+     * in medium form (such as {@code Jan 3, 2000}) for the context's locale.
      * @param context the application context
      * @return the {@link java.text.DateFormat} object that formats the date in long form.
      */
     public static java.text.DateFormat getMediumDateFormat(Context context) {
-        return java.text.DateFormat.getDateInstance(java.text.DateFormat.MEDIUM);
+        final Locale locale = context.getResources().getConfiguration().locale;
+        return java.text.DateFormat.getDateInstance(java.text.DateFormat.MEDIUM, locale);
     }
 
     /**
@@ -320,11 +319,13 @@
      * order returned here.
      */
     public static char[] getDateFormatOrder(Context context) {
-        return ICU.getDateFormatOrder(getDateFormatString());
+        return ICU.getDateFormatOrder(getDateFormatString(context));
     }
 
-    private static String getDateFormatString() {
-        java.text.DateFormat df = java.text.DateFormat.getDateInstance(java.text.DateFormat.SHORT);
+    private static String getDateFormatString(Context context) {
+        final Locale locale = context.getResources().getConfiguration().locale;
+        java.text.DateFormat df = java.text.DateFormat.getDateInstance(
+                java.text.DateFormat.SHORT, locale);
         if (df instanceof SimpleDateFormat) {
             return ((SimpleDateFormat) df).toPattern();
         }
@@ -375,6 +376,9 @@
      * Test if a format string contains the given designator. Always returns
      * {@code false} if the input format is {@code null}.
      *
+     * Note that this is intended for searching for designators, not arbitrary
+     * characters. So searching for a literal single quote would not work correctly.
+     *
      * @hide
      */
     public static boolean hasDesignator(CharSequence inFormat, char designator) {
@@ -382,52 +386,21 @@
 
         final int length = inFormat.length();
 
-        int c;
-        int count;
-
-        for (int i = 0; i < length; i += count) {
-            count = 1;
-            c = inFormat.charAt(i);
-
+        boolean insideQuote = false;
+        for (int i = 0; i < length; i++) {
+            final char c = inFormat.charAt(i);
             if (c == QUOTE) {
-                count = skipQuotedText(inFormat, i, length);
-            } else if (c == designator) {
-                return true;
+                insideQuote = !insideQuote;
+            } else if (!insideQuote) {
+                if (c == designator) {
+                    return true;
+                }
             }
         }
 
         return false;
     }
 
-    private static int skipQuotedText(CharSequence s, int i, int len) {
-        if (i + 1 < len && s.charAt(i + 1) == QUOTE) {
-            return 2;
-        }
-
-        int count = 1;
-        // skip leading quote
-        i++;
-
-        while (i < len) {
-            char c = s.charAt(i);
-
-            if (c == QUOTE) {
-                count++;
-                //  QUOTEQUOTE -> QUOTE
-                if (i + 1 < len && s.charAt(i + 1) == QUOTE) {
-                    i++;
-                } else {
-                    break;
-                }
-            } else {
-                i++;
-                count++;
-            }
-        }
-
-        return count;
-    }
-
     /**
      * Given a format string and a {@link java.util.Calendar} object, returns a CharSequence
      * containing the requested date.
diff --git a/core/java/android/text/style/TextAppearanceSpan.java b/core/java/android/text/style/TextAppearanceSpan.java
index abbd793..3a3646b 100644
--- a/core/java/android/text/style/TextAppearanceSpan.java
+++ b/core/java/android/text/style/TextAppearanceSpan.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
+import android.graphics.LeakyTypefaceStorage;
 import android.graphics.Typeface;
 import android.os.Parcel;
 import android.text.ParcelableSpan;
@@ -30,11 +31,12 @@
  * resource.
  */
 public class TextAppearanceSpan extends MetricAffectingSpan implements ParcelableSpan {
-    private final String mTypeface;
+    private final String mFamilyName;
     private final int mStyle;
     private final int mTextSize;
     private final ColorStateList mTextColor;
     private final ColorStateList mTextColorLink;
+    private final Typeface mTypeface;
 
     /**
      * Uses the specified TextAppearance resource to determine the
@@ -55,7 +57,7 @@
      */
     public TextAppearanceSpan(Context context, int appearance, int colorList) {
         ColorStateList textColor;
-        
+
         TypedArray a =
             context.obtainStyledAttributes(appearance,
                                            com.android.internal.R.styleable.TextAppearance);
@@ -68,28 +70,33 @@
                                         TextAppearance_textSize, -1);
 
         mStyle = a.getInt(com.android.internal.R.styleable.TextAppearance_textStyle, 0);
-        String family = a.getString(com.android.internal.R.styleable.TextAppearance_fontFamily);
-        if (family != null) {
-            mTypeface = family;
+        mTypeface = a.getFont(com.android.internal.R.styleable.TextAppearance_fontFamily);
+        if (mTypeface != null) {
+            mFamilyName = null;
         } else {
-            int tf = a.getInt(com.android.internal.R.styleable.TextAppearance_typeface, 0);
+            String family = a.getString(com.android.internal.R.styleable.TextAppearance_fontFamily);
+            if (family != null) {
+                mFamilyName = family;
+            } else {
+                int tf = a.getInt(com.android.internal.R.styleable.TextAppearance_typeface, 0);
 
-            switch (tf) {
-                case 1:
-                    mTypeface = "sans";
-                    break;
+                switch (tf) {
+                    case 1:
+                        mFamilyName = "sans";
+                        break;
 
-                case 2:
-                    mTypeface = "serif";
-                    break;
+                    case 2:
+                        mFamilyName = "serif";
+                        break;
 
-                case 3:
-                    mTypeface = "monospace";
-                    break;
+                    case 3:
+                        mFamilyName = "monospace";
+                        break;
 
-                default:
-                    mTypeface = null;
-                    break;
+                    default:
+                        mFamilyName = null;
+                        break;
+                }
             }
         }
 
@@ -102,7 +109,7 @@
             textColor = a.getColorStateList(colorList);
             a.recycle();
         }
-        
+
         mTextColor = textColor;
     }
 
@@ -112,15 +119,16 @@
      */
     public TextAppearanceSpan(String family, int style, int size,
                               ColorStateList color, ColorStateList linkColor) {
-        mTypeface = family;
+        mFamilyName = family;
         mStyle = style;
         mTextSize = size;
         mTextColor = color;
         mTextColorLink = linkColor;
+        mTypeface = null;
     }
 
     public TextAppearanceSpan(Parcel src) {
-        mTypeface = src.readString();
+        mFamilyName = src.readString();
         mStyle = src.readInt();
         mTextSize = src.readInt();
         if (src.readInt() != 0) {
@@ -133,8 +141,9 @@
         } else {
             mTextColorLink = null;
         }
+        mTypeface = LeakyTypefaceStorage.readTypefaceFromParcel(src);
     }
-    
+
     public int getSpanTypeId() {
         return getSpanTypeIdInternal();
     }
@@ -143,7 +152,7 @@
     public int getSpanTypeIdInternal() {
         return TextUtils.TEXT_APPEARANCE_SPAN;
     }
-    
+
     public int describeContents() {
         return 0;
     }
@@ -154,7 +163,7 @@
 
     /** @hide */
     public void writeToParcelInternal(Parcel dest, int flags) {
-        dest.writeString(mTypeface);
+        dest.writeString(mFamilyName);
         dest.writeInt(mStyle);
         dest.writeInt(mTextSize);
         if (mTextColor != null) {
@@ -169,6 +178,7 @@
         } else {
             dest.writeInt(0);
         }
+        LeakyTypefaceStorage.writeTypefaceToParcel(mTypeface, dest);
     }
 
     /**
@@ -176,7 +186,7 @@
      * if it does not specify one.
      */
     public String getFamily() {
-        return mTypeface;
+        return mFamilyName;
     }
 
     /**
@@ -226,9 +236,14 @@
 
     @Override
     public void updateMeasureState(TextPaint ds) {
-        if (mTypeface != null || mStyle != 0) {
+        final Typeface styledTypeface;
+        int style = 0;
+
+        if (mTypeface != null) {
+            style = mStyle;
+            styledTypeface = Typeface.create(mTypeface, style);
+        } else if (mFamilyName != null || mStyle != 0) {
             Typeface tf = ds.getTypeface();
-            int style = 0;
 
             if (tf != null) {
                 style = tf.getStyle();
@@ -236,15 +251,19 @@
 
             style |= mStyle;
 
-            if (mTypeface != null) {
-                tf = Typeface.create(mTypeface, style);
+            if (mFamilyName != null) {
+                styledTypeface = Typeface.create(mFamilyName, style);
             } else if (tf == null) {
-                tf = Typeface.defaultFromStyle(style);
+                styledTypeface = Typeface.defaultFromStyle(style);
             } else {
-                tf = Typeface.create(tf, style);
+                styledTypeface = Typeface.create(tf, style);
             }
+        } else {
+            styledTypeface = null;
+        }
 
-            int fake = style & ~tf.getStyle();
+        if (styledTypeface != null) {
+            int fake = style & ~styledTypeface.getStyle();
 
             if ((fake & Typeface.BOLD) != 0) {
                 ds.setFakeBoldText(true);
@@ -254,7 +273,7 @@
                 ds.setTextSkewX(-0.25f);
             }
 
-            ds.setTypeface(tf);
+            ds.setTypeface(styledTypeface);
         }
 
         if (mTextSize > 0) {
diff --git a/core/java/android/util/AtomicFile.java b/core/java/android/util/AtomicFile.java
index 2f1abe9..0122e49 100644
--- a/core/java/android/util/AtomicFile.java
+++ b/core/java/android/util/AtomicFile.java
@@ -202,6 +202,15 @@
     }
 
     /**
+     * @hide
+     * Checks if the original or backup file exists.
+     * @return whether the original or backup file exists.
+     */
+    public boolean exists() {
+        return mBaseName.exists() || mBackupName.exists();
+    }
+
+    /**
      * Gets the last modified time of the atomic file.
      * {@hide}
      *
diff --git a/graphics/java/android/graphics/LeakyTypefaceStorage.java b/graphics/java/android/graphics/LeakyTypefaceStorage.java
new file mode 100644
index 0000000..618e60d
--- /dev/null
+++ b/graphics/java/android/graphics/LeakyTypefaceStorage.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2017 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.graphics;
+
+import com.android.internal.annotations.GuardedBy;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Process;
+import android.util.ArrayMap;
+
+import java.util.ArrayList;
+
+/**
+ * This class is used for Parceling Typeface object.
+ * Note: Typeface object can not be passed over the process boundary.
+ *
+ * @hide
+ */
+public class LeakyTypefaceStorage {
+    private static final Object sLock = new Object();
+
+    @GuardedBy("sLock")
+    private static final ArrayList<Typeface> sStorage = new ArrayList<>();
+    @GuardedBy("sLock")
+    private static final ArrayMap<Typeface, Integer> sTypefaceMap = new ArrayMap<>();
+
+    /**
+     * Write typeface to parcel.
+     *
+     * You can't transfer Typeface to a different process. {@link readTypefaceFromParcel} will
+     * return {@code null} if the {@link readTypefaceFromParcel} is called in a different process.
+     *
+     * @param typeface A {@link Typeface} to be written.
+     * @param parcel A {@link Parcel} object.
+     */
+    public static void writeTypefaceToParcel(@Nullable Typeface typeface, @NonNull Parcel parcel) {
+        parcel.writeInt(Process.myPid());
+        synchronized (sLock) {
+            final int id;
+            final Integer i = sTypefaceMap.get(typeface);
+            if (i != null) {
+                id = i.intValue();
+            } else {
+                id = sStorage.size();
+                sStorage.add(typeface);
+                sTypefaceMap.put(typeface, id);
+            }
+            parcel.writeInt(id);
+        }
+    }
+
+    /**
+     * Read typeface from parcel.
+     *
+     * If the {@link Typeface} was created in another process, this method returns null.
+     *
+     * @param parcel A {@link Parcel} object
+     * @return A {@link Typeface} object.
+     */
+    public static @Nullable Typeface readTypefaceFromParcel(@NonNull Parcel parcel) {
+        final int pid = parcel.readInt();
+        final int typefaceId = parcel.readInt();
+        if (pid != Process.myPid()) {
+            return null;  // The Typeface was created and written in another process.
+        }
+        synchronized (sLock) {
+            return sStorage.get(typefaceId);
+        }
+    }
+}
diff --git a/media/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java
index 54be8fd..9862564 100644
--- a/media/java/android/media/browse/MediaBrowser.java
+++ b/media/java/android/media/browse/MediaBrowser.java
@@ -1134,7 +1134,9 @@
         }
 
         public SubscriptionCallback getCallback(Context context, Bundle options) {
-            options.setClassLoader(context.getClassLoader());
+            if (options != null) {
+                options.setClassLoader(context.getClassLoader());
+            }
             for (int i = 0; i < mOptionsList.size(); ++i) {
                 if (MediaBrowserUtils.areSameOptions(mOptionsList.get(i), options)) {
                     return mCallbacks.get(i);
@@ -1144,7 +1146,9 @@
         }
 
         public void putCallback(Context context, Bundle options, SubscriptionCallback callback) {
-            options.setClassLoader(context.getClassLoader());
+            if (options != null) {
+                options.setClassLoader(context.getClassLoader());
+            }
             for (int i = 0; i < mOptionsList.size(); ++i) {
                 if (MediaBrowserUtils.areSameOptions(mOptionsList.get(i), options)) {
                     mCallbacks.set(i, callback);
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 0f8096a..6086005 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -749,7 +749,7 @@
         if (WifiTracker.sVerboseLogging) {
             // Add RSSI/band information for this config, what was seen up to 6 seconds ago
             // verbose WiFi Logging is only turned on thru developers settings
-            if (mInfo != null && mNetworkInfo != null) { // This is the active connection
+            if (isActive() && mInfo != null) {
                 summary.append(" f=" + Integer.toString(mInfo.getFrequency()));
             }
             summary.append(" " + getVisibilityStatus());
@@ -814,7 +814,7 @@
 
         long now = System.currentTimeMillis();
 
-        if (mInfo != null) {
+        if (isActive() && mInfo != null) {
             bssid = mInfo.getBSSID();
             if (bssid != null) {
                 visibility.append(" ").append(bssid);
@@ -1084,7 +1084,7 @@
                 // are still seen, we will investigate further.
                 update(config); // Notifies the AccessPointListener of the change
             }
-            if (mRssi != info.getRssi()) {
+            if (mRssi != info.getRssi() && info.getRssi() != WifiInfo.INVALID_RSSI) {
                 mRssi = info.getRssi();
                 updated = true;
             } else if (mNetworkInfo != null && networkInfo != null
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
index 1fb8058..35c730e 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
@@ -655,6 +655,57 @@
     }
 
     @Test
+    public void testUpdateWithDifferentRssi_returnsTrue() {
+        int networkId = 123;
+        int rssi = -55;
+        WifiConfiguration config = new WifiConfiguration();
+        config.networkId = networkId;
+        WifiInfo wifiInfo = new WifiInfo();
+        wifiInfo.setNetworkId(networkId);
+        wifiInfo.setRssi(rssi);
+
+        NetworkInfo networkInfo =
+                new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0 /* subtype */, "WIFI", "");
+        networkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTING, "", "");
+
+        AccessPoint ap = new TestAccessPointBuilder(mContext)
+                .setNetworkInfo(networkInfo)
+                .setNetworkId(networkId)
+                .setRssi(rssi)
+                .setWifiInfo(wifiInfo)
+                .build();
+
+        NetworkInfo newInfo = new NetworkInfo(networkInfo); // same values
+        wifiInfo.setRssi(rssi + 1);
+        assertThat(ap.update(config, wifiInfo, newInfo)).isTrue();
+    }
+
+    @Test
+    public void testUpdateWithInvalidRssi_returnsFalse() {
+        int networkId = 123;
+        int rssi = -55;
+        WifiConfiguration config = new WifiConfiguration();
+        config.networkId = networkId;
+        WifiInfo wifiInfo = new WifiInfo();
+        wifiInfo.setNetworkId(networkId);
+        wifiInfo.setRssi(rssi);
+
+        NetworkInfo networkInfo =
+                new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0 /* subtype */, "WIFI", "");
+        networkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTING, "", "");
+
+        AccessPoint ap = new TestAccessPointBuilder(mContext)
+                .setNetworkInfo(networkInfo)
+                .setNetworkId(networkId)
+                .setRssi(rssi)
+                .setWifiInfo(wifiInfo)
+                .build();
+
+        NetworkInfo newInfo = new NetworkInfo(networkInfo); // same values
+        wifiInfo.setRssi(WifiInfo.INVALID_RSSI);
+        assertThat(ap.update(config, wifiInfo, newInfo)).isFalse();
+    }
+    @Test
     public void testUpdateWithConfigChangeOnly_returnsFalseButInvokesListener() {
         int networkId = 123;
         int rssi = -55;
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 15e6e45..146b349 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -2594,7 +2594,7 @@
             synchronized (mLock) {
                 final int key = makeKey(SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM);
                 File globalFile = getSettingsFile(key);
-                if (globalFile.exists()) {
+                if (SettingsState.stateFileExists(globalFile)) {
                     return;
                 }
 
@@ -2631,7 +2631,7 @@
             // Every user has secure settings and if no file we need to migrate.
             final int secureKey = makeKey(SETTINGS_TYPE_SECURE, userId);
             File secureFile = getSettingsFile(secureKey);
-            if (secureFile.exists()) {
+            if (SettingsState.stateFileExists(secureFile)) {
                 return;
             }
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 5f4b239..d3ac11a 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -689,17 +689,11 @@
 
     private void readStateSyncLocked() {
         FileInputStream in;
-        if (!mStatePersistFile.exists()) {
-            Slog.i(LOG_TAG, "No settings state " + mStatePersistFile);
-            addHistoricalOperationLocked(HISTORICAL_OPERATION_INITIALIZE, null);
-            return;
-        }
         try {
             in = new AtomicFile(mStatePersistFile).openRead();
         } catch (FileNotFoundException fnfe) {
-            String message = "No settings state " + mStatePersistFile;
-            Slog.wtf(LOG_TAG, message);
-            Slog.i(LOG_TAG, message);
+            Slog.i(LOG_TAG, "No settings state " + mStatePersistFile);
+            addHistoricalOperationLocked(HISTORICAL_OPERATION_INITIALIZE, null);
             return;
         }
         try {
@@ -715,6 +709,16 @@
         }
     }
 
+    /**
+     * Uses AtomicFile to check if the file or its backup exists.
+     * @param file The file to check for existence
+     * @return whether the original or backup exist
+     */
+    public static boolean stateFileExists(File file) {
+        AtomicFile stateFile = new AtomicFile(file);
+        return stateFile.exists();
+    }
+
     private void parseStateLocked(XmlPullParser parser)
             throws IOException, XmlPullParserException {
         final int outerDepth = parser.getDepth();
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml b/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
index f5efcaa..56fb73f 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
@@ -31,7 +31,7 @@
         android:layout_height="wrap_content"
         android:textColor="?attr/wallpaperTextColor"
         style="@style/widget_label"
-        android:letterSpacing="0.15"
+        android:letterSpacing="0.05"
         android:gravity="center"
         />
     <TextView android:id="@+id/alarm_status"
@@ -42,7 +42,7 @@
         android:drawableTint="?attr/wallpaperTextColorSecondary"
         android:drawableTintMode="src_in"
         android:textColor="?attr/wallpaperTextColorSecondary"
-        android:letterSpacing="0.15"
+        android:letterSpacing="0.05"
         style="@style/widget_label"
         android:layout_marginStart="6dp"
         android:gravity="center"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 9793615..c8e6021 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -801,6 +801,9 @@
     <!-- The shortest-edge size of the expanded PiP. -->
     <dimen name="pip_expanded_shortest_edge_size">160dp</dimen>
 
+    <!-- The additional offset to apply to the IME animation to account for the input field. -->
+    <dimen name="pip_ime_offset">48dp</dimen>
+
     <!-- The padding between actions in the PiP in landscape  Note that the PiP does not reflect
          the configuration of the device, so we can't use -land resources. -->
     <dimen name="pip_between_action_padding_land">8dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 278fdc3..d3be19d 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -27,6 +27,7 @@
 import android.app.IActivityManager;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.res.Resources;
 import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
@@ -122,6 +123,7 @@
     private boolean mIsMinimized;
     private boolean mIsImeShowing;
     private int mImeHeight;
+    private int mImeOffset;
     private float mSavedSnapFraction = -1f;
     private boolean mSendingHoverAccessibilityEvents;
     private boolean mMovementWithinMinimize;
@@ -192,8 +194,11 @@
         };
         mMotionHelper = new PipMotionHelper(mContext, mActivityManager, mMenuController,
                 mSnapAlgorithm, mFlingAnimationUtils);
-        mExpandedShortestEdgeSize = context.getResources().getDimensionPixelSize(
+
+        Resources res = context.getResources();
+        mExpandedShortestEdgeSize = res.getDimensionPixelSize(
                 R.dimen.pip_expanded_shortest_edge_size);
+        mImeOffset = res.getDimensionPixelSize(R.dimen.pip_ime_offset);
 
         // Register the listener for input consumer touch events
         inputConsumerController.setTouchListener(this::handleTouchEvent);
@@ -265,7 +270,6 @@
         mSnapAlgorithm.getMovementBounds(mExpandedBounds, insetBounds, expandedMovementBounds,
                 mIsImeShowing ? mImeHeight : 0);
 
-
         // If this is from an IME adjustment, then we should move the PiP so that it is not occluded
         // by the IME
         if (fromImeAdjustement) {
@@ -278,18 +282,22 @@
                         ? expandedMovementBounds
                         : normalMovementBounds;
                 if (mIsImeShowing) {
-                    // IME visible
+                    // IME visible, apply the IME offset if the space allows for it
+                    final int imeOffset = toMovementBounds.bottom - Math.max(toMovementBounds.top,
+                            toMovementBounds.bottom - mImeOffset);
                     if (bounds.top == mMovementBounds.bottom) {
                         // If the PIP is currently resting on top of the IME, then adjust it with
-                        // the hiding IME
-                        bounds.offsetTo(bounds.left, toMovementBounds.bottom);
+                        // the showing IME
+                        bounds.offsetTo(bounds.left, toMovementBounds.bottom - imeOffset);
                     } else {
-                        bounds.offset(0, Math.min(0, toMovementBounds.bottom - bounds.top));
+                        bounds.offset(0, Math.min(0, toMovementBounds.bottom - imeOffset
+                                - bounds.top));
                     }
                 } else {
                     // IME hidden
-                    if (bounds.top == mMovementBounds.bottom) {
-                        // If the PIP is resting on top of the IME, then adjust it with the hiding IME
+                    if (bounds.top >= (mMovementBounds.bottom - mImeOffset)) {
+                        // If the PIP is resting on top of the IME, then adjust it with the hiding
+                        // IME
                         bounds.offsetTo(bounds.left, toMovementBounds.bottom);
                     }
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index 81ec6a7..4e728f6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -19,13 +19,14 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.content.res.Resources;
+import android.os.SystemProperties;
 import android.service.quicksettings.Tile;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.Switch;
-
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settingslib.net.DataUsageController;
@@ -38,7 +39,6 @@
 import com.android.systemui.qs.CellTileView;
 import com.android.systemui.qs.CellTileView.SignalIcon;
 import com.android.systemui.qs.QSHost;
-import com.android.systemui.qs.SignalTileView;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
 import com.android.systemui.statusbar.policy.NetworkController;
 import com.android.systemui.statusbar.policy.NetworkController.IconState;
@@ -46,8 +46,17 @@
 
 /** Quick settings tile: Cellular **/
 public class CellularTile extends QSTileImpl<SignalState> {
-    static final Intent CELLULAR_SETTINGS = new Intent().setComponent(new ComponentName(
-            "com.android.settings", "com.android.settings.Settings$DataUsageSummaryActivity"));
+    private static final ComponentName CELLULAR_SETTING_COMPONENT = new ComponentName(
+            "com.android.settings", "com.android.settings.Settings$DataUsageSummaryActivity");
+    private static final ComponentName DATA_PLAN_CELLULAR_COMPONENT = new ComponentName(
+            "com.android.settings", "com.android.settings.Settings$DataPlanUsageSummaryActivity");
+
+    private static final Intent CELLULAR_SETTINGS =
+            new Intent().setComponent(CELLULAR_SETTING_COMPONENT);
+    private static final Intent DATA_PLAN_CELLULAR_SETTINGS =
+            new Intent().setComponent(DATA_PLAN_CELLULAR_COMPONENT);
+
+    private static final String ENABLE_SETTINGS_DATA_PLAN = "enable.settings.data.plan";
 
     private final NetworkController mController;
     private final DataUsageController mDataController;
@@ -90,7 +99,7 @@
 
     @Override
     public Intent getLongClickIntent() {
-        return CELLULAR_SETTINGS;
+        return getCellularSettingIntent(mContext);
     }
 
     @Override
@@ -103,7 +112,9 @@
         if (mDataController.isMobileDataSupported()) {
             showDetail(true);
         } else {
-            mActivityStarter.postStartActivityDismissingKeyguard(CELLULAR_SETTINGS, 0);
+            mActivityStarter
+                    .postStartActivityDismissingKeyguard(getCellularSettingIntent(mContext),
+                            0 /* delay */);
         }
     }
 
@@ -240,7 +251,28 @@
         public void setMobileDataEnabled(boolean enabled) {
             mDetailAdapter.setMobileDataEnabled(enabled);
         }
-    };
+    }
+
+    static Intent getCellularSettingIntent(Context context) {
+        // TODO(b/62349208): We should replace feature flag check below with data plans
+        // availability check. If the data plans are available we display the data plans usage
+        // summary otherwise we display data usage summary without data plans.
+        boolean isDataPlanFeatureEnabled =
+                SystemProperties.getBoolean(ENABLE_SETTINGS_DATA_PLAN, false /* default */);
+        context.getPackageManager()
+                .setComponentEnabledSetting(
+                        DATA_PLAN_CELLULAR_COMPONENT,
+                        isDataPlanFeatureEnabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
+                                : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+                        PackageManager.DONT_KILL_APP);
+        context.getPackageManager()
+                .setComponentEnabledSetting(
+                        CELLULAR_SETTING_COMPONENT,
+                        isDataPlanFeatureEnabled ? PackageManager.COMPONENT_ENABLED_STATE_DISABLED
+                                : PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
+                        PackageManager.DONT_KILL_APP);
+        return isDataPlanFeatureEnabled ? DATA_PLAN_CELLULAR_SETTINGS : CELLULAR_SETTINGS;
+    }
 
     private final class CellularDetailAdapter implements DetailAdapter {
 
@@ -258,7 +290,7 @@
 
         @Override
         public Intent getSettingsIntent() {
-            return CELLULAR_SETTINGS;
+            return getCellularSettingIntent(mContext);
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
index b796451..8b62beb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
@@ -14,18 +14,16 @@
 
 package com.android.systemui.qs.tiles;
 
-import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
 import android.content.Intent;
 import android.service.quicksettings.Tile;
 import android.widget.Switch;
-
-import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.systemui.Dependency;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
-import com.android.systemui.qs.QSHost;
 import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
 import com.android.systemui.statusbar.policy.DataSaverController;
@@ -57,9 +55,8 @@
 
     @Override
     public Intent getLongClickIntent() {
-        return CellularTile.CELLULAR_SETTINGS;
+        return CellularTile.getCellularSettingIntent(mContext);
     }
-
     @Override
     protected void handleClick() {
         if (mState.value
@@ -73,12 +70,7 @@
         dialog.setTitle(com.android.internal.R.string.data_saver_enable_title);
         dialog.setMessage(com.android.internal.R.string.data_saver_description);
         dialog.setPositiveButton(com.android.internal.R.string.data_saver_enable_button,
-                new DialogInterface.OnClickListener() {
-                    @Override
-                    public void onClick(DialogInterface dialog, int which) {
-                        toggleDataSaver();
-                    }
-                });
+                (OnClickListener) (dialogInterface, which) -> toggleDataSaver());
         dialog.setNegativeButton(com.android.internal.R.string.cancel, null);
         dialog.setShowForAllUsers(true);
         dialog.show();
@@ -126,4 +118,4 @@
     public void onDataSaverChanged(boolean isDataSaving) {
         refreshState(isDataSaving);
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 8046250..7bc1a39 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -1974,6 +1974,9 @@
         if (mGuts != null) {
             mGuts.setActualHeight(height);
         }
+        if (mMenuRow.getMenuView() != null) {
+            mMenuRow.onHeightUpdate();
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java
index fddc446..99b4b07 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java
@@ -431,7 +431,7 @@
         if (mParent == null || mMenuItems.size() == 0 || mMenuContainer == null) {
             return;
         }
-        int parentHeight = mParent.getCollapsedHeight();
+        int parentHeight = mParent.getActualHeight();
         float translationY;
         if (parentHeight < mVertSpaceForIcons) {
             translationY = (parentHeight / 2) - (mHorizSpaceForIcon / 2);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index e5f68ad..1889806 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -244,7 +244,7 @@
             boolean aboveShelf = ViewState.getFinalTranslationZ(row) > baseZHeight;
             boolean isLastChild = child == lastChild;
             float rowTranslationY = row.getTranslationY();
-            if (isLastChild || aboveShelf || backgroundForceHidden) {
+            if ((isLastChild && !child.isInShelf()) || aboveShelf || backgroundForceHidden) {
                 notificationClipEnd = shelfStart + getIntrinsicHeight();
             } else {
                 notificationClipEnd = shelfStart - mPaddingBetweenElements;
diff --git a/telephony/java/android/telephony/Telephony.java b/telephony/java/android/telephony/Telephony.java
index 765c73b..216d28c 100644
--- a/telephony/java/android/telephony/Telephony.java
+++ b/telephony/java/android/telephony/Telephony.java
@@ -2788,6 +2788,13 @@
         public static final String USER_VISIBLE = "user_visible";
 
         /**
+         * Is the user allowed to edit this APN?
+         * <p>Type: INTEGER (boolean) </p>
+         * @hide
+         */
+        public static final String USER_EDITABLE = "user_editable";
+
+        /**
          * Following are possible values for the EDITED field
          * @hide
          */
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index 0d4359e..8075e17 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -41,6 +41,8 @@
 import com.android.internal.util.AsyncChannel;
 import com.android.internal.util.Protocol;
 
+import dalvik.system.CloseGuard;
+
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -668,15 +670,21 @@
      * Most p2p operations require a Channel as an argument. An instance of Channel is obtained
      * by doing a call on {@link #initialize}
      */
-    public static class Channel {
-        Channel(Context context, Looper looper, ChannelListener l, Binder binder) {
+    public static class Channel implements AutoCloseable {
+        /** @hide */
+        public Channel(Context context, Looper looper, ChannelListener l, Binder binder,
+                WifiP2pManager p2pManager) {
             mAsyncChannel = new AsyncChannel();
             mHandler = new P2pHandler(looper);
             mChannelListener = l;
             mContext = context;
             mBinder = binder;
+            mP2pManager = p2pManager;
+
+            mCloseGuard.open("close");
         }
         private final static int INVALID_LISTENER_KEY = 0;
+        private final WifiP2pManager mP2pManager;
         private ChannelListener mChannelListener;
         private ServiceResponseListener mServRspListener;
         private DnsSdServiceResponseListener mDnsSdServRspListener;
@@ -686,6 +694,41 @@
         private final Object mListenerMapLock = new Object();
         private int mListenerKey = 0;
 
+        private final CloseGuard mCloseGuard = CloseGuard.get();
+
+        /**
+         * Close the current P2P connection and indicate to the P2P service that connections
+         * created by the app can be removed.
+         */
+        public void close() {
+            if (mP2pManager == null) {
+                Log.w(TAG, "Channel.close(): Null mP2pManager!?");
+            } else {
+                try {
+                    mP2pManager.mService.close(mBinder);
+                } catch (RemoteException e) {
+                    throw e.rethrowFromSystemServer();
+                }
+            }
+
+            mAsyncChannel.disconnect();
+            mCloseGuard.close();
+        }
+
+        /** @hide */
+        @Override
+        protected void finalize() throws Throwable {
+            try {
+                if (mCloseGuard != null) {
+                    mCloseGuard.warnIfOpen();
+                }
+
+                close();
+            } finally {
+                super.finalize();
+            }
+        }
+
         /* package */ final Binder mBinder;
 
         private AsyncChannel mAsyncChannel;
@@ -913,11 +956,12 @@
                                      Messenger messenger, Binder binder) {
         if (messenger == null) return null;
 
-        Channel c = new Channel(srcContext, srcLooper, listener, binder);
+        Channel c = new Channel(srcContext, srcLooper, listener, binder, this);
         if (c.mAsyncChannel.connectSync(srcContext, c.mHandler, messenger)
                 == AsyncChannel.STATUS_SUCCESSFUL) {
             return c;
         } else {
+            c.close();
             return null;
         }
     }
@@ -1422,24 +1466,6 @@
     }
 
     /**
-     * Close the current P2P connection and clean-up any configuration requested by the
-     * current app. Takes same action as taken when the app dies.
-     *
-     * @param c is the channel created at {@link #initialize}
-     *
-     * @hide
-     */
-    public void close(Channel c) {
-        try {
-            if (c != null) {
-                mService.close(c.mBinder);
-            }
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
      * Get a handover request message for use in WFA NFC Handover transfer.
      * @hide
      */
diff --git a/wifi/tests/Android.mk b/wifi/tests/Android.mk
index 8dc244f..afab1a3 100644
--- a/wifi/tests/Android.mk
+++ b/wifi/tests/Android.mk
@@ -50,6 +50,7 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
 	android-support-test \
+	core-test-rules \
 	guava \
 	mockito-target-minus-junit4 \
 	frameworks-base-testutils \
diff --git a/wifi/tests/src/android/net/wifi/p2p/WifiP2pManagerTest.java b/wifi/tests/src/android/net/wifi/p2p/WifiP2pManagerTest.java
new file mode 100644
index 0000000..1e8382f
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/p2p/WifiP2pManagerTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2017 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 static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.os.test.TestLooper;
+
+import libcore.junit.util.ResourceLeakageDetector;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit test harness for WifiP2pManager.
+ */
+public class WifiP2pManagerTest {
+    private WifiP2pManager mDut;
+    private TestLooper mTestLooper;
+
+    @Mock
+    public Context mContextMock;
+    @Mock
+    IWifiP2pManager mP2pServiceMock;
+
+    @Rule
+    public ResourceLeakageDetector.LeakageDetectorRule leakageDetectorRule =
+            ResourceLeakageDetector.getRule();
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        mDut = new WifiP2pManager(mP2pServiceMock);
+        mTestLooper = new TestLooper();
+    }
+
+    /**
+     * Validate that on finalize we close the channel and flag a resource leakage.
+     */
+    @Test
+    public void testChannelFinalize() throws Exception {
+        WifiP2pManager.Channel channel = new WifiP2pManager.Channel(mContextMock,
+                mTestLooper.getLooper(), null, null, mDut);
+
+        leakageDetectorRule.assertUnreleasedResourceCount(channel, 1);
+    }
+
+    /**
+     * Validate that when close is called on a channel it frees up resources (i.e. don't
+     * get flagged again on finalize).
+     */
+    @Test
+    public void testChannelClose() throws Exception {
+        WifiP2pManager.Channel channel = new WifiP2pManager.Channel(mContextMock,
+                mTestLooper.getLooper(), null, null, mDut);
+
+        channel.close();
+        verify(mP2pServiceMock).close(any());
+
+        leakageDetectorRule.assertUnreleasedResourceCount(channel, 0);
+    }
+}