Merge "Only use default backgrounds when not extending AlertDialog style" into lmp-dev
diff --git a/api/current.txt b/api/current.txt
index 334f789..a0d47e4 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -36795,7 +36795,6 @@
     method public deprecated void onGlobalFocusChanged(android.view.View, android.view.View);
     method public void onPause();
     method public void onResume();
-    method public static void optOutDataReductionProxy();
     method public boolean overlayHorizontalScrollbar();
     method public boolean overlayVerticalScrollbar();
     method public boolean pageDown(boolean);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 6cc6fb2..ebeaf34 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1917,7 +1917,7 @@
             mPeople = new ArrayList<String>();
 
             mColorUtil = context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.L ?
-                    NotificationColorUtil.getInstance() : null;
+                    NotificationColorUtil.getInstance(mContext) : null;
         }
 
         /**
@@ -2890,7 +2890,7 @@
         }
 
         private void processLegacyAction(Action action, RemoteViews button) {
-            if (!isLegacy() || mColorUtil.isGrayscale(mContext, action.icon)) {
+            if (!isLegacy() || mColorUtil.isGrayscaleIcon(mContext, action.icon)) {
                 button.setTextViewCompoundDrawablesRelativeColorFilter(R.id.action0, 0,
                         mContext.getResources().getColor(R.color.notification_action_color_filter),
                         PorterDuff.Mode.MULTIPLY);
@@ -2909,7 +2909,7 @@
          * Apply any necessary background to smallIcons being used in the largeIcon spot.
          */
         private void processSmallIconAsLarge(int largeIconId, RemoteViews contentView) {
-            if (!isLegacy() || mColorUtil.isGrayscale(mContext, largeIconId)) {
+            if (!isLegacy() || mColorUtil.isGrayscaleIcon(mContext, largeIconId)) {
                 applyLargeIconBackground(contentView);
             }
         }
@@ -2920,7 +2920,7 @@
          */
         // TODO: also check bounds, transparency, that sort of thing.
         private void processLargeLegacyIcon(Bitmap largeIcon, RemoteViews contentView) {
-            if (isLegacy() && mColorUtil.isGrayscale(largeIcon)) {
+            if (isLegacy() && mColorUtil.isGrayscaleIcon(largeIcon)) {
                 applyLargeIconBackground(contentView);
             } else {
                 removeLargeIconBackground(contentView);
@@ -2956,7 +2956,7 @@
          */
         private void processSmallRightIcon(int smallIconDrawableId,
                 RemoteViews contentView) {
-            if (!isLegacy() || mColorUtil.isGrayscale(mContext, smallIconDrawableId)) {
+            if (!isLegacy() || mColorUtil.isGrayscaleIcon(mContext, smallIconDrawableId)) {
                 contentView.setDrawableParameters(R.id.right_icon, false, -1,
                         0xFFFFFFFF,
                         PorterDuff.Mode.SRC_ATOP, -1);
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index e1f19ee..edfa7af3 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1810,21 +1810,6 @@
     }
 
     /**
-     * Sets whether the application wants to opt out from using the Data Reduction Proxy
-     * service.
-     * Data reduction proxy can only be enabled by the user and will almost always be
-     * transparent to the application. In rare cases where using the proxy interferes
-     * with the app, the application developer can use this API to opt out from using the
-     * proxy. Note that this may increase network bandwidth usage.
-     *
-     * See <a href=http://developer.chrome.com/multidevice/data-compression>
-     * Data Compression Proxy</a>
-     */
-    public static void optOutDataReductionProxy() {
-        getFactory().getStatics().optOutDataReductionProxy();
-    }
-
-    /**
      * Gets the list of currently loaded plugins.
      *
      * @return the list of currently loaded plugins
diff --git a/core/java/android/webkit/WebViewFactoryProvider.java b/core/java/android/webkit/WebViewFactoryProvider.java
index 48f3ca3..20bb932 100644
--- a/core/java/android/webkit/WebViewFactoryProvider.java
+++ b/core/java/android/webkit/WebViewFactoryProvider.java
@@ -61,12 +61,6 @@
 
         /**
          * Implements the API method:
-         * {@link android.webkit.WebView#optOutDataReductionProxy() }
-         */
-        void optOutDataReductionProxy();
-
-        /**
-         * Implements the API method:
          * {@link android.webkit.WebView#setSlowWholeDocumentDrawEnabled(boolean) }
          */
         void enableSlowWholeDocumentDraw();
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index ee17b78..345eafb 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -1878,6 +1878,9 @@
         if (mOnValueChangeListener != null) {
             mOnValueChangeListener.onValueChange(this, previous, mValue);
         }
+
+        mAccessibilityNodeProvider.sendAccessibilityEventForVirtualText(
+                AccessibilityEvent.TYPE_VIEW_FOCUSED);
     }
 
     /**
@@ -2556,6 +2559,7 @@
             getLocationOnScreen(locationOnScreen);
             boundsInScreen.offset(locationOnScreen[0], locationOnScreen[1]);
             info.setBoundsInScreen(boundsInScreen);
+            info.setLiveRegion(View.ACCESSIBILITY_LIVE_REGION_ASSERTIVE);
             return info;
         }
 
diff --git a/core/java/com/android/internal/util/ImageUtils.java b/core/java/com/android/internal/util/ImageUtils.java
index a5ce6e0ca..c153904 100644
--- a/core/java/com/android/internal/util/ImageUtils.java
+++ b/core/java/com/android/internal/util/ImageUtils.java
@@ -17,6 +17,10 @@
 package com.android.internal.util;
 
 import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
 
 /**
  * Utility class for image analysis and processing.
@@ -31,17 +35,49 @@
     // Alpha amount for which values below are considered transparent.
     private static final int ALPHA_TOLERANCE = 50;
 
+    // Size of the smaller bitmap we're actually going to scan.
+    private static final int COMPACT_BITMAP_SIZE = 64; // pixels
+
     private int[] mTempBuffer;
+    private Bitmap mTempCompactBitmap;
+    private Canvas mTempCompactBitmapCanvas;
+    private Paint mTempCompactBitmapPaint;
+    private final Matrix mTempMatrix = new Matrix();
 
     /**
      * Checks whether a bitmap is grayscale. Grayscale here means "very close to a perfect
      * gray".
+     *
+     * Instead of scanning every pixel in the bitmap, we first resize the bitmap to no more than
+     * COMPACT_BITMAP_SIZE^2 pixels using filtering. The hope is that any non-gray color elements
+     * will survive the squeezing process, contaminating the result with color.
      */
     public boolean isGrayscale(Bitmap bitmap) {
-        final int height = bitmap.getHeight();
-        final int width = bitmap.getWidth();
-        int size = height*width;
+        int height = bitmap.getHeight();
+        int width = bitmap.getWidth();
 
+        // shrink to a more manageable (yet hopefully no more or less colorful) size
+        if (height > COMPACT_BITMAP_SIZE || width > COMPACT_BITMAP_SIZE) {
+            if (mTempCompactBitmap == null) {
+                mTempCompactBitmap = Bitmap.createBitmap(
+                        COMPACT_BITMAP_SIZE, COMPACT_BITMAP_SIZE, Bitmap.Config.ARGB_8888
+                );
+                mTempCompactBitmapCanvas = new Canvas(mTempCompactBitmap);
+                mTempCompactBitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+                mTempCompactBitmapPaint.setFilterBitmap(true);
+            }
+            mTempMatrix.reset();
+            mTempMatrix.setScale(
+                    (float) COMPACT_BITMAP_SIZE / width,
+                    (float) COMPACT_BITMAP_SIZE / height,
+                    0, 0);
+            mTempCompactBitmapCanvas.drawColor(0, PorterDuff.Mode.SRC); // select all, erase
+            mTempCompactBitmapCanvas.drawBitmap(bitmap, mTempMatrix, mTempCompactBitmapPaint);
+            bitmap = mTempCompactBitmap;
+            width = height = COMPACT_BITMAP_SIZE;
+        }
+
+        final int size = height*width;
         ensureBufferSize(size);
         bitmap.getPixels(mTempBuffer, 0, width, 0, 0, width, height);
         for (int i = 0; i < size; i++) {
diff --git a/core/java/com/android/internal/util/NotificationColorUtil.java b/core/java/com/android/internal/util/NotificationColorUtil.java
index 665055c..3249ea3 100644
--- a/core/java/com/android/internal/util/NotificationColorUtil.java
+++ b/core/java/com/android/internal/util/NotificationColorUtil.java
@@ -50,23 +50,36 @@
     private final WeakHashMap<Bitmap, Pair<Boolean, Integer>> mGrayscaleBitmapCache =
             new WeakHashMap<Bitmap, Pair<Boolean, Integer>>();
 
-    public static NotificationColorUtil getInstance() {
+    private final int mGrayscaleIconMaxSize; // @dimen/notification_large_icon_width (64dp)
+
+    public static NotificationColorUtil getInstance(Context context) {
         synchronized (sLock) {
             if (sInstance == null) {
-                sInstance = new NotificationColorUtil();
+                sInstance = new NotificationColorUtil(context);
             }
             return sInstance;
         }
     }
 
+    private NotificationColorUtil(Context context) {
+        mGrayscaleIconMaxSize = context.getResources().getDimensionPixelSize(
+                com.android.internal.R.dimen.notification_large_icon_width);
+    }
+
     /**
-     * Checks whether a bitmap is grayscale. Grayscale here means "very close to a perfect
-     * gray".
+     * Checks whether a Bitmap is a small grayscale icon.
+     * Grayscale here means "very close to a perfect gray"; icon means "no larger than 64dp".
      *
      * @param bitmap The bitmap to test.
-     * @return Whether the bitmap is grayscale.
+     * @return True if the bitmap is grayscale; false if it is color or too large to examine.
      */
-    public boolean isGrayscale(Bitmap bitmap) {
+    public boolean isGrayscaleIcon(Bitmap bitmap) {
+        // quick test: reject large bitmaps
+        if (bitmap.getWidth() > mGrayscaleIconMaxSize
+                || bitmap.getHeight() > mGrayscaleIconMaxSize) {
+            return false;
+        }
+
         synchronized (sLock) {
             Pair<Boolean, Integer> cached = mGrayscaleBitmapCache.get(bitmap);
             if (cached != null) {
@@ -92,22 +105,22 @@
     }
 
     /**
-     * Checks whether a drawable is grayscale. Grayscale here means "very close to a perfect
-     * gray".
+     * Checks whether a Drawable is a small grayscale icon.
+     * Grayscale here means "very close to a perfect gray"; icon means "no larger than 64dp".
      *
      * @param d The drawable to test.
-     * @return Whether the drawable is grayscale.
+     * @return True if the bitmap is grayscale; false if it is color or too large to examine.
      */
-    public boolean isGrayscale(Drawable d) {
+    public boolean isGrayscaleIcon(Drawable d) {
         if (d == null) {
             return false;
         } else if (d instanceof BitmapDrawable) {
             BitmapDrawable bd = (BitmapDrawable) d;
-            return bd.getBitmap() != null && isGrayscale(bd.getBitmap());
+            return bd.getBitmap() != null && isGrayscaleIcon(bd.getBitmap());
         } else if (d instanceof AnimationDrawable) {
             AnimationDrawable ad = (AnimationDrawable) d;
             int count = ad.getNumberOfFrames();
-            return count > 0 && isGrayscale(ad.getFrame(0));
+            return count > 0 && isGrayscaleIcon(ad.getFrame(0));
         } else if (d instanceof VectorDrawable) {
             // We just assume you're doing the right thing if using vectors
             return true;
@@ -117,16 +130,16 @@
     }
 
     /**
-     * Checks whether a drawable with a resoure id is grayscale. Grayscale here means "very close
-     * to a perfect gray".
+     * Checks whether a drawable with a resoure id is a small grayscale icon.
+     * Grayscale here means "very close to a perfect gray"; icon means "no larger than 64dp".
      *
      * @param context The context to load the drawable from.
-     * @return Whether the drawable is grayscale.
+     * @return True if the bitmap is grayscale; false if it is color or too large to examine.
      */
-    public boolean isGrayscale(Context context, int drawableResId) {
+    public boolean isGrayscaleIcon(Context context, int drawableResId) {
         if (drawableResId != 0) {
             try {
-                return isGrayscale(context.getDrawable(drawableResId));
+                return isGrayscaleIcon(context.getDrawable(drawableResId));
             } catch (Resources.NotFoundException ex) {
                 Log.e(TAG, "Drawable not found: " + drawableResId);
                 return false;
diff --git a/core/tests/ConnectivityManagerTest/assets/accesspoints.xml b/core/tests/ConnectivityManagerTest/assets/accesspoints.xml
deleted file mode 100644
index ce6eebc..0000000
--- a/core/tests/ConnectivityManagerTest/assets/accesspoints.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
-  <accesspoint>
-    <ssid>opennet</ssid>
-    <security>NONE</security>
-  </accesspoint>
-  <accesspoint>
-    <ssid>GoogleGuest</ssid>
-    <security>NONE</security>
-  </accesspoint>
-  <accesspoint>
-    <ssid>securenetdhcp</ssid>
-    <security>PSK</security>
-    <password>androidwifi</password>
-  </accesspoint>
-  <accesspoint>
-    <ssid>securenetstatic</ssid>
-    <security>PSK</security>
-    <password>androidwifi</password>
-    <ip>192.168.14.2</ip>
-    <gateway>192.168.14.1</gateway>
-    <networkprefixlength>24</networkprefixlength>
-    <dns1>192.168.14.1</dns1>
-    <dns2>192.168.1.9</dns2>
-  </accesspoint>
-<!-- TODO: This AP is outdated and only supports 2.4GHz.
-           Need to switch to a new dual-band AP.
-           Enable this test case again once the configuration is completed.
-     bug#: 9470594
-  <accesspoint>
-    <ssid>botnet</ssid>
-    <security>EAP</security>
-    <eap>PEAP</eap>
-    <phase2>MSCHAPV2</phase2>
-    <identity>donut</identity>
-    <password>android</password>
-  </accesspoint>
--->
-</resources>
-
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java
deleted file mode 100644
index 1222c8b..0000000
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java
+++ /dev/null
@@ -1,373 +0,0 @@
-/*
- * Copyright (C) 2010, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.connectivitymanagertest;
-
-import javax.xml.parsers.SAXParser;
-import javax.xml.parsers.SAXParserFactory;
-
-import org.xml.sax.Attributes;
-import org.xml.sax.SAXException;
-import org.xml.sax.helpers.DefaultHandler;
-
-import android.net.IpConfiguration.IpAssignment;
-import android.net.IpConfiguration.ProxySettings;
-import android.net.LinkAddress;
-import android.net.RouteInfo;
-import android.net.StaticIpConfiguration;
-import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiConfiguration.AuthAlgorithm;
-import android.net.wifi.WifiConfiguration.KeyMgmt;
-import android.net.wifi.WifiEnterpriseConfig;
-
-import java.io.InputStream;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.List;
-
-
-/**
- * Help class to process configurations of access points saved in an XML file.
- * The configurations of an access point is included in tag
- * <accesspoint></accesspoint>. The supported configuration includes: ssid,
- * security, eap, phase2, identity, password, anonymousidentity, cacert, usercert,
- * in which each is included in the corresponding tags. Static IP setting is also supported.
- * Tags that can be used include: ip, gateway, networkprefixlength, dns1, dns2. All access points
- * have to be enclosed in tags of <resources></resources>.
- *
- * The following is a sample configuration file for an access point using EAP-PEAP with MSCHAP2.
- * <resources>
- *   <accesspoint>
- *   <ssid>testnet</ssid>
- *   <security>EAP</security>
- *   <eap>PEAP</eap>
- *   <phase2>MSCHAP2</phase2>
- *   <identity>donut</identity</identity>
- *   <password>abcdefgh</password>
- *   </accesspoint>
- * </resources>
- *
- * Note:ssid and security have to be the first two tags
- *      for static ip setting, tag "ip" should be listed before other fields: dns, gateway,
- *      networkprefixlength.
- */
-public class AccessPointParserHelper {
-    static final int NONE = 0;
-    static final int WEP = 1;
-    static final int PSK = 2;
-    static final int EAP = 3;
-
-    List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>();
-
-    private int getSecurityType (String security) {
-        if (security.equalsIgnoreCase("NONE")) {
-            return NONE;
-        } else if (security.equalsIgnoreCase("WEP")) {
-            return WEP;
-        } else if (security.equalsIgnoreCase("PSK")) {
-            return PSK;
-        } else if (security.equalsIgnoreCase("EAP")) {
-            return EAP;
-        } else {
-            return -1;
-        }
-    }
-
-    private boolean validateEapValue(String value) {
-        if (value.equalsIgnoreCase("PEAP") ||
-                value.equalsIgnoreCase("TLS") ||
-                value.equalsIgnoreCase("TTLS")) {
-            return true;
-        } else {
-            return false;
-        }
-    }
-
-    DefaultHandler mHandler = new DefaultHandler() {
-
-        boolean ssid = false;
-        boolean security = false;
-        boolean password = false;
-        boolean ip = false;
-        boolean gateway = false;
-        boolean networkprefix = false;
-        boolean dns1 = false;
-        boolean dns2 = false;
-        boolean eap = false;
-        boolean phase2 = false;
-        boolean identity = false;
-        boolean anonymousidentity = false;
-        boolean cacert = false;
-        boolean usercert = false;
-        WifiConfiguration config = null;
-        int securityType = NONE;
-        StaticIpConfiguration mStaticIpConfiguration = null;
-        InetAddress mInetAddr = null;
-
-        @Override
-        public void startElement(String uri, String localName, String tagName,
-                Attributes attributes) throws SAXException {
-            if (tagName.equalsIgnoreCase("accesspoint")) {
-                config = new WifiConfiguration();
-            }
-            if (tagName.equalsIgnoreCase("ssid")) {
-                ssid = true;
-            }
-            if (tagName.equalsIgnoreCase("security")) {
-                security = true;
-            }
-            if (tagName.equalsIgnoreCase("password")) {
-                password = true;
-            }
-            if (tagName.equalsIgnoreCase("eap")) {
-                eap = true;
-            }
-            if (tagName.equalsIgnoreCase("phase2")) {
-                phase2 = true;
-            }
-            if (tagName.equalsIgnoreCase("identity")) {
-                identity = true;
-            }
-            if (tagName.equalsIgnoreCase("anonymousidentity")) {
-                anonymousidentity = true;
-            }
-            if (tagName.equalsIgnoreCase("cacert")) {
-                cacert = true;
-            }
-            if (tagName.equalsIgnoreCase("usercert")) {
-                usercert = true;
-            }
-            if (tagName.equalsIgnoreCase("ip")) {
-                mStaticIpConfiguration = new StaticIpConfiguration();
-                ip = true;
-            }
-            if (tagName.equalsIgnoreCase("gateway")) {
-                gateway = true;
-            }
-            if (tagName.equalsIgnoreCase("networkprefixlength")) {
-                networkprefix = true;
-            }
-            if (tagName.equalsIgnoreCase("dns1")) {
-                dns1 = true;
-            }
-            if (tagName.equalsIgnoreCase("dns2")) {
-                dns2 = true;
-            }
-        }
-
-        @Override
-        public void endElement(String uri, String localName, String tagName) throws SAXException {
-            if (tagName.equalsIgnoreCase("accesspoint")) {
-                if (mStaticIpConfiguration != null) {
-                    config.setIpAssignment(IpAssignment.STATIC);
-                    config.setStaticIpConfiguration(mStaticIpConfiguration);
-                } else {
-                    config.setIpAssignment(IpAssignment.DHCP);
-                }
-                config.setProxySettings(ProxySettings.NONE);
-                networks.add(config);
-                mStaticIpConfiguration = null;
-            }
-        }
-
-        @Override
-        public void characters(char ch[], int start, int length) throws SAXException {
-            if (ssid) {
-                config.SSID = new String(ch, start, length);
-                ssid = false;
-            }
-            if (security) {
-                String securityStr = (new String(ch, start, length)).toUpperCase();
-                securityType = getSecurityType(securityStr);
-                switch (securityType) {
-                    case NONE:
-                        config.allowedKeyManagement.set(KeyMgmt.NONE);
-                        break;
-                    case WEP:
-                        config.allowedKeyManagement.set(KeyMgmt.NONE);
-                        config.allowedAuthAlgorithms.set(AuthAlgorithm.OPEN);
-                        config.allowedAuthAlgorithms.set(AuthAlgorithm.SHARED);
-                        break;
-                    case PSK:
-                        config.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
-                        break;
-                    case EAP:
-                        config.allowedKeyManagement.set(KeyMgmt.WPA_EAP);
-                        config.allowedKeyManagement.set(KeyMgmt.IEEE8021X);
-                        // Initialize other fields.
-                        config.enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.NONE);
-                        config.enterpriseConfig.setCaCertificateAlias("");
-                        config.enterpriseConfig.setClientCertificateAlias("");
-                        config.enterpriseConfig.setIdentity("");
-                        config.enterpriseConfig.setAnonymousIdentity("");
-                        break;
-                    default:
-                        throw new SAXException();
-                }
-                security = false;
-            }
-            if (password) {
-                String passwordStr = new String(ch, start, length);
-                int len = passwordStr.length();
-                if (len == 0) {
-                    throw new SAXException();
-                }
-                if (securityType == WEP) {
-                    if ((len == 10 || len == 26 || len == 58) &&
-                            passwordStr.matches("[0-9A-Fa-f]*")) {
-                        config.wepKeys[0] = passwordStr;
-                    } else {
-                        config.wepKeys[0] = '"' + passwordStr + '"';
-                    }
-                } else if (securityType == PSK) {
-                    if (passwordStr.matches("[0-9A-Fa-f]{64}")) {
-                        config.preSharedKey = passwordStr;
-                    } else {
-                        config.preSharedKey = '"' + passwordStr + '"';
-                    }
-                } else if (securityType == EAP) {
-                    config.enterpriseConfig.setPassword(passwordStr);
-                } else {
-                    throw new SAXException();
-                }
-                password = false;
-            }
-            if (eap) {
-                String eapValue = new String(ch, start, length);
-                if (!validateEapValue(eapValue)) {
-                    throw new SAXException();
-                }
-                if (eapValue.equals("TLS")) {
-                    config.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
-                } else if (eapValue.equals("TTLS")) {
-                    config.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TTLS);
-                } else if (eapValue.equals("PEAP")) {
-                    config.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.PEAP);
-                }
-                eap = false;
-            }
-            if (phase2) {
-                String phase2Value = new String(ch, start, length);
-                if (phase2Value.equals("PAP")) {
-                    config.enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.PAP);
-                } else if (phase2Value.equals("MSCHAP")) {
-                    config.enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.MSCHAP);
-                } else if (phase2Value.equals("MSCHAPV2")) {
-                    config.enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.MSCHAPV2);
-                } else if (phase2Value.equals("GTC")) {
-                    config.enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.GTC);
-                }
-                phase2 = false;
-            }
-            if (identity) {
-                String identityValue = new String(ch, start, length);
-                config.enterpriseConfig.setIdentity(identityValue);
-                identity = false;
-            }
-            if (anonymousidentity) {
-                String anonyId = new String(ch, start, length);
-                config.enterpriseConfig.setAnonymousIdentity(anonyId);
-                anonymousidentity = false;
-            }
-            if (cacert) {
-                String cacertValue = new String(ch, start, length);
-                config.enterpriseConfig.setCaCertificateAlias(cacertValue);
-                cacert = false;
-            }
-            if (usercert) {
-                String usercertValue = new String(ch, start, length);
-                config.enterpriseConfig.setClientCertificateAlias(usercertValue);
-                usercert = false;
-            }
-            if (ip) {
-                try {
-                    String ipAddr = new String(ch, start, length);
-                    if (!InetAddress.isNumeric(ipAddr)) {
-                        throw new SAXException();
-                    }
-                    mInetAddr = InetAddress.getByName(ipAddr);
-                } catch (UnknownHostException e) {
-                    throw new SAXException();
-                }
-                ip = false;
-            }
-            if (gateway) {
-                try {
-                    String gwAddr = new String(ch, start, length);
-                    if (!InetAddress.isNumeric(gwAddr)) {
-                        throw new SAXException();
-                    }
-                    mStaticIpConfiguration.gateway = InetAddress.getByName(gwAddr);
-                } catch (UnknownHostException e) {
-                    throw new SAXException();
-                }
-                gateway = false;
-            }
-            if (networkprefix) {
-                try {
-                    int nwPrefixLength = Integer.parseInt(new String(ch, start, length));
-                    if ((nwPrefixLength < 0) || (nwPrefixLength > 32)) {
-                        throw new SAXException();
-                    }
-                    mStaticIpConfiguration.ipAddress = new LinkAddress(mInetAddr, nwPrefixLength);
-                } catch (NumberFormatException e) {
-                    throw new SAXException();
-                }
-                networkprefix = false;
-            }
-            if (dns1) {
-                try {
-                    String dnsAddr = new String(ch, start, length);
-                    if (!InetAddress.isNumeric(dnsAddr)) {
-                        throw new SAXException();
-                    }
-                    mStaticIpConfiguration.dnsServers.add(InetAddress.getByName(dnsAddr));
-                } catch (UnknownHostException e) {
-                    throw new SAXException();
-                }
-                dns1 = false;
-            }
-            if (dns2) {
-                try {
-                    String dnsAddr = new String(ch, start, length);
-                    if (!InetAddress.isNumeric(dnsAddr)) {
-                        throw new SAXException();
-                    }
-                    mStaticIpConfiguration.dnsServers.add(InetAddress.getByName(dnsAddr));
-                } catch (UnknownHostException e) {
-                    throw new SAXException();
-                }
-                dns2 = false;
-            }
-        }
-    };
-
-    /**
-     * Process the InputStream in
-     * @param in is the InputStream that can be used for XML parsing
-     * @throws Exception
-     */
-    public AccessPointParserHelper(InputStream in) throws Exception {
-        SAXParserFactory factory = SAXParserFactory.newInstance();
-        SAXParser saxParser = factory.newSAXParser();
-        saxParser.parse(in, mHandler);
-    }
-
-    public List<WifiConfiguration> getNetworkConfigurations() throws Exception {
-        return networks;
-    }
-}
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java
index 0f9d8e9..a3c5351 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java
@@ -26,7 +26,6 @@
 import android.net.NetworkInfo.State;
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiConfiguration.KeyMgmt;
 import android.net.wifi.WifiManager;
 import android.os.PowerManager;
 import android.os.SystemClock;
@@ -35,10 +34,8 @@
 import android.view.KeyEvent;
 
 import java.io.IOException;
-import java.io.InputStream;
 import java.net.UnknownHostException;
 import java.util.List;
-import java.util.regex.Pattern;
 
 
 /**
@@ -52,8 +49,6 @@
  */
 public class ConnectivityManagerTestBase extends InstrumentationTestCase {
 
-    private static final String LOG_TAG = "ConnectivityManagerTestBase";
-    private static final String ACCESS_POINT_FILE = "accesspoints.xml";
     private static final String PING_IP_ADDR = "8.8.8.8";
 
     protected static final int WAIT_FOR_SCAN_RESULT = 10 * 1000; //10 seconds
@@ -69,9 +64,10 @@
     protected static final int FAILURE = 1;
     protected static final int INIT = -1;
 
+    protected final String mLogTag;
+
     private ConnectivityReceiver mConnectivityReceiver = null;
     private WifiReceiver mWifiReceiver = null;
-    private AccessPointParserHelper mParseHelper = null;
 
     private long mLastConnectivityChangeTime = -1;
     protected ConnectivityManager mCm;
@@ -82,6 +78,11 @@
     /* Control Wifi States */
     public WifiManager mWifiManager;
 
+    public ConnectivityManagerTestBase(String logTag) {
+        super();
+        mLogTag = logTag;
+    }
+
     protected long getLastConnectivityChangeTime() {
         return mLastConnectivityChangeTime;
     }
@@ -94,7 +95,7 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             mLastConnectivityChangeTime = SystemClock.uptimeMillis();
-            log("ConnectivityReceiver: " + intent);
+            logv("ConnectivityReceiver: " + intent);
             String action = intent.getAction();
             if (!action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
                 Log.v("ConnectivityReceiver", "onReceive() called with " + intent);
@@ -108,7 +109,7 @@
             String action = intent.getAction();
             Log.v("WifiReceiver", "onReceive() is calleld with " + intent);
             if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
-                log("scan results are available");
+                logv("scan results are available");
                 synchronized (mWifiScanResultLock) {
                     mLastScanResult = mWifiManager.getScanResults();
                     mWifiScanResultLock.notifyAll();
@@ -130,7 +131,7 @@
         if (mWifiManager.isWifiApEnabled()) {
             // if soft AP is enabled, disable it
             mWifiManager.setWifiApEnabled(null, false);
-            log("Disable soft ap");
+            logv("Disable soft ap");
         }
 
         // register a connectivity receiver for CONNECTIVITY_ACTION;
@@ -148,32 +149,25 @@
         mIntentFilter.addAction(ConnectivityManager.ACTION_TETHER_STATE_CHANGED);
         mContext.registerReceiver(mWifiReceiver, mIntentFilter);
 
-        log("Clear Wifi before we start the test.");
+        logv("Clear Wifi before we start the test.");
         removeConfiguredNetworksAndDisableWifi();
      }
 
-    protected List<WifiConfiguration> loadNetworkConfigurations() throws Exception {
-        InputStream in = mContext.getAssets().open(ACCESS_POINT_FILE);
-        mParseHelper = new AccessPointParserHelper(in);
-        return mParseHelper.getNetworkConfigurations();
-    }
-
     // wait for network connectivity state: CONNECTING, CONNECTED, SUSPENDED, DISCONNECTING,
     //                                      DISCONNECTED, UNKNOWN
     protected boolean waitForNetworkState(int networkType, State expectedState, long timeout) {
         long startTime = SystemClock.uptimeMillis();
         while (true) {
             NetworkInfo ni = mCm.getNetworkInfo(networkType);
-            String niString = ni == null ? "null" : ni.toString();
             if (ni != null && expectedState.equals(ni.getState())) {
-                log("waitForNetworkState success: " + niString);
+                logv("waitForNetworkState success: %s", ni);
                 return true;
             }
             if ((SystemClock.uptimeMillis() - startTime) > timeout) {
-                log("waitForNetworkState timeout: " + niString);
+                logv("waitForNetworkState timeout: %s", ni);
                 return false;
             }
-            log("waitForNetworkState interim: " + niString);
+            logv("waitForNetworkState interim: %s", ni);
             SystemClock.sleep(SHORT_TIMEOUT);
         }
     }
@@ -185,16 +179,14 @@
         while (true) {
             int state = mWifiManager.getWifiState();
             if (state == expectedState) {
-                log("waitForWifiState success: state=" + state);
+                logv("waitForWifiState success: state=" + state);
                 return true;
             }
             if ((SystemClock.uptimeMillis() - startTime) > timeout) {
-                log(String.format("waitForWifiState timeout: expected=%d, actual=%d",
-                        expectedState, state));
+                logv("waitForWifiState timeout: expected=%d, actual=%d", expectedState, state);
                 return false;
             }
-            log(String.format("waitForWifiState interim: expected=%d, actual=%d",
-                    expectedState, state));
+            logv("waitForWifiState interim: expected=%d, actual=%d", expectedState, state);
             SystemClock.sleep(SHORT_TIMEOUT);
         }
     }
@@ -206,15 +198,15 @@
         while (true) {
             int state = mWifiManager.getWifiApState();
             if (state == expectedState) {
-                log("waitForWifiAPState success: state=" + state);
+                logv("waitForWifiAPState success: state=" + state);
                 return true;
             }
             if ((SystemClock.uptimeMillis() - startTime) > timeout) {
-                log(String.format("waitForWifiAPState timeout: expected=%d, actual=%d",
+                logv(String.format("waitForWifiAPState timeout: expected=%d, actual=%d",
                         expectedState, state));
                 return false;
             }
-            log(String.format("waitForWifiAPState interim: expected=%d, actual=%d",
+            logv(String.format("waitForWifiAPState interim: expected=%d, actual=%d",
                     expectedState, state));
             SystemClock.sleep(SHORT_TIMEOUT);
         }
@@ -269,7 +261,7 @@
 
     // Turn screen off
     protected void turnScreenOff() {
-        log("Turn screen off");
+        logv("Turn screen off");
         PowerManager pm =
             (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
         pm.goToSleep(SystemClock.uptimeMillis());
@@ -277,7 +269,7 @@
 
     // Turn screen on
     protected void turnScreenOn() {
-        log("Turn screen on");
+        logv("Turn screen on");
         PowerManager pm =
                 (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
         pm.wakeUp(SystemClock.uptimeMillis());
@@ -305,7 +297,7 @@
                 // assume the chance that all servers are down is very small
                 for (int i = 0; i < hostList.length; i++ ) {
                     String host = hostList[i];
-                    log("Start ping test, ping " + host);
+                    logv("Start ping test, ping " + host);
                     Process p = Runtime.getRuntime().exec("ping -c 10 -w 100 " + host);
                     int status = p.waitFor();
                     if (status == 0) {
@@ -314,11 +306,11 @@
                     }
                 }
             } catch (UnknownHostException e) {
-                log("Ping test Fail: Unknown Host");
+                logv("Ping test Fail: Unknown Host");
             } catch (IOException e) {
-                log("Ping test Fail:  IOException");
+                logv("Ping test Fail:  IOException");
             } catch (InterruptedException e) {
-                log("Ping test Fail: InterruptedException");
+                logv("Ping test Fail: InterruptedException");
             }
         }
         // ping test timeout
@@ -331,29 +323,22 @@
      * We don't verify whether the connection is successful or not, leave this to the test
      */
     protected boolean connectToWifi(String knownSSID) {
-        WifiConfiguration config = new WifiConfiguration();
-        config.SSID = knownSSID;
-        config.allowedKeyManagement.set(KeyMgmt.NONE);
+        WifiConfiguration config = WifiConfigurationHelper.createOpenConfig(knownSSID);
         return connectToWifiWithConfiguration(config);
     }
 
     /**
      * Connect to Wi-Fi with the given configuration. Note the SSID in the configuration
      * is pure string, we need to convert it to quoted string.
-     * @param config
-     * @return
      */
     protected boolean connectToWifiWithConfiguration(WifiConfiguration config) {
-        String ssid = config.SSID;
-        config.SSID = convertToQuotedString(ssid);
-
         // If Wifi is not enabled, enable it
         if (!mWifiManager.isWifiEnabled()) {
-            log("Wifi is not enabled, enable it");
+            logv("Wifi is not enabled, enable it");
             mWifiManager.setWifiEnabled(true);
             // wait for the wifi state change before start scanning.
             if (!waitForWifiState(WifiManager.WIFI_STATE_ENABLED, LONG_TIMEOUT)) {
-                log("wait for WIFI_STATE_ENABLED failed");
+                logv("wait for WIFI_STATE_ENABLED failed");
                 return false;
             }
         }
@@ -361,10 +346,13 @@
         // Save network configuration and connect to network without scanning
         mWifiManager.connect(config,
             new WifiManager.ActionListener() {
+                @Override
                 public void onSuccess() {
                 }
+
+                @Override
                 public void onFailure(int reason) {
-                    log("connect failure " + reason);
+                    logv("connect failure " + reason);
                 }
             });
         return true;
@@ -376,25 +364,28 @@
     protected boolean disconnectAP() {
         // remove saved networks
         if (!mWifiManager.isWifiEnabled()) {
-            log("Enabled wifi before remove configured networks");
+            logv("Enabled wifi before remove configured networks");
             mWifiManager.setWifiEnabled(true);
             SystemClock.sleep(SHORT_TIMEOUT);
         }
 
         List<WifiConfiguration> wifiConfigList = mWifiManager.getConfiguredNetworks();
         if (wifiConfigList == null) {
-            log("no configuration list is null");
+            logv("no configuration list is null");
             return true;
         }
-        log("size of wifiConfigList: " + wifiConfigList.size());
+        logv("size of wifiConfigList: " + wifiConfigList.size());
         for (WifiConfiguration wifiConfig: wifiConfigList) {
-            log("remove wifi configuration: " + wifiConfig.networkId);
+            logv("remove wifi configuration: " + wifiConfig.networkId);
             int netId = wifiConfig.networkId;
             mWifiManager.forget(netId, new WifiManager.ActionListener() {
+                    @Override
                     public void onSuccess() {
                     }
+
+                    @Override
                     public void onFailure(int reason) {
-                        log("Failed to forget " + reason);
+                        logv("Failed to forget " + reason);
                     }
                 });
         }
@@ -431,15 +422,14 @@
         long startTime = SystemClock.uptimeMillis();
         while (true) {
             NetworkInfo ni = mCm.getActiveNetworkInfo();
-            String niString = ni == null ? "null" : ni.toString();
             if (ni != null && ni.isConnected()) {
                 return true;
             }
             if ((SystemClock.uptimeMillis() - startTime) > timeout) {
-                log("waitForActiveNetworkConnection timeout: " + niString);
+                logv("waitForActiveNetworkConnection timeout: %s", ni);
                 return false;
             }
-            log("waitForActiveNetworkConnection interim: " + niString);
+            logv("waitForActiveNetworkConnection interim: %s", ni);
             SystemClock.sleep(SHORT_TIMEOUT);
         }
     }
@@ -451,12 +441,11 @@
             if (ni == null) {
                 return true;
             }
-            String niString = ni.toString();
             if ((SystemClock.uptimeMillis() - startTime) > timeout) {
-                log("waitForActiveNetworkConnection timeout: " + niString);
+                logv("waitForActiveNetworkConnection timeout: %s", ni);
                 return false;
             }
-            log("waitForActiveNetworkConnection interim: " + niString);
+            logv("waitForActiveNetworkConnection interim: %s", ni);
             SystemClock.sleep(SHORT_TIMEOUT);
         }
     }
@@ -467,12 +456,9 @@
         try {
             Process proc = Runtime.getRuntime().exec(new String[]{
                     "/system/bin/ping", "-W", "30", "-c", "1", PING_IP_ADDR});
-            int exitCode = proc.waitFor();
-            return exitCode == 0;
-        } catch (InterruptedException ie) {
-            Log.e(LOG_TAG, "InterruptedException while waiting for ping");
-        } catch (IOException ioe) {
-            Log.e(LOG_TAG, "IOException during ping", ioe);
+            return proc.waitFor() == 0;
+        } catch (InterruptedException | IOException e) {
+            Log.e(mLogTag, "Ping failed", e);
         }
         return false;
     }
@@ -489,8 +475,8 @@
         super.tearDown();
     }
 
-    private void log(String message) {
-        Log.v(LOG_TAG, message);
+    protected void logv(String format, Object... args) {
+        Log.v(mLogTag, String.format(format, args));
     }
 
     /**
@@ -506,22 +492,10 @@
         // step 2: verify Wifi state and network state;
         assertTrue("wifi state not connected with " + config.SSID,
                 waitForNetworkState(ConnectivityManager.TYPE_WIFI,
-                State.CONNECTED, LONG_TIMEOUT));
+                State.CONNECTED, WIFI_CONNECTION_TIMEOUT));
 
         // step 3: verify the current connected network is the given SSID
         assertNotNull("no active wifi info", mWifiManager.getConnectionInfo());
         assertEquals("SSID mismatch", config.SSID, mWifiManager.getConnectionInfo().getSSID());
     }
-
-    /**
-     * checks if the input is a hexadecimal string of given length
-     *
-     * @param input string to be checked
-     * @param length required length of the string
-     * @return
-     */
-    protected static boolean isHex(String input, int length) {
-        Pattern p = Pattern.compile(String.format("[0-9A-Fa-f]{%d}", length));
-        return p.matcher(input).matches();
-    }
 }
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/WifiConfigurationHelper.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/WifiConfigurationHelper.java
new file mode 100644
index 0000000..f0a8367
--- /dev/null
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/WifiConfigurationHelper.java
@@ -0,0 +1,364 @@
+/*
+ * Copyright (C) 2010, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.connectivitymanagertest;
+
+import android.net.IpConfiguration.IpAssignment;
+import android.net.IpConfiguration.ProxySettings;
+import android.net.LinkAddress;
+import android.net.StaticIpConfiguration;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiConfiguration.AuthAlgorithm;
+import android.net.wifi.WifiConfiguration.KeyMgmt;
+import android.net.wifi.WifiEnterpriseConfig;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Helper for dealing with creating {@link WifiConfiguration} objects.
+ */
+public class WifiConfigurationHelper {
+    private static final int NONE = 0;
+    private static final int WEP = 1;
+    private static final int PSK = 2;
+    private static final int EAP = 3;
+
+    /**
+     * Private constructor since this a static class.
+     */
+    private WifiConfigurationHelper() {}
+
+    /**
+     * Create a {@link WifiConfiguration} for an open network
+     *
+     * @param ssid The SSID of the wifi network
+     * @return The {@link WifiConfiguration}
+     */
+    public static WifiConfiguration createOpenConfig(String ssid) {
+        WifiConfiguration config = createGenericConfig(ssid);
+
+        config.allowedKeyManagement.set(KeyMgmt.NONE);
+        return config;
+    }
+
+    /**
+     * Create a {@link WifiConfiguration} for a WEP secured network
+     *
+     * @param ssid The SSID of the wifi network
+     * @param password Either a 10, 26, or 58 character hex string or the plain text password
+     * @return The {@link WifiConfiguration}
+     */
+    public static WifiConfiguration createWepConfig(String ssid, String password) {
+        WifiConfiguration config = createGenericConfig(ssid);
+
+        if (isHex(password, 10) || isHex(password, 26) || isHex(password, 58)) {
+            config.wepKeys[0] = password;
+        } else {
+            config.wepKeys[0] = quotedString(password);
+        }
+
+        config.allowedKeyManagement.set(KeyMgmt.NONE);
+        config.allowedAuthAlgorithms.set(AuthAlgorithm.OPEN);
+        config.allowedAuthAlgorithms.set(AuthAlgorithm.SHARED);
+        return config;
+    }
+
+    /**
+     * Create a {@link WifiConfiguration} for a PSK secured network
+     *
+     * @param ssid The SSID of the wifi network
+     * @param password Either a 64 character hex string or the plain text password
+     * @return The {@link WifiConfiguration}
+     */
+    public static WifiConfiguration createPskConfig(String ssid, String password) {
+        WifiConfiguration config = createGenericConfig(ssid);
+
+        if (isHex(password, 64)) {
+            config.preSharedKey = password;
+        } else {
+            config.preSharedKey = quotedString(password);
+        }
+        config.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
+        return config;
+    }
+
+    /**
+     * Create a {@link WifiConfiguration} for an EAP secured network
+     *
+     * @param ssid The SSID of the wifi network
+     * @param password The password
+     * @param eapMethod The EAP method
+     * @param phase2 The phase 2 method or null
+     * @param identity The identity or null
+     * @param anonymousIdentity The anonymous identity or null
+     * @param caCert The CA certificate or null
+     * @param clientCert The client certificate or null
+     * @return The {@link WifiConfiguration}
+     */
+    public static WifiConfiguration createEapConfig(String ssid, String password, int eapMethod,
+            Integer phase2, String identity, String anonymousIdentity, String caCert,
+            String clientCert) {
+        WifiConfiguration config = new WifiConfiguration();
+        config.SSID = quotedString(ssid);
+
+        config.allowedKeyManagement.set(KeyMgmt.WPA_EAP);
+        config.allowedKeyManagement.set(KeyMgmt.IEEE8021X);
+
+        // Set defaults
+        if (phase2 == null) phase2 = WifiEnterpriseConfig.Phase2.NONE;
+        if (identity == null) identity = "";
+        if (anonymousIdentity == null) anonymousIdentity = "";
+        if (caCert == null) caCert = "";
+        if (clientCert == null) clientCert = "";
+
+        config.enterpriseConfig.setPassword(password);
+        config.enterpriseConfig.setEapMethod(eapMethod);
+        config.enterpriseConfig.setPhase2Method(phase2);
+        config.enterpriseConfig.setIdentity(identity);
+        config.enterpriseConfig.setAnonymousIdentity(anonymousIdentity);
+        config.enterpriseConfig.setCaCertificateAlias(caCert);
+        config.enterpriseConfig.setClientCertificateAlias(clientCert);
+        return config;
+    }
+
+    /**
+     * Create a generic {@link WifiConfiguration} used by the other create methods.
+     */
+    private static WifiConfiguration createGenericConfig(String ssid) {
+        WifiConfiguration config = new WifiConfiguration();
+        config.SSID = quotedString(ssid);
+        config.setIpAssignment(IpAssignment.DHCP);
+        config.setProxySettings(ProxySettings.NONE);
+        return config;
+    }
+
+    /**
+     * Parse a JSON string for WiFi configurations stored as a JSON string.
+     * <p>
+     * This json string should be a list of dictionaries, with each dictionary containing a single
+     * wifi configuration. The wifi configuration requires the fields "ssid" and "security" with
+     * security being one of NONE, WEP, PSK, or EAP. If WEP, PSK, or EAP are selected, the field
+     * "password" must also be provided.  If EAP is selected, then the fiels "eap", "phase2",
+     * "identity", "ananymous_identity", "ca_cert", and "client_cert" are also required. Lastly,
+     * static IP settings are also supported.  If the field "ip" is set, then the fields "gateway",
+     * "prefix_length", "dns1", and "dns2" are required.
+     * </p>
+     * @throws IllegalArgumentException if the input string was not valid JSON or if any mandatory
+     * fields are missing.
+     */
+    public static List<WifiConfiguration> parseJson(String in) {
+        try {
+            JSONArray jsonConfigs = new JSONArray(in);
+            List<WifiConfiguration> wifiConfigs = new ArrayList<>(jsonConfigs.length());
+
+            for (int i = 0; i < jsonConfigs.length(); i++) {
+                JSONObject jsonConfig = jsonConfigs.getJSONObject(i);
+
+                wifiConfigs.add(getWifiConfiguration(jsonConfig));
+            }
+            return wifiConfigs;
+        } catch (JSONException e) {
+            throw new IllegalArgumentException(e);
+        }
+    }
+
+    /**
+     * Parse a {@link JSONObject} and return the wifi configuration.
+     *
+     * @throws IllegalArgumentException if any mandatory fields are missing.
+     */
+    private static WifiConfiguration getWifiConfiguration(JSONObject jsonConfig)
+            throws JSONException {
+        String ssid = jsonConfig.getString("ssid");
+        String password = null;
+        WifiConfiguration config;
+
+        int securityType = getSecurityType(jsonConfig.getString("security"));
+        switch (securityType) {
+            case NONE:
+                config = createOpenConfig(ssid);
+                break;
+            case WEP:
+                password = jsonConfig.getString("password");
+                config = createWepConfig(ssid, password);
+                break;
+            case PSK:
+                password = jsonConfig.getString("password");
+                config = createPskConfig(ssid, password);
+                break;
+            case EAP:
+                password = jsonConfig.getString("password");
+                int eapMethod = getEapMethod(jsonConfig.getString("eap"));
+                Integer phase2 = null;
+                if (jsonConfig.has("phase2")) {
+                    phase2 = getPhase2(jsonConfig.getString("phase2"));
+                }
+                String identity = null;
+                if (jsonConfig.has("identity")) {
+                    identity = jsonConfig.getString("identity");
+                }
+                String anonymousIdentity = null;
+                if (jsonConfig.has("anonymous_identity")) {
+                    anonymousIdentity = jsonConfig.getString("anonymous_identity");
+                }
+                String caCert = null;
+                if (jsonConfig.has("ca_cert")) {
+                    caCert = (jsonConfig.getString("ca_cert"));
+                }
+                String clientCert = null;
+                if (jsonConfig.has("client_cert")) {
+                    clientCert = jsonConfig.getString("client_cert");
+                }
+                config = createEapConfig(ssid, password, eapMethod, phase2, identity,
+                        anonymousIdentity, caCert, clientCert);
+                break;
+            default:
+                // Should never reach here as getSecurityType will already throw an exception
+                throw new IllegalArgumentException();
+        }
+
+        if (jsonConfig.has("ip")) {
+            StaticIpConfiguration staticIpConfig = new StaticIpConfiguration();
+
+            InetAddress ipAddress = getInetAddress(jsonConfig.getString("ip"));
+            int prefixLength = getPrefixLength(jsonConfig.getInt("prefix_length"));
+            staticIpConfig.ipAddress = new LinkAddress(ipAddress, prefixLength);
+            staticIpConfig.gateway = getInetAddress(jsonConfig.getString("gateway"));
+            staticIpConfig.dnsServers.add(getInetAddress(jsonConfig.getString("dns1")));
+            staticIpConfig.dnsServers.add(getInetAddress(jsonConfig.getString("dns2")));
+
+            config.setIpAssignment(IpAssignment.STATIC);
+            config.setStaticIpConfiguration(staticIpConfig);
+        } else {
+            config.setIpAssignment(IpAssignment.DHCP);
+        }
+
+        config.setProxySettings(ProxySettings.NONE);
+        return config;
+    }
+
+    private static String quotedString(String s) {
+        return String.format("\"%s\"", s);
+    }
+
+    /**
+     * Get the security type from a string.
+     *
+     * @throws IllegalArgumentException if the string is not a supported security type.
+     */
+    private static int getSecurityType(String security) {
+        if ("NONE".equalsIgnoreCase(security)) {
+            return NONE;
+        }
+        if ("WEP".equalsIgnoreCase(security)) {
+            return WEP;
+        }
+        if ("PSK".equalsIgnoreCase(security)) {
+            return PSK;
+        }
+        if ("EAP".equalsIgnoreCase(security)) {
+            return EAP;
+        }
+        throw new IllegalArgumentException("Security type must be one of NONE, WEP, PSK, or EAP");
+    }
+
+    /**
+     * Get the EAP method from a string.
+     *
+     * @throws IllegalArgumentException if the string is not a supported EAP method.
+     */
+    private static int getEapMethod(String eapMethod) {
+        if ("TLS".equalsIgnoreCase(eapMethod)) {
+            return WifiEnterpriseConfig.Eap.TLS;
+        }
+        if ("TTLS".equalsIgnoreCase(eapMethod)) {
+            return WifiEnterpriseConfig.Eap.TTLS;
+        }
+        if ("PEAP".equalsIgnoreCase(eapMethod)) {
+            return WifiEnterpriseConfig.Eap.PEAP;
+        }
+        throw new IllegalArgumentException("EAP method must be one of TLS, TTLS, or PEAP");
+    }
+
+    /**
+     * Get the phase 2 method from a string.
+     *
+     * @throws IllegalArgumentException if the string is not a supported phase 2 method.
+     */
+    private static int getPhase2(String phase2) {
+        if ("PAP".equalsIgnoreCase(phase2)) {
+            return WifiEnterpriseConfig.Phase2.PAP;
+        }
+        if ("MSCHAP".equalsIgnoreCase(phase2)) {
+            return WifiEnterpriseConfig.Phase2.MSCHAP;
+        }
+        if ("MSCHAPV2".equalsIgnoreCase(phase2)) {
+            return WifiEnterpriseConfig.Phase2.MSCHAPV2;
+        }
+        if ("GTC".equalsIgnoreCase(phase2)) {
+            return WifiEnterpriseConfig.Phase2.GTC;
+        }
+        throw new IllegalArgumentException("Phase2 must be one of PAP, MSCHAP, MSCHAPV2, or GTC");
+    }
+
+    /**
+     * Get an {@link InetAddress} from a string
+     *
+     * @throws IllegalArgumentException if the string is not a valid IP address.
+     */
+    private static InetAddress getInetAddress(String ipAddress) {
+        if (!InetAddress.isNumeric(ipAddress)) {
+            throw new IllegalArgumentException(
+                    String.format("IP address %s is not numeric", ipAddress));
+        }
+
+        try {
+            return InetAddress.getByName(ipAddress);
+        } catch (UnknownHostException e) {
+            throw new IllegalArgumentException(
+                    String.format("IP address %s could not be resolved", ipAddress));
+        }
+    }
+
+    /**
+     * Get the prefix length from an int.
+     *
+     * @throws IllegalArgumentException if the prefix length is less than 0 or greater than 32.
+     */
+    private static int getPrefixLength(int prefixLength) {
+        if (prefixLength < 0 || prefixLength > 32) {
+            throw new IllegalArgumentException("Prefix length cannot be less than 0 or more than 32");
+        }
+        return prefixLength;
+    }
+
+    /**
+     * Utility method to check if a given string is a hexadecimal string of given length
+     */
+    public static boolean isHex(String input, int length) {
+        if (input == null || length < 0) {
+            return false;
+        }
+        return input.matches(String.format("[0-9A-Fa-f]{%d}", length));
+    }
+}
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java
index 01995c7..b280106 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java
@@ -23,14 +23,15 @@
 import android.os.SystemClock;
 import android.provider.Settings;
 import android.test.suitebuilder.annotation.LargeTest;
-import android.util.Log;
 
 import com.android.connectivitymanagertest.ConnectivityManagerTestBase;
 import com.android.connectivitymanagertest.ConnectivityManagerTestRunner;
 
-public class ConnectivityManagerMobileTest extends
-        ConnectivityManagerTestBase {
-    private static final String TAG = "ConnectivityManagerMobileTest";
+public class ConnectivityManagerMobileTest extends  ConnectivityManagerTestBase {
+
+    public ConnectivityManagerMobileTest() {
+        super(ConnectivityManagerMobileTest.class.getSimpleName());
+    }
 
     private String mTestAccessPoint;
     private boolean mWifiOnlyFlag;
@@ -46,7 +47,7 @@
         // Each test case will start with cellular connection
         if (Settings.Global.getInt(getInstrumentation().getContext().getContentResolver(),
                 Settings.Global.AIRPLANE_MODE_ON) == 1) {
-            log("airplane is not disabled, disable it.");
+            logv("airplane is not disabled, disable it.");
             mCm.setAirplaneMode(false);
         }
 
@@ -77,16 +78,14 @@
         assertTrue("not connected to cellular network", extraNetInfo.isConnected());
     }
 
-    private void log(String message) {
-        Log.v(TAG, message);
-    }
+
 
     // Test case 1: Test enabling Wifi without associating with any AP, no broadcast on network
     //              event should be expected.
     @LargeTest
     public void test3GToWifiNotification() {
         if (mWifiOnlyFlag) {
-            Log.v(TAG, getName() + " is excluded for wifi-only test");
+            logv(getName() + " is excluded for wifi-only test");
             return;
         }
 
@@ -225,7 +224,7 @@
     @LargeTest
     public void testDataConnectionWith3GToAmTo3G() {
         if (mWifiOnlyFlag) {
-            Log.v(TAG, getName() + " is excluded for wifi-only test");
+            logv(getName() + " is excluded for wifi-only test");
             return;
         }
         // disable wifi
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiAssociationTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiAssociationTest.java
index eb75b0d..c2b80dc 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiAssociationTest.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiAssociationTest.java
@@ -21,16 +21,15 @@
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiConfiguration.AuthAlgorithm;
 import android.net.wifi.WifiConfiguration.GroupCipher;
-import android.net.wifi.WifiConfiguration.KeyMgmt;
 import android.net.wifi.WifiConfiguration.PairwiseCipher;
 import android.net.wifi.WifiConfiguration.Protocol;
 import android.net.wifi.WifiInfo;
 import android.os.Bundle;
 import android.test.suitebuilder.annotation.LargeTest;
-import android.util.Log;
 
 import com.android.connectivitymanagertest.ConnectivityManagerTestBase;
 import com.android.connectivitymanagertest.WifiAssociationTestRunner;
+import com.android.connectivitymanagertest.WifiConfigurationHelper;
 
 /**
  * Test Wi-Fi connection with different configuration
@@ -40,7 +39,6 @@
  * -w com.android.connectivitymanagertest/.WifiAssociationTestRunner"
  */
 public class WifiAssociationTest extends ConnectivityManagerTestBase {
-    private static final String TAG = "WifiAssociationTest";
     private String mSsid = null;
     private String mPassword = null;
     private String mSecurityType = null;
@@ -51,6 +49,10 @@
         OPEN, WEP64, WEP128, WPA_TKIP, WPA2_AES
     }
 
+    public WifiAssociationTest() {
+        super(WifiAssociationTest.class.getSimpleName());
+    }
+
     @Override
     protected void setUp() throws Exception {
         super.setUp();
@@ -77,90 +79,58 @@
     private void validateFrequencyBand() {
         if (mFrequencyBand != null) {
             int currentFreq = mWifiManager.getFrequencyBand();
-            Log.v(TAG, "read frequency band: " + currentFreq);
+            logv("read frequency band: " + currentFreq);
             assertEquals("specified frequency band does not match operational band of WifiManager",
                     currentFreq, mBand);
          }
     }
 
-    private void log(String message) {
-        Log.v(TAG, message);
-    }
-
     @LargeTest
     public void testWifiAssociation() {
         assertNotNull("no test ssid", mSsid);
-        WifiConfiguration config = new WifiConfiguration();
-        config.SSID = mSsid;
+        WifiConfiguration config = null;
         SECURITY_TYPE security = SECURITY_TYPE.valueOf(mSecurityType);
-        log("Security type is " + security.toString());
+        logv("Security type is " + security.toString());
         switch (security) {
             // set network configurations
             case OPEN:
-                config.allowedKeyManagement.set(KeyMgmt.NONE);
+                config = WifiConfigurationHelper.createOpenConfig(mSsid);
                 break;
             case WEP64:
                 assertNotNull("password is empty", mPassword);
-                config.allowedKeyManagement.set(KeyMgmt.NONE);
-                config.allowedAuthAlgorithms.set(AuthAlgorithm.OPEN);
-                config.allowedAuthAlgorithms.set(AuthAlgorithm.SHARED);
+                // always use hex pair for WEP-40
+                assertTrue(WifiConfigurationHelper.isHex(mPassword, 10));
+                config = WifiConfigurationHelper.createWepConfig(mSsid, mPassword);
                 config.allowedGroupCiphers.set(GroupCipher.WEP40);
-                if (mPassword != null) {
-                    // always use hex pair for WEP-40
-                    if (isHex(mPassword, 10)) {
-                        config.wepKeys[0] = mPassword;
-                    } else {
-                        fail("password should be 10-character hex");
-                    }
-                }
                 break;
             case WEP128:
                 assertNotNull("password is empty", mPassword);
-                config.allowedKeyManagement.set(KeyMgmt.NONE);
-                config.allowedAuthAlgorithms.set(AuthAlgorithm.OPEN);
-                config.allowedAuthAlgorithms.set(AuthAlgorithm.SHARED);
+                // always use hex pair for WEP-104
+                assertTrue(WifiConfigurationHelper.isHex(mPassword, 26));
+                config = WifiConfigurationHelper.createWepConfig(mSsid, mPassword);
                 config.allowedGroupCiphers.set(GroupCipher.WEP104);
-                if (mPassword != null) {
-                    // always use hex pair for WEP-104
-                    if (isHex(mPassword, 26)) {
-                        config.wepKeys[0] = mPassword;
-                    } else {
-                        fail("password should be 26-character hex");
-                    }
-                }
                 break;
             case WPA_TKIP:
                 assertNotNull("password is empty", mPassword);
-                config.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
+                config = WifiConfigurationHelper.createPskConfig(mSsid, mPassword);
                 config.allowedAuthAlgorithms.set(AuthAlgorithm.OPEN);
                 config.allowedProtocols.set(Protocol.WPA);
                 config.allowedPairwiseCiphers.set(PairwiseCipher.TKIP);
                 config.allowedGroupCiphers.set(GroupCipher.TKIP);
-                if (isHex(mPassword, 64)) {
-                    config.preSharedKey = mPassword;
-                } else {
-                    config.preSharedKey = '"' + mPassword + '"';
-                }
                 break;
             case WPA2_AES:
                 assertNotNull("password is empty", mPassword);
-                config.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
+                config = WifiConfigurationHelper.createPskConfig(mSsid, mPassword);
                 config.allowedAuthAlgorithms.set(AuthAlgorithm.OPEN);
                 config.allowedProtocols.set(Protocol.RSN);
                 config.allowedPairwiseCiphers.set(PairwiseCipher.CCMP);
                 config.allowedGroupCiphers.set(GroupCipher.CCMP);
-                config.allowedProtocols.set(Protocol.RSN);
-                if (isHex(mPassword, 64)) {
-                    config.preSharedKey = mPassword;
-                } else {
-                    config.preSharedKey = '"' + mPassword + '"';
-                }
                 break;
             default:
                 fail("Not a valid security type: " + mSecurityType);
                 break;
         }
-        Log.v(TAG, "network config: " + config.toString());
+        logv("network config: %s", config.toString());
         connectToWifi(config);
         // verify that connection actually works
         assertTrue("no network connectivity at end of test", checkNetworkConnectivity());
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java
index 740ffb8..b37daa3 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java
@@ -16,54 +16,38 @@
 
 package com.android.connectivitymanagertest.functional;
 
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo.State;
 import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiInfo;
+import android.os.SystemClock;
 import android.test.suitebuilder.annotation.LargeTest;
-import android.util.Log;
 
 import com.android.connectivitymanagertest.ConnectivityManagerTestBase;
+import com.android.connectivitymanagertest.WifiConfigurationHelper;
 
-import java.util.ArrayList;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
 import java.util.List;
 
 /**
  * Test Wi-Fi connection with different configuration
  * To run this tests:
- *     adb shell am instrument -e class
- *          com.android.connectivitymanagertest.functional.WifiConnectionTest
- *          -w com.android.connectivitymanagertest/.ConnectivityManagerTestRunner
+ *     adb shell am instrument \
+ *         -e class com.android.connectivitymanagertest.functional.WifiConnectionTest \
+ *         -w com.android.connectivitymanagertest/.ConnectivityManagerTestRunner
  */
-public class WifiConnectionTest
-    extends ConnectivityManagerTestBase {
-    private static final String TAG = "WifiConnectionTest";
-    private static final boolean DEBUG = false;
-    private List<WifiConfiguration> mNetworks = new ArrayList<WifiConfiguration>();
+public class WifiConnectionTest extends ConnectivityManagerTestBase {
+    private static final String WIFI_CONFIG_FILE = "/data/wifi_configs.json";
+    private static final long PAUSE_DURATION_MS = 60 * 1000;
+
+    public WifiConnectionTest() {
+        super(WifiConnectionTest.class.getSimpleName());
+    }
 
     @Override
     public void setUp() throws Exception {
         super.setUp();
-        mNetworks = loadNetworkConfigurations();
-        if (DEBUG) {
-            printNetworkConfigurations();
-        }
-
-        // enable wifi and verify wpa_supplicant is started
-        assertTrue("enable Wifi failed", enableWifi());
-        assertTrue("wifi not connected", waitForNetworkState(
-                ConnectivityManager.TYPE_WIFI, State.CONNECTED, LONG_TIMEOUT));
-        WifiInfo wi = mWifiManager.getConnectionInfo();
-        assertNotNull("no active wifi info", wi);
-        assertTrue("failed to ping wpa_supplicant ", mWifiManager.pingSupplicant());
-    }
-
-    private void printNetworkConfigurations() {
-        log("==== print network configurations parsed from XML file ====");
-        log("number of access points: " + mNetworks.size());
-        for (WifiConfiguration config : mNetworks) {
-            log(config.toString());
-        }
+        assertTrue("Failed to enable wifi", enableWifi());
     }
 
     @Override
@@ -72,25 +56,67 @@
         super.tearDown();
     }
 
-    private void log(String message) {
-        Log.v(TAG, message);
-    }
-
     @LargeTest
     public void testWifiConnections() {
-        for (int i = 0; i < mNetworks.size(); i++) {
-            String ssid = mNetworks.get(i).SSID;
-            log("-- START Wi-Fi connection test to : " + ssid + " --");
-            connectToWifi(mNetworks.get(i));
-            // verify that connection actually works
-            assertTrue("no network connectivity at end of test", checkNetworkConnectivity());
-            log("-- END Wi-Fi connection test to " + ssid + " -- ");
-            log("pausing for 1 minute");
-            try {
-                Thread.sleep(60 * 1000);
-            } catch (InterruptedException e) {
-                // ignore
+        List<WifiConfiguration> wifiConfigs = loadConfigurations();
+
+        printWifiConfigurations(wifiConfigs);
+
+        assertFalse("No configurations to test against", wifiConfigs.isEmpty());
+
+        boolean shouldPause = false;
+        for (WifiConfiguration config : wifiConfigs) {
+            if (shouldPause) {
+                logv("Pausing for %d seconds", PAUSE_DURATION_MS / 1000);
+                SystemClock.sleep(PAUSE_DURATION_MS);
             }
+            logv("Start wifi connection test to: %s", config.SSID);
+            connectToWifi(config);
+
+            // verify that connection actually works
+            assertTrue("No connectivity at end of test", checkNetworkConnectivity());
+
+            // Disconnect and remove the network
+            assertTrue("Unable to remove network", disconnectAP());
+            logv("End wifi connection test to: %s", config.SSID);
+
+            shouldPause = true;
+        }
+    }
+
+    /**
+     * Load the configuration file from the root of the data partition
+     */
+    private List<WifiConfiguration> loadConfigurations() {
+        BufferedReader reader = null;
+        try {
+            reader = new BufferedReader(new FileReader(new File(WIFI_CONFIG_FILE)));
+            StringBuffer jsonBuffer = new StringBuffer();
+            String line;
+            while ((line = reader.readLine()) != null) {
+                jsonBuffer.append(line);
+            }
+            return WifiConfigurationHelper.parseJson(jsonBuffer.toString());
+        } catch (IllegalArgumentException | IOException e) {
+            throw new AssertionError("Error parsing file", e);
+        } finally {
+            if (reader != null) {
+                try {
+                    reader.close();
+                } catch (IOException e) {
+                    // Ignore
+                }
+            }
+        }
+    }
+
+    /**
+     * Print the wifi configurations to test against.
+     */
+    private void printWifiConfigurations(List<WifiConfiguration> wifiConfigs) {
+        logv("Wifi configurations to be tested");
+        for (WifiConfiguration config : wifiConfigs) {
+            logv(config.toString());
         }
     }
 }
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiApStress.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiApStress.java
index aead65b..41f01e6 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiApStress.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiApStress.java
@@ -17,16 +17,15 @@
 package com.android.connectivitymanagertest.stress;
 
 
-import com.android.connectivitymanagertest.ConnectivityManagerStressTestRunner;
-import com.android.connectivitymanagertest.ConnectivityManagerTestBase;
-
 import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiConfiguration.KeyMgmt;
 import android.net.wifi.WifiConfiguration.AuthAlgorithm;
+import android.net.wifi.WifiConfiguration.KeyMgmt;
 import android.net.wifi.WifiManager;
 import android.os.Environment;
 import android.test.suitebuilder.annotation.LargeTest;
-import android.util.Log;
+
+import com.android.connectivitymanagertest.ConnectivityManagerStressTestRunner;
+import com.android.connectivitymanagertest.ConnectivityManagerTestBase;
 
 import java.io.BufferedWriter;
 import java.io.File;
@@ -35,9 +34,7 @@
 /**
  * Stress test setting up device as wifi hotspot
  */
-public class WifiApStress
-    extends ConnectivityManagerTestBase {
-    private final static String TAG = "WifiApStress";
+public class WifiApStress extends ConnectivityManagerTestBase {
     private static String NETWORK_ID = "AndroidAPTest";
     private static String PASSWD = "androidwifi";
     private final static String OUTPUT_FILE = "WifiStressTestOutput.txt";
@@ -46,6 +43,10 @@
     private int mLastIteration = 0;
     private boolean mWifiOnlyFlag;
 
+    public WifiApStress() {
+        super(WifiApStress.class.getSimpleName());
+    }
+
     @Override
     protected void setUp() throws Exception {
         super.setUp();
@@ -71,7 +72,7 @@
     @LargeTest
     public void testWifiHotSpot() {
         if (mWifiOnlyFlag) {
-            Log.v(TAG, this.getName() + " is excluded for wi-fi only test");
+            logv(getName() + " is excluded for wi-fi only test");
             return;
         }
         WifiConfiguration config = new WifiConfiguration();
@@ -95,7 +96,7 @@
         }
         int i;
         for (i = 0; i < mTotalIterations; i++) {
-            Log.v(TAG, "iteration: " + i);
+            logv("iteration: " + i);
             mLastIteration = i;
             // enable Wifi tethering
             assertTrue("failed to enable wifi hotspot",
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java
index 6ef4f06..fbd4669 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java
@@ -19,13 +19,10 @@
 import android.app.Activity;
 import android.content.Context;
 import android.net.ConnectivityManager;
-import android.net.IpConfiguration.IpAssignment;
-import android.net.IpConfiguration.ProxySettings;
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.State;
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiConfiguration.KeyMgmt;
 import android.net.wifi.WifiManager;
 import android.os.Bundle;
 import android.os.Environment;
@@ -37,6 +34,7 @@
 
 import com.android.connectivitymanagertest.ConnectivityManagerStressTestRunner;
 import com.android.connectivitymanagertest.ConnectivityManagerTestBase;
+import com.android.connectivitymanagertest.WifiConfigurationHelper;
 
 import java.io.BufferedWriter;
 import java.io.File;
@@ -52,8 +50,6 @@
  *                  -w com.android.connectivitymanagertest/.ConnectivityManagerStressTestRunner
  */
 public class WifiStressTest extends ConnectivityManagerTestBase {
-    private final static String TAG = "WifiStressTest";
-
     private final static long SCREEN_OFF_TIMER = 500; //500ms
     /**
      * Wi-Fi idle time for default sleep policy
@@ -78,6 +74,10 @@
     private BufferedWriter mOutputWriter = null;
     private boolean mWifiOnlyFlag;
 
+    public WifiStressTest() {
+        super(WifiStressTest.class.getSimpleName());
+    }
+
     @Override
     protected void setUp() throws Exception {
         super.setUp();
@@ -89,14 +89,14 @@
         mScanIterations = mRunner.getScanIterations();
         mWifiSleepTime = mRunner.getSleepTime();
         mWifiOnlyFlag = mRunner.isWifiOnly();
-        log(String.format("mReconnectIterations(%d), mSsid(%s), mPassword(%s),"
+        logv(String.format("mReconnectIterations(%d), mSsid(%s), mPassword(%s),"
             + "mScanIterations(%d), mWifiSleepTime(%d)", mReconnectIterations, mSsid,
             mPassword, mScanIterations, mWifiSleepTime));
         mOutputWriter = new BufferedWriter(new FileWriter(new File(
                 Environment.getExternalStorageDirectory(), OUTPUT_FILE), true));
         turnScreenOn();
         if (!mWifiManager.isWifiEnabled()) {
-            log("Enable wi-fi before stress tests.");
+            logv("Enable wi-fi before stress tests.");
             if (!enableWifi()) {
                 tearDown();
                 fail("enable wifi failed.");
@@ -107,7 +107,7 @@
 
     @Override
     protected void tearDown() throws Exception {
-        log("tearDown()");
+        logv("tearDown()");
         if (mOutputWriter != null) {
             mOutputWriter.close();
         }
@@ -115,23 +115,19 @@
     }
 
     private void writeOutput(String s) {
-        log("write message: " + s);
+        logv("write message: " + s);
         if (mOutputWriter == null) {
-            log("no writer attached to file " + OUTPUT_FILE);
+            logv("no writer attached to file " + OUTPUT_FILE);
             return;
         }
         try {
             mOutputWriter.write(s + "\n");
             mOutputWriter.flush();
         } catch (IOException e) {
-            log("failed to write output.");
+            logv("failed to write output.");
         }
     }
 
-    public void log(String message) {
-        Log.v(TAG, message);
-    }
-
     private void sleep(long sometime, String errorMsg) {
         try {
             Thread.sleep(sometime);
@@ -149,7 +145,7 @@
         long scanTimeSum = 0, i, averageScanTime = -1;
         int ssidAppearInScanResultsCount = 0; // count times of given ssid appear in scan results.
         for (i = 1; i <= mScanIterations; i++) {
-            log("testWifiScanning: iteration: " + i);
+            logv("testWifiScanning: iteration: " + i);
             averageScanTime = scanTimeSum / i;
             writeOutput(String.format("iteration %d out of %d", i, mScanIterations));
             writeOutput(String.format("average scanning time is %d", averageScanTime));
@@ -173,9 +169,9 @@
             if (scanResultLocal == null || scanResultLocal.isEmpty()) {
                 fail("Scan results are empty ");
             }
-            log("size of scan result list: " + scanResultLocal.size());
+            logv("size of scan result list: " + scanResultLocal.size());
             for (ScanResult sr : scanResultLocal) {
-                log(String.format("scan result: " + sr.toString()));
+                logv(String.format("scan result: " + sr.toString()));
                 if (mSsid.equals(sr.SSID)) {
                     ssidAppearInScanResultsCount += 1;
                     break;
@@ -208,21 +204,12 @@
         Settings.Global.putLong(mRunner.getContext().getContentResolver(),
                 Settings.Global.WIFI_IDLE_MS, WIFI_IDLE_MS);
 
-        // Connect to a Wi-Fi network
-        WifiConfiguration config = new WifiConfiguration();
-        config.SSID = mSsid;
-        if (mPassword != null) {
-            config.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
-            if (isHex(mPassword, 64)) {
-                config.preSharedKey = mPassword;
-            } else {
-                config.preSharedKey = '"' + mPassword + '"';
-            }
+        WifiConfiguration config;
+        if (mPassword == null) {
+            config = WifiConfigurationHelper.createOpenConfig(mSsid);
         } else {
-            config.allowedKeyManagement.set(KeyMgmt.NONE);
+            config = WifiConfigurationHelper.createPskConfig(mSsid, mPassword);
         }
-        config.setIpAssignment(IpAssignment.DHCP);
-        config.setProxySettings(ProxySettings.NONE);
 
         assertTrue("Failed to connect to Wi-Fi network: " + mSsid,
                 connectToWifiWithConfiguration(config));
@@ -240,7 +227,7 @@
             // 5. Wake up the device, verify Wi-Fi is enabled and connected.
             writeOutput(String.format("iteration %d out of %d",
                     i, mReconnectIterations));
-            log("iteration: " + i);
+            logv("iteration: " + i);
             turnScreenOff();
             // Use clock time since boot for intervals.
             long start = SystemClock.uptimeMillis();
@@ -271,7 +258,7 @@
             if (mWifiOnlyFlag) {
                 NetworkInfo ni = mCm.getActiveNetworkInfo();
                 if (ni != null) {
-                    Log.e(TAG, "has active network while in wifi sleep: " + ni.toString());
+                    Log.e(mLogTag, "has active network while in wifi sleep: " + ni.toString());
                     fail("active network detected");
                 }
             } else {
@@ -292,7 +279,7 @@
             long connectionTime = SystemClock.uptimeMillis() - startTime;
             sum += connectionTime;
             avgReconnectTime = sum / i;
-            log("average reconnection time is: " + avgReconnectTime);
+            logv("average reconnection time is: " + avgReconnectTime);
 
             assertTrue("Reconnect to Wi-Fi network, but no data connection.", pingTest(null));
         }
diff --git a/docs/html/guide/topics/ui/accessibility/services.jd b/docs/html/guide/topics/ui/accessibility/services.jd
index c868080..d69af9f 100644
--- a/docs/html/guide/topics/ui/accessibility/services.jd
+++ b/docs/html/guide/topics/ui/accessibility/services.jd
@@ -71,24 +71,30 @@
 
 <h3 id="service-declaration">Accessibility service declaration</h3>
 
-<p>In order to be treated as an accessibility service, your application must include the
+<p>In order to be treated as an accessibility service, you must include a
 {@code service} element (rather than the {@code activity} element) within the {@code application}
-element in its manifest. In addition, within the {@code service} element, you must also include an
+element in your manifest. In addition, within the {@code service} element, you must also include an
 accessibility service intent filter. For compatiblity with Android 4.1 and higher, the manifest
 must also request the {@link android.Manifest.permission#BIND_ACCESSIBILITY_SERVICE} permission
 as shown in the following sample:</p>
 
 <pre>
-&lt;application&gt;
-  &lt;service android:name=&quot;.MyAccessibilityService&quot;
-      android:label=&quot;@string/accessibility_service_label&quot;
-      android:permission=&quot;android.permission.BIND_ACCESSIBILITY_SERVICE&quot&gt;
-    &lt;intent-filter&gt;
-      &lt;action android:name=&quot;android.accessibilityservice.AccessibilityService&quot; /&gt;
-    &lt;/intent-filter&gt;
-  &lt;/service&gt;
-  &lt;uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" /&gt;
-&lt;/application&gt;
+&lt;manifest&gt;
+  ...
+  &lt;uses-permission ... /&gt;
+  ...
+  &lt;application&gt;
+    ...
+    &lt;service android:name=&quot;.MyAccessibilityService&quot;
+        android:label=&quot;@string/accessibility_service_label&quot;
+        android:permission=&quot;android.permission.BIND_ACCESSIBILITY_SERVICE&quot&gt;
+      &lt;intent-filter&gt;
+        &lt;action android:name=&quot;android.accessibilityservice.AccessibilityService&quot; /&gt;
+      &lt;/intent-filter&gt;
+    &lt;/service&gt;
+    &lt;uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" /&gt;
+  &lt;/application&gt;
+&lt;/manifest&gt;
 </pre>
 
 <p>These declarations are required for all accessibility services deployed on Android 1.6 (API Level
diff --git a/docs/html/wear/images/partners/sony.png b/docs/html/wear/images/partners/sony.png
new file mode 100644
index 0000000..3e9483e
--- /dev/null
+++ b/docs/html/wear/images/partners/sony.png
Binary files differ
diff --git a/docs/html/wear/index.jd b/docs/html/wear/index.jd
index 5dd7690..c9a5cff 100644
--- a/docs/html/wear/index.jd
+++ b/docs/html/wear/index.jd
@@ -228,6 +228,10 @@
             <div class="col-4">
               <img src="/wear/images/partners/samsung.png" alt="Samsung">
             </div>
+            <div class="col-4">
+              <img src="/wear/images/partners/sony.png" alt="Sony"
+                   style="margin-left:57px;margin-top:17px;">
+            </div>
           </div>
         </div> <!-- end .wrap -->
       </div>
diff --git a/packages/PrintSpooler/Android.mk b/packages/PrintSpooler/Android.mk
index 4948a02..6e1402c 100644
--- a/packages/PrintSpooler/Android.mk
+++ b/packages/PrintSpooler/Android.mk
@@ -26,3 +26,5 @@
 LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 android-support-v7-recyclerview
 
 include $(BUILD_PACKAGE)
+
+include $(call all-makefiles-under, $(LOCAL_PATH))
\ No newline at end of file
diff --git a/packages/PrintSpooler/jni/Android.mk b/packages/PrintSpooler/jni/Android.mk
new file mode 100644
index 0000000..fbf56be
--- /dev/null
+++ b/packages/PrintSpooler/jni/Android.mk
@@ -0,0 +1,19 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+    com_android_printspooler_util_BitmapSerializeUtils.cpp \
+
+LOCAL_C_INCLUDES += \
+    $(JNI_H_INCLUDE)
+
+LOCAL_SHARED_LIBRARIES := \
+    libnativehelper
+
+LOCAL_LDLIBS := -lm -llog -ljnigraphics
+
+LOCAL_MODULE := libprintspooler_jni
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/packages/PrintSpooler/jni/com_android_printspooler_util_BitmapSerializeUtils.cpp b/packages/PrintSpooler/jni/com_android_printspooler_util_BitmapSerializeUtils.cpp
new file mode 100644
index 0000000..57281c8
--- /dev/null
+++ b/packages/PrintSpooler/jni/com_android_printspooler_util_BitmapSerializeUtils.cpp
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#define LOG_TAG "BitmapSerializeUtils"
+
+#include <jni.h>
+#include <JNIHelp.h>
+
+#include <android/bitmap.h>
+#include <android/log.h>
+
+namespace android {
+
+#define RGBA_8888_COLOR_DEPTH 4
+
+static bool writeAllBytes(const int fd, void* buffer, const size_t byteCount) {
+    char* writeBuffer = static_cast<char*>(buffer);
+    size_t remainingBytes = byteCount;
+    while (remainingBytes > 0) {
+        ssize_t writtenByteCount = write(fd, writeBuffer, remainingBytes);
+        if (writtenByteCount == -1) {
+            if (errno == EINTR) {
+                continue;
+            }
+            __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
+                    "Error writing to buffer: %d", errno);
+            return false;
+        }
+        remainingBytes -= writtenByteCount;
+        writeBuffer += writtenByteCount;
+    }
+    return true;
+}
+
+static bool readAllBytes(const int fd, void* buffer, const size_t byteCount) {
+    char* readBuffer = static_cast<char*>(buffer);
+    size_t remainingBytes = byteCount;
+    while (remainingBytes > 0) {
+        ssize_t readByteCount = read(fd, readBuffer, remainingBytes);
+        if (readByteCount == -1) {
+            if (errno == EINTR) {
+                continue;
+            }
+            __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
+                    "Error reading from buffer: %d", errno);
+            return false;
+        }
+        remainingBytes -= readByteCount;
+        readBuffer += readByteCount;
+    }
+    return true;
+}
+
+static void throwException(JNIEnv* env, const char* className, const char* message) {
+    jclass exceptionClass = env->FindClass(className);
+    env->ThrowNew(exceptionClass, message);
+}
+
+static void throwIllegalStateException(JNIEnv* env, char *message) {
+    const char* className = "java/lang/IllegalStateException";
+    throwException(env, className, message);
+}
+
+static void throwIllegalArgumentException(JNIEnv* env, char* message) {
+    const char* className = "java/lang/IllegalArgumentException";
+    throwException(env, className, message);
+}
+
+static void readBitmapPixels(JNIEnv* env, jclass clazz, jobject jbitmap, jint fd) {
+    // Read the info.
+    AndroidBitmapInfo readInfo;
+    bool read = readAllBytes(fd, (void*) &readInfo, sizeof(AndroidBitmapInfo));
+    if (!read) {
+        throwIllegalStateException(env, (char*) "Cannot read bitmap info");
+        return;
+    }
+
+    // Get the info of the target bitmap.
+    AndroidBitmapInfo targetInfo;
+    int result = AndroidBitmap_getInfo(env, jbitmap, &targetInfo);
+    if (result < 0) {
+        throwIllegalStateException(env, (char*) "Cannot get bitmap info");
+        return;
+    }
+
+    // Enforce we can reuse the bitmap.
+    if (readInfo.width != targetInfo.width || readInfo.height != targetInfo.height
+            || readInfo.stride != targetInfo.stride || readInfo.format != targetInfo.format
+            || readInfo.flags != targetInfo.flags) {
+        throwIllegalArgumentException(env, (char*) "Cannot reuse bitmap");
+        return;
+    }
+
+    // Lock the pixels.
+    void* pixels;
+    result = AndroidBitmap_lockPixels(env, jbitmap, &pixels);
+    if (result < 0) {
+        throwIllegalStateException(env, (char*) "Cannot lock bitmap pixels");
+        return;
+    }
+
+    // Read the pixels.
+    size_t byteCount = readInfo.stride * readInfo.height;
+    read = readAllBytes(fd, (void*) pixels, byteCount);
+    if (!read) {
+        throwIllegalStateException(env, (char*) "Cannot read bitmap pixels");
+        return;
+    }
+
+    // Unlock the pixels.
+    result = AndroidBitmap_unlockPixels(env, jbitmap);
+    if (result < 0) {
+        throwIllegalStateException(env, (char*) "Cannot unlock bitmap pixels");
+    }
+}
+
+static void writeBitmapPixels(JNIEnv* env, jclass clazz, jobject jbitmap, jint fd) {
+    // Get the info.
+    AndroidBitmapInfo info;
+    int result = AndroidBitmap_getInfo(env, jbitmap, &info);
+    if (result < 0) {
+        throwIllegalStateException(env, (char*) "Cannot get bitmap info");
+        return;
+    }
+
+    // Write the info.
+    bool written = writeAllBytes(fd, (void*) &info, sizeof(AndroidBitmapInfo));
+    if (!written) {
+        throwIllegalStateException(env, (char*) "Cannot write bitmap info");
+        return;
+    }
+
+    // Lock the pixels.
+    void* pixels;
+    result = AndroidBitmap_lockPixels(env, jbitmap, &pixels);
+    if (result < 0) {
+        throwIllegalStateException(env, (char*) "Cannot lock bitmap pixels");
+        return;
+    }
+
+    // Write the pixels.
+    size_t byteCount = info.stride * info.height;
+    written = writeAllBytes(fd, (void*) pixels, byteCount);
+    if (!written) {
+        throwIllegalStateException(env, (char*) "Cannot write bitmap pixels");
+        return;
+    }
+
+    // Unlock the pixels.
+    result = AndroidBitmap_unlockPixels(env, jbitmap);
+    if (result < 0) {
+        throwIllegalStateException(env, (char*) "Cannot unlock bitmap pixels");
+    }
+}
+
+static JNINativeMethod sMethods[] = {
+    {"nativeReadBitmapPixels", "(Landroid/graphics/Bitmap;I)V", (void *) readBitmapPixels},
+    {"nativeWriteBitmapPixels", "(Landroid/graphics/Bitmap;I)V", (void *) writeBitmapPixels},
+};
+
+int register_com_android_printspooler_util_BitmapSerializeUtils(JNIEnv* env) {
+    return jniRegisterNativeMethods(env, "com/android/printspooler/util/BitmapSerializeUtils",
+        sMethods, NELEM(sMethods));
+}
+
+}
+
+jint JNI_OnLoad(JavaVM* jvm, void*) {
+    JNIEnv *env = NULL;
+    if (jvm->GetEnv((void**) &env, JNI_VERSION_1_6)) {
+        return JNI_ERR;
+    }
+
+    if (android::register_com_android_printspooler_util_BitmapSerializeUtils(env) == -1) {
+        return JNI_ERR;
+    }
+
+    return JNI_VERSION_1_6;
+}
diff --git a/packages/PrintSpooler/res/layout/print_activity.xml b/packages/PrintSpooler/res/layout/print_activity.xml
index 761d58a..30b96292 100644
--- a/packages/PrintSpooler/res/layout/print_activity.xml
+++ b/packages/PrintSpooler/res/layout/print_activity.xml
@@ -33,7 +33,7 @@
 
         <Spinner
             android:id="@+id/destination_spinner"
-            android:layout_width="wrap_content"
+            android:layout_width="@dimen/preview_destination_spinner_width"
             android:layout_height="wrap_content"
             android:layout_marginTop="4dip"
             android:dropDownWidth="wrap_content"
diff --git a/packages/PrintSpooler/res/values-sw600dp-land/constants.xml b/packages/PrintSpooler/res/values-sw600dp-land/constants.xml
index f60d8e4..f7f8a18 100644
--- a/packages/PrintSpooler/res/values-sw600dp-land/constants.xml
+++ b/packages/PrintSpooler/res/values-sw600dp-land/constants.xml
@@ -17,6 +17,9 @@
 <resources>
 
     <integer name="print_option_column_count">6</integer>
+
     <integer name="preview_page_per_row_count">4</integer>
 
+    <dimen name="preview_destination_spinner_width">256dip</dimen>
+
 </resources>
diff --git a/packages/PrintSpooler/res/values/constants.xml b/packages/PrintSpooler/res/values/constants.xml
index b4e4777..357d3e1 100644
--- a/packages/PrintSpooler/res/values/constants.xml
+++ b/packages/PrintSpooler/res/values/constants.xml
@@ -45,4 +45,6 @@
     <dimen name="preview_page_footer_height">32dip</dimen>
     <dimen name="preview_page_min_width">128dip</dimen>
 
+    <dimen name="preview_destination_spinner_width">192dip</dimen>
+
 </resources>
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java b/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java
index cd2ccbd..a581e8a 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java
@@ -22,15 +22,15 @@
 import android.content.Intent;
 import android.content.ServiceConnection;
 import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
 import android.graphics.Color;
-import android.graphics.Rect;
 import android.graphics.drawable.BitmapDrawable;
 import android.os.AsyncTask;
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.print.PrintAttributes;
+import android.print.PrintAttributes.MediaSize;
+import android.print.PrintAttributes.Margins;
 import android.print.PrintDocumentInfo;
 import android.util.ArrayMap;
 import android.util.Log;
@@ -38,10 +38,10 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.printspooler.renderer.IPdfRenderer;
 import com.android.printspooler.renderer.PdfRendererService;
+import com.android.printspooler.util.BitmapSerializeUtils;
 import dalvik.system.CloseGuard;
 import libcore.io.IoUtils;
 
-import java.io.FileDescriptor;
 import java.io.IOException;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
@@ -354,13 +354,14 @@
     public static final class RenderSpec {
         final int bitmapWidth;
         final int bitmapHeight;
-        final PrintAttributes printAttributes;
+        final PrintAttributes printAttributes = new PrintAttributes.Builder().build();
 
         public RenderSpec(int bitmapWidth, int bitmapHeight,
-                PrintAttributes printAttributes) {
+                MediaSize mediaSize, Margins minMargins) {
             this.bitmapWidth = bitmapWidth;
             this.bitmapHeight = bitmapHeight;
-            this.printAttributes = printAttributes;
+            printAttributes.setMediaSize(mediaSize);
+            printAttributes.setMinMargins(minMargins);
         }
 
         @Override
@@ -451,6 +452,8 @@
         @GuardedBy("mLock")
         private IPdfRenderer mRenderer;
 
+        private boolean mBoundToService;
+
         public AsyncRenderer(Context context, OnMalformedPdfFileListener malformedPdfFileListener) {
             mContext = context;
             mOnMalformedPdfFileListener = malformedPdfFileListener;
@@ -463,6 +466,7 @@
 
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
+            mBoundToService = true;
             synchronized (mLock) {
                 mRenderer = IPdfRenderer.Stub.asInterface(service);
                 mLock.notifyAll();
@@ -505,6 +509,10 @@
                         } catch (RemoteException re) {
                             Log.e(LOG_TAG, "Cannot open PDF document");
                             return MALFORMED_PDF_FILE_ERROR;
+                        } finally {
+                            // Close the fd as we passed it to another process
+                            // which took ownership.
+                            IoUtils.closeQuietly(source);
                         }
                     }
                 }
@@ -559,7 +567,10 @@
 
                 @Override
                 public void onPostExecute(Void result) {
-                    mContext.unbindService(AsyncRenderer.this);
+                    if (mBoundToService) {
+                        mBoundToService = false;
+                        mContext.unbindService(AsyncRenderer.this);
+                    }
                     mPageContentCache.invalidate();
                     mPageContentCache.clear();
                 }
@@ -804,9 +815,7 @@
                     // ownership, so close our copy for the write to complete.
                     destination.close();
 
-                    BitmapFactory.Options options = new BitmapFactory.Options();
-                    options.inBitmap = bitmap;
-                    BitmapFactory.decodeFileDescriptor(source.getFileDescriptor(), null, options);
+                    BitmapSerializeUtils.readBitmapPixels(bitmap, source);
                 } catch (IOException|RemoteException e) {
                     Log.e(LOG_TAG, "Error rendering page:" + mPageIndex, e);
                 } finally {
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
index d1aa33b..09e8b39 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
@@ -55,7 +55,7 @@
 public final class RemotePrintDocument {
     private static final String LOG_TAG = "RemotePrintDocument";
 
-    private static final boolean DEBUG = true;
+    private static final boolean DEBUG = false;
 
     private static final int STATE_INITIAL = 0;
     private static final int STATE_STARTED = 1;
diff --git a/packages/PrintSpooler/src/com/android/printspooler/renderer/PdfRendererService.java b/packages/PrintSpooler/src/com/android/printspooler/renderer/PdfRendererService.java
index a4c6932..4d02c01 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/renderer/PdfRendererService.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/renderer/PdfRendererService.java
@@ -20,6 +20,7 @@
 import android.content.Intent;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
+import android.graphics.Color;
 import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.graphics.pdf.PdfRenderer;
@@ -32,8 +33,7 @@
 import android.util.Log;
 import android.view.View;
 import libcore.io.IoUtils;
-
-import java.io.FileOutputStream;
+import com.android.printspooler.util.BitmapSerializeUtils;
 import java.io.IOException;
 
 /**
@@ -76,7 +76,6 @@
         @Override
         public void renderPage(int pageIndex, int bitmapWidth, int bitmapHeight,
                 PrintAttributes attributes, ParcelFileDescriptor destination) {
-            FileOutputStream out = null;
             synchronized (mLock) {
                 try {
                     throwIfNotOpened();
@@ -136,10 +135,8 @@
 
                     page.close();
 
-                    out = new FileOutputStream(destination.getFileDescriptor());
-                    bitmap.compress(Bitmap.CompressFormat.PNG, 0, out);
+                    BitmapSerializeUtils.writeBitmapPixels(bitmap, destination);
                 } finally {
-                    IoUtils.closeQuietly(out);
                     IoUtils.closeQuietly(destination);
                 }
             }
@@ -171,11 +168,13 @@
         private Bitmap getBitmapForSize(int width, int height) {
             if (mBitmap != null) {
                 if (mBitmap.getWidth() == width && mBitmap.getHeight() == height) {
+                    mBitmap.eraseColor(Color.WHITE);
                     return mBitmap;
                 }
                 mBitmap.recycle();
             }
             mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+            mBitmap.eraseColor(Color.WHITE);
             return mBitmap;
         }
 
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintPreviewController.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintPreviewController.java
index ddf637e..2b5b41b 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintPreviewController.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintPreviewController.java
@@ -24,6 +24,7 @@
 import android.print.PrintAttributes.MediaSize;
 import android.print.PrintAttributes.Margins;
 import android.print.PrintDocumentInfo;
+import android.support.v7.widget.GridLayoutManager;
 import android.support.v7.widget.OrientationHelper;
 import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.RecyclerView.ViewHolder;
@@ -51,7 +52,7 @@
     private final MyHandler mHandler;
 
     private final PageAdapter mPageAdapter;
-    private final StaggeredGridLayoutManager mLayoutManger;
+    private final GridLayoutManager mLayoutManger;
 
     private final PrintOptionsLayout mPrintOptionsLayout;
     private final RecyclerView mRecyclerView;
@@ -73,7 +74,7 @@
         final int columnCount = mActivity.getResources().getInteger(
                 R.integer.preview_page_per_row_count);
 
-        mLayoutManger = new StaggeredGridLayoutManager(columnCount, OrientationHelper.VERTICAL);
+        mLayoutManger = new GridLayoutManager(mActivity, columnCount);
 
         mRecyclerView = (RecyclerView) activity.findViewById(R.id.preview_content);
         mRecyclerView.setLayoutManager(mLayoutManger);
diff --git a/packages/PrintSpooler/src/com/android/printspooler/util/BitmapSerializeUtils.java b/packages/PrintSpooler/src/com/android/printspooler/util/BitmapSerializeUtils.java
new file mode 100644
index 0000000..a1845f6
--- /dev/null
+++ b/packages/PrintSpooler/src/com/android/printspooler/util/BitmapSerializeUtils.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.printspooler.util;
+
+import android.graphics.Bitmap;
+import android.os.ParcelFileDescriptor;
+
+/**
+ * Helper for serialization of bitmaps in the very specific
+ * use case of having the same bitmap on both ends and just
+ * marshaling the pixels from one side to the other.
+ */
+public final class BitmapSerializeUtils {
+
+    static {
+        System.loadLibrary("printspooler_jni");
+    }
+
+    private BitmapSerializeUtils() {
+        /* do nothing */
+    }
+
+    /**
+     * Reads a bitmap pixels from a file descriptor.
+     *
+     * @param bitmap A bitmap whose pixels to populate.
+     * @param source The source file descriptor.
+     */
+    public static void readBitmapPixels(Bitmap bitmap, ParcelFileDescriptor source) {
+        nativeReadBitmapPixels(bitmap, source.getFd());
+    }
+
+    /**
+     * Writes a bitmap pixels to a file descriptor.
+     *
+     * @param bitmap The bitmap.
+     * @param destination The destination file descriptor.
+     */
+    public static void writeBitmapPixels(Bitmap bitmap, ParcelFileDescriptor destination) {
+        nativeWriteBitmapPixels(bitmap, destination.getFd());
+    }
+
+    private static native void nativeReadBitmapPixels(Bitmap bitmap, int fd);
+
+    private static native void nativeWriteBitmapPixels(Bitmap bitmap, int fd);
+}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/widget/PageContentView.java b/packages/PrintSpooler/src/com/android/printspooler/widget/PageContentView.java
index 76ff167..23a01bd 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/widget/PageContentView.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/widget/PageContentView.java
@@ -20,7 +20,6 @@
 import android.graphics.Canvas;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.ColorDrawable;
-import android.print.PrintAttributes;
 import android.print.PrintAttributes.MediaSize;
 import android.print.PrintAttributes.Margins;
 import android.util.AttributeSet;
@@ -39,14 +38,18 @@
 public class PageContentView extends View
         implements PageContentRepository.OnPageContentAvailableCallback {
 
-    private final PrintAttributes mAttributes = new PrintAttributes.Builder().build();
-
     private final ColorDrawable mEmptyState;
 
     private PageContentProvider mProvider;
 
+    private MediaSize mMediaSize;
+
+    private Margins mMinMargins;
+
     private boolean mContentRequested;
 
+    private boolean mNeedsLayout;
+
     public PageContentView(Context context, AttributeSet attrs) {
         super(context, attrs);
 
@@ -68,14 +71,14 @@
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
+        mNeedsLayout = false;
         requestPageContentIfNeeded();
     }
 
     @Override
     public void onPageContentAvailable(BitmapDrawable content) {
-        if (getBackground() != content) {
-            setBackground(content);
-        }
+        assert (getBackground() != content);
+        setBackground(content);
     }
 
     public PageContentProvider getPageContentProvider() {
@@ -83,18 +86,23 @@
     }
 
     public void init(PageContentProvider provider, MediaSize mediaSize, Margins minMargins) {
-        if (mProvider == null ? provider == null : mProvider.equals(provider)
-                && ((mAttributes.getMediaSize() == null)
-                        ? mediaSize == null : mAttributes.getMediaSize().equals(mediaSize))
-                && ((mAttributes.getMinMargins() == null)
-                        ? minMargins == null : mAttributes.getMinMargins().equals(minMargins))) {
+        final boolean providerChanged = (mProvider == null)
+                ? provider != null : !mProvider.equals(provider);
+        final boolean mediaSizeChanged = (mMediaSize == null)
+                ? mediaSize != null : !mMediaSize.equals(mediaSize);
+        final boolean marginsChanged = (mMinMargins == null)
+                ? minMargins != null : !mMinMargins.equals(minMargins);
+
+        if (!providerChanged && !mediaSizeChanged && !marginsChanged) {
             return;
         }
 
         mProvider = provider;
-        mAttributes.setMediaSize(mediaSize);
-        mAttributes.setMinMargins(minMargins);
+        mMediaSize = mediaSize;
+        mMinMargins = minMargins;
+
         mContentRequested = false;
+        mNeedsLayout = mNeedsLayout || mediaSizeChanged || marginsChanged;
 
         // If there is no provider we want immediately to switch to
         // the empty state, so pages with no content appear blank.
@@ -106,9 +114,11 @@
     }
 
     private void requestPageContentIfNeeded() {
-        if (getWidth() > 0 && getHeight() > 0 && !mContentRequested && mProvider != null) {
+        if (getWidth() > 0 && getHeight() > 0 && !mContentRequested
+                && mProvider != null && !mNeedsLayout) {
             mContentRequested = true;
-            mProvider.getPageContent(new RenderSpec(getWidth(), getHeight(), mAttributes), this);
+            mProvider.getPageContent(new RenderSpec(getWidth(), getHeight(),
+                    mMediaSize, mMinMargins), this);
         }
     }
 }
diff --git a/packages/SystemUI/res/drawable/ic_info.xml b/packages/SystemUI/res/drawable/ic_info.xml
new file mode 100644
index 0000000..65e7bf5
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_info.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2014 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:pathData="M12.000000,2.000000C6.500000,2.000000 2.000000,6.500000 2.000000,12.000000s4.500000,10.000000 10.000000,10.000000c5.500000,0.000000 10.000000,-4.500000 10.000000,-10.000000S17.500000,2.000000 12.000000,2.000000zM13.000000,17.000000l-2.000000,0.000000l0.000000,-6.000000l2.000000,0.000000L13.000000,17.000000zM13.000000,9.000000l-2.000000,0.000000L11.000000,7.000000l2.000000,0.000000L13.000000,9.000000z"
+        android:fillColor="#FFFFFF"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/notification_guts.xml b/packages/SystemUI/res/layout/notification_guts.xml
index ac8af1b..d52c274 100644
--- a/packages/SystemUI/res/layout/notification_guts.xml
+++ b/packages/SystemUI/res/layout/notification_guts.xml
@@ -77,13 +77,23 @@
         </LinearLayout>
 
         <ImageButton style="@android:style/Widget.Material.Light.Button.Borderless.Small"
+                 android:id="@+id/notification_inspect_app_provided_settings"
+                 android:layout_width="52dp"
+                 android:layout_height="match_parent"
+                 android:layout_weight="0"
+                 android:gravity="center"
+                 android:src="@drawable/ic_settings"
+                 android:visibility="gone"
+                />
+
+        <ImageButton style="@android:style/Widget.Material.Light.Button.Borderless.Small"
                 android:id="@+id/notification_inspect_item"
                 android:layout_width="52dp"
                 android:layout_height="match_parent"
                 android:layout_weight="0"
                 android:gravity="center"
                 android:contentDescription="@string/status_bar_notification_inspect_item_title"
-                android:src="@drawable/ic_settings"
+                android:src="@drawable/ic_info"
                 />
     </LinearLayout>
 </com.android.systemui.statusbar.NotificationGuts>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index b488c56..c7a91af 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -522,9 +522,13 @@
     <!-- Content description of the clear button in the notification panel for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_clear_all">Clear all notifications.</string>
 
-    <!-- Title shown in notification popup for inspecting the responsible
-         application [CHAR LIMIT=30] -->
-    <string name="status_bar_notification_inspect_item_title">Settings</string>
+    <!-- Content description of button in notification inspector for system settings relating to
+         notifications from this application [CHAR LIMIT=NONE] -->
+    <string name="status_bar_notification_inspect_item_title">Notification settings</string>
+
+    <!-- Content description of button in notification inspetor for application-provided settings
+         for its own notifications [CHAR LIMIT=NONE] -->
+    <string name="status_bar_notification_app_settings_title"><xliff:g id="app_name" example="Calendar">%s</xliff:g> settings</string>
 
     <!-- Description of the button in the phone-style notification panel that controls auto-rotation, when auto-rotation is on. [CHAR LIMIT=NONE] -->
     <string name="accessibility_rotation_lock_off">Screen will rotate automatically.</string>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index dcd89ee..2c1d70d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -34,13 +34,11 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
 import android.content.pm.UserInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.database.ContentObserver;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Color;
 import android.graphics.drawable.Drawable;
 import android.os.AsyncTask;
 import android.os.Build;
@@ -96,6 +94,7 @@
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 
 import java.util.ArrayList;
+import java.util.List;
 import java.util.Locale;
 
 import static com.android.keyguard.KeyguardHostView.OnDismissAction;
@@ -177,7 +176,7 @@
     // public mode, private notifications, etc
     private boolean mLockscreenPublicMode = false;
     private final SparseBooleanArray mUsersAllowingPrivateNotifications = new SparseBooleanArray();
-    private NotificationColorUtil mNotificationColorUtil = NotificationColorUtil.getInstance();
+    private NotificationColorUtil mNotificationColorUtil;
 
     private UserManager mUserManager;
 
@@ -435,6 +434,8 @@
         mDevicePolicyManager = (DevicePolicyManager)mContext.getSystemService(
                 Context.DEVICE_POLICY_SERVICE);
 
+        mNotificationColorUtil = NotificationColorUtil.getInstance(mContext);
+
         mNotificationData = new NotificationData(this);
 
         mDreamManager = IDreamManager.Stub.asInterface(
@@ -717,11 +718,23 @@
                entry.expandedBig.findViewById(com.android.internal.R.id.media_actions) != null;
     }
 
+    // The gear button in the guts that links to the app's own notification settings
+    private void startAppOwnNotificationSettingsActivity(Intent intent,
+            final int notificationId, final String notificationTag, final int appUid) {
+        intent.putExtra("notification_id", notificationId);
+        intent.putExtra("notification_tag", notificationTag);
+        startNotificationGutsIntent(intent, appUid);
+    }
+
+    // The (i) button in the guts that links to the system notification settings for that app
     private void startAppNotificationSettingsActivity(String packageName, final int appUid) {
         final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
         intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName);
         intent.putExtra(Settings.EXTRA_APP_UID, appUid);
+        startNotificationGutsIntent(intent, appUid);
+    }
 
+    private void startNotificationGutsIntent(final Intent intent, final int appUid) {
         final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
         dismissKeyguardThenExecute(new OnDismissAction() {
             @Override
@@ -1128,7 +1141,7 @@
                 entry.notification.getUser().getIdentifier());
 
         int maxHeight = mRowMaxHeight;
-        StatusBarNotification sbn = entry.notification;
+        final StatusBarNotification sbn = entry.notification;
         RemoteViews contentView = sbn.getNotification().contentView;
         RemoteViews bigContentView = sbn.getNotification().bigContentView;
 
@@ -1197,6 +1210,8 @@
         ((DateTimeView) row.findViewById(R.id.timestamp)).setTime(entry.notification.getPostTime());
         ((TextView) row.findViewById(R.id.pkgname)).setText(appname);
         final View settingsButton = guts.findViewById(R.id.notification_inspect_item);
+        final View appSettingsButton
+                = guts.findViewById(R.id.notification_inspect_app_provided_settings);
         if (appUid >= 0) {
             final int appUidF = appUid;
             settingsButton.setOnClickListener(new View.OnClickListener() {
@@ -1204,8 +1219,33 @@
                     startAppNotificationSettingsActivity(pkg, appUidF);
                 }
             });
+
+            final Intent appSettingsQueryIntent
+                    = new Intent(Intent.ACTION_MAIN)
+                        .addCategory(Notification.INTENT_CATEGORY_NOTIFICATION_PREFERENCES)
+                        .setPackage(pkg);
+            List<ResolveInfo> infos = pmUser.queryIntentActivities(appSettingsQueryIntent, 0);
+            if (infos.size() > 0) {
+                appSettingsButton.setVisibility(View.VISIBLE);
+                appSettingsButton.setContentDescription(
+                        mContext.getResources().getString(
+                                R.string.status_bar_notification_app_settings_title,
+                                appname
+                        ));
+                final Intent appSettingsLaunchIntent = new Intent(appSettingsQueryIntent)
+                        .setClassName(pkg, infos.get(0).activityInfo.name);
+                appSettingsButton.setOnClickListener(new View.OnClickListener() {
+                    public void onClick(View v) {
+                        startAppOwnNotificationSettingsActivity(appSettingsLaunchIntent,
+                                sbn.getId(),
+                                sbn.getTag(),
+                                appUidF);
+                    }
+                });
+            }
         } else {
             settingsButton.setVisibility(View.GONE);
+            appSettingsButton.setVisibility(View.GONE);
         }
 
         workAroundBadLayerDrawableOpacity(row);
@@ -1307,7 +1347,7 @@
 
             Drawable iconDrawable = StatusBarIconView.getIcon(mContext, ic);
             icon.setImageDrawable(iconDrawable);
-            if (mNotificationColorUtil.isGrayscale(iconDrawable)) {
+            if (mNotificationColorUtil.isGrayscaleIcon(iconDrawable)) {
                 icon.setBackgroundResource(
                         com.android.internal.R.drawable.notification_icon_legacy_bg);
                 int padding = mContext.getResources().getDimensionPixelSize(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowIconsView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowIconsView.java
index c75bd28..c4c9dac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowIconsView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowIconsView.java
@@ -36,7 +36,7 @@
     private TextView mMoreText;
     private int mTintColor;
     private int mIconSize;
-    private NotificationColorUtil mNotificationColorUtil = new NotificationColorUtil();
+    private NotificationColorUtil mNotificationColorUtil;
 
     public NotificationOverflowIconsView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -45,6 +45,7 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
+        mNotificationColorUtil = NotificationColorUtil.getInstance(getContext());
         mTintColor = getResources().getColor(R.color.keyguard_overflow_content_color);
         mIconSize = getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.status_bar_icon_size);
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 3e7a7e4..083aa9b 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -8395,10 +8395,15 @@
 
         Slog.i(TAG, "Auto restore => " + doAutoRestore);
 
-        synchronized (this) {
-            Settings.Secure.putInt(mContext.getContentResolver(),
-                    Settings.Secure.BACKUP_AUTO_RESTORE, doAutoRestore ? 1 : 0);
-            mAutoRestore = doAutoRestore;
+        final long oldId = Binder.clearCallingIdentity();
+        try {
+            synchronized (this) {
+                Settings.Secure.putInt(mContext.getContentResolver(),
+                        Settings.Secure.BACKUP_AUTO_RESTORE, doAutoRestore ? 1 : 0);
+                mAutoRestore = doAutoRestore;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(oldId);
         }
     }
 
@@ -8467,10 +8472,15 @@
         synchronized (mTransports) {
             String prevTransport = null;
             if (mTransports.get(transport) != null) {
-                prevTransport = mCurrentTransport;
-                mCurrentTransport = transport;
-                Settings.Secure.putString(mContext.getContentResolver(),
-                        Settings.Secure.BACKUP_TRANSPORT, transport);
+                final long oldId = Binder.clearCallingIdentity();
+                try {
+                    prevTransport = mCurrentTransport;
+                    mCurrentTransport = transport;
+                    Settings.Secure.putString(mContext.getContentResolver(),
+                            Settings.Secure.BACKUP_TRANSPORT, transport);
+                } finally {
+                    Binder.restoreCallingIdentity(oldId);
+                }
                 Slog.v(TAG, "selectBackupTransport() set " + mCurrentTransport
                         + " returning " + prevTransport);
             } else {
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 93c0f07..9314cf8 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -856,8 +856,11 @@
                 device.onDeviceRemoved();
                 // There is no explicit event for device removal unlike capability register event
                 // used for device addition . Hence we remove the device on hotplug event.
-                invokeDeviceEventListeners(device.getInfo(), DEVICE_EVENT_REMOVE_DEVICE);
-                updateSafeMhlInput();
+                HdmiDeviceInfo deviceInfo = device.getInfo();
+                if (deviceInfo != null) {
+                    invokeDeviceEventListeners(deviceInfo, DEVICE_EVENT_REMOVE_DEVICE);
+                    updateSafeMhlInput();
+                }
             } else {
                 Slog.w(TAG, "No device to remove:[portId=" + portId);
             }
@@ -1082,7 +1085,7 @@
                         // the connected mobile device, start routing control to switch ports.
                         // callback is handled by MHL action.
                         device.turnOn(callback);
-                        tv.doManualPortSwitching(device.getInfo().getPortId(), null);
+                        tv.doManualPortSwitching(device.getPortId(), null);
                         return;
                     }
                     tv.deviceSelect(deviceId, callback);
diff --git a/services/core/java/com/android/server/pm/KeySetManagerService.java b/services/core/java/com/android/server/pm/KeySetManagerService.java
index 37bedf3..4a8e318 100644
--- a/services/core/java/com/android/server/pm/KeySetManagerService.java
+++ b/services/core/java/com/android/server/pm/KeySetManagerService.java
@@ -538,14 +538,17 @@
                     pw.println("");
                 }
                 printedLabel = false;
-                for (long keySetId : pkg.keySetData.getSigningKeySets()) {
-                    if (!printedLabel) {
-                        pw.print("      Signing KeySets: ");
-                        printedLabel = true;
-                    } else {
-                        pw.print(", ");
+                final long[] signingKeySets = pkg.keySetData.getSigningKeySets();
+                if (signingKeySets != null) {
+                    for (long keySetId : signingKeySets) {
+                        if (!printedLabel) {
+                            pw.print("      Signing KeySets: ");
+                            printedLabel = true;
+                        } else {
+                            pw.print(", ");
+                        }
+                        pw.print(Long.toString(keySetId));
                     }
-                    pw.print(Long.toString(keySetId));
                 }
                 if (printedLabel) {
                     pw.println("");
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index 350c27e..34da1c9 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -221,18 +221,39 @@
 
     private final Handler mHandler;
 
+    /**
+     * Create a PhoneStateListener for the Phone with the default subscription.
+     * This class requires Looper.myLooper() not return null. To supply your
+     * own non-null looper use PhoneStateListener(Looper looper) below.
+     */
     public PhoneStateListener() {
         this(SubscriptionManager.DEFAULT_SUB_ID, Looper.myLooper());
     }
 
     /**
+     * Create a PhoneStateListener for the Phone with the default subscription
+     * using a particular non-null Looper.
+     * @hide
+     */
+    public PhoneStateListener(Looper looper) {
+        this(SubscriptionManager.DEFAULT_SUB_ID, looper);
+    }
+
+    /**
+     * Create a PhoneStateListener for the Phone using the specified subscription.
+     * This class requires Looper.myLooper() not return null. To supply your
+     * own non-null Looper use PhoneStateListener(long subId, Looper looper) below.
      * @hide
      */
     public PhoneStateListener(long subId) {
         this(subId, Looper.myLooper());
     }
 
-    /** @hide */
+    /**
+     * Create a PhoneStateListener for the Phone using the specified subscription
+     * and non-null Looper.
+     * @hide
+     */
     public PhoneStateListener(long subId, Looper looper) {
         if (DBG) log("ctor: subId=" + subId + " looper=" + looper);
         mSubId = subId;
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index f83f164..2bb2404 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1,18 +1,18 @@
 /*
-* Copyright (C) 2011-2014 MediaTek Inc.
-*
-* 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.
-*/
+ * Copyright (C) 2014 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.telephony;
 
@@ -30,7 +30,10 @@
 import java.util.List;
 
 /**
- *@hide
+ * SubscriptionManager is the application interface to SubscriptionController
+ * and provides information about the current Telephony Subscriptions.
+ *
+ * @hide
  */
 public class SubscriptionManager implements BaseColumns {
     private static final String LOG_TAG = "SUB";
@@ -38,123 +41,167 @@
     private static final boolean VDBG = false;
 
     // An invalid phone identifier
+    /** @hide */
     public static final int INVALID_PHONE_ID = -1000;
 
     // Indicates the caller wants the default phone id.
+    /** @hide */
     public static final int DEFAULT_PHONE_ID = Integer.MAX_VALUE;
 
     // An invalid slot identifier
+    /** @hide */
     public static final int INVALID_SLOT_ID = -1000;
 
     // Indicates the caller wants the default slot id.
+    /** @hide */
     public static final int DEFAULT_SLOT_ID = Integer.MAX_VALUE;
 
     // An invalid subscription identifier
+    /** @hide */
     public static final long INVALID_SUB_ID = -1000;
 
     // Indicates the user should be asked which sub to use.
+    /** @hide */
     public static final long ASK_USER_SUB_ID = -1001;
 
     // Indicates the caller wants the default sub id.
+    /** @hide */
     public static final long DEFAULT_SUB_ID = Long.MAX_VALUE;
 
+    /** @hide */
     public static final Uri CONTENT_URI = Uri.parse("content://telephony/siminfo");
 
+    /** @hide */
     public static final int DEFAULT_INT_VALUE = -100;
 
+    /** @hide */
     public static final String DEFAULT_STRING_VALUE = "N/A";
 
+    /** @hide */
     public static final int EXTRA_VALUE_NEW_SIM = 1;
+
+    /** @hide */
     public static final int EXTRA_VALUE_REMOVE_SIM = 2;
+    /** @hide */
     public static final int EXTRA_VALUE_REPOSITION_SIM = 3;
+    /** @hide */
     public static final int EXTRA_VALUE_NOCHANGE = 4;
 
+    /** @hide */
     public static final String INTENT_KEY_DETECT_STATUS = "simDetectStatus";
+    /** @hide */
     public static final String INTENT_KEY_SIM_COUNT = "simCount";
+    /** @hide */
     public static final String INTENT_KEY_NEW_SIM_SLOT = "newSIMSlot";
+    /** @hide */
     public static final String INTENT_KEY_NEW_SIM_STATUS = "newSIMStatus";
 
     /**
      * The ICC ID of a SIM.
      * <P>Type: TEXT (String)</P>
      */
+    /** @hide */
     public static final String ICC_ID = "icc_id";
 
     /**
      * <P>Type: INTEGER (int)</P>
      */
+    /** @hide */
     public static final String SIM_ID = "sim_id";
-
+    /** @hide */
     public static final int SIM_NOT_INSERTED = -1;
 
     /**
      * The display name of a SIM.
      * <P>Type: TEXT (String)</P>
      */
+    /** @hide */
     public static final String DISPLAY_NAME = "display_name";
 
+    /** @hide */
     public static final int DEFAULT_NAME_RES = com.android.internal.R.string.unknownName;
 
     /**
      * The display name source of a SIM.
      * <P>Type: INT (int)</P>
      */
+    /** @hide */
     public static final String NAME_SOURCE = "name_source";
 
+    /** @hide */
     public static final int NAME_SOURCE_UNDEFINDED = -1;
 
+    /** @hide */
     public static final int NAME_SOURCE_DEFAULT_SOURCE = 0;
 
+    /** @hide */
     public static final int NAME_SOURCE_SIM_SOURCE = 1;
 
+    /** @hide */
     public static final int NAME_SOURCE_USER_INPUT = 2;
 
     /**
      * The color of a SIM.
      * <P>Type: INTEGER (int)</P>
      */
+    /** @hide */
     public static final String COLOR = "color";
 
+    /** @hide */
     public static final int COLOR_1 = 0;
 
+    /** @hide */
     public static final int COLOR_2 = 1;
 
+    /** @hide */
     public static final int COLOR_3 = 2;
 
+    /** @hide */
     public static final int COLOR_4 = 3;
 
+    /** @hide */
     public static final int COLOR_DEFAULT = COLOR_1;
 
     /**
      * The phone number of a SIM.
      * <P>Type: TEXT (String)</P>
      */
+    /** @hide */
     public static final String NUMBER = "number";
 
     /**
      * The number display format of a SIM.
      * <P>Type: INTEGER (int)</P>
      */
+    /** @hide */
     public static final String DISPLAY_NUMBER_FORMAT = "display_number_format";
 
+    /** @hide */
     public static final int DISPLAY_NUMBER_NONE = 0;
 
+    /** @hide */
     public static final int DISPLAY_NUMBER_FIRST = 1;
 
+    /** @hide */
     public static final int DISPLAY_NUMBER_LAST = 2;
 
+    /** @hide */
     public static final int DISLPAY_NUMBER_DEFAULT = DISPLAY_NUMBER_FIRST;
 
     /**
      * Permission for data roaming of a SIM.
      * <P>Type: INTEGER (int)</P>
      */
+    /** @hide */
     public static final String DATA_ROAMING = "data_roaming";
 
+    /** @hide */
     public static final int DATA_ROAMING_ENABLE = 1;
 
+    /** @hide */
     public static final int DATA_ROAMING_DISABLE = 0;
 
+    /** @hide */
     public static final int DATA_ROAMING_DEFAULT = DATA_ROAMING_DISABLE;
 
     private static final int RES_TYPE_BACKGROUND_DARK = 0;
@@ -166,12 +213,13 @@
     /**
      * Broadcast Action: The user has changed one of the default subs related to
      * data, phone calls, or sms</p>
-     *
+     * @hide
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String SUB_DEFAULT_CHANGED_ACTION =
         "android.intent.action.SUB_DEFAULT_CHANGED";
 
+    /** @hide */
     public SubscriptionManager() {
         if (DBG) logd("SubscriptionManager created");
     }
@@ -180,6 +228,7 @@
      * Get the SubInfoRecord according to an index
      * @param subId The unique SubInfoRecord index in database
      * @return SubInfoRecord, maybe null
+     * @hide
      */
     public static SubInfoRecord getSubInfoUsingSubId(long subId) {
         if (!isValidSubId(subId)) {
@@ -206,6 +255,7 @@
      * Get the SubInfoRecord according to an IccId
      * @param iccId the IccId of SIM card
      * @return SubInfoRecord, maybe null
+     * @hide
      */
     public static List<SubInfoRecord> getSubInfoUsingIccId(String iccId) {
         if (VDBG) logd("[getSubInfoUsingIccId]+ iccId=" + iccId);
@@ -232,6 +282,7 @@
      * Get the SubInfoRecord according to slotId
      * @param slotId the slot which the SIM is inserted
      * @return SubInfoRecord, maybe null
+     * @hide
      */
     public static List<SubInfoRecord> getSubInfoUsingSlotId(int slotId) {
         // FIXME: Consider never returning null
@@ -257,6 +308,7 @@
     /**
      * Get all the SubInfoRecord(s) in subinfo database
      * @return Array list of all SubInfoRecords in database, include thsoe that were inserted before
+     * @hide
      */
     public static List<SubInfoRecord> getAllSubInfoList() {
         if (VDBG) logd("[getAllSubInfoList]+");
@@ -278,6 +330,7 @@
     /**
      * Get the SubInfoRecord(s) of the currently inserted SIM(s)
      * @return Array list of currently inserted SubInfoRecord(s)
+     * @hide
      */
     public static List<SubInfoRecord> getActiveSubInfoList() {
         List<SubInfoRecord> result = null;
@@ -297,6 +350,7 @@
     /**
      * Get the SUB count of all SUB(s) in subinfo database
      * @return all SIM count in database, include what was inserted before
+     * @hide
      */
     public static int getAllSubInfoCount() {
         if (VDBG) logd("[getAllSubInfoCount]+");
@@ -318,6 +372,7 @@
     /**
      * Get the count of active SUB(s)
      * @return active SIM count
+     * @hide
      */
     public static int getActiveSubInfoCount() {
         int result = 0;
@@ -339,6 +394,7 @@
      * @param iccId the IccId of the SIM card
      * @param slotId the slot which the SIM is inserted
      * @return the URL of the newly created row or the updated row
+     * @hide
      */
     public static Uri addSubInfoRecord(String iccId, int slotId) {
         if (VDBG) logd("[addSubInfoRecord]+ iccId:" + iccId + " slotId:" + slotId);
@@ -369,6 +425,7 @@
      * @param color the color of the SIM
      * @param subId the unique SubInfoRecord index in database
      * @return the number of records updated
+     * @hide
      */
     public static int setColor(int color, long subId) {
         if (VDBG) logd("[setColor]+ color:" + color + " subId:" + subId);
@@ -398,6 +455,7 @@
      * @param displayName the display name of SIM card
      * @param subId the unique SubInfoRecord index in database
      * @return the number of records updated
+     * @hide
      */
     public static int setDisplayName(String displayName, long subId) {
         return setDisplayName(displayName, subId, NAME_SOURCE_UNDEFINDED);
@@ -410,6 +468,7 @@
      * @param nameSource 0: NAME_SOURCE_DEFAULT_SOURCE, 1: NAME_SOURCE_SIM_SOURCE,
      *                   2: NAME_SOURCE_USER_INPUT, -1 NAME_SOURCE_UNDEFINED
      * @return the number of records updated or -1 if invalid subId
+     * @hide
      */
     public static int setDisplayName(String displayName, long subId, long nameSource) {
         if (VDBG) {
@@ -441,6 +500,7 @@
      * @param number the phone number of the SIM
      * @param subId the unique SubInfoRecord index in database
      * @return the number of records updated
+     * @hide
      */
     public static int setDisplayNumber(String number, long subId) {
         if (number == null || !isValidSubId(subId)) {
@@ -468,6 +528,7 @@
      * @param format the display format of phone number
      * @param subId the unique SubInfoRecord index in database
      * @return the number of records updated
+     * @hide
      */
     public static int setDisplayNumberFormat(int format, long subId) {
         if (VDBG) logd("[setDisplayNumberFormat]+ format:" + format + " subId:" + subId);
@@ -496,6 +557,7 @@
      * @param roaming 0:Don't allow data when roaming, 1:Allow data when roaming
      * @param subId the unique SubInfoRecord index in database
      * @return the number of records updated
+     * @hide
      */
     public static int setDataRoaming(int roaming, long subId) {
         if (VDBG) logd("[setDataRoaming]+ roaming:" + roaming + " subId:" + subId);
@@ -518,6 +580,7 @@
         return result;
     }
 
+    /** @hide */
     public static int getSlotId(long subId) {
         if (!isValidSubId(subId)) {
             logd("[getSlotId]- fail");
@@ -538,6 +601,7 @@
 
     }
 
+    /** @hide */
     public static long[] getSubId(int slotId) {
         if (!isValidSlotId(slotId)) {
             logd("[getSubId]- fail");
@@ -558,6 +622,7 @@
         return subId;
     }
 
+    /** @hide */
     public static int getPhoneId(long subId) {
         if (!isValidSubId(subId)) {
             logd("[getPhoneId]- fail");
@@ -613,6 +678,7 @@
      * @return the "system" defaultSubId on a voice capable device this
      * will be getDefaultVoiceSubId() and on a data only device it will be
      * getDefaultDataSubId().
+     * @hide
      */
     public static long getDefaultSubId() {
         long subId = INVALID_SUB_ID;
@@ -630,6 +696,7 @@
         return subId;
     }
 
+    /** @hide */
     public static long getDefaultVoiceSubId() {
         long subId = INVALID_SUB_ID;
 
@@ -646,6 +713,7 @@
         return subId;
     }
 
+    /** @hide */
     public static void setDefaultVoiceSubId(long subId) {
         if (VDBG) logd("setDefaultVoiceSubId sub id = " + subId);
         try {
@@ -658,14 +726,17 @@
         }
     }
 
+    /** @hide */
     public static SubInfoRecord getDefaultVoiceSubInfo() {
         return getSubInfoUsingSubId(getDefaultVoiceSubId());
     }
 
+    /** @hide */
     public static int getDefaultVoicePhoneId() {
         return getPhoneId(getDefaultVoiceSubId());
     }
 
+    /** @hide */
     public static long getDefaultSmsSubId() {
         long subId = INVALID_SUB_ID;
 
@@ -682,6 +753,7 @@
         return subId;
     }
 
+    /** @hide */
     public static void setDefaultSmsSubId(long subId) {
         if (VDBG) logd("setDefaultSmsSubId sub id = " + subId);
         try {
@@ -694,14 +766,17 @@
         }
     }
 
+    /** @hide */
     public static SubInfoRecord getDefaultSmsSubInfo() {
         return getSubInfoUsingSubId(getDefaultSmsSubId());
     }
 
+    /** @hide */
     public static int getDefaultSmsPhoneId() {
         return getPhoneId(getDefaultSmsSubId());
     }
 
+    /** @hide */
     public static long getDefaultDataSubId() {
         long subId = INVALID_SUB_ID;
 
@@ -718,6 +793,7 @@
         return subId;
     }
 
+    /** @hide */
     public static void setDefaultDataSubId(long subId) {
         if (VDBG) logd("setDataSubscription sub id = " + subId);
         try {
@@ -730,14 +806,17 @@
         }
     }
 
+    /** @hide */
     public static SubInfoRecord getDefaultDataSubInfo() {
         return getSubInfoUsingSubId(getDefaultDataSubId());
     }
 
+    /** @hide */
     public static int getDefaultDataPhoneId() {
         return getPhoneId(getDefaultDataSubId());
     }
 
+    /** @hide */
     public static void clearSubInfo() {
         try {
             ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
@@ -752,6 +831,7 @@
     }
 
     //FIXME this is vulnerable to race conditions
+    /** @hide */
     public static boolean allDefaultsSelected() {
         if (getDefaultDataSubId() == INVALID_SUB_ID) {
             return false;
@@ -768,6 +848,7 @@
     /**
      * If a default is set to subscription which is not active, this will reset that default back to
      * INVALID_SUB_ID.
+     * @hide
      */
     public static void clearDefaultsForInactiveSubIds() {
         if (VDBG) logd("clearDefaultsForInactiveSubIds");
@@ -781,19 +862,23 @@
         }
     }
 
+    /** @hide */
     public static boolean isValidSubId(long subId) {
         return subId > INVALID_SUB_ID ;
     }
 
+    /** @hide */
     public static boolean isValidSlotId(int slotId) {
         return slotId > INVALID_SLOT_ID && slotId < TelephonyManager.getDefault().getSimCount();
     }
 
+    /** @hide */
     public static boolean isValidPhoneId(int phoneId) {
         return phoneId > INVALID_PHONE_ID
                 && phoneId < TelephonyManager.getDefault().getPhoneCount();
     }
 
+    /** @hide */
     public static void putPhoneIdAndSubIdExtra(Intent intent, int phoneId) {
         long[] subIds = SubscriptionManager.getSubId(phoneId);
         if (subIds != null && subIds.length > 0) {
@@ -803,6 +888,7 @@
         }
     }
 
+    /** @hide */
     public static void putPhoneIdAndSubIdExtra(Intent intent, int phoneId, long subId) {
         if (VDBG) logd("putPhoneIdAndSubIdExtra: phoneId=" + phoneId + " subId=" + subId);
         intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
@@ -815,6 +901,7 @@
     /**
      * @return the list of subId's that are active,
      *         is never null but the length maybe 0.
+     * @hide
      */
     public static long[] getActiveSubIdList() {
         long[] subId = null;