Merge "Notify app widget hosts when cross-profile providers change." into lmp-dev
diff --git a/CleanSpec.mk b/CleanSpec.mk
index c7cf940..7bc30fd 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -221,6 +221,7 @@
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/services.core_intermediates)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/services_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/inputflinger $(PRODUCT_OUT)/symbols/system/bin/inputflinger)
 
 # ******************************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
diff --git a/api/current.txt b/api/current.txt
index 0d8aeba..5b8ba4d 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -28490,12 +28490,12 @@
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator CREATOR;
-    field public static int ROUTE_ALL;
-    field public static int ROUTE_BLUETOOTH;
-    field public static int ROUTE_EARPIECE;
-    field public static int ROUTE_SPEAKER;
-    field public static int ROUTE_WIRED_HEADSET;
-    field public static int ROUTE_WIRED_OR_EARPIECE;
+    field public static final int ROUTE_ALL = 15; // 0xf
+    field public static final int ROUTE_BLUETOOTH = 2; // 0x2
+    field public static final int ROUTE_EARPIECE = 1; // 0x1
+    field public static final int ROUTE_SPEAKER = 8; // 0x8
+    field public static final int ROUTE_WIRED_HEADSET = 4; // 0x4
+    field public static final int ROUTE_WIRED_OR_EARPIECE = 5; // 0x5
     field public final boolean isMuted;
     field public final int route;
     field public final int supportedRouteMask;
@@ -28543,8 +28543,8 @@
     method public final int getCallerDisplayNamePresentation();
     method public final android.telecomm.Conference getConference();
     method public final java.util.List<android.telecomm.Connection> getConferenceableConnections();
-    method public final int getFailureCode();
-    method public final java.lang.String getFailureMessage();
+    method public final int getDisconnectCause();
+    method public final java.lang.String getDisconnectMessage();
     method public final android.net.Uri getHandle();
     method public final int getHandlePresentation();
     method public final int getState();
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 0d2ad08..66928ca 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -16,25 +16,25 @@
 
 package android.app;
 
-import android.Manifest;
+import android.annotation.SystemApi;
+import android.app.usage.UsageStatsManager;
+import android.content.Context;
 import android.media.AudioAttributes.AttributeUsage;
 import android.os.Binder;
 import android.os.IBinder;
-import android.os.UserManager;
-import android.util.ArrayMap;
-
-import com.android.internal.app.IAppOpsService;
-import com.android.internal.app.IAppOpsCallback;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-
-import android.content.Context;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.UserManager;
+import android.util.ArrayMap;
+
+import com.android.internal.app.IAppOpsCallback;
+import com.android.internal.app.IAppOpsService;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
 
 /**
  * API for interacting with "application operation" tracking.
@@ -203,8 +203,10 @@
     public static final int OP_TOAST_WINDOW = 45;
     /** @hide Capture the device's display contents and/or audio */
     public static final int OP_PROJECT_MEDIA = 46;
+    /** @hide Activate a VPN connection without user intervention. */
+    public static final int OP_ACTIVATE_VPN = 47;
     /** @hide */
-    public static final int _NUM_OP = 47;
+    public static final int _NUM_OP = 48;
 
     /** Access to coarse location information. */
     public static final String OPSTR_COARSE_LOCATION =
@@ -218,6 +220,9 @@
     /** Continually monitoring location data with a relatively high power request. */
     public static final String OPSTR_MONITOR_HIGH_POWER_LOCATION
             = "android:monitor_location_high_power";
+    /** Activate a VPN connection without user intervention. @hide */
+    @SystemApi
+    public static final String OPSTR_ACTIVATE_VPN = "android:activate_vpn";
 
     /**
      * This maps each operation to the operation that serves as the
@@ -275,6 +280,7 @@
             OP_MUTE_MICROPHONE,
             OP_TOAST_WINDOW,
             OP_PROJECT_MEDIA,
+            OP_ACTIVATE_VPN,
     };
 
     /**
@@ -329,6 +335,7 @@
             null,
             null,
             null,
+            OPSTR_ACTIVATE_VPN,
     };
 
     /**
@@ -383,6 +390,7 @@
             "MUTE_MICROPHONE",
             "TOAST_WINDOW",
             "PROJECT_MEDIA",
+            "ACTIVATE_VPN",
     };
 
     /**
@@ -437,6 +445,7 @@
             null, // no permission for muting/unmuting microphone
             null, // no permission for displaying toasts
             null, // no permission for projecting media
+            null, // no permission for activating vpn
     };
 
     /**
@@ -492,6 +501,7 @@
             UserManager.DISALLOW_UNMUTE_MICROPHONE, // MUTE_MICROPHONE
             UserManager.DISALLOW_CREATE_WINDOWS, // TOAST_WINDOW
             null, //PROJECT_MEDIA
+            UserManager.DISALLOW_CONFIG_VPN, // ACTIVATE_VPN
     };
 
     /**
@@ -546,6 +556,7 @@
             false, //MUTE_MICROPHONE
             true, //TOAST_WINDOW
             false, //PROJECT_MEDIA
+            false, //ACTIVATE_VPN
     };
 
     /**
@@ -599,6 +610,7 @@
             AppOpsManager.MODE_ALLOWED,
             AppOpsManager.MODE_ALLOWED,
             AppOpsManager.MODE_IGNORED, // OP_PROJECT_MEDIA
+            AppOpsManager.MODE_IGNORED, // OP_ACTIVATE_VPN
     };
 
     /**
@@ -656,6 +668,7 @@
             false,
             false,
             false,
+            false,
     };
 
     private static HashMap<String, Integer> sOpStrToOp = new HashMap<String, Integer>();
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 053afb7..bc4d2c1 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2406,6 +2406,27 @@
     }
 
     /**
+     * @hide
+     * @param user The user for whom to fetch the profile owner name, if any.
+     * @return the human readable name of the organisation associated with this profile owner or
+     *         null if one is not set.
+     * @throws IllegalArgumentException if the userId is invalid.
+     */
+    @SystemApi
+    public String getProfileOwnerNameAsUser(UserHandle user) throws IllegalArgumentException {
+        if (mService != null) {
+            try {
+                return mService.getProfileOwnerName(user.getIdentifier());
+            } catch (RemoteException re) {
+                Log.w(TAG, "Failed to get profile owner");
+                throw new IllegalArgumentException(
+                        "Requested profile owner for invalid userId", re);
+            }
+        }
+        return null;
+    }
+
+    /**
      * Called by a profile owner or device owner to add a default intent handler activity for
      * intents that match a certain intent filter. This activity will remain the default intent
      * handler even if the set of potential event handlers for the intent filter changes and if
diff --git a/core/java/android/bluetooth/le/BluetoothLeScanner.java b/core/java/android/bluetooth/le/BluetoothLeScanner.java
index 988cea5..e1d4bbd2 100644
--- a/core/java/android/bluetooth/le/BluetoothLeScanner.java
+++ b/core/java/android/bluetooth/le/BluetoothLeScanner.java
@@ -130,20 +130,7 @@
             }
             BleScanCallbackWrapper wrapper = new BleScanCallbackWrapper(gatt, filters,
                     settings, callback, resultStorages);
-            try {
-                UUID uuid = UUID.randomUUID();
-                gatt.registerClient(new ParcelUuid(uuid), wrapper);
-                if (wrapper.scanStarted()) {
-                    mLeScanClients.put(callback, wrapper);
-                } else {
-                    postCallbackError(callback,
-                            ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED);
-                    return;
-                }
-            } catch (RemoteException e) {
-                Log.e(TAG, "GATT service exception when starting scan", e);
-                postCallbackError(callback, ScanCallback.SCAN_FAILED_INTERNAL_ERROR);
-            }
+            wrapper.startRegisteration();
         }
     }
 
@@ -159,8 +146,7 @@
         synchronized (mLeScanClients) {
             BleScanCallbackWrapper wrapper = mLeScanClients.remove(callback);
             if (wrapper == null) {
-                if (DBG)
-                    Log.d(TAG, "could not find callback wrapper");
+                if (DBG) Log.d(TAG, "could not find callback wrapper");
                 return;
             }
             wrapper.stopLeScan();
@@ -220,8 +206,8 @@
     /**
      * Bluetooth GATT interface callbacks
      */
-    private static class BleScanCallbackWrapper extends BluetoothGattCallbackWrapper {
-        private static final int REGISTRATION_CALLBACK_TIMEOUT_SECONDS = 5;
+    private class BleScanCallbackWrapper extends BluetoothGattCallbackWrapper {
+        private static final int REGISTRATION_CALLBACK_TIMEOUT_MILLIS = 2000;
 
         private final ScanCallback mScanCallback;
         private final List<ScanFilter> mFilters;
@@ -245,18 +231,25 @@
             mResultStorages = resultStorages;
         }
 
-        public boolean scanStarted() {
+        public void startRegisteration() {
             synchronized (this) {
-                if (mClientIf == -1) {
-                    return false;
-                }
+                // Scan stopped.
+                if (mClientIf == -1) return;
                 try {
-                    wait(REGISTRATION_CALLBACK_TIMEOUT_SECONDS);
-                } catch (InterruptedException e) {
-                    Log.e(TAG, "Callback reg wait interrupted: " + e);
+                    UUID uuid = UUID.randomUUID();
+                    mBluetoothGatt.registerClient(new ParcelUuid(uuid), this);
+                    wait(REGISTRATION_CALLBACK_TIMEOUT_MILLIS);
+                } catch (InterruptedException | RemoteException e) {
+                    Log.e(TAG, "application registeration exception", e);
+                    postCallbackError(mScanCallback, ScanCallback.SCAN_FAILED_INTERNAL_ERROR);
+                }
+                if (mClientIf > 0) {
+                    mLeScanClients.put(mScanCallback, this);
+                } else {
+                    postCallbackError(mScanCallback,
+                            ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED);
                 }
             }
-            return mClientIf > 0;
         }
 
         public void stopLeScan() {
@@ -272,7 +265,6 @@
                     Log.e(TAG, "Failed to stop scan and unregister", e);
                 }
                 mClientIf = -1;
-                notifyAll();
             }
         }
 
@@ -297,11 +289,9 @@
         public void onClientRegistered(int status, int clientIf) {
             Log.d(TAG, "onClientRegistered() - status=" + status +
                     " clientIf=" + clientIf);
-
             synchronized (this) {
                 if (mClientIf == -1) {
-                    if (DBG)
-                        Log.d(TAG, "onClientRegistered LE scan canceled");
+                    if (DBG) Log.d(TAG, "onClientRegistered LE scan canceled");
                 }
 
                 if (status == BluetoothGatt.GATT_SUCCESS) {
@@ -328,17 +318,15 @@
          */
         @Override
         public void onScanResult(final ScanResult scanResult) {
-            if (DBG)
-                Log.d(TAG, "onScanResult() - " + scanResult.toString());
+            if (DBG) Log.d(TAG, "onScanResult() - " + scanResult.toString());
 
             // Check null in case the scan has been stopped
             synchronized (this) {
-                if (mClientIf <= 0)
-                    return;
+                if (mClientIf <= 0) return;
             }
             Handler handler = new Handler(Looper.getMainLooper());
             handler.post(new Runnable() {
-                    @Override
+                @Override
                 public void run() {
                     mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult);
                 }
@@ -350,7 +338,7 @@
         public void onBatchScanResults(final List<ScanResult> results) {
             Handler handler = new Handler(Looper.getMainLooper());
             handler.post(new Runnable() {
-                    @Override
+                @Override
                 public void run() {
                     mScanCallback.onBatchScanResults(results);
                 }
@@ -394,7 +382,7 @@
 
     private void postCallbackError(final ScanCallback callback, final int errorCode) {
         mHandler.post(new Runnable() {
-                @Override
+            @Override
             public void run() {
                 callback.onScanFailed(errorCode);
             }
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 0814e0f..b2fc3be 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -108,6 +108,8 @@
 
     boolean prepareVpn(String oldPackage, String newPackage);
 
+    void setVpnPackageAuthorization(boolean authorized);
+
     ParcelFileDescriptor establishVpn(in VpnConfig config);
 
     VpnConfig getVpnConfig();
diff --git a/core/java/android/net/PacProxySelector.java b/core/java/android/net/PacProxySelector.java
index 8a2c2b6..8626d08 100644
--- a/core/java/android/net/PacProxySelector.java
+++ b/core/java/android/net/PacProxySelector.java
@@ -39,6 +39,9 @@
 public class PacProxySelector extends ProxySelector {
     private static final String TAG = "PacProxySelector";
     public static final String PROXY_SERVICE = "com.android.net.IProxyService";
+    private static final String SOCKS = "SOCKS ";
+    private static final String PROXY = "PROXY ";
+
     private IProxyService mProxyService;
     private final List<Proxy> mDefaultList;
 
@@ -88,16 +91,16 @@
             String trimmed = s.trim();
             if (trimmed.equals("DIRECT")) {
                 ret.add(java.net.Proxy.NO_PROXY);
-            } else if (trimmed.startsWith("PROXY ")) {
-                String[] hostPort = trimmed.substring(6).split(":");
-                String host = hostPort[0];
-                int port;
-                try {
-                    port = Integer.parseInt(hostPort[1]);
-                } catch (Exception e) {
-                    port = 8080;
+            } else if (trimmed.startsWith(PROXY)) {
+                Proxy proxy = proxyFromHostPort(Type.HTTP, trimmed.substring(PROXY.length()));
+                if (proxy != null) {
+                    ret.add(proxy);
                 }
-                ret.add(new Proxy(Type.HTTP, InetSocketAddress.createUnresolved(host, port)));
+            } else if (trimmed.startsWith(SOCKS)) {
+                Proxy proxy = proxyFromHostPort(Type.SOCKS, trimmed.substring(SOCKS.length()));
+                if (proxy != null) {
+                    ret.add(proxy);
+                }
             }
         }
         if (ret.size() == 0) {
@@ -106,6 +109,18 @@
         return ret;
     }
 
+    private static Proxy proxyFromHostPort(Proxy.Type type, String hostPortString) {
+        try {
+            String[] hostPort = hostPortString.split(":");
+            String host = hostPort[0];
+            int port = Integer.parseInt(hostPort[1]);
+            return new Proxy(type, InetSocketAddress.createUnresolved(host, port));
+        } catch (NumberFormatException|ArrayIndexOutOfBoundsException e) {
+            Log.d(TAG, "Unable to parse proxy " + hostPortString + " " + e);
+            return null;
+        }
+    }
+
     @Override
     public void connectFailed(URI uri, SocketAddress address, IOException failure) {
 
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 8c9b819..de90899 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -349,6 +349,8 @@
      */
     abstract HardwareLayer createTextureLayer();
 
+    abstract void buildLayer(RenderNode node);
+
     abstract boolean copyLayerInto(HardwareLayer layer, Bitmap bitmap);
 
     /**
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index f9333d5..50341fc 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -332,6 +332,11 @@
     }
 
     @Override
+    void buildLayer(RenderNode node) {
+        nBuildLayer(mNativeProxy, node.getNativeDisplayList());
+    }
+
+    @Override
     boolean copyLayerInto(final HardwareLayer layer, final Bitmap bitmap) {
         return nCopyLayerInto(mNativeProxy,
                 layer.getDeferredLayerUpdater(), bitmap.mNativeBitmap);
@@ -468,6 +473,7 @@
 
     private static native long nCreateDisplayListLayer(long nativeProxy, int width, int height);
     private static native long nCreateTextureLayer(long nativeProxy);
+    private static native void nBuildLayer(long nativeProxy, long node);
     private static native boolean nCopyLayerInto(long nativeProxy, long layer, long bitmap);
     private static native void nPushLayerUpdate(long nativeProxy, long layer);
     private static native void nCancelLayerUpdate(long nativeProxy, long layer);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index f17daaf..243d7d7 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -13658,11 +13658,10 @@
 
         switch (mLayerType) {
             case LAYER_TYPE_HARDWARE:
-                // The only part of a hardware layer we can build in response to
-                // this call is to ensure the display list is up to date.
-                // The actual rendering of the display list into the layer must
-                // be done at playback time
                 updateDisplayListIfDirty();
+                if (attachInfo.mHardwareRenderer != null && mRenderNode.isValid()) {
+                    attachInfo.mHardwareRenderer.buildLayer(mRenderNode);
+                }
                 break;
             case LAYER_TYPE_SOFTWARE:
                 buildDrawingCache(true);
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 636c712..61b4567 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -22,6 +22,7 @@
 import android.app.usage.UsageStatsManager;
 import android.os.AsyncTask;
 import android.provider.Settings;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Slog;
 import android.widget.AbsListView;
@@ -278,12 +279,15 @@
             });
         }
 
-        final TextView titleView = (TextView) findViewById(R.id.title);
-        if (titleView != null) {
-            if (title == null) {
-                title = getTitleForAction(intent.getAction(), defaultTitleRes);
+        if (title == null) {
+            title = getTitleForAction(intent.getAction(), defaultTitleRes);
+        }
+        if (!TextUtils.isEmpty(title)) {
+            final TextView titleView = (TextView) findViewById(R.id.title);
+            if (titleView != null) {
+                titleView.setText(title);
             }
-            titleView.setText(title);
+            setTitle(title);
         }
 
         final ImageView iconView = (ImageView) findViewById(R.id.icon);
diff --git a/core/java/com/android/internal/app/WindowDecorActionBar.java b/core/java/com/android/internal/app/WindowDecorActionBar.java
index b5ff0cc..b58e1db 100644
--- a/core/java/com/android/internal/app/WindowDecorActionBar.java
+++ b/core/java/com/android/internal/app/WindowDecorActionBar.java
@@ -925,6 +925,15 @@
     public void onContentScrollStopped() {
     }
 
+    @Override
+    public boolean collapseActionView() {
+        if (mDecorToolbar != null && mDecorToolbar.hasExpandedActionView()) {
+            mDecorToolbar.collapseActionView();
+            return true;
+        }
+        return false;
+    }
+
     /**
      * @hide 
      */
diff --git a/core/java/com/android/internal/net/LegacyVpnInfo.java b/core/java/com/android/internal/net/LegacyVpnInfo.java
index d6f6d0b..f812ad6 100644
--- a/core/java/com/android/internal/net/LegacyVpnInfo.java
+++ b/core/java/com/android/internal/net/LegacyVpnInfo.java
@@ -40,7 +40,6 @@
 
     public String key;
     public int state = -1;
-    public PendingIntent intent;
 
     @Override
     public int describeContents() {
@@ -51,7 +50,6 @@
     public void writeToParcel(Parcel out, int flags) {
         out.writeString(key);
         out.writeInt(state);
-        out.writeParcelable(intent, flags);
     }
 
     public static final Parcelable.Creator<LegacyVpnInfo> CREATOR =
@@ -61,7 +59,6 @@
             LegacyVpnInfo info = new LegacyVpnInfo();
             info.key = in.readString();
             info.state = in.readInt();
-            info.intent = in.readParcelable(null);
             return info;
         }
 
diff --git a/core/java/com/android/internal/net/VpnConfig.java b/core/java/com/android/internal/net/VpnConfig.java
index 0099269..aa66d7d 100644
--- a/core/java/com/android/internal/net/VpnConfig.java
+++ b/core/java/com/android/internal/net/VpnConfig.java
@@ -20,17 +20,19 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
+import android.net.LinkAddress;
+import android.net.RouteInfo;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.os.UserHandle;
-import android.net.RouteInfo;
-import android.net.LinkAddress;
 
 import java.net.Inet4Address;
 import java.net.InetAddress;
-import java.util.List;
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * A simple container used to carry information in VpnBuilder, VpnDialogs,
@@ -55,12 +57,19 @@
         return intent;
     }
 
-    public static PendingIntent getIntentForStatusPanel(Context context) {
-        Intent intent = new Intent();
-        intent.setClassName(DIALOGS_PACKAGE, DIALOGS_PACKAGE + ".ManageDialog");
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_HISTORY |
-                Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-        return PendingIntent.getActivityAsUser(context, 0, intent, 0, null, UserHandle.CURRENT);
+    public static CharSequence getVpnLabel(Context context, String packageName)
+            throws NameNotFoundException {
+        PackageManager pm = context.getPackageManager();
+        Intent intent = new Intent(SERVICE_INTERFACE);
+        intent.setPackage(packageName);
+        List<ResolveInfo> services = pm.queryIntentServices(intent, 0 /* flags */);
+        if (services != null && services.size() == 1) {
+            // This app contains exactly one VPN service. Call loadLabel, which will attempt to
+            // load the service's label, and fall back to the app label if none is present.
+            return services.get(0).loadLabel(pm);
+        } else {
+            return pm.getApplicationInfo(packageName, 0).loadLabel(pm);
+        }
     }
 
     public String user;
diff --git a/core/java/com/android/internal/widget/ActionBarContainer.java b/core/java/com/android/internal/widget/ActionBarContainer.java
index 254f602..a2b8ff2 100644
--- a/core/java/com/android/internal/widget/ActionBarContainer.java
+++ b/core/java/com/android/internal/widget/ActionBarContainer.java
@@ -268,16 +268,21 @@
 
         if (mActionBarView == null) return;
 
-        final LayoutParams lp = (LayoutParams) mActionBarView.getLayoutParams();
-        final int actionBarViewHeight = isCollapsed(mActionBarView) ? 0 :
-                mActionBarView.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
+        int nonTabMaxHeight = 0;
+        final int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            final View child = getChildAt(i);
+            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+            nonTabMaxHeight = isCollapsed(child) ? 0 :
+                    child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
+        }
 
         if (mTabContainer != null && mTabContainer.getVisibility() != GONE) {
             final int mode = MeasureSpec.getMode(heightMeasureSpec);
             if (mode == MeasureSpec.AT_MOST) {
                 final int maxHeight = MeasureSpec.getSize(heightMeasureSpec);
                 setMeasuredDimension(getMeasuredWidth(),
-                        Math.min(actionBarViewHeight + mTabContainer.getMeasuredHeight(),
+                        Math.min(nonTabMaxHeight + mTabContainer.getMeasuredHeight(),
                                 maxHeight));
             }
         }
diff --git a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
index 5709f659..cca48d3 100644
--- a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
+++ b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
@@ -184,8 +184,6 @@
                 Build.VERSION_CODES.KITKAT;
 
         mFlingEstimator = new OverScroller(context);
-
-        setFocusableInTouchMode(true);
     }
 
     @Override
@@ -661,27 +659,6 @@
     }
 
     @Override
-    public boolean dispatchKeyEvent(KeyEvent event) {
-        if (super.dispatchKeyEvent(event)) {
-            return true;
-        }
-
-        if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
-            final int action = event.getAction();
-
-            // Collapse any expanded action views.
-            if (mDecorToolbar != null && mDecorToolbar.hasExpandedActionView()) {
-                if (action == KeyEvent.ACTION_UP) {
-                    mDecorToolbar.collapseActionView();
-                }
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    @Override
     public void setWindowCallback(Window.Callback cb) {
         pullChildren();
         mDecorToolbar.setWindowCallback(cb);
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 2ce1b15..62ea351 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -18,6 +18,7 @@
 #include <android_runtime/AndroidRuntime.h>
 #include <androidfw/Asset.h>
 #include <androidfw/ResourceTypes.h>
+#include <cutils/compiler.h>
 #include <netinet/in.h>
 #include <stdio.h>
 #include <sys/mman.h>
@@ -87,27 +88,39 @@
     return options != NULL && env->GetBooleanField(options, gOptions_justBoundsFieldID);
 }
 
-static void scaleNinePatchChunk(android::Res_png_9patch* chunk, float scale) {
+static void scaleDivRange(int32_t* divs, int count, float scale, int maxValue) {
+    for (int i = 0; i < count; i++) {
+        divs[i] = int32_t(divs[i] * scale + 0.5f);
+        if (i > 0 && divs[i] == divs[i - 1]) {
+            divs[i]++; // avoid collisions
+        }
+    }
+
+    if (CC_UNLIKELY(divs[count - 1] > maxValue)) {
+        // if the collision avoidance above put some divs outside the bounds of the bitmap,
+        // slide outer stretchable divs inward to stay within bounds
+        int highestAvailable = maxValue;
+        for (int i = count - 1; i >= 0; i--) {
+            divs[i] = highestAvailable;
+            if (i > 0 && divs[i] <= divs[i-1]){
+                // keep shifting
+                highestAvailable = divs[i] - 1;
+            } else {
+                break;
+            }
+        }
+    }
+}
+
+static void scaleNinePatchChunk(android::Res_png_9patch* chunk, float scale,
+        int scaledWidth, int scaledHeight) {
     chunk->paddingLeft = int(chunk->paddingLeft * scale + 0.5f);
     chunk->paddingTop = int(chunk->paddingTop * scale + 0.5f);
     chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f);
     chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f);
 
-    int32_t* xDivs = chunk->getXDivs();
-    for (int i = 0; i < chunk->numXDivs; i++) {
-        xDivs[i] = int32_t(xDivs[i] * scale + 0.5f);
-        if (i > 0 && xDivs[i] == xDivs[i - 1]) {
-            xDivs[i]++;
-        }
-    }
-
-    int32_t* yDivs = chunk->getYDivs();
-    for (int i = 0; i < chunk->numYDivs; i++) {
-        yDivs[i] = int32_t(yDivs[i] * scale + 0.5f);
-        if (i > 0 && yDivs[i] == yDivs[i - 1]) {
-            yDivs[i]++;
-        }
-    }
+    scaleDivRange(chunk->getXDivs(), chunk->numXDivs, scale, scaledWidth);
+    scaleDivRange(chunk->getYDivs(), chunk->numYDivs, scale, scaledHeight);
 }
 
 static SkColorType colorTypeForScaledOutput(SkColorType colorType) {
@@ -330,7 +343,7 @@
     jbyteArray ninePatchChunk = NULL;
     if (peeker.mPatch != NULL) {
         if (willScale) {
-            scaleNinePatchChunk(peeker.mPatch, scale);
+            scaleNinePatchChunk(peeker.mPatch, scale, scaledWidth, scaledHeight);
         }
 
         size_t ninePatchArraySize = peeker.mPatch->serializedSize();
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index d183d8e..4e3419a 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -264,6 +264,13 @@
     return reinterpret_cast<jlong>(layer);
 }
 
+static void android_view_ThreadedRenderer_buildLayer(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jlong nodePtr) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    RenderNode* node = reinterpret_cast<RenderNode*>(nodePtr);
+    proxy->buildLayer(node);
+}
+
 static jboolean android_view_ThreadedRenderer_copyLayerInto(JNIEnv* env, jobject clazz,
         jlong proxyPtr, jlong layerPtr, jlong bitmapPtr) {
     RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
@@ -367,6 +374,7 @@
     { "nInvokeFunctor", "(JZ)V", (void*) android_view_ThreadedRenderer_invokeFunctor },
     { "nCreateDisplayListLayer", "(JII)J", (void*) android_view_ThreadedRenderer_createDisplayListLayer },
     { "nCreateTextureLayer", "(J)J", (void*) android_view_ThreadedRenderer_createTextureLayer },
+    { "nBuildLayer", "(JJ)V", (void*) android_view_ThreadedRenderer_buildLayer },
     { "nCopyLayerInto", "(JJJ)Z", (void*) android_view_ThreadedRenderer_copyLayerInto },
     { "nPushLayerUpdate", "(JJ)V", (void*) android_view_ThreadedRenderer_pushLayerUpdate },
     { "nCancelLayerUpdate", "(JJ)V", (void*) android_view_ThreadedRenderer_cancelLayerUpdate },
diff --git a/core/res/res/anim/progress_indeterminate_horizontal_rect1.xml b/core/res/res/anim/progress_indeterminate_horizontal_rect1.xml
new file mode 100644
index 0000000..980c8e4
--- /dev/null
+++ b/core/res/res/anim/progress_indeterminate_horizontal_rect1.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="2000"
+        android:propertyXName="translateX"
+        android:propertyYName="translateY"
+        android:repeatCount="infinite"
+        android:pathData="M -522.59998 0 l 0.0 0 l 0.0 0 l 0.0 0 l 0.0 0 l 0.0 0 l 0.0 0 l 0.0 0 l 0.0 0 l 0.0 0 l 0.0 0 l 0.0 0 l 0.0 0 l 0.0 0 l 0.0 0 l 0.0 0 l 0.0 0 l 0.0 0 l 0.0 0 l 0.0 0 l 0.0 0 l 0.0 0 l 0.0 0 l 0.0 0 l 0.0 0 l 0.1294 0 l 0.33832 0 l 0.5545 0 l 0.77088 0 l 0.98065 0 l 1.19641 0 l 1.41351 0 l 1.63153 0 l 1.85053 0 l 2.07052 0 l 2.29081 0 l 2.5115 0 l 2.73261 0 l 2.95355 0 l 3.17404 0 l 3.39423 0 l 3.61355 0 l 3.83164 0 l 4.04849 0 l 4.26367 0 l 5.74725 0 l 6.10266 0 l 6.45981 0 l 6.81781 0 l 7.17655 0 l 7.53366 0 l 7.88861 0 l 8.23975 0 l 8.58447 0 l 8.92157 0 l 9.24811 0 l 9.56137 0 l 9.85907 0 l 10.13778 0 l 10.39557 0 l 10.62876 0 l 10.83572 0 l 11.01492 0 l 11.16397 0 l 11.28324 0 l 11.3714 0 l 11.43011 0 l 11.45966 0 l 11.4611 0 l 11.43691 0 l 11.38878 0 l 11.31834 0 l 11.2276 0 l 11.11856 0 l 10.99338 0 l 10.85347 0 l 10.69954 0 l 10.53393 0 l 10.37447 0 l 10.37077 0 l 10.43095 0 l 10.52757 0 l 10.6715 0 l 10.8764 0 l 11.15665 0 l 11.52708 0 l 11.9948 0 l 12.55024 0 l 13.14534 0 l 13.68079 0 l 14.02233 0 l 14.06503 0 l 13.79804 0 l 13.295 0 l 12.65849 0 l 11.9693 0 l 11.2773 0 l 10.60766 0 l 9.97053 0 l 9.36723 0 l 8.79752 0 l 8.25793 0 l 7.74495 0 l 7.25633 0 l 6.78856 0 l 6.33934 0 l 5.9071 0 l 5.48941 0 l 5.08502 0 l 4.69292 0 l 4.33431 0 l 4.00734 0 l 3.68829 0 l 3.37685 0 l 3.07246 0 l 2.7744 0 l 2.48253 0 l 2.20102 0 l 1.91748 0 l 1.63726 0 l 1.36773 0 "
+        android:interpolator="@interpolator/progress_indeterminate_horizontal_rect1_grp_position" />
+    <objectAnimator
+        android:duration="2000"
+        android:propertyXName="scaleX"
+        android:propertyYName="scaleY"
+        android:repeatCount="infinite"
+        android:pathData="M 0.1 1 l 0.0 0 l 0.00882427215576 0 l 0.00982859611511 0 l 0.0108650398254 0 l 0.011930847168 0 l 0.0130220413208 0 l 0.0141334056854 0 l 0.0152582168579 0 l 0.0163880157471 0 l 0.0175127220154 0 l 0.0186203575134 0 l 0.0196973228455 0 l 0.0207285499573 0 l 0.0216978645325 0 l 0.0225887107849 0 l 0.0233847427368 0 l 0.0240707015991 0 l 0.0246334838867 0 l 0.0250626373291 0 l 0.0253514099121 0 l 0.0254969406128 0 l 0.0255004882813 0 l 0.0253670883179 0 l 0.0251052856445 0 l 0.0247262573242 0 l 0.0242431640625 0 l 0.0236701583862 0 l 0.0230218887329 0 l 0.0223124694824 0 l 0.021555557251 0 l 0.0207632446289 0 l 0.0199468231201 0 l 0.0191157531738 0 l 0.0182782745361 0 l 0.0173241424561 0 l 0.0152210998535 0 l 0.0126258087158 0 l 0.00973388671875 0 l 0.00647575378418 0 l 0.00276618957519 0 l -0.00149223327636 0 l -0.00639404296875 0 l -0.0119906616211 0 l -0.0182067108154 0 l -0.0247090148926 0 l -0.0308044433594 0 l -0.0355574798584 0 l -0.0382397460938 0 l -0.0387688446045 0 l -0.0376621246338 0 l -0.0356225204468 0 l -0.03321434021 0 l -0.0307815170288 0 l -0.0284958267212 0 l -0.0264254379272 0 l -0.024584236145 0 l -0.0229611587524 0 l -0.0215351867676 0 l -0.0202828598023 0 l -0.0191815567017 0 l -0.018210849762 0 l -0.0173528671265 0 l -0.0165923118591 0 l -0.0159160423279 0 l -0.0153129196167 0 l -0.0147735023499 0 l -0.0141336250305 0 l -0.0133926582336 0 l -0.01270362854 0 l -0.0120610809326 0 l -0.0114603328705 0 l -0.0108972930908 0 l -0.0103683567047 0 l -0.00987038612366 0 l -0.00940062522888 0 l -0.00895661354065 0 l -0.00853617668152 0 "
+        android:interpolator="@interpolator/progress_indeterminate_horizontal_rect1_grp_scale" />
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/progress_indeterminate_horizontal_rect1_scale.xml b/core/res/res/anim/progress_indeterminate_horizontal_rect1_scale.xml
deleted file mode 100644
index 7c782a6..0000000
--- a/core/res/res/anim/progress_indeterminate_horizontal_rect1_scale.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- 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.
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
-
-    <objectAnimator
-        android:duration="2016"
-        android:pathData="M 0.1 1 l 0 0 l 0.00882427215576 0 l 0.00982859611511 0
-l 0.01086503982544 0 l 0.01193084716797 0 l 0.0130220413208 0 l 0.01413340568542 0
-l 0.01525821685791 0 l 0.01638801574707 0 l 0.01751272201538 0 l 0.01862035751343 0
-l 0.01969732284546 0 l 0.02072854995728 0 l 0.02169786453247 0 l 0.02258871078491 0
-l 0.02338474273682 0 l 0.02407070159912 0 l 0.02463348388672 0 l 0.0250626373291 0
-l 0.02535140991211 0 l 0.02549694061279 0 l 0.02550048828125 0 l 0.02536708831787 0
-l 0.02510528564453 0 l 0.02472625732422 0 l 0.0242431640625 0 l 0.02367015838623 0
-l 0.02302188873291 0 l 0.02231246948242 0 l 0.02155555725098 0 l 0.02076324462891 0
-l 0.01994682312012 0 l 0.01911575317383 0 l 0.01827827453613 0 l 0.01732414245605 0
-l 0.01522109985352 0 l 0.01262580871582 0 l 0.00973388671875 0 l 0.00647575378418 0
-l 0.0027661895752 0 l -0.00149223327637 0 l -0.00639404296875 0 l -0.01199066162109 0
-l -0.01820671081543 0 l -0.02470901489258 0 l -0.03080444335937 0 l -0.0355574798584 0
-l -0.03823974609375 0 l -0.03876884460449 0 l -0.03766212463379 0 l -0.03562252044678 0
-l -0.03321434020996 0 l -0.03078151702881 0 l -0.02849582672119 0 l -0.02642543792725 0
-l -0.02458423614502 0 l -0.02296115875244 0 l -0.02153518676758 0 l -0.02028285980225 0
-l -0.01918155670166 0 l -0.01821084976196 0 l -0.01735286712646 0 l -0.01659231185913 0
-l -0.01591604232788 0 l -0.0153129196167 0 l -0.01477350234985 0 l -0.01413362503052 0
-l -0.01339265823364 0 l -0.01270362854004 0 l -0.01206108093262 0 l -0.01146033287048 0
-l -0.01089729309082 0 l -0.01036835670471 0 l -0.00987038612366 0 l -0.00940062522888 0
-l -0.00895661354065 0 l -0.00853617668152 0"
-        android:propertyXName="scaleX"
-        android:repeatCount="infinite" />
-
-</set>
\ No newline at end of file
diff --git a/core/res/res/anim/progress_indeterminate_horizontal_rect1_translate.xml b/core/res/res/anim/progress_indeterminate_horizontal_rect1_translate.xml
deleted file mode 100644
index c26bb5d..0000000
--- a/core/res/res/anim/progress_indeterminate_horizontal_rect1_translate.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- 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.
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
-
-    <objectAnimator
-        android:duration="2016"
-        android:pathData="M -522.599975585938 0 l 0 0 l 0.12939453125 0
-l 0.33831787109375 0 l 0.55450439453125 0 l 0.7708740234375 0 l 0.98065185546875 0
-l 1.1964111328125 0 l 1.41351318359375 0 l 1.63153076171875 0 l 1.85052490234375 0
-l 2.07052612304688 0 l 2.29080200195312 0 l 2.51150512695312 0 l 2.73260498046875 0
-l 2.95355224609375 0 l 3.17404174804688 0 l 3.39422607421875 0 l 3.61355590820312 0
-l 3.83163452148438 0 l 4.04849243164062 0 l 4.263671875 0 l 5.74725341796875 0
-l 6.1026611328125 0 l 6.45980834960938 0 l 6.81781005859375 0 l 7.17654418945312 0
-l 7.53366088867188 0 l 7.88861083984375 0 l 8.23974609375 0 l 8.58447265625 0
-l 8.92156982421875 0 l 9.24810791015625 0 l 9.56137084960938 0 l 9.85906982421875 0
-l 10.1377868652344 0 l 10.3955688476562 0 l 10.6287536621094 0 l 10.8357238769531 0
-l 11.0149230957031 0 l 11.1639709472656 0 l 11.2832336425781 0 l 11.3713989257812 0
-l 11.4301147460938 0 l 11.4596557617188 0 l 11.4611053466797 0 l 11.4369049072266 0
-l 11.3887786865234 0 l 11.3183441162109 0 l 11.2276000976562 0 l 11.1185607910156 0
-l 10.9933776855469 0 l 10.8534698486328 0 l 10.6995391845703 0 l 10.533935546875 0
-l 10.3744659423828 0 l 10.3707733154297 0 l 10.4309463500977 0 l 10.5275726318359 0
-l 10.671501159668 0 l 10.8763961791992 0 l 11.1566543579102 0 l 11.5270767211914 0
-l 11.9947967529297 0 l 12.5502433776855 0 l 13.1453399658203 0 l 13.680793762207 0
-l 14.0223298072815 0 l 14.0650296211243 0 l 13.798041343689 0 l 13.2949924468994 0
-l 12.6584892272949 0 l 11.9693031311035 0 l 11.2772979736328 0 l 10.607666015625 0
-l 9.97052764892578 0 l 9.36723327636719 0 l 8.79751586914062 0 l 8.25792694091797 0
-l 7.74495697021484 0 l 7.25632476806641 0 l 6.78855895996094 0 l 6.33934020996094 0
-l 5.9071044921875 0 l 5.48941040039062 0 l 5.08502197265625 0 l 4.69291687011719 0
-l 4.33430480957031 0 l 4.00733947753906 0 l 3.68829345703125 0 l 3.37684631347656 0
-l 3.07246398925781 0 l 2.77439880371094 0 l 2.48252868652344 0 l 2.20101928710938 0
-l 1.91748046875 0 l 1.63726806640625 0 l 1.36772155761719 0"
-        android:propertyXName="translateX"
-        android:repeatCount="infinite" />
-
-</set>
\ No newline at end of file
diff --git a/core/res/res/anim/progress_indeterminate_horizontal_rect2.xml b/core/res/res/anim/progress_indeterminate_horizontal_rect2.xml
new file mode 100644
index 0000000..8f0b2e8
--- /dev/null
+++ b/core/res/res/anim/progress_indeterminate_horizontal_rect2.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="2000"
+        android:propertyXName="translateX"
+        android:propertyYName="translateY"
+        android:repeatCount="infinite"
+        android:pathData="M -197.60001 0 l 1.42626 0 l 1.80754 0 l 2.18779 0 l 2.5611 0 l 2.9181 0 l 3.25482 0 l 3.5716 0 l 3.86255 0 l 4.12494 0 l 4.35758 0 l 4.56035 0 l 4.73427 0 l 4.88091 0 l 5.00271 0 l 5.10274 0 l 5.18401 0 l 5.24911 0 l 5.30098 0 l 5.34226 0 l 5.37535 0 l 5.40181 0 l 5.42322 0 l 5.44123 0 l 5.45705 0 l 5.47099 0 l 5.48396 0 l 5.4967 0 l 5.5095 0 l 5.52215 0 l 5.53528 0 l 5.54913 0 l 5.56306 0 l 5.57743 0 l 5.59244 0 l 5.60744 0 l 5.62244 0 l 5.63767 0 l 5.65263 0 l 5.6669 0 l 5.6807 0 l 5.69401 0 l 5.70899 0 l 5.7517 0 l 5.80327 0 l 5.8571 0 l 5.914 0 l 5.9745 0 l 6.03849 0 l 6.10729 0 l 6.18126 0 l 6.26117 0 l 6.3484 0 l 6.44406 0 l 6.54867 0 l 6.66372 0 l 6.79021 0 l 6.92859 0 l 7.07807 0 l 7.23712 0 l 7.40254 0 l 7.56885 0 l 7.72841 0 l 7.87199 0 l 7.98993 0 l 8.07417 0 l 8.12013 0 l 8.12656 0 l 8.09511 0 l 8.03091 0 l 7.93996 0 l 7.82788 0 l 7.69977 0 l 7.56065 0 l 7.41323 0 l 7.26063 0 l 7.10471 0 l 6.94624 0 l 6.78694 0 l 6.63904 0 l 6.50302 0 l 6.36688 0 l 6.23044 0 l 6.09357 0 l 5.95706 0 l 5.82065 0 l 5.68396 0 l 5.54773 0 l 5.41144 0 l 5.27533 0 l 5.13922 0 l 5.00348 0 l 4.86804 0 l 4.73251 0 l 4.59732 0 l 4.46259 0 l 4.32812 0 l 4.19373 0 l 4.05993 0 l 3.92673 0 l 3.79376 0 l 3.6612 0 l 3.52936 0 l 3.39819 0 l 3.26749 0 l 3.13726 0 l 3.00797 0 l 2.87939 0 l 2.75159 0 l 2.62445 0 l 2.49811 0 l 2.37268 0 l 2.24817 0 l 2.12457 0 l 2.00174 0 l 1.87997 0 l 1.76185 0 l 1.64154 0 l 1.51962 0 l 1.40018 0 l 1.28421 0 "
+        android:interpolator="@interpolator/progress_indeterminate_horizontal_rect2_grp_position" />
+    <objectAnimator
+        android:duration="2000"
+        android:propertyXName="scaleX"
+        android:propertyYName="scaleY"
+        android:repeatCount="infinite"
+        android:pathData="M 0.1 1 l 0.00930031776428 0 l 0.0112302875519 0 l 0.0131314373016 0 l 0.014971075058 0 l 0.0167151069641 0 l 0.0183303451538 0 l 0.0197867202759 0 l 0.0210597610474 0 l 0.0221322822571 0 l 0.0229952049255 0 l 0.0236479568482 0 l 0.0240972709656 0 l 0.0243561935425 0 l 0.0244421386719 0 l 0.0243751525879 0 l 0.0241764450073 0 l 0.0238669586182 0 l 0.0234665298462 0 l 0.0229933547974 0 l 0.0224634552002 0 l 0.0218908691406 0 l 0.0212874603272 0 l 0.0206631851196 0 l 0.0200262451172 0 l 0.019383354187 0 l 0.0187397766113 0 l 0.018099899292 0 l 0.0174669647217 0 l 0.0168434906006 0 l 0.0162316131592 0 l 0.0156324005127 0 l 0.0150471496582 0 l 0.0144763183594 0 l 0.0139205169678 0 l 0.0133796691894 0 l 0.0128540802002 0 l 0.0123434448242 0 l 0.0118475341797 0 l 0.0113663482666 0 l 0.0108992004395 0 l 0.0104459381103 0 l 0.00998542785645 0 l 0.00933837890625 0 l 0.0086334991455 0 l 0.00791206359864 0 l 0.00717010498047 0 l 0.00640274047851 0 l 0.00560478210449 0 l 0.00477012634278 0 l 0.00389221191406 0 l 0.00296325683594 0 l 0.00197517395019 0 l 0.00091903686524 0 l -0.00021408081055 0 l -0.00143287658691 0 l -0.00274444580079 0 l -0.00415267944336 0 l -0.00565589904785 0 l -0.00724327087402 0 l -0.00889205932617 0 l -0.0105648040771 0 l -0.0122087860107 0 l -0.0137604522705 0 l -0.0151544952393 0 l -0.0163356018066 0 l -0.0172690582275 0 l -0.017946395874 0 l -0.0183829498291 0 l -0.0186113739014 0 l -0.018671798706 0 l -0.0186050415039 0 l -0.0184476470947 0 l -0.018229598999 0 l -0.017974319458 0 l -0.0176993560791 0 l -0.0174169921875 0 l -0.0171360397339 0 l -0.0168621444702 0 l -0.0165135955811 0 l -0.0160948562622 0 l -0.0156935882568 0 l -0.0153102493286 0 l -0.0149446105957 0 l -0.0145963287353 0 l -0.0142646408081 0 l -0.0139489364624 0 l -0.0136483383179 0 l -0.0133620071411 0 l -0.0130891799927 0 l -0.0128289794922 0 l -0.0125807571411 0 l -0.0123436355591 0 l -0.0121170043945 0 l -0.0119002914429 0 l -0.0116927337646 0 l -0.0114939498901 0 l -0.0113032531738 0 l -0.0111202430725 0 l -0.010944442749 0 l -0.0107754516601 0 l -0.0106128692627 0 l -0.0104563140869 0 l -0.0103054428101 0 l -0.0101600074768 0 l -0.0100196266174 0 l -0.0098840713501 0 l -0.00975311279297 0 l -0.00962644577026 0 l -0.00950393676758 0 l -0.00938529968262 0 l -0.00927038192749 0 l -0.00915899276733 0 l -0.00905097961426 0 l -0.00894614219665 0 l -0.0088443851471 0 l -0.00874552726745 0 l -0.00864946365357 0 l -0.00855606079101 0 l -0.00846519470215 0 l -0.00837676048279 0 "
+        android:interpolator="@interpolator/progress_indeterminate_horizontal_rect2_grp_scale" />
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/progress_indeterminate_horizontal_rect2_scale.xml b/core/res/res/anim/progress_indeterminate_horizontal_rect2_scale.xml
deleted file mode 100644
index ef1677d..0000000
--- a/core/res/res/anim/progress_indeterminate_horizontal_rect2_scale.xml
+++ /dev/null
@@ -1,56 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- 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.
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
-
-    <objectAnimator
-        android:duration="2016"
-        android:pathData="M 0.1 1 l 0.00930031776428 0 l 0.01123028755188 0
-l 0.01313143730164 0 l 0.01497107505798 0 l 0.01671510696411 0 l 0.01833034515381 0
-l 0.01978672027588 0 l 0.02105976104736 0 l 0.02213228225708 0 l 0.02299520492554 0
-l 0.02364795684814 0 l 0.02409727096558 0 l 0.02435619354248 0 l 0.02444213867188 0
-l 0.02437515258789 0 l 0.02417644500732 0 l 0.02386695861816 0 l 0.02346652984619 0
-l 0.02299335479736 0 l 0.0224634552002 0 l 0.02189086914062 0 l 0.02128746032715 0
-l 0.02066318511963 0 l 0.02002624511719 0 l 0.01938335418701 0 l 0.01873977661133 0
-l 0.01809989929199 0 l 0.01746696472168 0 l 0.01684349060059 0 l 0.01623161315918 0
-l 0.0156324005127 0 l 0.0150471496582 0 l 0.01447631835938 0 l 0.01392051696777 0
-l 0.01337966918945 0 l 0.0128540802002 0 l 0.01234344482422 0 l 0.01184753417969 0
-l 0.0113663482666 0 l 0.01089920043945 0 l 0.01044593811035 0 l 0.00998542785645 0
-l 0.00933837890625 0 l 0.00863349914551 0 l 0.00791206359863 0 l 0.00717010498047 0
-l 0.00640274047852 0 l 0.00560478210449 0 l 0.00477012634277 0 l 0.00389221191406 0
-l 0.00296325683594 0 l 0.0019751739502 0 l 0.00091903686523 0 l -0.00021408081055 0
-l -0.00143287658691 0 l -0.00274444580078 0 l -0.00415267944336 0 l -0.00565589904785 0
-l -0.00724327087402 0 l -0.00889205932617 0 l -0.01056480407715 0 l -0.01220878601074 0
-l -0.01376045227051 0 l -0.01515449523926 0 l -0.01633560180664 0 l -0.01726905822754 0
-l -0.01794639587402 0 l -0.0183829498291 0 l -0.01861137390137 0 l -0.01867179870605 0
-l -0.01860504150391 0 l -0.01844764709473 0 l -0.01822959899902 0 l -0.01797431945801 0
-l -0.0176993560791 0 l -0.0174169921875 0 l -0.01713603973389 0 l -0.01686214447021 0
-l -0.01651359558105 0 l -0.01609485626221 0 l -0.01569358825684 0 l -0.01531024932861 0
-l -0.0149446105957 0 l -0.01459632873535 0 l -0.01426464080811 0 l -0.0139489364624 0
-l -0.01364833831787 0 l -0.01336200714111 0 l -0.01308917999268 0 l -0.01282897949219 0
-l -0.01258075714111 0 l -0.01234363555908 0 l -0.01211700439453 0 l -0.01190029144287 0
-l -0.01169273376465 0 l -0.01149394989014 0 l -0.01130325317383 0 l -0.01112024307251 0
-l -0.01094444274902 0 l -0.01077545166016 0 l -0.0106128692627 0 l -0.01045631408691 0
-l -0.01030544281006 0 l -0.01016000747681 0 l -0.01001962661743 0 l -0.0098840713501 0
-l -0.00975311279297 0 l -0.00962644577026 0 l -0.00950393676758 0 l -0.00938529968262 0
-l -0.00927038192749 0 l -0.00915899276733 0 l -0.00905097961426 0 l -0.00894614219666 0
-l -0.00884438514709 0 l -0.00874552726746 0 l -0.00864946365356 0 l -0.00855606079102 0
-l -0.00846519470215 0 l -0.00837676048279 0 "
-        android:propertyXName="scaleX"
-        android:repeatCount="infinite" />
-
-</set>
\ No newline at end of file
diff --git a/core/res/res/anim/progress_indeterminate_horizontal_rect2_translate.xml b/core/res/res/anim/progress_indeterminate_horizontal_rect2_translate.xml
deleted file mode 100644
index f4cf83d..0000000
--- a/core/res/res/anim/progress_indeterminate_horizontal_rect2_translate.xml
+++ /dev/null
@@ -1,56 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- 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.
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
-
-    <objectAnimator
-        android:duration="2016"
-        android:pathData="M -197.600006103516 0 l 1.42625427246094 0
-l 1.80754089355469 0 l 2.18778991699219 0 l 2.56109619140625 0 l 2.91810607910156 0
-l 3.25482177734375 0 l 3.57159423828125 0 l 3.862548828125 0 l 4.12493896484375 0
-l 4.35758972167969 0 l 4.56034851074219 0 l 4.73426818847656 0 l 4.88090515136719 0
-l 5.00271606445312 0 l 5.10273742675781 0 l 5.18400573730469 0 l 5.24911499023438 0
-l 5.30097961425781 0 l 5.34226226806641 0 l 5.37535095214844 0 l 5.40180206298828 0
-l 5.42322540283203 0 l 5.44123077392578 0 l 5.45704650878906 0 l 5.47099304199219 0
-l 5.48395538330078 0 l 5.4967041015625 0 l 5.50949859619141 0 l 5.52214813232422 0
-l 5.53528594970703 0 l 5.54912567138672 0 l 5.56306457519531 0 l 5.57742691040039 0
-l 5.59244155883789 0 l 5.60744094848633 0 l 5.62243270874023 0 l 5.6376781463623 0
-l 5.65262794494629 0 l 5.66689777374268 0 l 5.68069934844971 0 l 5.69401162862778 0
-l 5.70898681879044 0 l 5.75169992446899 0 l 5.80327129364014 0 l 5.85710144042969 0
-l 5.91399765014648 0 l 5.97450065612793 0 l 6.03849411010742 0 l 6.10729217529297 0
-l 6.18125534057617 0 l 6.26116561889648 0 l 6.34840393066406 0 l 6.44406127929688 0
-l 6.54866790771484 0 l 6.66371917724609 0 l 6.79020690917969 0 l 6.92859649658203 0
-l 7.07807159423828 0 l 7.23712158203125 0 l 7.40253448486328 0 l 7.56884765625 0
-l 7.72840881347656 0 l 7.87199401855469 0 l 7.98992919921875 0 l 8.07417297363281 0
-l 8.12013244628906 0 l 8.12655639648438 0 l 8.09510803222656 0 l 8.03091430664062 0
-l 7.93995666503906 0 l 7.827880859375 0 l 7.69976806640625 0 l 7.56065368652344 0
-l 7.41322326660156 0 l 7.26063537597656 0 l 7.10470581054688 0 l 6.94624328613281 0
-l 6.78694152832031 0 l 6.6390380859375 0 l 6.50302124023438 0 l 6.36688232421875 0
-l 6.23043823242188 0 l 6.09356689453125 0 l 5.95706176757812 0 l 5.82064819335938 0
-l 5.6839599609375 0 l 5.5477294921875 0 l 5.41143798828125 0 l 5.27532958984375 0
-l 5.13922119140625 0 l 5.00347900390625 0 l 4.8680419921875 0 l 4.73251342773438 0
-l 4.59732055664062 0 l 4.46258544921875 0 l 4.328125 0 l 4.1937255859375 0
-l 4.0599365234375 0 l 3.92672729492188 0 l 3.79376220703125 0 l 3.66119384765625 0
-l 3.52935791015625 0 l 3.398193359375 0 l 3.26748657226562 0 l 3.13726806640625 0
-l 3.00796508789062 0 l 2.87939453125 0 l 2.7515869140625 0 l 2.62445068359375 0
-l 2.49810791015625 0 l 2.3726806640625 0 l 2.2481689453125 0 l 2.12457275390625 0
-l 2.00173950195312 0 l 1.87997436523438 0 l 1.7618408203125 0 l 1.64154052734375 0
-l 1.51962280273438 0 l 1.40017700195312 0 l 1.28421020507812 0 "
-        android:propertyXName="translateX"
-        android:repeatCount="infinite" />
-
-</set>
\ No newline at end of file
diff --git a/core/res/res/drawable/progress_indeterminate_horizontal_material.xml b/core/res/res/drawable/progress_indeterminate_horizontal_material.xml
index 4fc68ce..e92f090 100644
--- a/core/res/res/drawable/progress_indeterminate_horizontal_material.xml
+++ b/core/res/res/drawable/progress_indeterminate_horizontal_material.xml
@@ -1,4 +1,3 @@
-<?xml version="1.0" encoding="utf-8"?>
 <!--
  Copyright (C) 2014 The Android Open Source Project
 
@@ -14,20 +13,13 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
+
 <animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
     android:drawable="@drawable/vector_drawable_progress_indeterminate_horizontal" >
-
     <target
-        android:name="path1"
-        android:animation="@anim/progress_indeterminate_horizontal_rect1_translate" />
+        android:name="rect2_grp"
+        android:animation="@anim/progress_indeterminate_horizontal_rect2" />
     <target
-        android:name="path1"
-        android:animation="@anim/progress_indeterminate_horizontal_rect1_scale" />
-
-    <target
-        android:name="path2"
-        android:animation="@anim/progress_indeterminate_horizontal_rect2_translate" />
-    <target
-        android:name="path2"
-        android:animation="@anim/progress_indeterminate_horizontal_rect2_scale" />
-</animated-vector>
+        android:name="rect1_grp"
+        android:animation="@anim/progress_indeterminate_horizontal_rect1" />
+</animated-vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/vector_drawable_progress_indeterminate_horizontal.xml b/core/res/res/drawable/vector_drawable_progress_indeterminate_horizontal.xml
index 0cc7202..7ceb772 100644
--- a/core/res/res/drawable/vector_drawable_progress_indeterminate_horizontal.xml
+++ b/core/res/res/drawable/vector_drawable_progress_indeterminate_horizontal.xml
@@ -13,48 +13,47 @@
      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.
--->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:height="4dp"
-    android:viewportHeight="4"
+    android:height="10dp"
+    android:viewportHeight="10"
     android:viewportWidth="360"
     android:width="360dp" >
 
     <group
-        android:name="linear_indeterminate"
-        android:translateX="180.0"
-        android:translateY="0.0" >
-        <group
-            android:name="path1"
-            android:scaleX="0.1"
-            android:translateX="-522.59" >
-            <path
-                android:name="rect1"
-                android:fillColor="?attr/colorControlActivated"
-                android:pathData="m 0 1.6 l 288 0 l 0 0.8 l -288 0 z" />
-        </group>
-        <group
-            android:name="path2"
-            android:scaleX="0.1"
-            android:translateX="-197.6" >
-            <path
-                android:name="rect2"
-                android:fillColor="?attr/colorControlActivated"
-                android:pathData="m 0 1.6 l 288 0 l 0 0.8 l -288 0 z" />
+        android:name="v21"
+        android:translateX="180"
+        android:translateY="5" >
+        <group android:name="v21_pivot" >
+            <group
+                android:name="rectangle_path_1_position"
+                android:alpha="0.1" >
+                <path
+                    android:name="rectangle_path_1"
+                    android:fillColor="?attr/colorControlActivated"
+                    android:pathData="M -180.0 -1.0 l 360 0 l 0 2 l -360 0 Z" />
+            </group>
+            <group
+                android:name="rect2_grp"
+                android:scaleX="0.1"
+                android:scaleY="1"
+                android:translateX="-197.60001" >
+                <path
+                    android:name="rect2"
+                    android:fillColor="?attr/colorControlActivated"
+                    android:pathData="M -144.0 -1.0 l 288 0 l 0 2 l -288 0 Z" />
+            </group>
+            <group
+                android:name="rect1_grp"
+                android:scaleX="0.1"
+                android:scaleY="1"
+                android:translateX="-522.59998" >
+                <path
+                    android:name="rect1"
+                    android:fillColor="?attr/colorControlActivated"
+                    android:pathData="M -144.0 -1.0 l 288 0 l 0 2 l -288 0 Z" />
+            </group>
         </group>
     </group>
+
 </vector>
\ No newline at end of file
diff --git a/core/res/res/interpolator/progress_indeterminate_horizontal_rect1_grp_position.xml b/core/res/res/interpolator/progress_indeterminate_horizontal_rect1_grp_position.xml
new file mode 100644
index 0000000..1e5490b
--- /dev/null
+++ b/core/res/res/interpolator/progress_indeterminate_horizontal_rect1_grp_position.xml
@@ -0,0 +1,17 @@
+<!--
+ 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.
+-->
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:pathData="M 0.0 0.0 L 0.00833333333334 0.0 L 0.0166666666667 0.0 L 0.025 0.0 L 0.0333333333333 0.0 L 0.0416666666667 0.0 L 0.05 0.0 L 0.0583333333333 0.0 L 0.0666666666667 0.0 L 0.075 0.0 L 0.0833333333333 0.0 L 0.0916666666667 0.0 L 0.1 0.0 L 0.108333333333 0.0 L 0.116666666667 0.0 L 0.125 0.0 L 0.133333333333 0.0 L 0.141666666667 0.0 L 0.15 0.0 L 0.158333333333 0.0 L 0.166666666667 0.0 L 0.175 0.0 L 0.183333333333 0.0 L 0.191666666667 0.0 L 0.2 0.0 L 0.208333333333 0.000179174746319 L 0.216666666667 0.000647632243805 L 0.225 0.0014154251096 L 0.233333333333 0.00248283027531 L 0.241666666667 0.00384069515149 L 0.25 0.00549731383962 L 0.258333333333 0.00745454178143 L 0.266666666667 0.00971365286228 L 0.275 0.012276004047 L 0.283333333333 0.0151429661471 L 0.291666666667 0.0183149545599 L 0.3 0.0217925231486 L 0.308333333333 0.0255762534696 L 0.316666666667 0.0296659101311 L 0.325 0.0340608700368 L 0.333333333333 0.0387607177895 L 0.341666666667 0.0437642487367 L 0.35 0.049069759749 L 0.358333333333 0.0546755338504 L 0.366666666667 0.0605792586621 L 0.375 0.0685372344023 L 0.383333333333 0.0769873314454 L 0.391666666667 0.0859319590963 L 0.4 0.0953722943142 L 0.408333333333 0.105309361746 L 0.416666666667 0.1157409044 L 0.425 0.126663931413 L 0.433333333333 0.13807316724 L 0.441666666667 0.149959722376 L 0.45 0.162313045726 L 0.458333333333 0.175118515302 L 0.466666666667 0.188357742846 L 0.475 0.20200918308 L 0.483333333333 0.216046541347 L 0.491666666667 0.230440850602 L 0.5 0.245158048258 L 0.508333333333 0.260161814735 L 0.516666666667 0.275413711928 L 0.525 0.290871992396 L 0.533333333333 0.306495421026 L 0.541666666667 0.322240921106 L 0.55 0.338067714457 L 0.558333333333 0.353935424452 L 0.566666666667 0.369805128355 L 0.575 0.385641337381 L 0.583333333333 0.401410902817 L 0.591666666667 0.417082932942 L 0.6 0.4326293192 L 0.608333333333 0.448024722349 L 0.616666666667 0.463246794008 L 0.625 0.478275138165 L 0.633333333333 0.493090341915 L 0.641666666667 0.507676232452 L 0.65 0.522041325423 L 0.658333333333 0.536401295159 L 0.666666666667 0.550844593615 L 0.675 0.565421677727 L 0.683333333333 0.580198055666 L 0.691666666667 0.595258150031 L 0.7 0.610706294803 L 0.708333333333 0.626667358442 L 0.716666666667 0.643276054324 L 0.725 0.66065384465 L 0.733333333333 0.678855644958 L 0.741666666667 0.697798860396 L 0.75 0.71721499193 L 0.758333333333 0.736690248362 L 0.766666666667 0.755795814951 L 0.775 0.774204843176 L 0.783333333333 0.791732522732 L 0.791666666667 0.808305909835 L 0.8 0.823921113596 L 0.808333333333 0.838609094968 L 0.816666666667 0.852414869183 L 0.825 0.865385279222 L 0.833333333333 0.877566835746 L 0.841666666667 0.889001244655 L 0.85 0.899725351699 L 0.858333333333 0.909772887147 L 0.866666666667 0.919172721118 L 0.875 0.927950539019 L 0.883333333333 0.936129852342 L 0.891666666667 0.943730807861 L 0.9 0.950771821528 L 0.908333333333 0.95726991079 L 0.916666666667 0.963271447844 L 0.925 0.968820243268 L 0.933333333333 0.973927263555 L 0.941666666667 0.978603045951 L 0.95 0.982857352297 L 0.958333333333 0.986698947476 L 0.966666666667 0.990136402522 L 0.975 0.993184062492 L 0.983333333333 0.995839116531 L 0.991666666667 0.998106161702 L 1.0 1.0 " />
diff --git a/core/res/res/interpolator/progress_indeterminate_horizontal_rect1_grp_scale.xml b/core/res/res/interpolator/progress_indeterminate_horizontal_rect1_grp_scale.xml
new file mode 100644
index 0000000..dc0e485
--- /dev/null
+++ b/core/res/res/interpolator/progress_indeterminate_horizontal_rect1_grp_scale.xml
@@ -0,0 +1,17 @@
+<!--
+ 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.
+-->
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:pathData="M 0.0 0.0 L 0.366666666667 0.0 L 0.375 0.00607022199531 L 0.383333333333 0.0128313190317 L 0.391666666667 0.0203053863048 L 0.4 0.0285126230744 L 0.408333333333 0.0374704929422 L 0.416666666667 0.0471928710088 L 0.425 0.0576890073411 L 0.433333333333 0.0689623329923 L 0.441666666667 0.0810093447457 L 0.45 0.0938182996083 L 0.458333333333 0.107368099555 L 0.466666666667 0.121627281237 L 0.475 0.136553254984 L 0.483333333333 0.152092042392 L 0.491666666667 0.168178420646 L 0.5 0.184736670404 L 0.508333333333 0.201682058428 L 0.516666666667 0.218922661357 L 0.525 0.236361911116 L 0.533333333333 0.253901271529 L 0.541666666667 0.271443072385 L 0.55 0.288893107328 L 0.558333333333 0.306163048058 L 0.566666666667 0.323172254984 L 0.575 0.339849141772 L 0.583333333333 0.356131857619 L 0.591666666667 0.371968628391 L 0.6 0.387317389244 L 0.608333333333 0.402145469729 L 0.616666666667 0.416428517896 L 0.625 0.430149949228 L 0.633333333333 0.443299687056 L 0.641666666667 0.455873322838 L 0.65 0.467790610602 L 0.658333333333 0.478261214125 L 0.666666666667 0.486946515351 L 0.675 0.493642461739 L 0.683333333333 0.498097136568 L 0.691666666667 0.5 L 0.7 0.501026508147 L 0.708333333333 0.505424974058 L 0.716666666667 0.513673357225 L 0.725 0.526197764281 L 0.733333333333 0.543195110129 L 0.741666666667 0.564385504796 L 0.75 0.588845516061 L 0.758333333333 0.615150659843 L 0.766666666667 0.641819770802 L 0.775 0.667727568443 L 0.783333333333 0.692232321167 L 0.791666666667 0.715080485292 L 0.8 0.736255108924 L 0.808333333333 0.755857404851 L 0.816666666667 0.774035479111 L 0.825 0.790946989585 L 0.833333333333 0.806741984167 L 0.841666666667 0.821556051785 L 0.85 0.835508642948 L 0.858333333333 0.848703647061 L 0.866666666667 0.861230901301 L 0.875 0.873167948783 L 0.883333333333 0.884581809849 L 0.891666666667 0.895530464709 L 0.9 0.906064231101 L 0.908333333333 0.916226932039 L 0.916666666667 0.925949460993 L 0.925 0.935162278452 L 0.933333333333 0.943901111981 L 0.941666666667 0.952197936634 L 0.95 0.960081506342 L 0.958333333333 0.967577760656 L 0.966666666667 0.974710159318 L 0.975 0.981500003726 L 0.983333333333 0.987966699339 L 0.991666666667 0.994127959051 L 1.0 1.0 " />
diff --git a/core/res/res/interpolator/progress_indeterminate_horizontal_rect2_grp_position.xml b/core/res/res/interpolator/progress_indeterminate_horizontal_rect2_grp_position.xml
new file mode 100644
index 0000000..e2c463d
--- /dev/null
+++ b/core/res/res/interpolator/progress_indeterminate_horizontal_rect2_grp_position.xml
@@ -0,0 +1,17 @@
+<!--
+ 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.
+-->
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:pathData="M 0.0 0.0 L 0.00833333333334 0.00229967744922 L 0.0166666666667 0.00521412430783 L 0.025 0.00874167982129 L 0.0333333333333 0.0128711540512 L 0.0416666666667 0.0175762490301 L 0.05 0.0228242656297 L 0.0583333333333 0.0285830529319 L 0.0666666666667 0.0348109630825 L 0.075 0.0414619464217 L 0.0833333333333 0.0484880345538 L 0.0916666666667 0.0558410655969 L 0.1 0.0634745223001 L 0.108333333333 0.0713444188538 L 0.116666666667 0.079410703663 L 0.125 0.0876382751487 L 0.133333333333 0.0959968850049 L 0.141666666667 0.104460460998 L 0.15 0.113007671299 L 0.158333333333 0.121621440773 L 0.166666666667 0.130288564002 L 0.175 0.138998350887 L 0.183333333333 0.147742658893 L 0.191666666667 0.156516005917 L 0.2 0.165314860841 L 0.208333333333 0.174136192385 L 0.216666666667 0.182978436537 L 0.225 0.191841222449 L 0.233333333333 0.200724646865 L 0.241666666667 0.209628467926 L 0.25 0.218553459576 L 0.258333333333 0.227500782731 L 0.266666666667 0.236470566383 L 0.275 0.245463519979 L 0.283333333333 0.254480675444 L 0.291666666667 0.263522016655 L 0.3 0.272587543612 L 0.308333333333 0.281677627163 L 0.316666666667 0.290791831964 L 0.325 0.299929045471 L 0.333333333333 0.309088509865 L 0.341666666667 0.318269435077 L 0.35 0.327474513787 L 0.358333333333 0.336748457377 L 0.366666666667 0.346105551561 L 0.375 0.355549440324 L 0.383333333333 0.365085073683 L 0.391666666667 0.374718256217 L 0.4 0.384454615142 L 0.408333333333 0.394301906021 L 0.416666666667 0.404268464874 L 0.425 0.414363869256 L 0.433333333333 0.424599921812 L 0.441666666667 0.434990214931 L 0.45 0.445549179441 L 0.458333333333 0.45629364862 L 0.466666666667 0.467242068132 L 0.475 0.478413609209 L 0.483333333333 0.489826169306 L 0.491666666667 0.501495178926 L 0.5 0.513430908951 L 0.508333333333 0.525634794401 L 0.516666666667 0.53809595169 L 0.525 0.550788614937 L 0.533333333333 0.563671442642 L 0.541666666667 0.576690097495 L 0.55 0.589782857472 L 0.558333333333 0.602885985073 L 0.566666666667 0.615938403227 L 0.575 0.628887306389 L 0.583333333333 0.641689563312 L 0.591666666667 0.654311104343 L 0.6 0.666726082982 L 0.608333333333 0.678916746891 L 0.616666666667 0.69086971329 L 0.625 0.702576630036 L 0.633333333333 0.714032144017 L 0.641666666667 0.725232143656 L 0.65 0.736175290675 L 0.658333333333 0.746879966241 L 0.666666666667 0.757365325464 L 0.675 0.767631174859 L 0.683333333333 0.77767703071 L 0.691666666667 0.787502199694 L 0.7 0.797107262267 L 0.708333333333 0.806492379668 L 0.716666666667 0.81565710043 L 0.725 0.82460216625 L 0.733333333333 0.833327480383 L 0.741666666667 0.841833333059 L 0.75 0.850119724279 L 0.758333333333 0.858187250623 L 0.766666666667 0.866036395807 L 0.775 0.873667014716 L 0.783333333333 0.88107965556 L 0.791666666667 0.888275060036 L 0.8 0.895253647364 L 0.808333333333 0.902015546533 L 0.816666666667 0.90856170885 L 0.825 0.914893101745 L 0.833333333333 0.921010096065 L 0.841666666667 0.926913352889 L 0.85 0.932604033131 L 0.858333333333 0.938083217089 L 0.866666666667 0.943351662581 L 0.875 0.94841012743 L 0.883333333333 0.953260127273 L 0.891666666667 0.957902806904 L 0.9 0.962339423981 L 0.908333333333 0.966571042677 L 0.916666666667 0.970598952899 L 0.925 0.974424621915 L 0.933333333333 0.978049533117 L 0.941666666667 0.981475153774 L 0.95 0.984702725421 L 0.958333333333 0.987733957184 L 0.966666666667 0.990574734261 L 0.975 0.993221525533 L 0.983333333333 0.995671735064 L 0.991666666667 0.997929361563 L 1.0 1.0 " />
diff --git a/core/res/res/interpolator/progress_indeterminate_horizontal_rect2_grp_scale.xml b/core/res/res/interpolator/progress_indeterminate_horizontal_rect2_grp_scale.xml
new file mode 100644
index 0000000..e19e3bd
--- /dev/null
+++ b/core/res/res/interpolator/progress_indeterminate_horizontal_rect2_grp_scale.xml
@@ -0,0 +1,17 @@
+<!--
+ 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.
+-->
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:pathData="M 0.0 0.0 L 0.00833333333334 0.00574128947512 L 0.0166666666667 0.0126739914922 L 0.025 0.0207803148119 L 0.0333333333333 0.0300222867359 L 0.0416666666667 0.0403408876828 L 0.05 0.0516566104757 L 0.0583333333333 0.0638713854701 L 0.0666666666667 0.0768720363634 L 0.075 0.0905347780463 L 0.0833333333333 0.104730220757 L 0.0916666666667 0.1193286215 L 0.1 0.134204393671 L 0.108333333333 0.149240004408 L 0.116666666667 0.164328670953 L 0.125 0.179375985524 L 0.133333333333 0.194300633561 L 0.141666666667 0.209034228885 L 0.15 0.223520630773 L 0.158333333333 0.237714931359 L 0.166666666667 0.25158211334 L 0.175 0.265095825429 L 0.183333333333 0.278237040065 L 0.191666666667 0.290992875969 L 0.2 0.303355514884 L 0.208333333333 0.315321283173 L 0.216666666667 0.326889756956 L 0.225 0.33806322048 L 0.233333333333 0.348845959658 L 0.241666666667 0.35924381463 L 0.25 0.369263944281 L 0.258333333333 0.378914166866 L 0.266666666667 0.388203101304 L 0.275 0.397139649102 L 0.283333333333 0.40573308855 L 0.291666666667 0.413992650841 L 0.3 0.421927755558 L 0.308333333333 0.429547633895 L 0.316666666667 0.436861375749 L 0.325 0.44387807102 L 0.333333333333 0.450606385724 L 0.341666666667 0.457054891684 L 0.35 0.463219114597 L 0.358333333333 0.468983900047 L 0.366666666667 0.474313547811 L 0.375 0.47919783764 L 0.383333333333 0.483624100194 L 0.391666666667 0.487576651865 L 0.4 0.491036606388 L 0.408333333333 0.493981309661 L 0.416666666667 0.496384057166 L 0.425 0.498213340392 L 0.433333333333 0.499432658452 L 0.441666666667 0.5 L 0.45 0.500132156764 L 0.458333333333 0.501016702806 L 0.466666666667 0.502710909197 L 0.475 0.505274449001 L 0.483333333333 0.50876595913 L 0.491666666667 0.51323738859 L 0.5 0.518726651206 L 0.508333333333 0.525248535726 L 0.516666666667 0.532785286233 L 0.525 0.541279914244 L 0.533333333333 0.550635115456 L 0.541666666667 0.560719439572 L 0.55 0.571380006744 L 0.558333333333 0.582458709253 L 0.566666666667 0.593806906062 L 0.575 0.605296114045 L 0.583333333333 0.61682262358 L 0.591666666667 0.628307922435 L 0.6 0.639696058281 L 0.608333333333 0.65094958827 L 0.616666666667 0.662045528618 L 0.625 0.67297172806 L 0.633333333333 0.6837236181 L 0.641666666667 0.694302070048 L 0.65 0.704711440462 L 0.658333333333 0.714905644026 L 0.666666666667 0.724841350655 L 0.675 0.734529345772 L 0.683333333333 0.743980697388 L 0.691666666667 0.753206332221 L 0.7 0.762216965048 L 0.708333333333 0.771022839665 L 0.716666666667 0.779633823089 L 0.725 0.788059240706 L 0.733333333333 0.796307899828 L 0.741666666667 0.804388136787 L 0.75 0.812307746289 L 0.758333333333 0.820074122707 L 0.766666666667 0.827694118788 L 0.775 0.835174210498 L 0.783333333333 0.842520520564 L 0.791666666667 0.849738700738 L 0.8 0.856834167281 L 0.808333333333 0.863811912571 L 0.816666666667 0.870676681725 L 0.825 0.877432925497 L 0.833333333333 0.884084847374 L 0.841666666667 0.890636403584 L 0.85 0.897091314861 L 0.858333333333 0.90345309 L 0.866666666667 0.909725084729 L 0.875 0.915910419285 L 0.883333333333 0.92201207261 L 0.891666666667 0.928032882355 L 0.9 0.933975497778 L 0.908333333333 0.939842485715 L 0.916666666667 0.945636236386 L 0.925 0.951359045815 L 0.933333333333 0.95701309228 L 0.941666666667 0.962600459864 L 0.95 0.968123109018 L 0.958333333333 0.973582941322 L 0.966666666667 0.978981746494 L 0.975 0.984321249498 L 0.983333333333 0.989603092873 L 0.991666666667 0.994828842625 L 1.0 1.0 " />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index a59a489..cf9a415 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1371,6 +1371,13 @@
          may have a specific value set in an overlay config.xml file. -->
     <integer name="config_mobile_mtu">1500</integer>
 
+    <!-- Configure mobile tcp buffer sizes in the form:
+         rat-name:rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max
+         If no value is found for the rat-name in use, the system default will be applied.
+    -->
+    <string-array name="config_mobile_tcp_buffers">
+    </string-array>
+
     <!-- Whether WiFi display is supported by this device.
          There are many prerequisites for this feature to work correctly.
          Here are a few of them:
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 0b3a132..304c81b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -321,6 +321,7 @@
   <java-symbol type="integer" name="config_multiuserMaximumUsers" />
   <java-symbol type="integer" name="config_safe_media_volume_index" />
   <java-symbol type="integer" name="config_mobile_mtu" />
+  <java-symbol type="array"   name="config_mobile_tcp_buffers" />
   <java-symbol type="integer" name="config_volte_replacement_rat"/>
   <java-symbol type="integer" name="config_valid_wappush_index" />
   <java-symbol type="integer" name="config_overrideHasPermanentMenuKey" />
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java
index 93b16e7..1222c8b 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java
@@ -66,7 +66,6 @@
  *      networkprefixlength.
  */
 public class AccessPointParserHelper {
-    private static final String TAG = "AccessPointParserHelper";
     static final int NONE = 0;
     static final int WEP = 1;
     static final int PSK = 2;
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerStressTestRunner.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerStressTestRunner.java
index 3ec9031..fbaf0f3 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerStressTestRunner.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerStressTestRunner.java
@@ -35,14 +35,14 @@
  */
 
 public class ConnectivityManagerStressTestRunner extends InstrumentationTestRunner {
-    public int mSoftapIterations = 100;
-    public int mScanIterations = 100;
-    public int mReconnectIterations = 100;
+    private int mSoftApIterations = 100;
+    private int mScanIterations = 100;
+    private int mReconnectIterations = 100;
     // sleep time before restart wifi, default is set to 2 minutes
-    public int mSleepTime = 2 * 60 * 1000;
-    public String mReconnectSsid = "securenetdhcp";
-    public String mReconnectPassword = "androidwifi";
-    public boolean mWifiOnlyFlag = false;
+    private long mSleepTime = 2 * 60 * 1000;
+    private String mReconnectSsid = null;
+    private String mReconnectPassword = null;
+    private boolean mWifiOnlyFlag = false;
 
     @Override
     public TestSuite getAllTests() {
@@ -60,15 +60,15 @@
     @Override
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
-        String valueStr = (String) icicle.get("softap_iterations");
+        String valueStr = icicle.getString("softap_iterations");
         if (valueStr != null) {
             int iteration = Integer.parseInt(valueStr);
             if (iteration > 0) {
-                mSoftapIterations = iteration;
+                mSoftApIterations = iteration;
             }
         }
 
-        String scanIterationStr = (String) icicle.get("scan_iterations");
+        String scanIterationStr = icicle.getString("scan_iterations");
         if (scanIterationStr != null) {
             int scanIteration = Integer.parseInt(scanIterationStr);
             if (scanIteration > 0) {
@@ -76,17 +76,17 @@
             }
         }
 
-        String ssidStr= (String) icicle.get("reconnect_ssid");
+        String ssidStr= icicle.getString("reconnect_ssid");
         if (ssidStr != null) {
             mReconnectSsid = ssidStr;
         }
 
-        String passwordStr = (String) icicle.get("reconnect_password");
+        String passwordStr = icicle.getString("reconnect_password");
         if (passwordStr != null) {
             mReconnectPassword = passwordStr;
         }
 
-        String reconnectStr = (String) icicle.get("reconnect_iterations");
+        String reconnectStr = icicle.getString("reconnect_iterations");
         if (reconnectStr != null) {
             int iteration = Integer.parseInt(reconnectStr);
             if (iteration > 0) {
@@ -94,7 +94,7 @@
             }
         }
 
-        String sleepTimeStr = (String) icicle.get("sleep_time");
+        String sleepTimeStr = icicle.getString("sleep_time");
         if (sleepTimeStr != null) {
             int sleepTime = Integer.parseInt(sleepTimeStr);
             if (sleepTime > 0) {
@@ -102,9 +102,37 @@
             }
         }
 
-        String wifiOnlyFlag = (String) icicle.get("wifi-only");
+        String wifiOnlyFlag = icicle.getString("wifi-only");
         if (wifiOnlyFlag != null) {
             mWifiOnlyFlag = true;
         }
     }
+
+    public int getSoftApInterations() {
+        return mSoftApIterations;
+    }
+
+    public int getScanIterations() {
+        return mScanIterations;
+    }
+
+    public int getReconnectIterations() {
+        return mReconnectIterations;
+    }
+
+    public boolean isWifiOnly() {
+        return mWifiOnlyFlag;
+    }
+
+    public long getSleepTime() {
+        return mSleepTime;
+    }
+
+    public String getReconnectSsid() {
+        return mReconnectSsid;
+    }
+
+    public String getReconnectPassword() {
+        return mReconnectPassword;
+    }
 }
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java
index 30eda75..0f9d8e9 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java
@@ -17,31 +17,28 @@
 package com.android.connectivitymanagertest;
 
 import android.app.KeyguardManager;
-import android.content.Context;
 import android.content.BroadcastReceiver;
+import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.State;
+import android.net.wifi.ScanResult;
 import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiManager;
 import android.net.wifi.WifiConfiguration.KeyMgmt;
-import android.os.Handler;
-import android.os.Message;
+import android.net.wifi.WifiManager;
 import android.os.PowerManager;
 import android.os.SystemClock;
 import android.test.InstrumentationTestCase;
 import android.util.Log;
 import android.view.KeyEvent;
 
-import com.android.internal.util.AsyncChannel;
-
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.UnknownHostException;
-import java.util.ArrayList;
 import java.util.List;
+import java.util.regex.Pattern;
 
 
 /**
@@ -55,51 +52,39 @@
  */
 public class ConnectivityManagerTestBase extends InstrumentationTestCase {
 
-    public static final String LOG_TAG = "ConnectivityManagerTestBase";
-    public static final int WAIT_FOR_SCAN_RESULT = 10 * 1000; //10 seconds
-    public static final int WIFI_SCAN_TIMEOUT = 50 * 1000; // 50 seconds
-    public static final int SHORT_TIMEOUT = 5 * 1000; // 5 seconds
-    public static final long LONG_TIMEOUT = 50 * 1000;  // 50 seconds
-    public static final long WIFI_CONNECTION_TIMEOUT = 5 * 60 * 1000; // 5 minutes
-    // 2 minutes timer between wifi stop and start
-    public static final long  WIFI_STOP_START_INTERVAL = 2 * 60 * 1000; // 2 minutes
-    // Set ping test timer to be 3 minutes
-    public static final long PING_TIMER = 3 * 60 *1000; // 3 minutes
-    public static final int SUCCESS = 0;  // for Wifi tethering state change
-    public static final int FAILURE = 1;
-    public static final int INIT = -1;
+    private static final String LOG_TAG = "ConnectivityManagerTestBase";
     private static final String ACCESS_POINT_FILE = "accesspoints.xml";
-    public ConnectivityReceiver mConnectivityReceiver = null;
-    public WifiReceiver mWifiReceiver = null;
+    private static final String PING_IP_ADDR = "8.8.8.8";
+
+    protected static final int WAIT_FOR_SCAN_RESULT = 10 * 1000; //10 seconds
+    protected static final int WIFI_SCAN_TIMEOUT = 50 * 1000; // 50 seconds
+    protected static final int SHORT_TIMEOUT = 5 * 1000; // 5 seconds
+    protected static final long LONG_TIMEOUT = 50 * 1000;  // 50 seconds
+    protected static final long WIFI_CONNECTION_TIMEOUT = 5 * 60 * 1000; // 5 minutes
+    // 2 minutes timer between wifi stop and start
+    protected static final long  WIFI_STOP_START_INTERVAL = 2 * 60 * 1000; // 2 minutes
+    // Set ping test timer to be 3 minutes
+    protected static final long PING_TIMER = 3 * 60 *1000; // 3 minutes
+    protected static final int SUCCESS = 0;  // for Wifi tethering state change
+    protected static final int FAILURE = 1;
+    protected static final int INIT = -1;
+
+    private ConnectivityReceiver mConnectivityReceiver = null;
+    private WifiReceiver mWifiReceiver = null;
     private AccessPointParserHelper mParseHelper = null;
-    /*
-     * Track network connectivity information
-     */
-    public State mState;
-    public NetworkInfo mNetworkInfo;
-    public NetworkInfo mOtherNetworkInfo;
-    public boolean mIsFailOver;
-    public String mReason;
-    public boolean mScanResultIsAvailable = false;
-    public ConnectivityManager mCM;
-    public Object wifiObject = new Object();
-    public Object connectivityObject = new Object();
-    public int mWifiState;
-    public NetworkInfo mWifiNetworkInfo;
-    public String mBssid;
-    public String mPowerSsid = "opennet"; //Default power SSID
+
+    private long mLastConnectivityChangeTime = -1;
+    protected ConnectivityManager mCm;
     private Context mContext;
-    public boolean scanResultAvailable = false;
+    protected List<ScanResult> mLastScanResult;
+    protected Object mWifiScanResultLock = new Object();
 
     /* Control Wifi States */
     public WifiManager mWifiManager;
-    /* Verify connectivity state */
-    public static final int NUM_NETWORK_TYPES = ConnectivityManager.MAX_NETWORK_TYPE + 1;
-    NetworkState[] connectivityState = new NetworkState[NUM_NETWORK_TYPES];
 
-    // For wifi tethering tests
-    private String[] mWifiRegexs;
-    public int mWifiTetherResult = INIT;    // -1 is initialization state
+    protected long getLastConnectivityChangeTime() {
+        return mLastConnectivityChangeTime;
+    }
 
     /**
      * A wrapper of a broadcast receiver which provides network connectivity information
@@ -108,40 +93,12 @@
     private class ConnectivityReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
-            log("ConnectivityReceiver: onReceive() is called with " + intent);
+            mLastConnectivityChangeTime = SystemClock.uptimeMillis();
+            log("ConnectivityReceiver: " + intent);
             String action = intent.getAction();
             if (!action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
                 Log.v("ConnectivityReceiver", "onReceive() called with " + intent);
-                return;
             }
-
-            boolean noConnectivity =
-                intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
-
-            if (noConnectivity) {
-                mState = State.DISCONNECTED;
-            } else {
-                mState = State.CONNECTED;
-            }
-
-            mNetworkInfo = (NetworkInfo)
-                intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
-
-            mOtherNetworkInfo = (NetworkInfo)
-                intent.getParcelableExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO);
-
-            mReason = intent.getStringExtra(ConnectivityManager.EXTRA_REASON);
-            mIsFailOver = intent.getBooleanExtra(ConnectivityManager.EXTRA_IS_FAILOVER, false);
-
-            log("mNetworkInfo: " + mNetworkInfo.toString());
-            if (mOtherNetworkInfo != null) {
-                log("mOtherNetworkInfo: " + mOtherNetworkInfo.toString());
-            }
-            recordNetworkState(mNetworkInfo.getType(), mNetworkInfo.getState());
-            if (mOtherNetworkInfo != null) {
-                recordNetworkState(mOtherNetworkInfo.getType(), mOtherNetworkInfo.getState());
-            }
-            notifyNetworkConnectivityChange();
         }
     }
 
@@ -152,62 +109,21 @@
             Log.v("WifiReceiver", "onReceive() is calleld with " + intent);
             if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
                 log("scan results are available");
-                notifyScanResult();
-            } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
-                mWifiNetworkInfo =
-                    (NetworkInfo) intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
-                log("mWifiNetworkInfo: " + mWifiNetworkInfo.toString());
-                if (mWifiNetworkInfo.getState() == State.CONNECTED) {
-                    mBssid = intent.getStringExtra(WifiManager.EXTRA_BSSID);
+                synchronized (mWifiScanResultLock) {
+                    mLastScanResult = mWifiManager.getScanResults();
+                    mWifiScanResultLock.notifyAll();
                 }
-                notifyWifiState();
-            } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
-                mWifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
-                                                WifiManager.WIFI_STATE_UNKNOWN);
-                notifyWifiState();
-            } else if (action.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) {
-                notifyWifiAPState();
-            } else if (action.equals(ConnectivityManager.ACTION_TETHER_STATE_CHANGED)) {
-                ArrayList<String> available = intent.getStringArrayListExtra(
-                        ConnectivityManager.EXTRA_AVAILABLE_TETHER);
-                ArrayList<String> active = intent.getStringArrayListExtra(
-                        ConnectivityManager.EXTRA_ACTIVE_TETHER);
-                ArrayList<String> errored = intent.getStringArrayListExtra(
-                        ConnectivityManager.EXTRA_ERRORED_TETHER);
-                updateTetherState(available.toArray(), active.toArray(), errored.toArray());
-            }
-            else {
-                return;
-            }
-        }
-    }
-
-    private class WifiServiceHandler extends Handler {
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
-                    if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
-                        //AsyncChannel in msg.obj
-                    } else {
-                        log("Failed to establish AsyncChannel connection");
-                    }
-                    break;
-                default:
-                    //Ignore
-                    break;
             }
         }
     }
 
     @Override
-    public void setUp() throws Exception {
-        mState = State.UNKNOWN;
-        scanResultAvailable = false;
+    protected void setUp() throws Exception {
+        mLastScanResult = null;
         mContext = getInstrumentation().getContext();
 
         // Get an instance of ConnectivityManager
-        mCM = (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+        mCm = (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
         // Get an instance of WifiManager
         mWifiManager =(WifiManager)mContext.getSystemService(Context.WIFI_SERVICE);
 
@@ -217,8 +133,6 @@
             log("Disable soft ap");
         }
 
-        initializeNetworkStates();
-
         // register a connectivity receiver for CONNECTIVITY_ACTION;
         mConnectivityReceiver = new ConnectivityReceiver();
         mContext.registerReceiver(mConnectivityReceiver,
@@ -236,211 +150,73 @@
 
         log("Clear Wifi before we start the test.");
         removeConfiguredNetworksAndDisableWifi();
-        mWifiRegexs = mCM.getTetherableWifiRegexs();
      }
 
-    public List<WifiConfiguration> loadNetworkConfigurations() throws Exception {
+    protected List<WifiConfiguration> loadNetworkConfigurations() throws Exception {
         InputStream in = mContext.getAssets().open(ACCESS_POINT_FILE);
         mParseHelper = new AccessPointParserHelper(in);
         return mParseHelper.getNetworkConfigurations();
     }
 
-    // for each network type, initialize network states to UNKNOWN, and no verification flag is set
-    public void initializeNetworkStates() {
-        for (int networkType = NUM_NETWORK_TYPES - 1; networkType >=0; networkType--) {
-            connectivityState[networkType] =  new NetworkState();
-            log("Initialize network state for " + networkType + ": " +
-                    connectivityState[networkType].toString());
-        }
-    }
-
-    // deposit a network state
-    public void recordNetworkState(int networkType, State networkState) {
-        log("record network state for network " +  networkType +
-                ", state is " + networkState);
-        if (connectivityState == null) {
-             log("ConnectivityState is null");
-        }
-        if (connectivityState[networkType] == null) {
-             log("connectivityState[networkType] is null");
-        }
-        connectivityState[networkType].recordState(networkState);
-    }
-
-    // set the state transition criteria
-    public void setStateTransitionCriteria(int networkType, State initState,
-            int transitionDir, State targetState) {
-        connectivityState[networkType].setStateTransitionCriteria(
-                initState, transitionDir, targetState);
-    }
-
-    // Validate the states recorded
-    public boolean validateNetworkStates(int networkType) {
-        log("validate network state for " + networkType + ": ");
-        return connectivityState[networkType].validateStateTransition();
-    }
-
-    // return result from network state validation
-    public String getTransitionFailureReason(int networkType) {
-        log("get network state transition failure reason for " + networkType + ": " +
-                connectivityState[networkType].toString());
-        return connectivityState[networkType].getReason();
-    }
-
-    private void notifyNetworkConnectivityChange() {
-        synchronized(connectivityObject) {
-            log("notify network connectivity changed");
-            connectivityObject.notifyAll();
-        }
-    }
-    private void notifyScanResult() {
-        synchronized (this) {
-            log("notify that scan results are available");
-            scanResultAvailable = true;
-            this.notify();
-        }
-    }
-
-    private void notifyWifiState() {
-        synchronized (wifiObject) {
-            log("notify wifi state changed");
-            wifiObject.notify();
-        }
-    }
-
-    private void notifyWifiAPState() {
-        synchronized (this) {
-            log("notify wifi AP state changed");
-            this.notify();
-        }
-    }
-
-    // Update wifi tethering state
-    private void updateTetherState(Object[] available, Object[] tethered, Object[] errored) {
-        boolean wifiTethered = false;
-        boolean wifiErrored = false;
-
-        synchronized (this) {
-            for (Object obj: tethered) {
-                String str = (String)obj;
-                for (String tethRex: mWifiRegexs) {
-                    log("str: " + str +"tethRex: " + tethRex);
-                    if (str.matches(tethRex)) {
-                        wifiTethered = true;
-                    }
-                }
-            }
-
-            for (Object obj: errored) {
-                String str = (String)obj;
-                for (String tethRex: mWifiRegexs) {
-                    log("error: str: " + str +"tethRex: " + tethRex);
-                    if (str.matches(tethRex)) {
-                        wifiErrored = true;
-                    }
-                }
-            }
-
-            if (wifiTethered) {
-                mWifiTetherResult = SUCCESS;   // wifi tethering is successful
-            } else if (wifiErrored) {
-                mWifiTetherResult = FAILURE;   // wifi tethering failed
-            }
-            log("mWifiTetherResult: " + mWifiTetherResult);
-            this.notify();
-        }
-    }
-
-
-    // Wait for network connectivity state: CONNECTING, CONNECTED, SUSPENDED,
-    //                                      DISCONNECTING, DISCONNECTED, UNKNOWN
-    public boolean waitForNetworkState(int networkType, State expectedState, long timeout) {
-        long startTime = System.currentTimeMillis();
+    // 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) {
-            if ((System.currentTimeMillis() - startTime) > timeout) {
-                log("waitForNetworkState time out, the state of network type " + networkType +
-                        " is: " + mCM.getNetworkInfo(networkType).getState());
-                if (mCM.getNetworkInfo(networkType).getState() != expectedState) {
-                    return false;
-                } else {
-                    // the broadcast has been sent out. the state has been changed.
-                    log("networktype: " + networkType + " state: " +
-                            mCM.getNetworkInfo(networkType));
-                    return true;
-                }
-            }
-            log("Wait for the connectivity state for network: " + networkType +
-                    " to be " + expectedState.toString());
-            synchronized (connectivityObject) {
-                try {
-                    connectivityObject.wait(SHORT_TIMEOUT);
-                } catch (InterruptedException e) {
-                    e.printStackTrace();
-                }
-                if ((mNetworkInfo.getType() != networkType) ||
-                    (mNetworkInfo.getState() != expectedState)) {
-                    log("network state for " + mNetworkInfo.getType() +
-                            "is: " + mNetworkInfo.getState());
-                    continue;
-                }
+            NetworkInfo ni = mCm.getNetworkInfo(networkType);
+            String niString = ni == null ? "null" : ni.toString();
+            if (ni != null && expectedState.equals(ni.getState())) {
+                log("waitForNetworkState success: " + niString);
                 return true;
             }
+            if ((SystemClock.uptimeMillis() - startTime) > timeout) {
+                log("waitForNetworkState timeout: " + niString);
+                return false;
+            }
+            log("waitForNetworkState interim: " + niString);
+            SystemClock.sleep(SHORT_TIMEOUT);
         }
     }
 
-    // Wait for Wifi state: WIFI_STATE_DISABLED, WIFI_STATE_DISABLING, WIFI_STATE_ENABLED,
+    // wait for Wifi state: WIFI_STATE_DISABLED, WIFI_STATE_DISABLING, WIFI_STATE_ENABLED,
     //                      WIFI_STATE_ENALBING, WIFI_STATE_UNKNOWN
-    public boolean waitForWifiState(int expectedState, long timeout) {
-        long startTime = System.currentTimeMillis();
+    protected boolean waitForWifiState(int expectedState, long timeout) {
+        long startTime = SystemClock.uptimeMillis();
         while (true) {
-            if ((System.currentTimeMillis() - startTime) > timeout) {
-                if (mWifiState != expectedState) {
-                    return false;
-                } else {
-                    return true;
-                }
-            }
-            log("Wait for wifi state to be: " + expectedState);
-            synchronized (wifiObject) {
-                try {
-                    wifiObject.wait(SHORT_TIMEOUT);
-                } catch (InterruptedException e) {
-                    e.printStackTrace();
-                }
-                if (mWifiState != expectedState) {
-                    log("Wifi state is: " + mWifiState);
-                    continue;
-                }
+            int state = mWifiManager.getWifiState();
+            if (state == expectedState) {
+                log("waitForWifiState success: state=" + state);
                 return true;
             }
+            if ((SystemClock.uptimeMillis() - startTime) > timeout) {
+                log(String.format("waitForWifiState timeout: expected=%d, actual=%d",
+                        expectedState, state));
+                return false;
+            }
+            log(String.format("waitForWifiState interim: expected=%d, actual=%d",
+                    expectedState, state));
+            SystemClock.sleep(SHORT_TIMEOUT);
         }
     }
 
     // Wait for Wifi AP state: WIFI_AP_STATE_DISABLED, WIFI_AP_STATE_DISABLING,
     //                         WIFI_AP_STATE_ENABLED, WIFI_STATE_ENALBING, WIFI_STATE_UNKNOWN
-    public boolean waitForWifiAPState(int expectedState, long timeout) {
-        long startTime = System.currentTimeMillis();
+    protected boolean waitForWifiApState(int expectedState, long timeout) {
+        long startTime = SystemClock.uptimeMillis();
         while (true) {
-            if ((System.currentTimeMillis() - startTime) > timeout) {
-                if (mWifiManager.getWifiApState() != expectedState) {
-                    return false;
-                } else {
-                    return true;
-                }
-            }
-            log("Wait for wifi AP state to be: " + expectedState);
-            synchronized (wifiObject) {
-                try {
-                    wifiObject.wait(SHORT_TIMEOUT);
-                } catch (InterruptedException e) {
-                    e.printStackTrace();
-                }
-                if (mWifiManager.getWifiApState() != expectedState) {
-                    log("Wifi state is: " + mWifiManager.getWifiApState());
-                    continue;
-                }
+            int state = mWifiManager.getWifiApState();
+            if (state == expectedState) {
+                log("waitForWifiAPState success: state=" + state);
                 return true;
             }
+            if ((SystemClock.uptimeMillis() - startTime) > timeout) {
+                log(String.format("waitForWifiAPState timeout: expected=%d, actual=%d",
+                        expectedState, state));
+                return false;
+            }
+            log(String.format("waitForWifiAPState interim: expected=%d, actual=%d",
+                    expectedState, state));
+            SystemClock.sleep(SHORT_TIMEOUT);
         }
     }
 
@@ -450,44 +226,49 @@
      * @return SUCCESS if tethering result is successful
      *         FAILURE if tethering result returns error.
      */
-    public int waitForTetherStateChange(long timeout) {
-        long startTime = System.currentTimeMillis();
+    protected boolean waitForTetherStateChange(long timeout) {
+        long startTime = SystemClock.uptimeMillis();
+        String[] wifiRegexes = mCm.getTetherableWifiRegexs();
         while (true) {
-            if ((System.currentTimeMillis() - startTime) > timeout) {
-                return mWifiTetherResult;
+            if ((SystemClock.uptimeMillis() - startTime) > timeout) {
+                return false;
             }
-            log("Wait for wifi tethering result.");
-            synchronized (this) {
-                try {
-                    this.wait(SHORT_TIMEOUT);
-                } catch (InterruptedException e) {
-                    e.printStackTrace();
-                }
-                if (mWifiTetherResult == INIT ) {
-                    continue;
-                } else {
-                    return mWifiTetherResult;
+            String[] active = mCm.getTetheredIfaces();
+            String[] error = mCm.getTetheringErroredIfaces();
+            for (String iface: active) {
+                for (String regex: wifiRegexes) {
+                    if (iface.matches(regex)) {
+                        return true;
+                    }
                 }
             }
+            for (String iface: error) {
+                for (String regex: wifiRegexes) {
+                    if (iface.matches(regex)) {
+                        return false;
+                    }
+                }
+            }
+            SystemClock.sleep(SHORT_TIMEOUT);
         }
     }
 
     // Return true if device is currently connected to mobile network
-    public boolean isConnectedToMobile() {
-        return (mNetworkInfo.getType() == ConnectivityManager.TYPE_MOBILE);
+    protected boolean isConnectedToMobile() {
+        return (mCm.getActiveNetworkInfo().getType() == ConnectivityManager.TYPE_MOBILE);
     }
 
     // Return true if device is currently connected to Wifi
-    public boolean isConnectedToWifi() {
-        return (mNetworkInfo.getType() == ConnectivityManager.TYPE_WIFI);
+    protected boolean isConnectedToWifi() {
+        return (mCm.getActiveNetworkInfo().getType() == ConnectivityManager.TYPE_WIFI);
     }
 
-    public boolean enableWifi() {
+    protected boolean enableWifi() {
         return mWifiManager.setWifiEnabled(true);
     }
 
     // Turn screen off
-    public void turnScreenOff() {
+    protected void turnScreenOff() {
         log("Turn screen off");
         PowerManager pm =
             (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
@@ -495,7 +276,7 @@
     }
 
     // Turn screen on
-    public void turnScreenOn() {
+    protected void turnScreenOn() {
         log("Turn screen on");
         PowerManager pm =
                 (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
@@ -511,7 +292,7 @@
      * @param pingServerList a list of servers that can be used for ping test, can be null
      * @return true if the ping test is successful, false otherwise.
      */
-    public boolean pingTest(String[] pingServerList) {
+    protected boolean pingTest(String[] pingServerList) {
         String[] hostList = {"www.google.com", "www.yahoo.com",
                 "www.bing.com", "www.facebook.com", "www.ask.com"};
         if (pingServerList != null) {
@@ -549,7 +330,7 @@
      * If the device is already associated with a WiFi, disconnect and forget it,
      * We don't verify whether the connection is successful or not, leave this to the test
      */
-    public boolean connectToWifi(String knownSSID) {
+    protected boolean connectToWifi(String knownSSID) {
         WifiConfiguration config = new WifiConfiguration();
         config.SSID = knownSSID;
         config.allowedKeyManagement.set(KeyMgmt.NONE);
@@ -562,7 +343,7 @@
      * @param config
      * @return
      */
-    public boolean connectToWifiWithConfiguration(WifiConfiguration config) {
+    protected boolean connectToWifiWithConfiguration(WifiConfiguration config) {
         String ssid = config.SSID;
         config.SSID = convertToQuotedString(ssid);
 
@@ -571,7 +352,7 @@
             log("Wifi is not enabled, enable it");
             mWifiManager.setWifiEnabled(true);
             // wait for the wifi state change before start scanning.
-            if (!waitForWifiState(WifiManager.WIFI_STATE_ENABLED, 2*SHORT_TIMEOUT)) {
+            if (!waitForWifiState(WifiManager.WIFI_STATE_ENABLED, LONG_TIMEOUT)) {
                 log("wait for WIFI_STATE_ENABLED failed");
                 return false;
             }
@@ -592,12 +373,12 @@
     /*
      * Disconnect from the current AP and remove configured networks.
      */
-    public boolean disconnectAP() {
+    protected boolean disconnectAP() {
         // remove saved networks
         if (!mWifiManager.isWifiEnabled()) {
             log("Enabled wifi before remove configured networks");
             mWifiManager.setWifiEnabled(true);
-            sleep(SHORT_TIMEOUT);
+            SystemClock.sleep(SHORT_TIMEOUT);
         }
 
         List<WifiConfiguration> wifiConfigList = mWifiManager.getConfiguredNetworks();
@@ -623,37 +404,81 @@
      * Disable Wifi
      * @return true if Wifi is disabled successfully
      */
-    public boolean disableWifi() {
+    protected boolean disableWifi() {
         return mWifiManager.setWifiEnabled(false);
     }
 
     /**
      * Remove configured networks and disable wifi
      */
-    public boolean removeConfiguredNetworksAndDisableWifi() {
+    protected boolean removeConfiguredNetworksAndDisableWifi() {
         if (!disconnectAP()) {
            return false;
         }
-        sleep(SHORT_TIMEOUT);
+        SystemClock.sleep(SHORT_TIMEOUT);
         if (!mWifiManager.setWifiEnabled(false)) {
             return false;
         }
-        sleep(SHORT_TIMEOUT);
+        SystemClock.sleep(SHORT_TIMEOUT);
         return true;
     }
 
-    private void sleep(long sleeptime) {
-        try {
-            Thread.sleep(sleeptime);
-        } catch (InterruptedException e) {}
-    }
-
     protected static String convertToQuotedString(String string) {
         return "\"" + string + "\"";
     }
 
+    protected boolean waitForActiveNetworkConnection(long timeout) {
+        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);
+                return false;
+            }
+            log("waitForActiveNetworkConnection interim: " + niString);
+            SystemClock.sleep(SHORT_TIMEOUT);
+        }
+    }
+
+    protected boolean waitUntilNoActiveNetworkConnection(long timeout) {
+        long startTime = SystemClock.uptimeMillis();
+        while (true) {
+            NetworkInfo ni = mCm.getActiveNetworkInfo();
+            if (ni == null) {
+                return true;
+            }
+            String niString = ni.toString();
+            if ((SystemClock.uptimeMillis() - startTime) > timeout) {
+                log("waitForActiveNetworkConnection timeout: " + niString);
+                return false;
+            }
+            log("waitForActiveNetworkConnection interim: " + niString);
+            SystemClock.sleep(SHORT_TIMEOUT);
+        }
+    }
+
+    // use ping request against Google public DNS to verify connectivity
+    protected boolean checkNetworkConnectivity() {
+        assertTrue("no active network connection", waitForActiveNetworkConnection(LONG_TIMEOUT));
+        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 false;
+    }
+
     @Override
-    public void tearDown() throws Exception{
+    protected void tearDown() throws Exception{
         //Unregister receiver
         if (mConnectivityReceiver != null) {
           mContext.unregisterReceiver(mConnectivityReceiver);
@@ -667,4 +492,36 @@
     private void log(String message) {
         Log.v(LOG_TAG, message);
     }
+
+    /**
+     * Connect to the provided Wi-Fi network
+     * @param config is the network configuration
+     * @throws AssertionError if fails to associate and connect to wifi ap
+     */
+    protected void connectToWifi(WifiConfiguration config) {
+        // step 1: connect to the test access point
+        assertTrue("failed to associate with " + config.SSID,
+                connectToWifiWithConfiguration(config));
+
+        // step 2: verify Wifi state and network state;
+        assertTrue("wifi state not connected with " + config.SSID,
+                waitForNetworkState(ConnectivityManager.TYPE_WIFI,
+                State.CONNECTED, LONG_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/NetworkState.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/NetworkState.java
deleted file mode 100644
index 9d97ac5..0000000
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/NetworkState.java
+++ /dev/null
@@ -1,203 +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 android.net.NetworkInfo.State;
-import android.util.Log;
-
-import java.util.List;
-import java.util.ArrayList;
-
-public class NetworkState {
-    public static final int TO_DISCONNECTION = 0; // transition to disconnection
-    public static final int TO_CONNECTION = 1; // transition to connection
-    public static final int DO_NOTHING = -1;   // no state change
-    private final String LOG_TAG = "NetworkState";
-    private List<State> mStateDepository;
-    private State mTransitionTarget;
-    private int mTransitionDirection;
-    private String mReason = null;         // record mReason of state transition failure
-
-    public NetworkState() {
-        mStateDepository = new ArrayList<State>();
-        mTransitionDirection = DO_NOTHING;
-        mTransitionTarget = State.UNKNOWN;
-    }
-
-    public NetworkState(State currentState) {
-        mStateDepository = new ArrayList<State>();
-        mStateDepository.add(currentState);
-        mTransitionDirection = DO_NOTHING;
-        mTransitionTarget = State.UNKNOWN;
-    }
-
-    // Reinitialize the network state
-    public void resetNetworkState() {
-        mStateDepository.clear();
-        mTransitionDirection = DO_NOTHING;
-        mTransitionTarget = State.UNKNOWN;
-    }
-
-    // set the transition criteria, transitionDir could be:
-    // DO_NOTHING, TO_CONNECTION, TO_DISCONNECTION
-    public void setStateTransitionCriteria(State initState, int transitionDir, State targetState) {
-        if (!mStateDepository.isEmpty()) {
-            mStateDepository.clear();
-        }
-        mStateDepository.add(initState);
-        mTransitionDirection = transitionDir;
-        mTransitionTarget = targetState;
-        Log.v(LOG_TAG, "setStateTransitionCriteria: " + printStates());
-    }
-
-    public void recordState(State currentState) {
-        mStateDepository.add(currentState);
-    }
-
-    // Verify state transition
-    public boolean validateStateTransition() {
-        Log.v(LOG_TAG, "print state depository: " + printStates());
-        if (mTransitionDirection == DO_NOTHING) {
-            if (mStateDepository.isEmpty()) {
-                Log.v(LOG_TAG, "no state is recorded");
-                mReason = "no state is recorded.";
-                return false;
-            } else if (mStateDepository.size() > 1) {
-                for (int i = 0; i < mStateDepository.size(); i++) {
-                    if (mStateDepository.get(i) != mTransitionTarget) {
-                        Log.v(LOG_TAG, "state changed.");
-                        mReason = "Unexpected state change";
-                        return false;
-                    }
-                }
-            } else if (mStateDepository.get(0) != mTransitionTarget) {
-                Log.v(LOG_TAG, mTransitionTarget + " is expected, but it is " +
-                        mStateDepository.get(0));
-                mReason = mTransitionTarget + " is expected, but it is " + mStateDepository.get(0);
-                return false;
-            }
-            return true;
-        } else if (mTransitionDirection == TO_CONNECTION) {
-            Log.v(LOG_TAG, "transition to CONNECTED");
-            return transitToConnection();
-        } else {
-            Log.v(LOG_TAG, "transition to DISCONNECTED");
-            return transitToDisconnection();
-        }
-    }
-
-    /*
-     * Verifies state transition from CONNECTED->...-> DISCONNECTED.
-     *
-     * returns false if initial state or target state is not correct, or if there is
-     * any transition from DISCONNECTING/DISCONNECTED -> CONNECTED.
-     */
-    public boolean transitToDisconnection () {
-        mReason = "states: " + printStates();
-        if (mStateDepository.get(0) != State.CONNECTED) {
-            mReason += " initial state should be CONNECTED, but it is " +
-                    mStateDepository.get(0) + ".";
-            return false;
-        }
-        State lastState = mStateDepository.get(mStateDepository.size() - 1);
-        if ( lastState != mTransitionTarget) {
-            mReason += " the last state should be DISCONNECTED, but it is " + lastState;
-            return false;
-        }
-        for (int i = 1; i < mStateDepository.size() - 1; i++) {
-            State preState = mStateDepository.get(i-1);
-            State curState = mStateDepository.get(i);
-            if (preState == curState) {
-                continue;
-            } else if ((preState == State.CONNECTED) && ((curState == State.DISCONNECTING) ||
-                    (curState == State.DISCONNECTED))) {
-                continue;
-            } else if ((preState == State.DISCONNECTING) && (curState == State.DISCONNECTED)) {
-                continue;
-            } else {
-                mReason += " Transition state from " + preState.toString() + " to " +
-                        curState.toString() + " is not valid.";
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /*
-     * Verifies state transition from DISCONNECTED->...-> CONNECTED.
-     *
-     * returns false if initial state or target state is not correct, or if there is
-     * any transition from CONNECED -> DISCONNECTED.
-     */
-    public boolean transitToConnection() {
-        mReason = "states: " + printStates();
-        if (mStateDepository.get(0) != State.DISCONNECTED) {
-            mReason += " initial state should be DISCONNECTED, but it is " +
-                    mStateDepository.get(0) + ".";
-            return false;
-        }
-        State lastState = mStateDepository.get(mStateDepository.size() - 1);
-        if ( lastState != mTransitionTarget) {
-            mReason += "The last state should be " + mTransitionTarget + ", but it is " + lastState;
-            return false;
-        }
-        for (int i = 1; i < mStateDepository.size(); i++) {
-            State preState = mStateDepository.get(i-1);
-            State curState = mStateDepository.get(i);
-            if (preState == curState) {
-                continue;
-            }
-            if ((preState == State.DISCONNECTED) && ((curState == State.CONNECTING) ||
-                    (curState == State.CONNECTED))) {
-                continue;
-             } else if ((preState == State.CONNECTING) && (curState == State.CONNECTED)) {
-                 continue;
-             } else {
-                mReason += " Transition state from " + preState.toString() + " to " +
-                        curState.toString() + " is not valid.";
-                return false;
-            }
-        }
-        return true;
-    }
-
-    public List<State> getTransitionStates() {
-        return mStateDepository;
-    }
-
-    // return state failure mReason
-    public String getReason() {
-        return mReason;
-    }
-
-    public String printStates() {
-        StringBuilder stateBuilder = new StringBuilder("");
-        for (int i = 0; i < mStateDepository.size(); i++) {
-            stateBuilder.append(" ").append(mStateDepository.get(i).toString()).append("->");
-        }
-        return stateBuilder.toString();
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder builder = new StringBuilder(" ");
-        builder.append("mTransitionDirection: ").append(Integer.toString(mTransitionDirection)).
-                append("; ").append("states:").
-                append(printStates()).append("; ");
-        return builder.toString();
-    }
-}
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/WifiAssociationTestRunner.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/WifiAssociationTestRunner.java
index 722df2e..2354484 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/WifiAssociationTestRunner.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/WifiAssociationTestRunner.java
@@ -57,8 +57,7 @@
     @Override
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
-        Bundle arguments = icicle;
-        String mFrequencyBand = arguments.getString("frequency-band");
+        String mFrequencyBand = icicle.getString("frequency-band");
         if (mFrequencyBand != null) {
             setFrequencyBand(mFrequencyBand);
         }
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 05462b4..b4b0e53 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java
@@ -16,27 +16,23 @@
 
 package com.android.connectivitymanagertest.functional;
 
-import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.State;
 import android.net.wifi.WifiManager;
-import android.os.PowerManager;
-import android.os.PowerManager.WakeLock;
+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;
-import com.android.connectivitymanagertest.NetworkState;
 
 public class ConnectivityManagerMobileTest extends
         ConnectivityManagerTestBase {
     private static final String TAG = "ConnectivityManagerMobileTest";
 
     private String mTestAccessPoint;
-    private WakeLock wl;
     private boolean mWifiOnlyFlag;
 
     @Override
@@ -47,15 +43,11 @@
         mTestAccessPoint = mRunner.mTestSsid;
         mWifiOnlyFlag = mRunner.mWifiOnlyFlag;
 
-        PowerManager pm = (PowerManager)getInstrumentation().
-                getContext().getSystemService(Context.POWER_SERVICE);
-        wl = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "CMWakeLock");
-        wl.acquire();
         // 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.");
-            mCM.setAirplaneMode(false);
+            mCm.setAirplaneMode(false);
         }
 
         if (!mWifiOnlyFlag) {
@@ -72,15 +64,14 @@
 
     @Override
     public void tearDown() throws Exception {
-        wl.release();
         removeConfiguredNetworksAndDisableWifi();
-        mCM.setAirplaneMode(false);
+        mCm.setAirplaneMode(false);
         super.tearDown();
     }
 
     // help function to verify 3G connection
     public void verifyCellularConnection() {
-        NetworkInfo extraNetInfo = mCM.getActiveNetworkInfo();
+        NetworkInfo extraNetInfo = mCm.getActiveNetworkInfo();
         assertEquals("network type is not MOBILE", ConnectivityManager.TYPE_MOBILE,
                 extraNetInfo.getType());
         assertTrue("not connected to cellular network", extraNetInfo.isConnected());
@@ -90,431 +81,262 @@
         Log.v(TAG, message);
     }
 
-    private void sleep(long sleeptime) {
-        try {
-            Thread.sleep(sleeptime);
-        } catch (InterruptedException e) {}
-    }
-
     // 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, this.getName() + " is excluded for wifi-only test");
+            Log.v(TAG, getName() + " is excluded for wifi-only test");
             return;
         }
-        // Enable Wi-Fi to avoid initial UNKNOWN state
-        enableWifi();
-        sleep(2 * SHORT_TIMEOUT);
 
-        // Wi-Fi is disabled
-        disableWifi();
+        // disable WiFi
+        assertTrue("failed to disable WiFi", disableWifi());
 
-        assertTrue(waitForNetworkState(ConnectivityManager.TYPE_WIFI,
-                State.DISCONNECTED, LONG_TIMEOUT));
-        assertTrue(waitForNetworkState(ConnectivityManager.TYPE_MOBILE,
-                State.CONNECTED, LONG_TIMEOUT));
-        // Wait for 10 seconds for broadcasts to be sent out
-        sleep(10 * 1000);
+        // wait for mobile
+        assertTrue("failed to wait for mobile connection", waitForNetworkState(
+                ConnectivityManager.TYPE_MOBILE, State.CONNECTED, LONG_TIMEOUT));
 
-        // As Wifi stays in DISCONNETED, Mobile statys in CONNECTED,
-        // the connectivity manager will not broadcast any network connectivity event for Wifi
-        NetworkInfo networkInfo = mCM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
-        setStateTransitionCriteria(ConnectivityManager.TYPE_MOBILE,
-                networkInfo.getState(), NetworkState.DO_NOTHING, State.CONNECTED);
-        networkInfo = mCM.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
-        setStateTransitionCriteria(ConnectivityManager.TYPE_WIFI, networkInfo.getState(),
-                NetworkState.DO_NOTHING, State.DISCONNECTED);
-        // Eanble Wifi without associating with any AP
-        enableWifi();
-        sleep(2 * SHORT_TIMEOUT);
+        // assert that we are indeed using mobile
+        NetworkInfo ni = mCm.getActiveNetworkInfo();
+        assertEquals("active network is not mobile", ConnectivityManager.TYPE_MOBILE, ni.getType());
 
-        // validate state and broadcast
-        if (!validateNetworkStates(ConnectivityManager.TYPE_WIFI)) {
-            log("the state for WIFI is changed");
-            log("reason: " +
-                    getTransitionFailureReason(ConnectivityManager.TYPE_WIFI));
-            assertTrue("state validation fail", false);
-        }
-        if (!validateNetworkStates(ConnectivityManager.TYPE_MOBILE)) {
-            log("the state for MOBILE is changed");
-            log("reason: " +
-                    getTransitionFailureReason(ConnectivityManager.TYPE_MOBILE));
-            assertTrue("state validation fail", false);
-        }
-        // Verify that the device is still connected to MOBILE
+        long timestamp = SystemClock.uptimeMillis();
+        // now enable WiFi
+        assertTrue("failed to enable WiFi", enableWifi());
+        // assert that WiFi state settles at disconnected since no AP should be configured
+        assertTrue("WiFi state is not DISCONNECTED after enabling", waitForWifiState(
+                WifiManager.WIFI_STATE_DISABLED, LONG_TIMEOUT));
+
+        // assert that no connectivity change broadcast was sent since we enable wifi
+        assertTrue("connectivity has changed since wifi enable",
+                timestamp > getLastConnectivityChangeTime());
+
+        // verify that the device is still connected to MOBILE
         verifyCellularConnection();
+        // verify that connection actually works
+        assertTrue("no network connectivity at end of test", checkNetworkConnectivity());
     }
 
     // Test case 2: test connection to a given AP
     @LargeTest
     public void testConnectToWifi() {
         assertNotNull("SSID is null", mTestAccessPoint);
-        NetworkInfo networkInfo;
-        if (!mWifiOnlyFlag) {
-            //Prepare for connectivity verification
-            networkInfo = mCM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
-            setStateTransitionCriteria(ConnectivityManager.TYPE_MOBILE,
-                    networkInfo.getState(), NetworkState.TO_DISCONNECTION, State.DISCONNECTED);
-        }
-        networkInfo = mCM.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
-        setStateTransitionCriteria(ConnectivityManager.TYPE_WIFI, networkInfo.getState(),
-                NetworkState.TO_CONNECTION, State.CONNECTED);
 
-        // Enable Wifi and connect to a test access point
+        // assert that we are able to connect to the ap
         assertTrue("failed to connect to " + mTestAccessPoint,
                 connectToWifi(mTestAccessPoint));
-
+        // assert that WifiManager reports correct state
         assertTrue(waitForWifiState(WifiManager.WIFI_STATE_ENABLED, LONG_TIMEOUT));
-        log("wifi state is enabled");
+        // assert that ConnectivityManager reports correct state for Wifi
         assertTrue(waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
                 WIFI_CONNECTION_TIMEOUT));
-        if (!mWifiOnlyFlag) {
-            assertTrue(waitForNetworkState(ConnectivityManager.TYPE_MOBILE,
-                    State.DISCONNECTED, LONG_TIMEOUT));
-        }
-
-        // validate states
-        if (!validateNetworkStates(ConnectivityManager.TYPE_WIFI)) {
-            log("Wifi state transition validation failed.");
-            log("reason: " +
-                    getTransitionFailureReason(ConnectivityManager.TYPE_WIFI));
-            assertTrue(false);
-        }
-        if (!mWifiOnlyFlag) {
-            if (!validateNetworkStates(ConnectivityManager.TYPE_MOBILE)) {
-                log("Mobile state transition validation failed.");
-                log("reason: " +
-                        getTransitionFailureReason(ConnectivityManager.TYPE_MOBILE));
-                assertTrue(false);
-            }
-        }
+        // below check disbabled since we have bug in what ConnectivityManager returns
+//        if (!mWifiOnlyFlag) {
+//            // assert that ConnectivityManager reports correct state for mobile
+//            assertTrue("mobile not disconnected", waitForNetworkState(
+//                    ConnectivityManager.TYPE_MOBILE, State.DISCONNECTED, LONG_TIMEOUT));
+//        }
+        // verify that connection actually works
+        assertTrue("no network connectivity at end of test", checkNetworkConnectivity());
     }
 
-    // Test case 3: connect to Wifi with known AP
+    // Test case 3: connect & reconnect to Wifi with known AP
     @LargeTest
     public void testConnectToWifWithKnownAP() {
         assertNotNull("SSID is null", mTestAccessPoint);
-        // Connect to mTestAccessPoint
-        assertTrue("failed to connect to " + mTestAccessPoint,
-                connectToWifi(mTestAccessPoint));
-        assertTrue(waitForWifiState(WifiManager.WIFI_STATE_ENABLED, LONG_TIMEOUT));
-        assertTrue(waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
-                WIFI_CONNECTION_TIMEOUT));
+        // enable WiFi
+        assertTrue("failed to enable wifi", enableWifi());
+        // wait for wifi enable
+        assertTrue("wifi not enabled", waitForWifiState(
+                WifiManager.WIFI_STATE_ENABLED, LONG_TIMEOUT));
+        // Connect to AP
+        assertTrue("failed to connect to " + mTestAccessPoint, connectToWifi(mTestAccessPoint));
+        // verify wifi connected as reported by ConnectivityManager
+        assertTrue("wifi not connected", waitForNetworkState(
+                ConnectivityManager.TYPE_WIFI, State.CONNECTED, WIFI_CONNECTION_TIMEOUT));
 
-        sleep(SHORT_TIMEOUT);
-        // Disable Wifi
-        log("Disable Wifi");
-        if (!disableWifi()) {
-            log("disable Wifi failed");
-            return;
-        }
+        assertTrue("failed to disable wifi", disableWifi());
 
         // Wait for the Wifi state to be DISABLED
-        assertTrue(waitForWifiState(WifiManager.WIFI_STATE_DISABLED, LONG_TIMEOUT));
-        assertTrue(waitForNetworkState(ConnectivityManager.TYPE_WIFI,
-                State.DISCONNECTED, LONG_TIMEOUT));
+        assertTrue("wifi state not disabled", waitForWifiState(
+                WifiManager.WIFI_STATE_DISABLED, LONG_TIMEOUT));
+        // below check disbabled since we have bug in what ConnectivityManager returns
+//        assertTrue("wifi not disconnected", waitForNetworkState(ConnectivityManager.TYPE_WIFI,
+//                State.DISCONNECTED, LONG_TIMEOUT));
         if (!mWifiOnlyFlag) {
-            assertTrue(waitForNetworkState(ConnectivityManager.TYPE_MOBILE,
-                    State.CONNECTED, LONG_TIMEOUT));
+            assertTrue("mobile not connected after wifi disable", waitForNetworkState(
+                    ConnectivityManager.TYPE_MOBILE, State.CONNECTED, LONG_TIMEOUT));
         }
 
-        NetworkInfo networkInfo;
-        if (!mWifiOnlyFlag) {
-            //Prepare for connectivity state verification
-            networkInfo = mCM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
-            setStateTransitionCriteria(ConnectivityManager.TYPE_MOBILE,
-                                                  networkInfo.getState(), NetworkState.DO_NOTHING,
-                                                  State.DISCONNECTED);
-        }
-        networkInfo = mCM.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
-        setStateTransitionCriteria(ConnectivityManager.TYPE_WIFI, networkInfo.getState(),
-                NetworkState.TO_CONNECTION, State.CONNECTED);
+        // wait for 30s before restart wifi
+        SystemClock.sleep(LONG_TIMEOUT);
+        assertTrue("failed to enable wifi after disable", enableWifi());
 
-        // wait for 2 minutes before restart wifi
-        sleep(WIFI_STOP_START_INTERVAL);
-        // Enable Wifi again
-        log("Enable Wifi again");
-        enableWifi();
-
+        // wait for wifi enable
+        assertTrue("wifi not enabled after toggle", waitForWifiState(
+                WifiManager.WIFI_STATE_ENABLED, LONG_TIMEOUT));
         // Wait for Wifi to be connected and mobile to be disconnected
-        assertTrue(waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
-                WIFI_CONNECTION_TIMEOUT));
-        if (!mWifiOnlyFlag) {
-            assertTrue(waitForNetworkState(ConnectivityManager.TYPE_MOBILE,
-                    State.DISCONNECTED, LONG_TIMEOUT));
-        }
-
-        // validate wifi states
-        if (!validateNetworkStates(ConnectivityManager.TYPE_WIFI)) {
-            log("Wifi state transition validation failed.");
-            log("reason: " +
-                    getTransitionFailureReason(ConnectivityManager.TYPE_WIFI));
-            assertTrue(false);
-        }
+        assertTrue("wifi not connected after toggle", waitForNetworkState(
+                ConnectivityManager.TYPE_WIFI, State.CONNECTED, WIFI_CONNECTION_TIMEOUT));
+        // below check disbabled since we have bug in what ConnectivityManager returns
+//        if (!mWifiOnlyFlag) {
+//            assertTrue("mobile not disconnected after wifi toggle", waitForNetworkState(
+//                    ConnectivityManager.TYPE_MOBILE, State.DISCONNECTED, LONG_TIMEOUT));
+//        }
+        // verify that connection actually works
+        assertTrue("no network connectivity at end of test", checkNetworkConnectivity());
     }
 
-    // Test case 4:  test disconnect Wifi
+    // Test case 4:  test disconnect and clear wifi settings
     @LargeTest
     public void testDisconnectWifi() {
         assertNotNull("SSID is null", mTestAccessPoint);
 
+        // enable WiFi
+        assertTrue("failed to enable wifi", enableWifi());
+        // wait for wifi enable
+        assertTrue("wifi not enabled", waitForWifiState(
+                WifiManager.WIFI_STATE_ENABLED, LONG_TIMEOUT));
         // connect to Wifi
         assertTrue("failed to connect to " + mTestAccessPoint,
                 connectToWifi(mTestAccessPoint));
 
-        assertTrue(waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
-                WIFI_CONNECTION_TIMEOUT));
-
-        // Wait for a few seconds to avoid the state that both Mobile and Wifi is connected
-        sleep(SHORT_TIMEOUT);
-
-        NetworkInfo networkInfo;
-        if (!mWifiOnlyFlag) {
-            networkInfo = mCM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
-            setStateTransitionCriteria(ConnectivityManager.TYPE_MOBILE,
-                                                  networkInfo.getState(),
-                                                  NetworkState.TO_CONNECTION,
-                                                  State.CONNECTED);
-        }
-        networkInfo = mCM.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
-        setStateTransitionCriteria(ConnectivityManager.TYPE_WIFI, networkInfo.getState(),
-                NetworkState.TO_DISCONNECTION, State.DISCONNECTED);
+        assertTrue("wifi not connected", waitForNetworkState(
+                ConnectivityManager.TYPE_WIFI, State.CONNECTED, WIFI_CONNECTION_TIMEOUT));
 
         // clear Wifi
         removeConfiguredNetworksAndDisableWifi();
 
-        assertTrue(waitForNetworkState(ConnectivityManager.TYPE_WIFI,
-                State.DISCONNECTED, LONG_TIMEOUT));
+        // assert that wifi has been disabled
+        assertTrue("wifi state not disabled", waitForWifiState(
+                WifiManager.WIFI_STATE_DISABLED, LONG_TIMEOUT));
         if (!mWifiOnlyFlag) {
-            assertTrue(waitForNetworkState(ConnectivityManager.TYPE_MOBILE,
+            // assert that mobile is now connected
+            assertTrue("mobile not enabled", waitForNetworkState(ConnectivityManager.TYPE_MOBILE,
                     State.CONNECTED, LONG_TIMEOUT));
-        }
-
-        // validate states
-        if (!validateNetworkStates(ConnectivityManager.TYPE_WIFI)) {
-            log("Wifi state transition validation failed.");
-            log("reason: " +
-                    getTransitionFailureReason(ConnectivityManager.TYPE_WIFI));
-            assertTrue(false);
-        }
-        if (!mWifiOnlyFlag) {
-            if (!validateNetworkStates(ConnectivityManager.TYPE_MOBILE)) {
-                log("Mobile state transition validation failed.");
-                log("reason: " +
-                        getTransitionFailureReason(ConnectivityManager.TYPE_MOBILE));
-                assertTrue(false);
-            }
+            // verify that connection actually works
+            assertTrue("no network connectivity at end of test", checkNetworkConnectivity());
         }
     }
 
-    // Test case 5: test connectivity from 3G to airplane mode, then to 3G again
+    // Test case 5: test connectivity with mobile->airplane mode->mobile
     @LargeTest
     public void testDataConnectionWith3GToAmTo3G() {
         if (mWifiOnlyFlag) {
-            Log.v(TAG, this.getName() + " is excluded for wifi-only test");
+            Log.v(TAG, getName() + " is excluded for wifi-only test");
             return;
         }
-        //Prepare for state verification
-        NetworkInfo networkInfo = mCM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
-        setStateTransitionCriteria(ConnectivityManager.TYPE_MOBILE,
-                                              networkInfo.getState(),
-                                              NetworkState.TO_DISCONNECTION,
-                                              State.DISCONNECTED);
-        networkInfo = mCM.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
-        assertEquals(State.DISCONNECTED, networkInfo.getState());
+        // disable wifi
+        assertTrue("failed to disable wifi", disableWifi());
+        assertTrue("wifi state not disabled", waitForWifiState(
+                WifiManager.WIFI_STATE_DISABLED, LONG_TIMEOUT));
+        // assert that we have mobile connection
+        assertTrue("no mobile connection", waitForNetworkState(
+                ConnectivityManager.TYPE_MOBILE, State.CONNECTED, LONG_TIMEOUT));
 
-        // Enable airplane mode
-        log("Enable airplane mode");
-        mCM.setAirplaneMode(true);
-        sleep(SHORT_TIMEOUT);
-
-        networkInfo = mCM.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
-        assertEquals(State.DISCONNECTED, networkInfo.getState());
-        // wait until mobile is turn off
-        assertTrue(waitForNetworkState(ConnectivityManager.TYPE_MOBILE,
-                State.DISCONNECTED, LONG_TIMEOUT));
-        if (!validateNetworkStates(ConnectivityManager.TYPE_MOBILE)) {
-            log("Mobile state transition validation failed.");
-            log("reason: " +
-                    getTransitionFailureReason(ConnectivityManager.TYPE_MOBILE));
-            assertTrue(false);
-        }
-
-        // reset state recorder
-        networkInfo = mCM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
-        setStateTransitionCriteria(ConnectivityManager.TYPE_MOBILE,
-                                              networkInfo.getState(),
-                                              NetworkState.TO_CONNECTION,
-                                              State.CONNECTED);
-        networkInfo = mCM.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
-        setStateTransitionCriteria(ConnectivityManager.TYPE_WIFI, networkInfo.getState(),
-                NetworkState.DO_NOTHING, State.DISCONNECTED);
+        // enable airplane mode
+        mCm.setAirplaneMode(true);
+        // assert no active network connection after airplane mode enabled
+        assertTrue("still has active network connection",
+                waitUntilNoActiveNetworkConnection(LONG_TIMEOUT));
 
         // disable airplane mode
-        mCM.setAirplaneMode(false);
+        mCm.setAirplaneMode(false);
+        // assert there is active network connection after airplane mode disabled
+        assertTrue("no active network connection after airplane mode disable",
+                waitForActiveNetworkConnection(LONG_TIMEOUT));
 
-        assertTrue(waitForNetworkState(ConnectivityManager.TYPE_MOBILE,
-                State.CONNECTED, LONG_TIMEOUT));
-
-        // Validate the state transition
-        if (!validateNetworkStates(ConnectivityManager.TYPE_MOBILE)) {
-            log("Mobile state transition validation failed.");
-            log("reason: " +
-                    getTransitionFailureReason(ConnectivityManager.TYPE_MOBILE));
-            assertTrue(false);
-        }
-        if (!validateNetworkStates(ConnectivityManager.TYPE_WIFI)) {
-          log("Wifi state transition validation failed.");
-          log("reason: " +
-                  getTransitionFailureReason(ConnectivityManager.TYPE_WIFI));
-          assertTrue(false);
-        }
+        // assert that we have mobile connection
+        assertTrue("no mobile connection", waitForNetworkState(
+                ConnectivityManager.TYPE_MOBILE, State.CONNECTED, LONG_TIMEOUT));
+        // verify that connection actually works
+        assertTrue("no network connectivity at end of test", checkNetworkConnectivity());
     }
 
-    // Test case 6: test connectivity with airplane mode Wifi connected
+    // Test case 6: test connectivity with airplane mode on but wifi enabled
     @LargeTest
     public void testDataConnectionOverAMWithWifi() {
-        if (mWifiOnlyFlag) {
-            Log.v(TAG, this.getName() + " is excluded for wifi-only test");
-            return;
-        }
         assertNotNull("SSID is null", mTestAccessPoint);
-        // Eanble airplane mode
-        log("Enable airplane mode");
-        mCM.setAirplaneMode(true);
+        // enable airplane mode
+        mCm.setAirplaneMode(true);
+        // assert there is active network connection after airplane mode disabled
+        assertTrue("still has active network connection",
+                waitUntilNoActiveNetworkConnection(LONG_TIMEOUT));
 
-        NetworkInfo networkInfo;
-        if (!mWifiOnlyFlag) {
-            assertTrue(waitForNetworkState(ConnectivityManager.TYPE_MOBILE,
-                    State.DISCONNECTED, LONG_TIMEOUT));
-            networkInfo = mCM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
-            setStateTransitionCriteria(ConnectivityManager.TYPE_MOBILE,
-                                                  networkInfo.getState(),
-                                                  NetworkState.DO_NOTHING,
-                                                  State.DISCONNECTED);
-        }
-        networkInfo = mCM.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
-        setStateTransitionCriteria(ConnectivityManager.TYPE_WIFI, networkInfo.getState(),
-                                              NetworkState.TO_CONNECTION, State.CONNECTED);
-
-        // Connect to Wifi
+        // connect to Wifi
         assertTrue("failed to connect to " + mTestAccessPoint,
                 connectToWifi(mTestAccessPoint));
         assertTrue(waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
                 WIFI_CONNECTION_TIMEOUT));
+        // verify that connection actually works
+        assertTrue("no network connectivity after wifi enable", checkNetworkConnectivity());
 
-        // validate state and broadcast
-        if (!validateNetworkStates(ConnectivityManager.TYPE_WIFI)) {
-            log("state validate for Wifi failed");
-            log("reason: " +
-                    getTransitionFailureReason(ConnectivityManager.TYPE_WIFI));
-            assertTrue("State validation failed", false);
-        }
-        if (!mWifiOnlyFlag) {
-            if (!validateNetworkStates(ConnectivityManager.TYPE_MOBILE)) {
-                log("state validation for Mobile failed");
-                log("reason: " +
-                        getTransitionFailureReason(ConnectivityManager.TYPE_MOBILE));
-                assertTrue("state validation failed", false);
-            }
-        }
-        mCM.setAirplaneMode(false);
+        // disable airplane mode
+        mCm.setAirplaneMode(false);
     }
 
     // Test case 7: test connectivity while transit from Wifi->AM->Wifi
     @LargeTest
     public void testDataConnectionWithWifiToAMToWifi () {
-        if (mWifiOnlyFlag) {
-            Log.v(TAG, this.getName() + " is excluded for wifi-only test");
-            return;
-        }
-        // Connect to mTestAccessPoint
+        // connect to mTestAccessPoint
         assertNotNull("SSID is null", mTestAccessPoint);
-        // Connect to Wifi
+        // enable WiFi
+        assertTrue("failed to enable wifi", enableWifi());
+        // wait for wifi enable
+        assertTrue("wifi not enabled", waitForWifiState(
+                WifiManager.WIFI_STATE_ENABLED, LONG_TIMEOUT));
+        // connect to Wifi
         assertTrue("failed to connect to " + mTestAccessPoint,
                 connectToWifi(mTestAccessPoint));
-
         assertTrue(waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
                 WIFI_CONNECTION_TIMEOUT));
 
-        try {
-            Thread.sleep(SHORT_TIMEOUT);
-        } catch (Exception e) {
-            log("exception: " + e.toString());
-        }
+        // enable airplane mode without clearing Wifi
+        mCm.setAirplaneMode(true);
+        // assert there is active network connection after airplane mode disabled
+        assertTrue("still has active network connection",
+                waitUntilNoActiveNetworkConnection(LONG_TIMEOUT));
 
-        // Enable airplane mode without clearing Wifi
-        mCM.setAirplaneMode(true);
-
-        assertTrue(waitForNetworkState(ConnectivityManager.TYPE_WIFI,
-                State.DISCONNECTED, LONG_TIMEOUT));
-
-        try {
-            Thread.sleep(SHORT_TIMEOUT);
-        } catch (Exception e) {
-            log("exception: " + e.toString());
-        }
-
-        // Prepare for state validation
-        NetworkInfo networkInfo = mCM.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
-        assertEquals(State.DISCONNECTED, networkInfo.getState());
-        setStateTransitionCriteria(ConnectivityManager.TYPE_WIFI,
-                networkInfo.getState(), NetworkState.TO_CONNECTION, State.CONNECTED);
-
-        // Disable airplane mode
-        mCM.setAirplaneMode(false);
-
-        assertTrue(waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
-                WIFI_CONNECTION_TIMEOUT));
-        if (!mWifiOnlyFlag) {
-            assertTrue(waitForNetworkState(ConnectivityManager.TYPE_MOBILE,
-                    State.DISCONNECTED, LONG_TIMEOUT));
-        }
-
-        // validate the state transition
-        if (!validateNetworkStates(ConnectivityManager.TYPE_WIFI)) {
-            log("Wifi state transition validation failed.");
-            log("reason: " +
-                    getTransitionFailureReason(ConnectivityManager.TYPE_WIFI));
-            assertTrue(false);
-        }
+        // disable airplane mode
+        mCm.setAirplaneMode(false);
+        // assert there is active network connection after airplane mode disabled
+        assertTrue("no active network connection after airplane mode disable",
+                waitForActiveNetworkConnection(LONG_TIMEOUT));
+        // assert that we have a Wifi connection
+        assertTrue("wifi not connected after airplane mode disable", waitForNetworkState(
+                ConnectivityManager.TYPE_WIFI, State.CONNECTED, WIFI_CONNECTION_TIMEOUT));
+        // verify that connection actually works
+        assertTrue("no network connectivity at end of test", checkNetworkConnectivity());
     }
 
     // Test case 8: test wifi state change while connecting/disconnecting to/from an AP
     @LargeTest
     public void testWifiStateChange () {
         assertNotNull("SSID is null", mTestAccessPoint);
-        //Connect to mTestAccessPoint
+        // enable WiFi
+        assertTrue("failed to enable wifi", enableWifi());
+        // wait for wifi enable
+        assertTrue("wifi not enabled", waitForWifiState(
+                WifiManager.WIFI_STATE_ENABLED, LONG_TIMEOUT));
+        // connect to Wifi
         assertTrue("failed to connect to " + mTestAccessPoint,
                 connectToWifi(mTestAccessPoint));
-        assertTrue(waitForWifiState(WifiManager.WIFI_STATE_ENABLED, LONG_TIMEOUT));
         assertTrue(waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
                 WIFI_CONNECTION_TIMEOUT));
-        assertNotNull("Not associated with any AP",
-                      mWifiManager.getConnectionInfo().getBSSID());
+        assertNotNull("not associated with any AP", mWifiManager.getConnectionInfo().getBSSID());
 
-        try {
-            Thread.sleep(SHORT_TIMEOUT);
-        } catch (Exception e) {
-            log("exception: " + e.toString());
-        }
+        // disconnect from the current AP
+        assertTrue("failed to disconnect from AP", disconnectAP());
 
-        // Disconnect from the current AP
-        log("disconnect from the AP");
-        if (!disconnectAP()) {
-            log("failed to disconnect from " + mTestAccessPoint);
-        }
-
+        // below check disbabled since we have bug in what ConnectivityManager returns
         // Verify the connectivity state for Wifi is DISCONNECTED
-        assertTrue(waitForNetworkState(ConnectivityManager.TYPE_WIFI,
-                State.DISCONNECTED, LONG_TIMEOUT));
+//        assertTrue(waitForNetworkState(ConnectivityManager.TYPE_WIFI,
+//                State.DISCONNECTED, LONG_TIMEOUT));
 
-        if (!disableWifi()) {
-            log("disable Wifi failed");
-            return;
-        }
-        assertTrue(waitForWifiState(WifiManager.WIFI_STATE_DISABLED, LONG_TIMEOUT));
+        // disable WiFi
+        assertTrue("failed to disable wifi", disableWifi());
+        assertTrue("wifi state not disabled", waitForWifiState(
+                WifiManager.WIFI_STATE_DISABLED, LONG_TIMEOUT));
     }
 }
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 183f2a9..eb75b0d 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiAssociationTest.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiAssociationTest.java
@@ -16,24 +16,22 @@
 
 package com.android.connectivitymanagertest.functional;
 
-import com.android.connectivitymanagertest.ConnectivityManagerTestBase;
-import com.android.connectivitymanagertest.WifiAssociationTestRunner;
-
-import android.content.Context;
-import android.os.Bundle;
-import android.net.wifi.WifiInfo;
-import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiConfiguration.KeyMgmt;
-import android.net.wifi.WifiConfiguration.AuthAlgorithm;
-import android.net.wifi.WifiConfiguration.GroupCipher;
-import android.net.wifi.WifiConfiguration.PairwiseCipher;
-import android.net.wifi.WifiConfiguration.Protocol;
-import android.net.wifi.WifiManager;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo.State;
+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;
+
 /**
  * Test Wi-Fi connection with different configuration
  * To run this tests:
@@ -41,8 +39,7 @@
  * -e security-type [OPEN|WEP64|WEP128|WPA_TKIP|WPA2_AES] -e frequency-band [2.4|5.0|auto]
  * -w com.android.connectivitymanagertest/.WifiAssociationTestRunner"
  */
-public class WifiAssociationTest
-        extends ConnectivityManagerTestBase {
+public class WifiAssociationTest extends ConnectivityManagerTestBase {
     private static final String TAG = "WifiAssociationTest";
     private String mSsid = null;
     private String mPassword = null;
@@ -55,67 +52,37 @@
     }
 
     @Override
-    public void setUp() throws Exception {
+    protected void setUp() throws Exception {
         super.setUp();
-        WifiAssociationTestRunner mRunner = (WifiAssociationTestRunner)getInstrumentation();
-        Bundle arguments = mRunner.getArguments();
+        WifiAssociationTestRunner runner = (WifiAssociationTestRunner)getInstrumentation();
+        Bundle arguments = runner.getArguments();
         mSecurityType = arguments.getString("security-type");
         mSsid = arguments.getString("ssid");
         mPassword = arguments.getString("password");
         mFrequencyBand = arguments.getString("frequency-band");
-        mBand = mRunner.mBand;
-        assertNotNull("Security type is empty", mSecurityType);
-        assertNotNull("Ssid is empty", mSsid);
+        mBand = runner.mBand;
+        assertNotNull("security type is empty", mSecurityType);
+        assertNotNull("ssid is empty", mSsid);
         validateFrequencyBand();
-        // enable Wifi and verify wpa_supplicant is started
-        assertTrue("enable Wifi failed", enableWifi());
-        sleep(2 * SHORT_TIMEOUT, "interrupted while waiting for WPA_SUPPLICANT to start");
-        WifiInfo mConnection = mWifiManager.getConnectionInfo();
-        assertNotNull(mConnection);
-        assertTrue("wpa_supplicant is not started ", mWifiManager.pingSupplicant());
-    }
 
-    @Override
-    public void tearDown() throws Exception {
-        super.tearDown();
+        // 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 validateFrequencyBand() {
         if (mFrequencyBand != null) {
             int currentFreq = mWifiManager.getFrequencyBand();
             Log.v(TAG, "read frequency band: " + currentFreq);
-            assertTrue("device frequency band is not set successfully", (mBand == currentFreq));
+            assertEquals("specified frequency band does not match operational band of WifiManager",
+                    currentFreq, mBand);
          }
     }
 
-    /**
-     * Connect to the provided Wi-Fi network
-     * @param config is the network configuration
-     * @return true if the connection is successful.
-     */
-    private void connectToWifi(WifiConfiguration config) {
-        // step 1: connect to the test access point
-        assertTrue("failed to associate with " + config.SSID,
-                connectToWifiWithConfiguration(config));
-
-        // step 2: verify Wifi state and network state;
-        assertTrue("failed to connect with " + config.SSID,
-                waitForNetworkState(ConnectivityManager.TYPE_WIFI,
-                State.CONNECTED, WIFI_CONNECTION_TIMEOUT));
-
-        // step 3: verify the current connected network is the given SSID
-        assertNotNull("Wifi connection returns null", mWifiManager.getConnectionInfo());
-        assertTrue(config.SSID.contains(mWifiManager.getConnectionInfo().getSSID()));
-    }
-
-    private void sleep(long sometime, String errorMsg) {
-        try {
-            Thread.sleep(sometime);
-        } catch (InterruptedException e) {
-            fail(errorMsg);
-        }
-    }
-
     private void log(String message) {
         Log.v(TAG, message);
     }
@@ -133,62 +100,57 @@
                 config.allowedKeyManagement.set(KeyMgmt.NONE);
                 break;
             case WEP64:
-                // always use hex pair for WEP-40
-                assertTrue("not a WEP64 security type?", mPassword.length() == 10);
+                assertNotNull("password is empty", mPassword);
                 config.allowedKeyManagement.set(KeyMgmt.NONE);
                 config.allowedAuthAlgorithms.set(AuthAlgorithm.OPEN);
                 config.allowedAuthAlgorithms.set(AuthAlgorithm.SHARED);
                 config.allowedGroupCiphers.set(GroupCipher.WEP40);
                 if (mPassword != null) {
-                    int length = mPassword.length();
-                    // WEP-40
-                    if (mPassword.matches("[0-9A-Fa-f]*")) {
+                    // always use hex pair for WEP-40
+                    if (isHex(mPassword, 10)) {
                         config.wepKeys[0] = mPassword;
                     } else {
-                        fail("Please type hex pair for the password");
+                        fail("password should be 10-character hex");
                     }
                 }
                 break;
             case WEP128:
                 assertNotNull("password is empty", mPassword);
-                // always use hex pair for WEP-104
-                assertTrue("not a WEP128 security type?", mPassword.length() == 26);
                 config.allowedKeyManagement.set(KeyMgmt.NONE);
                 config.allowedAuthAlgorithms.set(AuthAlgorithm.OPEN);
                 config.allowedAuthAlgorithms.set(AuthAlgorithm.SHARED);
                 config.allowedGroupCiphers.set(GroupCipher.WEP104);
                 if (mPassword != null) {
-                    int length = mPassword.length();
-                    // WEP-40
-                    if (mPassword.matches("[0-9A-Fa-f]*")) {
+                    // always use hex pair for WEP-104
+                    if (isHex(mPassword, 26)) {
                         config.wepKeys[0] = mPassword;
                     } else {
-                        fail("Please type hex pair for the password");
+                        fail("password should be 26-character hex");
                     }
                 }
                 break;
             case WPA_TKIP:
-                assertNotNull("missing password", mPassword);
+                assertNotNull("password is empty", mPassword);
                 config.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
                 config.allowedAuthAlgorithms.set(AuthAlgorithm.OPEN);
                 config.allowedProtocols.set(Protocol.WPA);
                 config.allowedPairwiseCiphers.set(PairwiseCipher.TKIP);
                 config.allowedGroupCiphers.set(GroupCipher.TKIP);
-                if (mPassword.matches("[0-9A-Fa-f]{64}")) {
+                if (isHex(mPassword, 64)) {
                     config.preSharedKey = mPassword;
                 } else {
                     config.preSharedKey = '"' + mPassword + '"';
                 }
                 break;
             case WPA2_AES:
-                assertNotNull("missing password", mPassword);
+                assertNotNull("password is empty", mPassword);
                 config.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
                 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 (mPassword.matches("[0-9A-Fa-f]{64}")) {
+                if (isHex(mPassword, 64)) {
                     config.preSharedKey = mPassword;
                 } else {
                     config.preSharedKey = '"' + mPassword + '"';
@@ -200,5 +162,7 @@
         }
         Log.v(TAG, "network config: " + 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 ad73ee1..740ffb8 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java
@@ -16,21 +16,17 @@
 
 package com.android.connectivitymanagertest.functional;
 
-import com.android.connectivitymanagertest.ConnectivityManagerTestBase;
-import com.android.connectivitymanagertest.ConnectivityManagerTestRunner;
-
-import android.content.Context;
-import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiInfo;
-import android.net.wifi.WifiManager;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo.State;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiInfo;
 import android.test.suitebuilder.annotation.LargeTest;
 import android.util.Log;
 
+import com.android.connectivitymanagertest.ConnectivityManagerTestBase;
+
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Set;
 
 /**
  * Test Wi-Fi connection with different configuration
@@ -43,28 +39,29 @@
     extends ConnectivityManagerTestBase {
     private static final String TAG = "WifiConnectionTest";
     private static final boolean DEBUG = false;
-    private List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>();
+    private List<WifiConfiguration> mNetworks = new ArrayList<WifiConfiguration>();
 
     @Override
     public void setUp() throws Exception {
         super.setUp();
-        networks = loadNetworkConfigurations();
+        mNetworks = loadNetworkConfigurations();
         if (DEBUG) {
             printNetworkConfigurations();
         }
 
-        // enable Wifi and verify wpa_supplicant is started
+        // enable wifi and verify wpa_supplicant is started
         assertTrue("enable Wifi failed", enableWifi());
-        sleep(2 * SHORT_TIMEOUT, "interrupted while waiting for WPA_SUPPLICANT to start");
-        WifiInfo mConnection = mWifiManager.getConnectionInfo();
-        assertNotNull(mConnection);
-        assertTrue("wpa_supplicant is not started ", mWifiManager.pingSupplicant());
+        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: " + networks.size());
-        for (WifiConfiguration config : networks) {
+        log("number of access points: " + mNetworks.size());
+        for (WifiConfiguration config : mNetworks) {
             log(config.toString());
         }
     }
@@ -75,51 +72,25 @@
         super.tearDown();
     }
 
-    /**
-     * Connect to the provided Wi-Fi network
-     * @param config is the network configuration
-     * @return true if the connection is successful.
-     */
-    private void connectToWifi(WifiConfiguration config) {
-        // step 1: connect to the test access point
-        assertTrue("failed to connect to " + config.SSID,
-                connectToWifiWithConfiguration(config));
-
-        // step 2: verify Wifi state and network state;
-        assertTrue(waitForNetworkState(ConnectivityManager.TYPE_WIFI,
-                State.CONNECTED, WIFI_CONNECTION_TIMEOUT));
-
-        // step 3: verify the current connected network is the given SSID
-        assertNotNull("Wifi connection returns null", mWifiManager.getConnectionInfo());
-        if (DEBUG) {
-            log("config.SSID = " + config.SSID);
-            log("mWifiManager.getConnectionInfo.getSSID()" +
-                    mWifiManager.getConnectionInfo().getSSID());
-        }
-        assertTrue(config.SSID.contains(mWifiManager.getConnectionInfo().getSSID()));
-    }
-
-    private void sleep(long sometime, String errorMsg) {
-        try {
-            Thread.sleep(sometime);
-        } catch (InterruptedException e) {
-            fail(errorMsg);
-        }
-    }
-
     private void log(String message) {
         Log.v(TAG, message);
     }
 
     @LargeTest
     public void testWifiConnections() {
-        for (int i = 0; i < networks.size(); i++) {
-            String ssid = networks.get(i).SSID;
+        for (int i = 0; i < mNetworks.size(); i++) {
+            String ssid = mNetworks.get(i).SSID;
             log("-- START Wi-Fi connection test to : " + ssid + " --");
-            connectToWifi(networks.get(i));
-            // wait for 2 minutes between wifi stop and start
-            sleep(WIFI_STOP_START_INTERVAL, "interruped while connected to wifi");
+            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
+            }
         }
     }
 }
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 790ca38..aead65b 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiApStress.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiApStress.java
@@ -33,7 +33,7 @@
 import java.io.FileWriter;
 
 /**
- * Stress the wifi driver as access point.
+ * Stress test setting up device as wifi hotspot
  */
 public class WifiApStress
     extends ConnectivityManagerTestBase {
@@ -41,27 +41,28 @@
     private static String NETWORK_ID = "AndroidAPTest";
     private static String PASSWD = "androidwifi";
     private final static String OUTPUT_FILE = "WifiStressTestOutput.txt";
-    private int iterations;
+    private int mTotalIterations;
     private BufferedWriter mOutputWriter = null;
     private int mLastIteration = 0;
     private boolean mWifiOnlyFlag;
 
     @Override
-    public void setUp() throws Exception {
+    protected void setUp() throws Exception {
         super.setUp();
         ConnectivityManagerStressTestRunner mRunner =
             (ConnectivityManagerStressTestRunner)getInstrumentation();
-        iterations = mRunner.mSoftapIterations;
-        mWifiOnlyFlag = mRunner.mWifiOnlyFlag;
+        mTotalIterations = mRunner.getSoftApInterations();
+        mWifiOnlyFlag = mRunner.isWifiOnly();
         turnScreenOn();
     }
 
     @Override
-    public void tearDown() throws Exception {
+    protected void tearDown() throws Exception {
         // write the total number of iterations into output file
         mOutputWriter = new BufferedWriter(new FileWriter(new File(
                 Environment.getExternalStorageDirectory(), OUTPUT_FILE)));
-        mOutputWriter.write(String.format("iteration %d out of %d\n", mLastIteration, iterations));
+        mOutputWriter.write(String.format("iteration %d out of %d\n",
+                mLastIteration + 1, mTotalIterations));
         mOutputWriter.flush();
         mOutputWriter.close();
         super.tearDown();
@@ -79,40 +80,44 @@
         config.allowedAuthAlgorithms.set(AuthAlgorithm.OPEN);
         config.preSharedKey = PASSWD;
 
-        // If Wifi is enabled, disable it
+        // if wifiap enabled, disable it
+        assertTrue("failed to disable wifi hotspot",
+                mWifiManager.setWifiApEnabled(config, false));
+        assertTrue("wifi hotspot not enabled", waitForWifiApState(
+                WifiManager.WIFI_AP_STATE_DISABLED, 2 * LONG_TIMEOUT));
+
+        // if Wifi is enabled, disable it
         if (mWifiManager.isWifiEnabled()) {
-            disableWifi();
+            assertTrue("failed to disable wifi", disableWifi());
+            // wait for the wifi state to be DISABLED
+            assertTrue("wifi state not disabled", waitForWifiState(
+                    WifiManager.WIFI_STATE_DISABLED, LONG_TIMEOUT));
         }
         int i;
-        for (i = 0; i < iterations; i++) {
+        for (i = 0; i < mTotalIterations; i++) {
             Log.v(TAG, "iteration: " + i);
             mLastIteration = i;
             // enable Wifi tethering
-            assertTrue(mWifiManager.setWifiApEnabled(config, true));
-            // Wait for wifi ap state to be ENABLED
-            assertTrue(waitForWifiAPState(WifiManager.WIFI_AP_STATE_ENABLED, 2 * LONG_TIMEOUT));
-            // Wait for wifi tethering result
-            assertEquals(SUCCESS, waitForTetherStateChange(2 * SHORT_TIMEOUT));
-            // Allow the wifi tethering to be enabled for 10 seconds
+            assertTrue("failed to enable wifi hotspot",
+                    mWifiManager.setWifiApEnabled(config, true));
+            // wait for wifi ap state to be ENABLED
+            assertTrue("wifi hotspot not enabled", waitForWifiApState(
+                    WifiManager.WIFI_AP_STATE_ENABLED, 2 * LONG_TIMEOUT));
+            // wait for wifi tethering result
+            assertTrue("tether state not changed", waitForTetherStateChange(LONG_TIMEOUT));
+            // allow the wifi tethering to be enabled for 10 seconds
             try {
                 Thread.sleep(2 * SHORT_TIMEOUT);
             } catch (Exception e) {
-                fail("thread in sleep is interrupted");
+                // ignore
             }
             assertTrue("no uplink data connection after Wi-Fi tethering", pingTest(null));
-            // Disable soft AP
-            assertTrue(mWifiManager.setWifiApEnabled(config, false));
-            // Wait for 30 seconds until Wi-Fi tethering is stopped
-            try {
-                Thread.sleep(30 * 1000);
-                Log.v(TAG, "wait for Wi-Fi tethering to be disabled.");
-            } catch (Exception e) {
-                fail("thread in sleep is interrupted");
-            }
-            assertFalse("Wi-Fi AP disable failed", mWifiManager.isWifiApEnabled());
-        }
-        if (i == iterations) {
-            mLastIteration = iterations;
+            // disable wifi hotspot
+            assertTrue("failed to disable wifi hotspot",
+                    mWifiManager.setWifiApEnabled(config, false));
+            assertTrue("wifi hotspot not enabled", waitForWifiApState(
+                    WifiManager.WIFI_AP_STATE_DISABLED, 2 * LONG_TIMEOUT));
+            assertFalse("wifi hotspot still enabled", mWifiManager.isWifiApEnabled());
         }
     }
 
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 08618d6..859c30c 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java
@@ -16,20 +16,22 @@
 
 package com.android.connectivitymanagertest.stress;
 
+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.KeyMgmt;
 import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiConfiguration.KeyMgmt;
 import android.net.wifi.WifiManager;
+import android.os.Bundle;
 import android.os.Environment;
 import android.os.PowerManager;
 import android.os.SystemClock;
 import android.provider.Settings;
-import android.view.KeyEvent;
 import android.test.suitebuilder.annotation.LargeTest;
 import android.util.Log;
 
@@ -49,15 +51,14 @@
  * adb shell am instrument -e class com.android.connectivitymanagertest.stress.WifiStressTest
  *                  -w com.android.connectivitymanagertest/.ConnectivityManagerStressTestRunner
  */
-public class WifiStressTest
-        extends ConnectivityManagerTestBase {
+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
      */
-    private final static long WIFI_IDLE_MS = 60 * 1000;
+    private final static long WIFI_IDLE_MS = 15 * 1000;
 
     /**
      * Delay after issuing wifi shutdown.
@@ -69,7 +70,7 @@
 
     private final static String OUTPUT_FILE = "WifiStressTestOutput.txt";
     private int mReconnectIterations;
-    private int mWifiSleepTime;
+    private long mWifiSleepTime;
     private int mScanIterations;
     private String mSsid;
     private String mPassword;
@@ -78,16 +79,16 @@
     private boolean mWifiOnlyFlag;
 
     @Override
-    public void setUp() throws Exception {
+    protected void setUp() throws Exception {
         super.setUp();
 
         mRunner = (ConnectivityManagerStressTestRunner) getInstrumentation();
-        mReconnectIterations = mRunner.mReconnectIterations;
-        mSsid = mRunner.mReconnectSsid;
-        mPassword = mRunner.mReconnectPassword;
-        mScanIterations = mRunner.mScanIterations;
-        mWifiSleepTime = mRunner.mSleepTime;
-        mWifiOnlyFlag = mRunner.mWifiOnlyFlag;
+        mReconnectIterations = mRunner.getReconnectIterations();
+        mSsid = mRunner.getReconnectSsid();
+        mPassword = mRunner.getReconnectPassword();
+        mScanIterations = mRunner.getScanIterations();
+        mWifiSleepTime = mRunner.getSleepTime();
+        mWifiOnlyFlag = mRunner.isWifiOnly();
         log(String.format("mReconnectIterations(%d), mSsid(%s), mPassword(%s),"
             + "mScanIterations(%d), mWifiSleepTime(%d)", mReconnectIterations, mSsid,
             mPassword, mScanIterations, mWifiSleepTime));
@@ -105,7 +106,7 @@
     }
 
     @Override
-    public void tearDown() throws Exception {
+    protected void tearDown() throws Exception {
         log("tearDown()");
         if (mOutputWriter != null) {
             mOutputWriter.close();
@@ -145,109 +146,90 @@
      */
     @LargeTest
     public void testWifiScanning() {
-        int scanTimeSum = 0;
-        int i;
+        long scanTimeSum = 0, i, averageScanTime = -1;
         int ssidAppearInScanResultsCount = 0; // count times of given ssid appear in scan results.
-        for (i = 0; i < mScanIterations; i++) {
+        for (i = 1; i <= mScanIterations; i++) {
             log("testWifiScanning: iteration: " + i);
-            int averageScanTime = 0;
-            if (i > 0) {
-                averageScanTime = scanTimeSum/i;
-            }
-            writeOutput(String.format("iteration %d out of %d",
-                    i, mScanIterations));
+            averageScanTime = scanTimeSum / i;
+            writeOutput(String.format("iteration %d out of %d", i, mScanIterations));
             writeOutput(String.format("average scanning time is %d", averageScanTime));
             writeOutput(String.format("ssid appear %d out of %d scan iterations",
                     ssidAppearInScanResultsCount, i));
-            long startTime = SystemClock.uptimeMillis();
-            scanResultAvailable = false;
-            assertTrue("start scan failed", mWifiManager.startScan());
-            while (true) {
-                if ((SystemClock.uptimeMillis() - startTime) >
-                WIFI_SCAN_TIMEOUT) {
-                    fail("Wifi scanning takes more than " + WIFI_SCAN_TIMEOUT + " ms");
+            List<ScanResult> scanResultLocal = null;
+            // wait for a scan result
+            long start = 0;
+            synchronized (mWifiScanResultLock) {
+                start = SystemClock.uptimeMillis();
+                assertTrue("start scan failed", mWifiManager.startScan());
+                try {
+                    mWifiScanResultLock.wait(WAIT_FOR_SCAN_RESULT);
+                } catch (InterruptedException e) {
+                    // ignore
                 }
-                synchronized(this) {
-                    try {
-                        wait(WAIT_FOR_SCAN_RESULT);
-                    } catch (InterruptedException e) {
-                        e.printStackTrace();
-                    }
-                    if (scanResultAvailable) {
-                        long scanTime = (SystemClock.uptimeMillis() - startTime);
-                        scanTimeSum += scanTime;
-                        break;
-                    }
-                }
+                scanTimeSum += SystemClock.uptimeMillis() - start;
+                // save the scan result while in lock
+                scanResultLocal = mLastScanResult;
             }
-            if ((mWifiManager.getScanResults() == null) ||
-                    (mWifiManager.getScanResults().size() <= 0)) {
+            if (scanResultLocal == null || scanResultLocal.isEmpty()) {
                 fail("Scan results are empty ");
             }
-
-            List<ScanResult> netList = mWifiManager.getScanResults();
-            if (netList != null) {
-                log("size of scan result list: " + netList.size());
-                for (int s = 0; s < netList.size(); s++) {
-                    ScanResult sr= netList.get(s);
-                    log(String.format("scan result for %s is: %s", sr.SSID, sr.toString()));
-                    log(String.format("signal level for %s is %d ", sr.SSID, sr.level));
-                    if (sr.SSID.equals(mSsid)) {
-                        ssidAppearInScanResultsCount += 1;
-                        log("Number of times " + mSsid + " appear in the scan list: " +
-                                ssidAppearInScanResultsCount);
-                        break;
-                    }
+            log("size of scan result list: " + scanResultLocal.size());
+            for (ScanResult sr : scanResultLocal) {
+                log(String.format("scan result: " + sr.toString()));
+                if (mSsid.equals(sr.SSID)) {
+                    ssidAppearInScanResultsCount += 1;
+                    break;
                 }
             }
         }
-        if (i == mScanIterations) {
-            writeOutput(String.format("iteration %d out of %d",
-                    i, mScanIterations));
-            writeOutput(String.format("average scanning time is %d", scanTimeSum/mScanIterations));
+        Bundle result = new Bundle();
+        result.putLong("actual-iterations", i - 1);
+        result.putLong("avg-scan-time", averageScanTime);
+        result.putInt("ap-discovered", ssidAppearInScanResultsCount);
+        getInstrumentation().sendStatus(Activity.RESULT_FIRST_USER, result);
+        if (i == mScanIterations + 1) {
+            writeOutput(String.format("iteration %d out of %d", i, mScanIterations));
+            writeOutput(String.format("average scanning time is %d", scanTimeSum / (i - 1)));
             writeOutput(String.format("ssid appear %d out of %d scan iterations",
-                    ssidAppearInScanResultsCount, mScanIterations));
+                    ssidAppearInScanResultsCount, i));
         }
     }
 
     // Stress Wifi reconnection to secure net after sleep
     @LargeTest
     public void testWifiReconnectionAfterSleep() {
-        int value = Settings.Global.getInt(mRunner.getContext().getContentResolver(),
-                Settings.Global.WIFI_SLEEP_POLICY, -1);
-        log("wifi sleep policy is: " + value);
-        if (value != Settings.Global.WIFI_SLEEP_POLICY_DEFAULT) {
-            Settings.Global.putInt(mRunner.getContext().getContentResolver(),
-                    Settings.Global.WIFI_SLEEP_POLICY, Settings.Global.WIFI_SLEEP_POLICY_DEFAULT);
-            log("set wifi sleep policy to default value");
-        }
+        // set wifi sleep policy to never on while in sleep
+        Settings.Global.putInt(mRunner.getContext().getContentResolver(),
+                Settings.Global.WIFI_SLEEP_POLICY, Settings.Global.WIFI_SLEEP_POLICY_NEVER);
+        // set idle timeout for wifi to 15s
         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;
-        config.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
-        if (mPassword.matches("[0-9A-Fa-f]{64}")) {
-            config.preSharedKey = mPassword;
+        if (mPassword != null) {
+            config.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
+            if (isHex(mPassword, 64)) {
+                config.preSharedKey = mPassword;
+            } else {
+                config.preSharedKey = '"' + mPassword + '"';
+            }
         } else {
-            config.preSharedKey = '"' + mPassword + '"';
+            config.allowedKeyManagement.set(KeyMgmt.NONE);
         }
         config.setIpAssignment(IpAssignment.DHCP);
         config.setProxySettings(ProxySettings.NONE);
 
         assertTrue("Failed to connect to Wi-Fi network: " + mSsid,
                 connectToWifiWithConfiguration(config));
-        assertTrue(waitForWifiState(WifiManager.WIFI_STATE_ENABLED,
-                SHORT_TIMEOUT));
-        assertTrue(waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
-                WIFI_CONNECTION_TIMEOUT));
+        assertTrue("wifi not connected", waitForNetworkState(ConnectivityManager.TYPE_WIFI,
+                State.CONNECTED, WIFI_CONNECTION_TIMEOUT));
         // Run ping test to verify the data connection
         assertTrue("Wi-Fi is connected, but no data connection.", pingTest(null));
 
-        int i;
-        long sum = 0;
-        for (i = 0; i < mReconnectIterations; i++) {
+        long i, sum = 0, avgReconnectTime = 0;
+        for (i = 1; i <= mReconnectIterations; i++) {
             // 1. Put device into sleep mode
             // 2. Wait for the device to sleep for sometime, verify wi-fi is off and mobile is on.
             // 3. Maintain the sleep mode for some time,
@@ -261,53 +243,61 @@
             long start = SystemClock.uptimeMillis();
             PowerManager pm =
                 (PowerManager)mRunner.getContext().getSystemService(Context.POWER_SERVICE);
-            while (pm.isScreenOn() && ((SystemClock.uptimeMillis() - start) < SCREEN_OFF_TIMER)) {
-                sleep(100, "wait for screen off");
+            while (pm.isInteractive() &&
+                    ((SystemClock.uptimeMillis() - start) < SCREEN_OFF_TIMER)) {
+                SystemClock.sleep(100);
             }
-            assertFalse(pm.isScreenOn());
-            sleep(WIFI_IDLE_MS + WIFI_SHUTDOWN_DELAY, "Interruped while wait for wifi to be idle");
-            assertTrue("Wait for Wi-Fi to idle timeout",
-                    waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.DISCONNECTED,
-                    6 * SHORT_TIMEOUT));
-            if (!mWifiOnlyFlag) {
+            assertFalse("screen still on", pm.isInteractive());
+            // wait for WiFi timeout
+            SystemClock.sleep(WIFI_IDLE_MS + WIFI_SHUTDOWN_DELAY);
+            // below check temporarily disabled due to bug in ConnectivityManager return
+//            assertTrue("Wait for Wi-Fi to idle timeout",
+//                    waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.DISCONNECTED,
+//                    6 * SHORT_TIMEOUT));
+            if (mWifiOnlyFlag) {
+                assertTrue("expected wifi disconnect, still has active connection",
+                        waitUntilNoActiveNetworkConnection(2 * LONG_TIMEOUT));
+            } else {
                 // use long timeout as the pppd startup may take several retries.
-                assertTrue("Wait for cellular connection timeout",
+                assertTrue("no fallback on mobile or wifi didn't disconnect",
                         waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.CONNECTED,
                         2 * LONG_TIMEOUT));
             }
-            sleep(mWifiSleepTime, "Interrupted while device is in sleep mode");
-            // Verify the wi-fi is still off and data connection is on
-            assertEquals("Wi-Fi is reconnected", State.DISCONNECTED,
-                    mCM.getNetworkInfo(ConnectivityManager.TYPE_WIFI).getState());
-
-            if (!mWifiOnlyFlag) {
-                assertEquals("Cellular connection is down", State.CONNECTED,
-                             mCM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE).getState());
-                assertTrue("Mobile is connected, but no data connection.", pingTest(null));
+            SystemClock.sleep(mWifiSleepTime);
+            // verify the wi-fi is still off and either we have no connectivity or fallback on mobile
+            if (mWifiOnlyFlag) {
+                NetworkInfo ni = mCm.getActiveNetworkInfo();
+                if (ni != null) {
+                    Log.e(TAG, "has active network while in wifi sleep: " + ni.toString());
+                    fail("active network detected");
+                }
+            } else {
+                assertEquals("mobile not connected", State.CONNECTED,
+                        mCm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE).getState());
+                assertTrue("no connectivity over mobile", pingTest(null));
             }
 
             // Turn screen on again
             turnScreenOn();
-            // Wait for 2 seconds for the lock screen
-            sleep(2 * 1000, "wait 2 seconds for lock screen");
-            // Disable lock screen by inject menu key event
-            mRunner.sendKeyDownUpSync(KeyEvent.KEYCODE_MENU);
-
             // Measure the time for Wi-Fi to get connected
             long startTime = SystemClock.uptimeMillis();
-            assertTrue("Wait for Wi-Fi enable timeout after wake up",
-                    waitForWifiState(WifiManager.WIFI_STATE_ENABLED,
-                    SHORT_TIMEOUT));
-            assertTrue("Wait for Wi-Fi connection timeout after wake up",
+            assertTrue("screen on: wifi not enabled before timeout",
+                    waitForWifiState(WifiManager.WIFI_STATE_ENABLED, SHORT_TIMEOUT));
+            assertTrue("screen on: wifi not connected before timeout",
                     waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
-                    WIFI_CONNECTION_TIMEOUT));
+                    LONG_TIMEOUT));
             long connectionTime = SystemClock.uptimeMillis() - startTime;
             sum += connectionTime;
-            log("average reconnection time is: " + sum/(i+1));
+            avgReconnectTime = sum / i;
+            log("average reconnection time is: " + avgReconnectTime);
 
             assertTrue("Reconnect to Wi-Fi network, but no data connection.", pingTest(null));
         }
-        if (i == mReconnectIterations) {
+        Bundle result = new Bundle();
+        result.putLong("actual-iterations", i - 1);
+        result.putLong("avg-reconnect-time", avgReconnectTime);
+        getInstrumentation().sendStatus(Activity.RESULT_FIRST_USER, result);
+        if (i == mReconnectIterations + 1) {
             writeOutput(String.format("iteration %d out of %d",
                     i, mReconnectIterations));
         }
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiClientTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiClientTest.java
index 7a9bc78..5c2f388 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiClientTest.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiClientTest.java
@@ -38,7 +38,6 @@
 public class WifiClientTest extends AndroidTestCase {
 
     private WifiManager mWifiManager;
-    private final String TAG = "WifiClientTest";
 
     //10s delay for turning on wifi
     private static final int DELAY = 10000;
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index 00c92fa..4dcbc40 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -17,11 +17,15 @@
 import android.animation.Animator;
 import android.animation.AnimatorInflater;
 import android.animation.ValueAnimator;
+import android.annotation.NonNull;
+import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
+import android.graphics.Outline;
+import android.graphics.PorterDuff;
 import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -129,7 +133,6 @@
 
     private final AnimatedVectorDrawableState mAnimatedVectorState;
 
-
     public AnimatedVectorDrawable() {
         mAnimatedVectorState = new AnimatedVectorDrawableState(
                 new AnimatedVectorDrawableState(null));
@@ -163,6 +166,16 @@
     }
 
     @Override
+    protected boolean onStateChange(int[] state) {
+        return mAnimatedVectorState.mVectorDrawable.setState(state);
+    }
+
+    @Override
+    protected boolean onLevelChange(int level) {
+        return mAnimatedVectorState.mVectorDrawable.setLevel(level);
+    }
+
+    @Override
     public int getAlpha() {
         return mAnimatedVectorState.mVectorDrawable.getAlpha();
     }
@@ -178,6 +191,43 @@
     }
 
     @Override
+    public void setTintList(ColorStateList tint) {
+        mAnimatedVectorState.mVectorDrawable.setTintList(tint);
+    }
+
+    @Override
+    public void setHotspot(float x, float y) {
+        mAnimatedVectorState.mVectorDrawable.setHotspot(x, y);
+    }
+
+    @Override
+    public void setHotspotBounds(int left, int top, int right, int bottom) {
+        mAnimatedVectorState.mVectorDrawable.setHotspotBounds(left, top, right, bottom);
+    }
+
+    @Override
+    public void setTintMode(PorterDuff.Mode tintMode) {
+        mAnimatedVectorState.mVectorDrawable.setTintMode(tintMode);
+    }
+
+    @Override
+    public boolean setVisible(boolean visible, boolean restart) {
+        mAnimatedVectorState.mVectorDrawable.setVisible(visible, restart);
+        return super.setVisible(visible, restart);
+    }
+
+    /** {@hide} */
+    @Override
+    public void setLayoutDirection(int layoutDirection) {
+        mAnimatedVectorState.mVectorDrawable.setLayoutDirection(layoutDirection);
+    }
+
+    @Override
+    public boolean isStateful() {
+        return mAnimatedVectorState.mVectorDrawable.isStateful();
+    }
+
+    @Override
     public int getOpacity() {
         return mAnimatedVectorState.mVectorDrawable.getOpacity();
     }
@@ -193,6 +243,11 @@
     }
 
     @Override
+    public void getOutline(@NonNull Outline outline) {
+        mAnimatedVectorState.mVectorDrawable.getOutline(outline);
+    }
+
+    @Override
     public void inflate(Resources res, XmlPullParser parser, AttributeSet attrs, Theme theme)
             throws XmlPullParserException, IOException {
 
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index e673b0d..5922135 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -244,6 +244,29 @@
     thread.renderState().invokeFunctor(functor, mode, NULL);
 }
 
+void CanvasContext::buildLayer(RenderNode* node) {
+    ATRACE_CALL();
+    if (!mEglManager.hasEglContext() || !mCanvas) {
+        return;
+    }
+    requireGlContext();
+    // buildLayer() will leave the tree in an unknown state, so we must stop drawing
+    stopDrawing();
+
+    TreeInfo info(TreeInfo::MODE_FULL, mRenderThread.renderState());
+    info.frameTimeMs = mRenderThread.timeLord().frameTimeMs();
+    info.damageAccumulator = &mDamageAccumulator;
+    info.renderer = mCanvas;
+    node->prepareTree(info);
+    SkRect ignore;
+    mDamageAccumulator.finish(&ignore);
+    // Tickle the GENERIC property on node to mark it as dirty for damaging
+    // purposes when the frame is actually drawn
+    node->setPropertyFieldsDirty(RenderNode::GENERIC);
+
+    mCanvas->flushLayerUpdates();
+}
+
 bool CanvasContext::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
     requireGlContext();
     layer->apply();
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 2a01027..0cbed6f 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -65,6 +65,7 @@
     // IFrameCallback, Chroreographer-driven frame callback entry point
     virtual void doFrame();
 
+    void buildLayer(RenderNode* node);
     bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
 
     void destroyHardwareResources();
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index d9b96f6c..405ce24 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -284,6 +284,18 @@
     return layer;
 }
 
+CREATE_BRIDGE2(buildLayer, CanvasContext* context, RenderNode* node) {
+    args->context->buildLayer(args->node);
+    return NULL;
+}
+
+void RenderProxy::buildLayer(RenderNode* node) {
+    SETUP_TASK(buildLayer);
+    args->context = mContext;
+    args->node = node;
+    postAndWait(task);
+}
+
 CREATE_BRIDGE3(copyLayerInto, CanvasContext* context, DeferredLayerUpdater* layer,
         SkBitmap* bitmap) {
     bool success = args->context->copyLayerInto(args->layer, args->bitmap);
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 28d0173..eea3674 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -81,6 +81,7 @@
     static void enqueueDestroyLayer(Layer* layer);
     ANDROID_API DeferredLayerUpdater* createDisplayListLayer(int width, int height);
     ANDROID_API DeferredLayerUpdater* createTextureLayer();
+    ANDROID_API void buildLayer(RenderNode* node);
     ANDROID_API bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
     ANDROID_API void pushLayerUpdate(DeferredLayerUpdater* layer);
     ANDROID_API void cancelLayerUpdate(DeferredLayerUpdater* layer);
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 7cda961..6a4bf07 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -190,14 +190,6 @@
 
             final int mainType = mCurAudioRoutesInfo.mMainType;
 
-            boolean a2dpEnabled;
-            try {
-                a2dpEnabled = mAudioService.isBluetoothA2dpOn();
-            } catch (RemoteException e) {
-                Log.e(TAG, "Error querying Bluetooth A2DP state", e);
-                a2dpEnabled = false;
-            }
-
             if (!TextUtils.equals(newRoutes.mBluetoothName, mCurAudioRoutesInfo.mBluetoothName)) {
                 mCurAudioRoutesInfo.mBluetoothName = newRoutes.mBluetoothName;
                 if (mCurAudioRoutesInfo.mBluetoothName != null) {
@@ -220,6 +212,7 @@
             }
 
             if (mBluetoothA2dpRoute != null) {
+                final boolean a2dpEnabled = isBluetoothA2dpOn();
                 if (mainType != AudioRoutesInfo.MAIN_SPEAKER &&
                         mSelectedRoute == mBluetoothA2dpRoute && !a2dpEnabled) {
                     selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, mDefaultAudioVideo, false);
@@ -230,6 +223,15 @@
             }
         }
 
+        boolean isBluetoothA2dpOn() {
+            try {
+                return mAudioService.isBluetoothA2dpOn();
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error querying Bluetooth A2DP state", e);
+                return false;
+            }
+        }
+
         void updateDiscoveryRequest() {
             // What are we looking for today?
             int routeTypes = 0;
@@ -950,7 +952,7 @@
     static void selectDefaultRouteStatic() {
         // TODO: Be smarter about the route types here; this selects for all valid.
         if (sStatic.mSelectedRoute != sStatic.mBluetoothA2dpRoute
-                && sStatic.mBluetoothA2dpRoute != null) {
+                && sStatic.mBluetoothA2dpRoute != null && sStatic.isBluetoothA2dpOn()) {
             selectRouteStatic(ROUTE_TYPE_ANY, sStatic.mBluetoothA2dpRoute, false);
         } else {
             selectRouteStatic(ROUTE_TYPE_ANY, sStatic.mDefaultAudioVideo, false);
diff --git a/packages/PrintSpooler/AndroidManifest.xml b/packages/PrintSpooler/AndroidManifest.xml
index 055587a..540a2f3 100644
--- a/packages/PrintSpooler/AndroidManifest.xml
+++ b/packages/PrintSpooler/AndroidManifest.xml
@@ -45,8 +45,7 @@
         android:allowClearUserData="true"
         android:label="@string/app_label"
         android:allowBackup= "false"
-        android:supportsRtl="true"
-        android:icon="@drawable/ic_print">
+        android:supportsRtl="true">
 
         <service
             android:name=".model.PrintSpoolerService"
diff --git a/packages/PrintSpooler/res/drawable/ic_savetopdf.xml b/packages/PrintSpooler/res/drawable/ic_savetopdf.xml
deleted file mode 100644
index 60ed33a..0000000
--- a/packages/PrintSpooler/res/drawable/ic_savetopdf.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_menu_savetopdf"
-    android:tint="@color/promoted_action_background_color" />
diff --git a/packages/PrintSpooler/res/layout/print_activity_controls.xml b/packages/PrintSpooler/res/layout/print_activity_controls.xml
index 31bda7e..0629481 100644
--- a/packages/PrintSpooler/res/layout/print_activity_controls.xml
+++ b/packages/PrintSpooler/res/layout/print_activity_controls.xml
@@ -24,241 +24,215 @@
     android:elevation="@dimen/preview_controls_elevation"
     android:background="?android:attr/colorPrimary">
 
-        <LinearLayout
-         android:id="@+id/draggable_content"
-         android:layout_width="fill_parent"
-         android:layout_height="wrap_content"
-         android:orientation="vertical">
+    <LinearLayout
+        android:id="@+id/draggable_content"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
 
-         <com.android.printspooler.widget.PrintOptionsLayout
-             android:id="@+id/options_container"
-             android:layout_width="fill_parent"
-             android:layout_height="wrap_content"
-             printspooler:columnCount="@integer/print_option_column_count">
+        <com.android.printspooler.widget.PrintOptionsLayout
+            android:id="@+id/options_container"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            printspooler:columnCount="@integer/print_option_column_count">
 
-             <LinearLayout
-                 android:layout_width="wrap_content"
-                 android:layout_height="wrap_content"
-                 android:layout_marginStart="16dip"
-                 android:layout_marginEnd="16dip"
-                 android:orientation="vertical">
+            <LinearLayout
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="16dip"
+                android:layout_marginEnd="16dip"
+                android:orientation="vertical">
 
-                 <!-- Copies -->
+                <!-- Copies -->
 
-                 <TextView
-                     android:layout_width="wrap_content"
-                     android:layout_height="wrap_content"
-                     android:layout_marginTop="8dip"
-                     android:layout_marginStart="12dip"
-                     android:textAppearance="?android:attr/textAppearanceSmall"
-                     android:labelFor="@+id/copies_edittext"
-                     android:text="@string/label_copies">
-                 </TextView>
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="8dip"
+                    android:layout_marginStart="12dip"
+                    android:textAppearance="?android:attr/textAppearanceSmall"
+                    android:labelFor="@+id/copies_edittext"
+                    android:text="@string/label_copies">
+                </TextView>
 
-                 <view
-                     class="com.android.printspooler.widget.FirstFocusableEditText"
-                     android:id="@+id/copies_edittext"
-                     android:layout_width="fill_parent"
-                     android:layout_height="wrap_content"
-                     style="?android:attr/editTextStyle"
-                     android:singleLine="true"
-                     android:ellipsize="end"
-                     android:inputType="numberDecimal">
-                 </view>
+                <view
+                    class="com.android.printspooler.widget.FirstFocusableEditText"
+                    android:id="@+id/copies_edittext"
+                    android:layout_width="fill_parent"
+                    android:layout_height="wrap_content"
+                    style="?android:attr/editTextStyle"
+                    android:singleLine="true"
+                    android:ellipsize="end"
+                    android:inputType="numberDecimal">
+                </view>
 
-             </LinearLayout>
+            </LinearLayout>
 
-             <LinearLayout
-                 android:layout_width="wrap_content"
-                 android:layout_height="wrap_content"
-                 android:layout_marginStart="16dip"
-                 android:layout_marginEnd="16dip"
-                 android:orientation="vertical">
+            <LinearLayout
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="16dip"
+                android:layout_marginEnd="16dip"
+                android:orientation="vertical">
 
-                 <!-- Paper size -->
+                <!-- Paper size -->
 
-                 <TextView
-                     android:layout_width="wrap_content"
-                     android:layout_height="wrap_content"
-                     android:layout_marginTop="8dip"
-                     android:layout_marginStart="12dip"
-                     android:textAppearance="?android:attr/textAppearanceSmall"
-                     android:labelFor="@+id/paper_size_spinner"
-                     android:text="@string/label_paper_size">
-                 </TextView>
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="8dip"
+                    android:layout_marginStart="12dip"
+                    android:textAppearance="?android:attr/textAppearanceSmall"
+                    android:labelFor="@+id/paper_size_spinner"
+                    android:text="@string/label_paper_size">
+                </TextView>
 
-                 <Spinner
-                     android:id="@+id/paper_size_spinner"
-                     android:layout_width="fill_parent"
-                     android:layout_height="wrap_content">
-                 </Spinner>
+                <Spinner
+                    android:id="@+id/paper_size_spinner"
+                    android:layout_width="fill_parent"
+                    android:layout_height="wrap_content">
+                </Spinner>
 
-             </LinearLayout>
+            </LinearLayout>
 
-             <LinearLayout
-                 android:layout_width="wrap_content"
-                 android:layout_height="wrap_content"
-                 android:layout_marginStart="16dip"
-                 android:layout_marginEnd="16dip"
-                 android:orientation="vertical">
+            <LinearLayout
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="16dip"
+                android:layout_marginEnd="16dip"
+                android:orientation="vertical">
 
-                 <!-- Color -->
+                <!-- Color -->
 
-                 <TextView
-                     android:layout_width="wrap_content"
-                     android:layout_height="wrap_content"
-                     android:layout_marginTop="8dip"
-                     android:layout_marginStart="12dip"
-                     android:textAppearance="?android:attr/textAppearanceSmall"
-                     android:labelFor="@+id/color_spinner"
-                     android:text="@string/label_color">
-                 </TextView>
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="8dip"
+                    android:layout_marginStart="12dip"
+                    android:textAppearance="?android:attr/textAppearanceSmall"
+                    android:labelFor="@+id/color_spinner"
+                    android:text="@string/label_color">
+                </TextView>
 
-                 <Spinner
-                     android:id="@+id/color_spinner"
-                     android:layout_width="fill_parent"
-                     android:layout_height="wrap_content">
-                 </Spinner>
+                <Spinner
+                    android:id="@+id/color_spinner"
+                    android:layout_width="fill_parent"
+                    android:layout_height="wrap_content">
+                </Spinner>
 
-             </LinearLayout>
+            </LinearLayout>
 
-             <LinearLayout
-                 android:layout_width="wrap_content"
-                 android:layout_height="wrap_content"
-                 android:layout_marginStart="16dip"
-                 android:layout_marginEnd="16dip"
-                 android:orientation="vertical">
+            <LinearLayout
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="16dip"
+                android:layout_marginEnd="16dip"
+                android:orientation="vertical">
 
-                 <!-- Orientation -->
+                <!-- Orientation -->
 
-                 <TextView
-                     android:layout_width="wrap_content"
-                     android:layout_height="wrap_content"
-                     android:layout_marginTop="8dip"
-                     android:layout_marginStart="12dip"
-                     android:textAppearance="?android:attr/textAppearanceSmall"
-                     android:labelFor="@+id/orientation_spinner"
-                     android:text="@string/label_orientation">
-                 </TextView>
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="8dip"
+                    android:layout_marginStart="12dip"
+                    android:textAppearance="?android:attr/textAppearanceSmall"
+                    android:labelFor="@+id/orientation_spinner"
+                    android:text="@string/label_orientation">
+                </TextView>
 
-                 <Spinner
-                     android:id="@+id/orientation_spinner"
-                     android:layout_width="fill_parent"
-                     android:layout_height="wrap_content">
-                 </Spinner>
+                <Spinner
+                    android:id="@+id/orientation_spinner"
+                    android:layout_width="fill_parent"
+                    android:layout_height="wrap_content">
+                </Spinner>
 
-             </LinearLayout>
+            </LinearLayout>
 
-             <LinearLayout
-                 android:layout_width="wrap_content"
-                 android:layout_height="wrap_content"
-                 android:layout_marginStart="16dip"
-                 android:layout_marginEnd="16dip"
-                 android:orientation="vertical">
+            <LinearLayout
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="16dip"
+                android:layout_marginEnd="16dip"
+                android:orientation="vertical">
 
-                 <!-- Range options -->
+                <!-- Range options -->
 
-                 <TextView
-                     android:layout_width="wrap_content"
-                     android:layout_height="wrap_content"
-                     android:layout_marginTop="8dip"
-                     android:layout_marginStart="12dip"
-                     android:textAppearance="?android:attr/textAppearanceSmall"
-                     android:labelFor="@+id/range_options_spinner"
-                     android:text="@string/label_pages">
-                 </TextView>
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="8dip"
+                    android:layout_marginStart="12dip"
+                    android:textAppearance="?android:attr/textAppearanceSmall"
+                    android:labelFor="@+id/range_options_spinner"
+                    android:text="@string/label_pages">
+                </TextView>
 
-                 <Spinner
-                     android:id="@+id/range_options_spinner"
-                     android:layout_width="fill_parent"
-                     android:layout_height="wrap_content">
-                 </Spinner>
+                <Spinner
+                    android:id="@+id/range_options_spinner"
+                    android:layout_width="fill_parent"
+                    android:layout_height="wrap_content">
+                </Spinner>
 
-             </LinearLayout>
+            </LinearLayout>
 
-             <LinearLayout
-                 android:layout_width="wrap_content"
-                 android:layout_height="wrap_content"
-                 android:layout_marginStart="16dip"
-                 android:layout_marginEnd="16dip"
-                 android:orientation="vertical">
+            <LinearLayout
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="16dip"
+                android:layout_marginEnd="16dip"
+                android:orientation="vertical">
 
-                 <!-- Pages -->
+                <!-- Pages -->
 
-                 <TextView
-                     android:id="@+id/page_range_title"
-                     android:layout_width="wrap_content"
-                     android:layout_height="wrap_content"
-                     android:layout_marginTop="8dip"
-                     android:layout_marginStart="12dip"
-                     android:textAppearance="?android:attr/textAppearanceSmall"
-                     android:text="@string/pages_range_example"
-                     android:labelFor="@+id/page_range_edittext"
-                     android:textAllCaps="false"
-                     android:visibility="visible">
-                 </TextView>
+                <TextView
+                    android:id="@+id/page_range_title"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="8dip"
+                    android:layout_marginStart="12dip"
+                    android:textAppearance="?android:attr/textAppearanceSmall"
+                    android:text="@string/pages_range_example"
+                    android:labelFor="@+id/page_range_edittext"
+                    android:textAllCaps="false"
+                    android:visibility="visible">
+                </TextView>
 
-                 <view
-                     class="com.android.printspooler.widget.FirstFocusableEditText"
-                     android:id="@+id/page_range_edittext"
-                     android:layout_width="fill_parent"
-                     android:layout_height="wrap_content"
-                     android:layout_gravity="bottom|fill_horizontal"
-                     android:singleLine="true"
-                     android:ellipsize="end"
-                     android:visibility="visible"
-                     android:inputType="textNoSuggestions">
-                 </view>
+                <view
+                    class="com.android.printspooler.widget.FirstFocusableEditText"
+                    android:id="@+id/page_range_edittext"
+                    android:layout_width="fill_parent"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="bottom|fill_horizontal"
+                    android:singleLine="true"
+                    android:ellipsize="end"
+                    android:visibility="visible"
+                    android:inputType="textNoSuggestions">
+                </view>
 
-             </LinearLayout>
+            </LinearLayout>
 
-         </com.android.printspooler.widget.PrintOptionsLayout>
+        </com.android.printspooler.widget.PrintOptionsLayout>
 
-         <!-- More options -->
+        <!-- More options -->
 
-         <LinearLayout
-             android:id="@+id/more_options_container"
-             android:layout_width="fill_parent"
-             android:layout_height="wrap_content"
-             android:paddingStart="28dip"
-             android:paddingEnd="28dip"
-             android:orientation="vertical"
-             android:visibility="visible">
+        <Button
+            android:id="@+id/more_options_button"
+            style="?android:attr/borderlessButtonStyle"
+            android:textColor="?android:attr/textColorPrimary"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="16dip"
+            android:layout_marginEnd="16dip"
+            android:text="@string/more_options_button"
+            android:gravity="start|center_vertical">
+        </Button>
 
-             <ImageView
-                 android:layout_width="fill_parent"
-                 android:layout_height="1dip"
-                 android:layout_gravity="fill_horizontal"
-                 android:background="?android:attr/colorControlNormal"
-                 android:contentDescription="@null">
-             </ImageView>
+    </LinearLayout>
 
-             <Button
-                 android:id="@+id/more_options_button"
-                 style="?android:attr/borderlessButtonStyle"
-                 android:layout_width="fill_parent"
-                 android:layout_height="wrap_content"
-                 android:layout_gravity="fill_horizontal"
-                 android:text="@string/more_options_button"
-                 android:gravity="start|center_vertical"
-                 android:textAllCaps="false">
-             </Button>
+    <!-- Expand/collapse handle -->
 
-             <ImageView
-                 android:layout_width="fill_parent"
-                 android:layout_height="1dip"
-                 android:layout_gravity="fill_horizontal"
-                 android:background="?android:attr/colorControlNormal"
-                 android:contentDescription="@null">
-             </ImageView>
-
-         </LinearLayout>
-
-        </LinearLayout>
-
-        <!-- Expand/collapse handle -->
-
-        <FrameLayout
+    <FrameLayout
          android:id="@+id/expand_collapse_handle"
          android:layout_width="fill_parent"
          android:layout_height="wrap_content">
diff --git a/packages/PrintSpooler/res/layout/print_error_fragment.xml b/packages/PrintSpooler/res/layout/print_error_fragment.xml
index dc44339..3ea2abd 100644
--- a/packages/PrintSpooler/res/layout/print_error_fragment.xml
+++ b/packages/PrintSpooler/res/layout/print_error_fragment.xml
@@ -33,6 +33,9 @@
         android:id="@+id/message"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
+        android:layout_marginStart="16dip"
+        android:layout_marginEnd="16dip"
+        android:gravity="center_horizontal"
         android:text="@string/print_error_default_message"
         android:textAppearance="?android:attr/textAppearanceLargeInverse">
     </TextView>
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index 6b29e5f..fe17516 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -180,8 +180,6 @@
     private TextView mSummaryCopies;
     private TextView mSummaryPaperSize;
 
-    private View mAdvancedPrintOptionsContainer;
-
     private Button mMoreOptionsButton;
 
     private ImageView mPrintButton;
@@ -1024,7 +1022,6 @@
         mPageRangeEditText.addTextChangedListener(new RangeTextWatcher());
 
         // Advanced options button.
-        mAdvancedPrintOptionsContainer = findViewById(R.id.more_options_container);
         mMoreOptionsButton = (Button) findViewById(R.id.more_options_button);
         mMoreOptionsButton.setOnClickListener(clickListener);
 
@@ -1294,10 +1291,10 @@
         ComponentName serviceName = mCurrentPrinter.getId().getServiceName();
         if (!TextUtils.isEmpty(PrintOptionUtils.getAdvancedOptionsActivityName(
                 this, serviceName))) {
-            mAdvancedPrintOptionsContainer.setVisibility(View.VISIBLE);
+            mMoreOptionsButton.setVisibility(View.VISIBLE);
             mMoreOptionsButton.setEnabled(true);
         } else {
-            mAdvancedPrintOptionsContainer.setVisibility(View.GONE);
+            mMoreOptionsButton.setVisibility(View.GONE);
             mMoreOptionsButton.setEnabled(false);
         }
 
@@ -1704,7 +1701,7 @@
                 if (position == 0 && getPdfPrinter() != null) {
                     PrinterHolder printerHolder = (PrinterHolder) getItem(position);
                     title = printerHolder.printer.getName();
-                    icon = getResources().getDrawable(R.drawable.ic_savetopdf);
+                    icon = getResources().getDrawable(R.drawable.ic_menu_savetopdf);
                 } else if (position == 1) {
                     title = getString(R.string.all_printers);
                 }
@@ -1712,7 +1709,7 @@
                 if (position == 1 && getPdfPrinter() != null) {
                     PrinterHolder printerHolder = (PrinterHolder) getItem(position);
                     title = printerHolder.printer.getName();
-                    icon = getResources().getDrawable(R.drawable.ic_savetopdf);
+                    icon = getResources().getDrawable(R.drawable.ic_menu_savetopdf);
                 } else if (position == getCount() - 1) {
                     title = getString(R.string.all_printers);
                 } else {
diff --git a/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java b/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java
index efb030e..e428948 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java
@@ -53,7 +53,7 @@
 
     private View mDraggableContent;
     private View mPrintButton;
-    private ViewGroup mMoreOptionsContainer;
+    private View mMoreOptionsButton;
     private ViewGroup mOptionsContainer;
 
     private View mEmbeddedContentContainer;
@@ -140,7 +140,7 @@
         mDynamicContent = findViewById(R.id.dynamic_content);
         mDraggableContent = findViewById(R.id.draggable_content);
         mPrintButton = findViewById(R.id.print_button);
-        mMoreOptionsContainer = (ViewGroup) findViewById(R.id.more_options_container);
+        mMoreOptionsButton = findViewById(R.id.more_options_button);
         mOptionsContainer = (ViewGroup) findViewById(R.id.options_container);
         mEmbeddedContentContainer = findViewById(R.id.embedded_content_container);
         mEmbeddedContentScrim = findViewById(R.id.embedded_content_scrim);
@@ -301,14 +301,14 @@
                 || (mDragProgress == 1.0f && progress < 1.0f)) {
             mSummaryContent.setLayerType(View.LAYER_TYPE_HARDWARE, null);
             mDraggableContent.setLayerType(View.LAYER_TYPE_HARDWARE, null);
-            mMoreOptionsContainer.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+            mMoreOptionsButton.setLayerType(View.LAYER_TYPE_HARDWARE, null);
             ensureImeClosedAndInputFocusCleared();
         }
         if ((mDragProgress > 0 && progress == 0)
                 || (mDragProgress < 1.0f && progress == 1.0f)) {
             mSummaryContent.setLayerType(View.LAYER_TYPE_NONE, null);
             mDraggableContent.setLayerType(View.LAYER_TYPE_NONE, null);
-            mMoreOptionsContainer.setLayerType(View.LAYER_TYPE_NONE, null);
+            mMoreOptionsButton.setLayerType(View.LAYER_TYPE_NONE, null);
         }
 
         mDragProgress = progress;
@@ -317,7 +317,7 @@
 
         final float inverseAlpha = 1.0f - progress;
         mOptionsContainer.setAlpha(inverseAlpha);
-        mMoreOptionsContainer.setAlpha(inverseAlpha);
+        mMoreOptionsButton.setAlpha(inverseAlpha);
 
         mEmbeddedContentScrim.setBackgroundColor(computeScrimColor());
 
@@ -336,8 +336,8 @@
             if (mOptionsStateChangeListener != null) {
                 mOptionsStateChangeListener.onOptionsClosed();
             }
-            if (mMoreOptionsContainer.getVisibility() != View.GONE) {
-                mMoreOptionsContainer.setVisibility(View.INVISIBLE);
+            if (mMoreOptionsButton.getVisibility() != View.GONE) {
+                mMoreOptionsButton.setVisibility(View.INVISIBLE);
             }
             mDraggableContent.setVisibility(View.INVISIBLE);
             // If we change the scrim visibility the dimming is lagging
@@ -346,8 +346,8 @@
             mEmbeddedContentScrim.setClickable(false);
             mExpandCollapseIcon.setBackgroundResource(R.drawable.ic_expand_more);
         } else {
-            if (mMoreOptionsContainer.getVisibility() != View.GONE) {
-                mMoreOptionsContainer.setVisibility(View.VISIBLE);
+            if (mMoreOptionsButton.getVisibility() != View.GONE) {
+                mMoreOptionsButton.setVisibility(View.VISIBLE);
             }
             mDraggableContent.setVisibility(View.VISIBLE);
         }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 13301fb..edefb13 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -70,7 +70,7 @@
     // database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion'
     // is properly propagated through your change.  Not doing so will result in a loss of user
     // settings.
-    private static final int DATABASE_VERSION = 107;
+    private static final int DATABASE_VERSION = 108;
 
     private Context mContext;
     private int mUserHandle;
@@ -1714,6 +1714,25 @@
             upgradeVersion = 107;
         }
 
+        if (upgradeVersion < 108) {
+            // Reset the auto-brightness setting to default since the behavior
+            // of the feature is now quite different and is being presented to
+            // the user in a new way as "adaptive brightness".
+            db.beginTransaction();
+            SQLiteStatement stmt = null;
+            try {
+                stmt = db.compileStatement("INSERT OR REPLACE INTO system(name,value)"
+                        + " VALUES(?,?);");
+                loadBooleanSetting(stmt, Settings.System.SCREEN_BRIGHTNESS_MODE,
+                        R.bool.def_screen_brightness_automatic_mode);
+                db.setTransactionSuccessful();
+            } finally {
+                db.endTransaction();
+                if (stmt != null) stmt.close();
+            }
+            upgradeVersion = 108;
+        }
+
         // *** Remember to update DATABASE_VERSION above!
 
         if (upgradeVersion != currentVersion) {
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index 8e9b501..ca07c87 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -31,7 +31,7 @@
         android:layout_gravity="bottom|center_horizontal"
         android:textStyle="italic"
         android:textColor="#ffffff"
-        android:textAppearance="?android:attr/textAppearanceSmall"/>
+        android:textAppearance="?android:attr/textAppearanceSmall" />
 
     <FrameLayout
         android:id="@+id/preview_container"
@@ -66,6 +66,7 @@
         android:layout_gravity="bottom|center_horizontal"
         android:src="@drawable/ic_lock_24dp"
         android:scaleType="center"
-        android:tint="#ffffffff" />
+        android:tint="#ffffffff"
+        android:contentDescription="@string/accessibility_unlock_button" />
 
 </com.android.systemui.statusbar.phone.KeyguardBottomAreaView>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 1d33d7a..8f05f7b 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -213,6 +213,14 @@
     <string name="accessibility_camera_button">Camera</string>
     <!-- Content description of the phone button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_phone_button">Phone</string>
+    <!-- Content description of the unlock button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
+    <string name="accessibility_unlock_button">Unlock</string>
+    <!-- Click action label for accessibility for the unlock button. [CHAR LIMIT=NONE] -->
+    <string name="unlock_label">unlock</string>
+    <!-- Click action label for accessibility for the phone button. [CHAR LIMIT=NONE] -->
+    <string name="phone_label">open phone</string>
+    <!-- Click action label for accessibility for the phone button. [CHAR LIMIT=NONE] -->
+    <string name="camera_label">open camera</string>
 
     <!-- Content description of the switch input method button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_ime_switch_button">Switch input method button.</string>
@@ -826,8 +834,8 @@
     <!-- Monitoring dialog title for normal devices  [CHAR LIMIT=35]-->
     <string name="monitoring_title">Network monitoring</string>
 
-    <!-- Monitoring dialog open app button [CHAR LIMIT=30] -->
-    <string name="open_app">Open app</string>
+    <!-- Monitoring dialog disable vpn button [CHAR LIMIT=30] -->
+    <string name="disable_vpn">Disable VPN</string>
 
     <!-- Monitoring dialog disconnect vpn button [CHAR LIMIT=30] -->
     <string name="disconnect_vpn">Disconnect VPN</string>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
index bfbc56c..a8199fa 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
@@ -119,11 +119,7 @@
     @Override
     public void onClick(DialogInterface dialog, int which) {
         if (which == DialogInterface.BUTTON_NEGATIVE) {
-            if (mSecurityController.isLegacyVpn()) {
-                mSecurityController.disconnectFromLegacyVpn();
-            } else {
-                mSecurityController.openVpnApp();
-            }
+            mSecurityController.disconnectFromVpn();
         }
     }
 
@@ -142,7 +138,7 @@
         if (mSecurityController.isLegacyVpn()) {
             return mContext.getString(R.string.disconnect_vpn);
         } else {
-            return mContext.getString(R.string.open_app);
+            return mContext.getString(R.string.disable_vpn);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 61246b0..44d8d23 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -26,6 +26,7 @@
 import android.content.pm.ResolveInfo;
 import android.content.res.Configuration;
 import android.os.AsyncTask;
+import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.phone.PhoneManager;
@@ -36,7 +37,7 @@
 import android.util.TypedValue;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.FrameLayout;
 import android.widget.TextView;
 
@@ -44,17 +45,23 @@
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.KeyguardIndicationController;
-import com.android.systemui.statusbar.policy.FlashlightController;
+import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.KeyguardAffordanceView;
+import com.android.systemui.statusbar.KeyguardIndicationController;
+import com.android.systemui.statusbar.policy.AccessibilityController;
+import com.android.systemui.statusbar.policy.FlashlightController;
 import com.android.systemui.statusbar.policy.PreviewInflater;
 
+import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
+import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+
 /**
  * Implementation for the bottom area of the Keyguard, including camera/phone affordance and status
  * text.
  */
 public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickListener,
-        UnlockMethodCache.OnUnlockMethodChangedListener {
+        UnlockMethodCache.OnUnlockMethodChangedListener,
+        AccessibilityController.AccessibilityStateChangedCallback, View.OnLongClickListener {
 
     final static String TAG = "PhoneStatusBar/KeyguardBottomAreaView";
 
@@ -80,6 +87,8 @@
     private FlashlightController mFlashlightController;
     private PreviewInflater mPreviewInflater;
     private KeyguardIndicationController mIndicationController;
+    private AccessibilityController mAccessibilityController;
+    private PhoneStatusBar mPhoneStatusBar;
 
     private final TrustDrawable mTrustDrawable;
 
@@ -101,6 +110,40 @@
         mTrustDrawable = new TrustDrawable(mContext);
     }
 
+    private AccessibilityDelegate mAccessibilityDelegate = new AccessibilityDelegate() {
+        @Override
+        public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+            super.onInitializeAccessibilityNodeInfo(host, info);
+            String label = null;
+            if (host == mLockIcon) {
+                label = getResources().getString(R.string.unlock_label);
+            } else if (host == mCameraImageView) {
+                label = getResources().getString(R.string.camera_label);
+            } else if (host == mPhoneImageView) {
+                label = getResources().getString(R.string.phone_label);
+            }
+            info.addAction(new AccessibilityAction(ACTION_CLICK, label));
+        }
+
+        @Override
+        public boolean performAccessibilityAction(View host, int action, Bundle args) {
+            if (action == ACTION_CLICK) {
+                if (host == mLockIcon) {
+                    mPhoneStatusBar.animateCollapsePanels(
+                            CommandQueue.FLAG_EXCLUDE_NONE, true /* force */);
+                    return true;
+                } else if (host == mCameraImageView) {
+                    launchCamera();
+                    return true;
+                } else if (host == mPhoneImageView) {
+                    launchPhone();
+                    return true;
+                }
+            }
+            return super.performAccessibilityAction(host, action, args);
+        }
+    };
+
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
@@ -111,7 +154,6 @@
         mLockIcon = (KeyguardAffordanceView) findViewById(R.id.lock_icon);
         mIndicationText = (TextView) findViewById(R.id.keyguard_indication_text);
         watchForCameraPolicyChanges();
-        watchForAccessibilityChanges();
         updateCameraVisibility();
         updatePhoneVisibility();
         mUnlockMethodCache = UnlockMethodCache.getInstance(getContext());
@@ -123,6 +165,16 @@
         inflatePreviews();
         mLockIcon.setOnClickListener(this);
         mLockIcon.setBackground(mTrustDrawable);
+        mLockIcon.setOnLongClickListener(this);
+        mCameraImageView.setOnClickListener(this);
+        mPhoneImageView.setOnClickListener(this);
+        initAccessibility();
+    }
+
+    private void initAccessibility() {
+        mLockIcon.setAccessibilityDelegate(mAccessibilityDelegate);
+        mPhoneImageView.setAccessibilityDelegate(mAccessibilityDelegate);
+        mCameraImageView.setAccessibilityDelegate(mAccessibilityDelegate);
     }
 
     @Override
@@ -150,6 +202,15 @@
         mFlashlightController = flashlightController;
     }
 
+    public void setAccessibilityController(AccessibilityController accessibilityController) {
+        mAccessibilityController = accessibilityController;
+        accessibilityController.addStateChangedCallback(this);
+    }
+
+    public void setPhoneStatusBar(PhoneStatusBar phoneStatusBar) {
+        mPhoneStatusBar = phoneStatusBar;
+    }
+
     private Intent getCameraIntent() {
         KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
         boolean currentUserHasTrust = updateMonitor.getUserHasTrust(
@@ -203,28 +264,24 @@
         KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitorCallback);
     }
 
-    private void watchForAccessibilityChanges() {
-        final AccessibilityManager am =
-                (AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
-
-        // Set the initial state
-        enableAccessibility(am.isTouchExplorationEnabled());
-
-        // Watch for changes
-        am.addTouchExplorationStateChangeListener(
-                new AccessibilityManager.TouchExplorationStateChangeListener() {
-            @Override
-            public void onTouchExplorationStateChanged(boolean enabled) {
-                enableAccessibility(enabled);
-            }
-        });
+    @Override
+    public void onStateChanged(boolean accessibilityEnabled, boolean touchExplorationEnabled) {
+        mCameraImageView.setClickable(touchExplorationEnabled);
+        mPhoneImageView.setClickable(touchExplorationEnabled);
+        mCameraImageView.setFocusable(accessibilityEnabled);
+        mPhoneImageView.setFocusable(accessibilityEnabled);
+        updateLockIconClickability();
     }
 
-    private void enableAccessibility(boolean touchExplorationEnabled) {
-        mCameraImageView.setOnClickListener(touchExplorationEnabled ? this : null);
-        mCameraImageView.setClickable(touchExplorationEnabled);
-        mPhoneImageView.setOnClickListener(touchExplorationEnabled ? this : null);
-        mPhoneImageView.setClickable(touchExplorationEnabled);
+    private void updateLockIconClickability() {
+        if (mAccessibilityController == null) {
+            return;
+        }
+        mLockIcon.setClickable(mUnlockMethodCache.isTrustManaged()
+                || mAccessibilityController.isTouchExplorationEnabled());
+        mLockIcon.setLongClickable(mAccessibilityController.isTouchExplorationEnabled()
+                && mUnlockMethodCache.isTrustManaged());
+        mLockIcon.setFocusable(mAccessibilityController.isAccessibilityEnabled());
     }
 
     @Override
@@ -234,12 +291,27 @@
         } else if (v == mPhoneImageView) {
             launchPhone();
         } if (v == mLockIcon) {
-            mIndicationController.showTransientIndication(
-                    R.string.keyguard_indication_trust_disabled);
-            mLockPatternUtils.requireCredentialEntry(mLockPatternUtils.getCurrentUser());
+            if (!mAccessibilityController.isAccessibilityEnabled()) {
+                handleTrustCircleClick();
+            } else {
+                mPhoneStatusBar.animateCollapsePanels(
+                        CommandQueue.FLAG_EXCLUDE_NONE, true /* force */);
+            }
         }
     }
 
+    @Override
+    public boolean onLongClick(View v) {
+        handleTrustCircleClick();
+        return true;
+    }
+
+    private void handleTrustCircleClick() {
+        mIndicationController.showTransientIndication(
+                R.string.keyguard_indication_trust_disabled);
+        mLockPatternUtils.requireCredentialEntry(mLockPatternUtils.getCurrentUser());
+    }
+
     public void launchCamera() {
         mFlashlightController.killFlashlight();
         Intent intent = getCameraIntent();
@@ -304,7 +376,9 @@
         mLockIcon.setImageResource(iconRes);
         boolean trustManaged = mUnlockMethodCache.isTrustManaged();
         mTrustDrawable.setTrustManaged(trustManaged);
-        mLockIcon.setClickable(trustManaged);
+
+        // TODO: Update content description depending on state
+        updateLockIconClickability();
     }
 
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index eae64ca..b3042b9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -132,6 +132,7 @@
 import com.android.systemui.statusbar.SpeedBumpView;
 import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.policy.AccessibilityController;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
 import com.android.systemui.statusbar.policy.BluetoothControllerImpl;
@@ -237,6 +238,7 @@
     NextAlarmController mNextAlarmController;
     KeyguardMonitor mKeyguardMonitor;
     BrightnessMirrorController mBrightnessMirrorController;
+    AccessibilityController mAccessibilityController;
 
     int mNaturalBarHeight = -1;
     int mIconSize = -1;
@@ -805,6 +807,9 @@
 
         mFlashlightController = new FlashlightController(mContext);
         mKeyguardBottomArea.setFlashlightController(mFlashlightController);
+        mKeyguardBottomArea.setPhoneStatusBar(this);
+        mAccessibilityController = new AccessibilityController(mContext);
+        mKeyguardBottomArea.setAccessibilityController(mAccessibilityController);
         mNextAlarmController = new NextAlarmController(mContext);
         mKeyguardMonitor = new KeyguardMonitor();
         mUserSwitcherController = new UserSwitcherController(mContext, mKeyguardMonitor);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityController.java
new file mode 100644
index 0000000..89ed787
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityController.java
@@ -0,0 +1,90 @@
+/*
+ * 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.systemui.statusbar.policy;
+
+import android.content.Context;
+import android.util.Log;
+import android.view.accessibility.AccessibilityManager;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+public class AccessibilityController implements
+        AccessibilityManager.AccessibilityStateChangeListener,
+        AccessibilityManager.TouchExplorationStateChangeListener {
+
+    private final ArrayList<AccessibilityStateChangedCallback> mChangeCallbacks = new ArrayList<>();
+
+    private boolean mAccessibilityEnabled;
+    private boolean mTouchExplorationEnabled;
+
+    public AccessibilityController(Context context) {
+        AccessibilityManager am =
+                (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
+        am.addTouchExplorationStateChangeListener(this);
+        am.addAccessibilityStateChangeListener(this);
+        mAccessibilityEnabled = am.isEnabled();
+        mTouchExplorationEnabled = am.isTouchExplorationEnabled();
+    }
+
+    public boolean isAccessibilityEnabled() {
+        return mAccessibilityEnabled;
+    }
+
+    public boolean isTouchExplorationEnabled() {
+        return mTouchExplorationEnabled;
+    }
+
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("AccessibilityController state:");
+        pw.print("  mAccessibilityEnabled="); pw.println(mAccessibilityEnabled);
+        pw.print("  mTouchExplorationEnabled="); pw.println(mTouchExplorationEnabled);
+    }
+
+    public void addStateChangedCallback(AccessibilityStateChangedCallback cb) {
+        mChangeCallbacks.add(cb);
+        cb.onStateChanged(mAccessibilityEnabled, mTouchExplorationEnabled);
+    }
+
+    public void removeStateChangedCallback(AccessibilityStateChangedCallback cb) {
+        mChangeCallbacks.remove(cb);
+    }
+
+    private void fireChanged() {
+        final int N = mChangeCallbacks.size();
+        for (int i = 0; i < N; i++) {
+            mChangeCallbacks.get(i).onStateChanged(mAccessibilityEnabled, mTouchExplorationEnabled);
+        }
+    }
+
+    @Override
+    public void onAccessibilityStateChanged(boolean enabled) {
+        mAccessibilityEnabled = enabled;
+        fireChanged();
+    }
+
+    @Override
+    public void onTouchExplorationStateChanged(boolean enabled) {
+        mTouchExplorationEnabled = enabled;
+        fireChanged();
+    }
+
+    public interface AccessibilityStateChangedCallback {
+        void onStateChanged(boolean accessibilityEnabled, boolean touchExplorationEnabled);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
index ede8129..3a5a53b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
@@ -23,8 +23,7 @@
     String getVpnApp();
     boolean isLegacyVpn();
     String getLegacyVpnName();
-    void openVpnApp();
-    void disconnectFromLegacyVpn();
+    void disconnectFromVpn();
 
     void addCallback(VpnCallback callback);
     void removeCallback(VpnCallback callback);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
index 8e04e5e..ae0291b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -18,7 +18,6 @@
 import android.app.admin.DevicePolicyManager;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.net.ConnectivityManager;
 import android.net.ConnectivityManager.NetworkCallback;
@@ -109,18 +108,17 @@
     }
 
     @Override
-    public void openVpnApp() {
-        Intent i = mContext.getPackageManager().getLaunchIntentForPackage(mVpnConfig.user);
-        if (i != null) {
-            i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-            mContext.startActivity(i);
-        }
-    }
-
-    @Override
-    public void disconnectFromLegacyVpn() {
+    public void disconnectFromVpn() {
         try {
-            mConnectivityService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN);
+            if (isLegacyVpn()) {
+                mConnectivityService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN);
+            } else {
+                // Prevent this app from initiating VPN connections in the future without user
+                // intervention.
+                mConnectivityService.setVpnPackageAuthorization(false);
+
+                mConnectivityService.prepareVpn(mVpnConfig.user, VpnConfig.LEGACY_VPN);
+            }
         } catch (Exception e) {
             Log.e(TAG, "Unable to disconnect from VPN", e);
         }
@@ -154,9 +152,7 @@
             mIsVpnEnabled = mVpnConfig != null;
 
             if (mVpnConfig != null && !mVpnConfig.legacy) {
-                ApplicationInfo info =
-                        mContext.getPackageManager().getApplicationInfo(mVpnConfig.user, 0);
-                mVpnName = mContext.getPackageManager().getApplicationLabel(info).toString();
+                mVpnName = VpnConfig.getVpnLabel(mContext, mVpnConfig.user).toString();
             }
         } catch (RemoteException | NameNotFoundException e) {
             Log.w(TAG, "Unable to get current VPN", e);
diff --git a/packages/VpnDialogs/AndroidManifest.xml b/packages/VpnDialogs/AndroidManifest.xml
index 03d920a..1768400 100644
--- a/packages/VpnDialogs/AndroidManifest.xml
+++ b/packages/VpnDialogs/AndroidManifest.xml
@@ -28,14 +28,5 @@
                 <category android:name="android.intent.category.DEFAULT"/>
             </intent-filter>
         </activity>
-
-        <activity android:name=".ManageDialog"
-                android:theme="@*android:style/Theme.DeviceDefault.Light.Dialog.Alert"
-                android:noHistory="true">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN"/>
-                <category android:name="android.intent.category.DEFAULT"/>
-            </intent-filter>
-        </activity>
     </application>
 </manifest>
diff --git a/packages/VpnDialogs/res/drawable-hdpi/ic_vpn_dialog.png b/packages/VpnDialogs/res/drawable-hdpi/ic_vpn_dialog.png
new file mode 100644
index 0000000..a0b4b61
--- /dev/null
+++ b/packages/VpnDialogs/res/drawable-hdpi/ic_vpn_dialog.png
Binary files differ
diff --git a/packages/VpnDialogs/res/drawable-mdpi/ic_vpn_dialog.png b/packages/VpnDialogs/res/drawable-mdpi/ic_vpn_dialog.png
new file mode 100644
index 0000000..df5dfe8
--- /dev/null
+++ b/packages/VpnDialogs/res/drawable-mdpi/ic_vpn_dialog.png
Binary files differ
diff --git a/packages/VpnDialogs/res/drawable-xhdpi/ic_vpn_dialog.png b/packages/VpnDialogs/res/drawable-xhdpi/ic_vpn_dialog.png
new file mode 100644
index 0000000..18d5a3a
--- /dev/null
+++ b/packages/VpnDialogs/res/drawable-xhdpi/ic_vpn_dialog.png
Binary files differ
diff --git a/packages/VpnDialogs/res/drawable-xxhdpi/ic_vpn_dialog.png b/packages/VpnDialogs/res/drawable-xxhdpi/ic_vpn_dialog.png
new file mode 100644
index 0000000..4d475dc
--- /dev/null
+++ b/packages/VpnDialogs/res/drawable-xxhdpi/ic_vpn_dialog.png
Binary files differ
diff --git a/packages/VpnDialogs/res/drawable-xxxhdpi/ic_vpn_dialog.png b/packages/VpnDialogs/res/drawable-xxxhdpi/ic_vpn_dialog.png
new file mode 100644
index 0000000..9d458b4
--- /dev/null
+++ b/packages/VpnDialogs/res/drawable-xxxhdpi/ic_vpn_dialog.png
Binary files differ
diff --git a/packages/VpnDialogs/res/layout/confirm.xml b/packages/VpnDialogs/res/layout/confirm.xml
index ee7f4b8..66fec59 100644
--- a/packages/VpnDialogs/res/layout/confirm.xml
+++ b/packages/VpnDialogs/res/layout/confirm.xml
@@ -18,41 +18,12 @@
 <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
         android:layout_width="match_parent"
         android:layout_height="wrap_content">
-    <LinearLayout android:layout_width="match_parent"
+    <TextView android:id="@+id/warning"
+            android:layout_width="fill_parent"
             android:layout_height="wrap_content"
-            android:orientation="vertical"
-            android:padding="3mm">
-
-        <LinearLayout android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:orientation="horizontal"
-                android:gravity="center_vertical">
-
-            <ImageView android:id="@+id/icon"
-                    android:layout_width="@android:dimen/app_icon_size"
-                    android:layout_height="@android:dimen/app_icon_size"
-                    android:paddingRight="1mm"/>
-
-            <TextView android:id="@+id/prompt"
-                    android:layout_width="fill_parent"
-                    android:layout_height="wrap_content"
-                    android:textSize="18sp"/>
-        </LinearLayout>
-
-        <TextView android:id="@+id/warning"
-                android:layout_width="fill_parent"
-                android:layout_height="wrap_content"
-                android:paddingTop="1mm"
-                android:paddingBottom="1mm"
-                android:text="@string/warning"
-                android:textSize="18sp"/>
-
-        <CheckBox android:id="@+id/check"
-                android:layout_width="fill_parent"
-                android:layout_height="wrap_content"
-                android:text="@string/accept"
-                android:textSize="20sp"
-                android:filterTouchesWhenObscured="true"
-                android:checked="false"/>
-    </LinearLayout>
+            android:textSize="18sp"
+            android:paddingTop="4mm"
+            android:paddingLeft="3mm"
+            android:paddingRight="3mm"
+            android:paddingBottom="4mm"/>
 </ScrollView>
diff --git a/packages/VpnDialogs/res/layout/manage.xml b/packages/VpnDialogs/res/layout/manage.xml
deleted file mode 100644
index 56332c3..0000000
--- a/packages/VpnDialogs/res/layout/manage.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 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.
--->
-
-<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:padding="3mm"
-        android:stretchColumns="0,1"
-        android:shrinkColumns="1">
-
-    <TableRow>
-        <TextView android:text="@string/session" style="@style/label"/>
-        <TextView android:id="@+id/session" style="@style/value"/>
-    </TableRow>
-
-    <TableRow>
-        <TextView android:text="@string/duration" style="@style/label"/>
-        <TextView android:id="@+id/duration" style="@style/value"/>
-    </TableRow>
-
-    <TableRow android:id="@+id/data_transmitted_row" android:visibility="gone">
-        <TextView android:text="@string/data_transmitted" style="@style/label"/>
-        <TextView android:id="@+id/data_transmitted" style="@style/value"/>
-    </TableRow>
-
-    <TableRow android:id="@+id/data_received_row" android:visibility="gone">
-        <TextView android:text="@string/data_received" style="@style/label"/>
-        <TextView android:id="@+id/data_received" style="@style/value"/>
-    </TableRow>
-
-</TableLayout>
diff --git a/packages/VpnDialogs/res/values/strings.xml b/packages/VpnDialogs/res/values/strings.xml
index 3ff767a..84206a1 100644
--- a/packages/VpnDialogs/res/values/strings.xml
+++ b/packages/VpnDialogs/res/values/strings.xml
@@ -17,40 +17,15 @@
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
 
     <!-- Dialog title to identify the request from a VPN application. [CHAR LIMIT=60] -->
-    <string name="prompt"><xliff:g id="app">%s</xliff:g>
-        attempts to create a VPN connection.
-    </string>
+    <string name="prompt">Connection request</string>
 
     <!-- Dialog message to warn about the risk of using a VPN application. [CHAR LIMIT=NONE] -->
-    <string name="warning">By proceeding, you are giving the application
-        permission to intercept all network traffic.
-        <b>Do NOT accept unless you trust the application.</b> Otherwise,
-        you run the risk of having your data compromised by a malicious
-        software.
-    </string>
-
-    <!-- Checkbox label to accept the request from a VPN application. [CHAR LIMIT=60] -->
-    <string name="accept">I trust this application.</string>
-
-    <!-- Dialog title for built-in VPN. [CHAR LIMIT=40]  -->
-    <string name="legacy_title">VPN is connected</string>
-    <!-- Button label to configure the current VPN session. [CHAR LIMIT=20] -->
-    <string name="configure">Configure</string>
-    <!-- Button label to disconnect the current VPN session. [CHAR LIMIT=20] -->
-    <string name="disconnect">Disconnect</string>
-
-    <!-- Label for the name of the current VPN session. [CHAR LIMIT=20] -->
-    <string name="session">Session:</string>
-    <!-- Label for the duration of the current VPN session. [CHAR LIMIT=20] -->
-    <string name="duration">Duration:</string>
-    <!-- Label for the network usage of data transmitted over VPN. [CHAR LIMIT=20] -->
-    <string name="data_transmitted">Sent:</string>
-    <!-- Label for the network usage of data received over VPN. [CHAR LIMIT=20] -->
-    <string name="data_received">Received:</string>
-
-    <!-- Formatted string for the network usage over VPN. [CHAR LIMIT=40] -->
-    <string name="data_value_format">
-        <xliff:g id="number">%1$s</xliff:g> bytes /
-        <xliff:g id="number">%2$s</xliff:g> packets
+    <string name="warning"><xliff:g id="app">%s</xliff:g> wants to set up a VPN connection
+        that allows it to monitor network traffic. Only accept if you trust the source.
+        <![CDATA[
+        <br />
+        <br />
+        <img src="vpn_icon" />
+        ]]> appears at the top of your screen when VPN is active.
     </string>
 </resources>
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
index ddafc66..897c96cf 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
@@ -18,21 +18,28 @@
 
 import android.content.Context;
 import android.content.DialogInterface;
+import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.graphics.drawable.Drawable;
 import android.net.IConnectivityManager;
+import android.net.VpnService;
 import android.os.ServiceManager;
+import android.text.Html;
+import android.text.Html.ImageGetter;
 import android.util.Log;
 import android.view.View;
 import android.widget.Button;
-import android.widget.CompoundButton;
-import android.widget.ImageView;
 import android.widget.TextView;
 
 import com.android.internal.app.AlertActivity;
+import com.android.internal.net.VpnConfig;
 
-public class ConfirmDialog extends AlertActivity implements
-        CompoundButton.OnCheckedChangeListener, DialogInterface.OnClickListener {
+import java.util.List;
+
+public class ConfirmDialog extends AlertActivity
+        implements DialogInterface.OnClickListener, ImageGetter {
     private static final String TAG = "VpnConfirm";
 
     private String mPackage;
@@ -56,27 +63,22 @@
                 return;
             }
 
-            PackageManager pm = getPackageManager();
-            ApplicationInfo app = pm.getApplicationInfo(mPackage, 0);
-
             View view = View.inflate(this, R.layout.confirm, null);
-            ((ImageView) view.findViewById(R.id.icon)).setImageDrawable(app.loadIcon(pm));
-            ((TextView) view.findViewById(R.id.prompt)).setText(
-                    getString(R.string.prompt, app.loadLabel(pm)));
-            ((CompoundButton) view.findViewById(R.id.check)).setOnCheckedChangeListener(this);
 
-            mAlertParams.mIconAttrId = android.R.attr.alertDialogIcon;
-            mAlertParams.mTitle = getText(android.R.string.dialog_alert_title);
+            ((TextView) view.findViewById(R.id.warning)).setText(
+                    Html.fromHtml(
+                            getString(R.string.warning, VpnConfig.getVpnLabel(this, mPackage)),
+                    this, null /* tagHandler */));
+
+            mAlertParams.mTitle = getText(R.string.prompt);
             mAlertParams.mPositiveButtonText = getText(android.R.string.ok);
             mAlertParams.mPositiveButtonListener = this;
             mAlertParams.mNegativeButtonText = getText(android.R.string.cancel);
-            mAlertParams.mNegativeButtonListener = this;
             mAlertParams.mView = view;
             setupAlert();
 
             getWindow().setCloseOnTouchOutside(false);
             mButton = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
-            mButton.setEnabled(false);
             mButton.setFilterTouchesWhenObscured(true);
         } catch (Exception e) {
             Log.e(TAG, "onResume", e);
@@ -85,18 +87,24 @@
     }
 
     @Override
-    public void onBackPressed() {
+    public Drawable getDrawable(String source) {
+        // Should only reach this when fetching the VPN icon for the warning string.
+        Drawable icon = getDrawable(R.drawable.ic_vpn_dialog);
+        icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
+        return icon;
     }
 
     @Override
-    public void onCheckedChanged(CompoundButton button, boolean checked) {
-        mButton.setEnabled(checked);
+    public void onBackPressed() {
     }
 
     @Override
     public void onClick(DialogInterface dialog, int which) {
         try {
-            if (which == DialogInterface.BUTTON_POSITIVE && mService.prepareVpn(null, mPackage)) {
+            if (mService.prepareVpn(null, mPackage)) {
+                // Authorize this app to initiate VPN connections in the future without user
+                // intervention.
+                mService.setVpnPackageAuthorization(true);
                 setResult(RESULT_OK);
             }
         } catch (Exception e) {
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
deleted file mode 100644
index eb20995..0000000
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * Copyright (C) 2011 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.vpndialogs;
-
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.net.IConnectivityManager;
-import android.os.Handler;
-import android.os.Message;
-import android.os.ServiceManager;
-import android.os.SystemClock;
-import android.util.Log;
-import android.view.View;
-import android.widget.TextView;
-
-import com.android.internal.app.AlertActivity;
-import com.android.internal.net.VpnConfig;
-
-import java.io.DataInputStream;
-import java.io.FileInputStream;
-
-public class ManageDialog extends AlertActivity implements
-        DialogInterface.OnClickListener, Handler.Callback {
-    private static final String TAG = "VpnManage";
-
-    private VpnConfig mConfig;
-
-    private IConnectivityManager mService;
-
-    private TextView mDuration;
-    private TextView mDataTransmitted;
-    private TextView mDataReceived;
-    private boolean mDataRowsHidden;
-
-    private Handler mHandler;
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-
-        if (getCallingPackage() != null) {
-            Log.e(TAG, getCallingPackage() + " cannot start this activity");
-            finish();
-            return;
-        }
-
-        try {
-
-            mService = IConnectivityManager.Stub.asInterface(
-                    ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
-
-            mConfig = mService.getVpnConfig();
-
-            // mConfig can be null if we are a restricted user, in that case don't show this dialog
-            if (mConfig == null) {
-                finish();
-                return;
-            }
-
-            View view = View.inflate(this, R.layout.manage, null);
-            if (mConfig.session != null) {
-                ((TextView) view.findViewById(R.id.session)).setText(mConfig.session);
-            }
-            mDuration = (TextView) view.findViewById(R.id.duration);
-            mDataTransmitted = (TextView) view.findViewById(R.id.data_transmitted);
-            mDataReceived = (TextView) view.findViewById(R.id.data_received);
-            mDataRowsHidden = true;
-
-            if (mConfig.legacy) {
-                mAlertParams.mIconId = android.R.drawable.ic_dialog_info;
-                mAlertParams.mTitle = getText(R.string.legacy_title);
-            } else {
-                PackageManager pm = getPackageManager();
-                ApplicationInfo app = pm.getApplicationInfo(mConfig.user, 0);
-                mAlertParams.mIcon = app.loadIcon(pm);
-                mAlertParams.mTitle = app.loadLabel(pm);
-            }
-            if (mConfig.configureIntent != null) {
-                mAlertParams.mPositiveButtonText = getText(R.string.configure);
-                mAlertParams.mPositiveButtonListener = this;
-            }
-            mAlertParams.mNeutralButtonText = getText(R.string.disconnect);
-            mAlertParams.mNeutralButtonListener = this;
-            mAlertParams.mNegativeButtonText = getText(android.R.string.cancel);
-            mAlertParams.mNegativeButtonListener = this;
-            mAlertParams.mView = view;
-            setupAlert();
-
-            if (mHandler == null) {
-                mHandler = new Handler(this);
-            }
-            mHandler.sendEmptyMessage(0);
-        } catch (Exception e) {
-            Log.e(TAG, "onResume", e);
-            finish();
-        }
-    }
-
-    @Override
-    protected void onPause() {
-        super.onPause();
-        if (!isFinishing()) {
-            finish();
-        }
-    }
-
-    @Override
-    public void onClick(DialogInterface dialog, int which) {
-        try {
-            if (which == DialogInterface.BUTTON_POSITIVE) {
-                mConfig.configureIntent.send();
-            } else if (which == DialogInterface.BUTTON_NEUTRAL) {
-                if (mConfig.legacy) {
-                    mService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN);
-                } else {
-                    mService.prepareVpn(mConfig.user, VpnConfig.LEGACY_VPN);
-                }
-            }
-        } catch (Exception e) {
-            Log.e(TAG, "onClick", e);
-            finish();
-        }
-    }
-
-    @Override
-    public boolean handleMessage(Message message) {
-        mHandler.removeMessages(0);
-
-        if (!isFinishing()) {
-            if (mConfig.startTime != -1) {
-                long seconds = (SystemClock.elapsedRealtime() - mConfig.startTime) / 1000;
-                mDuration.setText(String.format("%02d:%02d:%02d",
-                        seconds / 3600, seconds / 60 % 60, seconds % 60));
-            }
-
-            String[] numbers = getNumbers();
-            if (numbers != null) {
-                // First unhide the related data rows.
-                if (mDataRowsHidden) {
-                    findViewById(R.id.data_transmitted_row).setVisibility(View.VISIBLE);
-                    findViewById(R.id.data_received_row).setVisibility(View.VISIBLE);
-                    mDataRowsHidden = false;
-                }
-
-                // [1] and [2] are received data in bytes and packets.
-                mDataReceived.setText(getString(R.string.data_value_format,
-                        numbers[1], numbers[2]));
-
-                // [9] and [10] are transmitted data in bytes and packets.
-                mDataTransmitted.setText(getString(R.string.data_value_format,
-                        numbers[9], numbers[10]));
-            }
-            mHandler.sendEmptyMessageDelayed(0, 1000);
-        }
-        return true;
-    }
-
-    private String[] getNumbers() {
-        DataInputStream in = null;
-        try {
-            // See dev_seq_printf_stats() in net/core/dev.c.
-            in = new DataInputStream(new FileInputStream("/proc/net/dev"));
-            String prefix = mConfig.interfaze + ':';
-
-            while (true) {
-                String line = in.readLine().trim();
-                if (line.startsWith(prefix)) {
-                    String[] numbers = line.substring(prefix.length()).split(" +");
-                    for (int i = 1; i < 17; ++i) {
-                        if (!numbers[i].equals("0")) {
-                            return numbers;
-                        }
-                    }
-                    break;
-                }
-            }
-        } catch (Exception e) {
-            // ignore
-        } finally {
-            try {
-                in.close();
-            } catch (Exception e) {
-                // ignore
-            }
-        }
-        return null;
-    }
-}
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 2e5d43f..b576324 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -34,7 +34,6 @@
 import android.app.backup.IFullBackupRestoreObserver;
 import android.app.backup.IRestoreObserver;
 import android.app.backup.IRestoreSession;
-import android.app.job.JobParameters;
 import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -79,6 +78,7 @@
 import android.provider.Settings;
 import android.system.ErrnoException;
 import android.system.Os;
+import android.util.ArrayMap;
 import android.util.AtomicFile;
 import android.util.EventLog;
 import android.util.Log;
@@ -308,12 +308,13 @@
     volatile boolean mClearingData;
 
     // Transport bookkeeping
-    final HashMap<String,String> mTransportNames
-            = new HashMap<String,String>();             // component name -> registration name
-    final HashMap<String,IBackupTransport> mTransports
-            = new HashMap<String,IBackupTransport>();   // registration name -> binder
-    final ArrayList<TransportConnection> mTransportConnections
-            = new ArrayList<TransportConnection>();
+    final Intent mTransportServiceIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST);
+    final ArrayMap<String,String> mTransportNames
+            = new ArrayMap<String,String>();             // component name -> registration name
+    final ArrayMap<String,IBackupTransport> mTransports
+            = new ArrayMap<String,IBackupTransport>();   // registration name -> binder
+    final ArrayMap<String,TransportConnection> mTransportConnections
+            = new ArrayMap<String,TransportConnection>();
     String mCurrentTransport;
     ActiveRestoreSession mActiveRestoreSession;
 
@@ -1064,9 +1065,8 @@
         if (DEBUG) Slog.v(TAG, "Starting with transport " + mCurrentTransport);
 
         // Find transport hosts and bind to their services
-        Intent transportServiceIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST);
         List<ResolveInfo> hosts = mPackageManager.queryIntentServicesAsUser(
-                transportServiceIntent, 0, UserHandle.USER_OWNER);
+                mTransportServiceIntent, 0, UserHandle.USER_OWNER);
         if (DEBUG) {
             Slog.v(TAG, "Found transports: " + ((hosts == null) ? "null" : hosts.size()));
         }
@@ -1082,17 +1082,7 @@
                     ServiceInfo info = hosts.get(i).serviceInfo;
                     PackageInfo packInfo = mPackageManager.getPackageInfo(info.packageName, 0);
                     if ((packInfo.applicationInfo.flags & ApplicationInfo.FLAG_PRIVILEGED) != 0) {
-                        ComponentName svcName = new ComponentName(info.packageName, info.name);
-                        if (DEBUG) {
-                            Slog.i(TAG, "Binding to transport host " + svcName);
-                        }
-                        Intent intent = new Intent(transportServiceIntent);
-                        intent.setComponent(svcName);
-                        TransportConnection connection = new TransportConnection();
-                        mTransportConnections.add(connection);
-                        context.bindServiceAsUser(intent,
-                                connection, Context.BIND_AUTO_CREATE,
-                                UserHandle.OWNER);
+                        bindTransport(info);
                     } else {
                         Slog.w(TAG, "Transport package not privileged: " + info.packageName);
                     }
@@ -1752,6 +1742,7 @@
             String action = intent.getAction();
             boolean replacing = false;
             boolean added = false;
+            boolean rebind = false;
             Bundle extras = intent.getExtras();
             String pkgList[] = null;
             if (Intent.ACTION_PACKAGE_ADDED.equals(action) ||
@@ -1764,7 +1755,7 @@
                 if (pkgName != null) {
                     pkgList = new String[] { pkgName };
                 }
-                added = Intent.ACTION_PACKAGE_ADDED.equals(action);
+                rebind = added = Intent.ACTION_PACKAGE_ADDED.equals(action);
                 replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false);
             } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
                 added = true;
@@ -1797,6 +1788,20 @@
                             enqueueFullBackup(packageName, now);
                             scheduleNextFullBackupJob();
                         }
+
+                        // if this was the PACKAGE_ADDED conclusion of an upgrade of the package
+                        // hosting one of our transports, we need to explicitly rebind now.
+                        if (rebind) {
+                            synchronized (mTransportConnections) {
+                                final TransportConnection conn = mTransportConnections.get(packageName);
+                                if (conn != null) {
+                                    if (DEBUG) {
+                                        Slog.i(TAG, "Transport package changed; rebinding");
+                                    }
+                                    bindTransport(conn.mTransport);
+                                }
+                            }
+                        }
                     } catch (NameNotFoundException e) {
                         // doesn't really exist; ignore it
                         if (DEBUG) {
@@ -1804,6 +1809,7 @@
                         }
                     }
                 }
+
             } else {
                 if (replacing) {
                     // The package is being updated.  We'll receive a PACKAGE_ADDED shortly.
@@ -1818,6 +1824,12 @@
 
     // ----- Track connection to transports service -----
     class TransportConnection implements ServiceConnection {
+        ServiceInfo mTransport;
+
+        public TransportConnection(ServiceInfo transport) {
+            mTransport = transport;
+        }
+
         @Override
         public void onServiceConnected(ComponentName component, IBinder service) {
             if (DEBUG) Slog.v(TAG, "Connected to transport " + component);
@@ -1841,6 +1853,32 @@
         }
     };
 
+    void bindTransport(ServiceInfo transport) {
+        ComponentName svcName = new ComponentName(transport.packageName, transport.name);
+        if (DEBUG) {
+            Slog.i(TAG, "Binding to transport host " + svcName);
+        }
+        Intent intent = new Intent(mTransportServiceIntent);
+        intent.setComponent(svcName);
+
+        TransportConnection connection;
+        synchronized (mTransportConnections) {
+            connection = mTransportConnections.get(transport.packageName);
+            if (null == connection) {
+                connection = new TransportConnection(transport);
+                mTransportConnections.put(transport.packageName, connection);
+            } else {
+                // This is a rebind due to package upgrade.  The service won't be
+                // automatically relaunched for us until we explicitly rebind, but
+                // we need to unbind the now-orphaned original connection.
+                mContext.unbindService(connection);
+            }
+        }
+        mContext.bindServiceAsUser(intent,
+                connection, Context.BIND_AUTO_CREATE,
+                UserHandle.OWNER);
+    }
+
     // Add the backup agents in the given packages to our set of known backup participants.
     // If 'packageNames' is null, adds all backup agents in the whole system.
     void addPackageParticipantsLocked(String[] packageNames) {
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 07c9048..7b64139 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -121,6 +121,7 @@
     private int mState;
     private final BluetoothHandler mHandler;
     private int mErrorRecoveryRetryCounter;
+    private final int mSystemUiUid;
 
     private void registerForAirplaneMode(IntentFilter filter) {
         final ContentResolver resolver = mContext.getContentResolver();
@@ -218,6 +219,15 @@
         if (isBluetoothPersistedStateOn()) {
             mEnableExternal = true;
         }
+
+        int sysUiUid = -1;
+        try {
+            sysUiUid = mContext.getPackageManager().getPackageUid("com.android.systemui",
+                    UserHandle.USER_OWNER);
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.wtf(TAG, "Unable to resolve SystemUI's UID.", e);
+        }
+        mSystemUiUid = sysUiUid;
     }
 
     /**
@@ -1118,7 +1128,8 @@
         try {
             foregroundUser = ActivityManager.getCurrentUser();
             valid = (callingUser == foregroundUser) ||
-                    callingAppId == Process.NFC_UID;
+                    callingAppId == Process.NFC_UID ||
+                    callingAppId == mSystemUiUid;
             if (DBG) {
                 Log.d(TAG, "checkIfCallerIsForegroundUser: valid=" + valid
                     + " callingUser=" + callingUser
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 67c01e5..96f8324 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -756,10 +756,20 @@
         return mNextNetworkRequestId++;
     }
 
-    private synchronized int nextNetId() {
-        int netId = mNextNetId;
-        if (++mNextNetId > MAX_NET_ID) mNextNetId = MIN_NET_ID;
-        return netId;
+    private void assignNextNetId(NetworkAgentInfo nai) {
+        synchronized (mNetworkForNetId) {
+            for (int i = MIN_NET_ID; i <= MAX_NET_ID; i++) {
+                int netId = mNextNetId;
+                if (++mNextNetId > MAX_NET_ID) mNextNetId = MIN_NET_ID;
+                // Make sure NetID unused.  http://b/16815182
+                if (mNetworkForNetId.get(netId) == null) {
+                    nai.network = new Network(netId);
+                    mNetworkForNetId.put(netId, nai);
+                    return;
+                }
+            }
+        }
+        throw new IllegalStateException("No free netIds");
     }
 
     private int getConnectivityChangeDelay() {
@@ -1733,6 +1743,19 @@
         }
     }
 
+    private boolean isLiveNetworkAgent(NetworkAgentInfo nai, String msg) {
+        final NetworkAgentInfo officialNai;
+        synchronized (mNetworkForNetId) {
+            officialNai = mNetworkForNetId.get(nai.network.netId);
+        }
+        if (officialNai != null && officialNai.equals(nai)) return true;
+        if (officialNai != null || VDBG) {
+            loge(msg + " - validateNetworkAgent found mismatched netId: " + officialNai +
+                " - " + nai);
+        }
+        return false;
+    }
+
     // must be stateless - things change under us.
     private class NetworkStateTrackerHandler extends Handler {
         public NetworkStateTrackerHandler(Looper looper) {
@@ -1862,23 +1885,30 @@
                 }
                 case NetworkMonitor.EVENT_NETWORK_VALIDATED: {
                     NetworkAgentInfo nai = (NetworkAgentInfo)msg.obj;
-                    handleConnectionValidated(nai);
+                    if (isLiveNetworkAgent(nai, "EVENT_NETWORK_VALIDATED")) {
+                        handleConnectionValidated(nai);
+                    }
                     break;
                 }
                 case NetworkMonitor.EVENT_NETWORK_LINGER_COMPLETE: {
                     NetworkAgentInfo nai = (NetworkAgentInfo)msg.obj;
-                    handleLingerComplete(nai);
+                    if (isLiveNetworkAgent(nai, "EVENT_NETWORK_LINGER_COMPLETE")) {
+                        handleLingerComplete(nai);
+                    }
                     break;
                 }
                 case NetworkMonitor.EVENT_PROVISIONING_NOTIFICATION: {
+                    NetworkAgentInfo nai = null;
+                    synchronized (mNetworkForNetId) {
+                        nai = mNetworkForNetId.get(msg.arg2);
+                    }
+                    if (nai == null) {
+                        loge("EVENT_PROVISIONING_NOTIFICATION from unknown NetworkMonitor");
+                        break;
+                    }
                     if (msg.arg1 == 0) {
                         setProvNotificationVisibleIntent(false, msg.arg2, 0, null, null);
                     } else {
-                        NetworkAgentInfo nai = mNetworkForNetId.get(msg.arg2);
-                        if (nai == null) {
-                            loge("EVENT_PROVISIONING_NOTIFICATION from unknown NetworkMonitor");
-                            break;
-                        }
                         setProvNotificationVisibleIntent(true, msg.arg2, nai.networkInfo.getType(),
                                 nai.networkInfo.getExtraInfo(), (PendingIntent)msg.obj);
                     }
@@ -2089,13 +2119,21 @@
             if (newCap.satisfiedByNetworkCapabilities(network.networkCapabilities)) {
                 if (VDBG) log("apparently satisfied.  currentScore=" + network.currentScore);
                 if ((bestNetwork == null) || bestNetwork.currentScore < network.currentScore) {
-                    bestNetwork = network;
+                    if (!nri.isRequest) {
+                        // Not setting bestNetwork here as a listening NetworkRequest may be
+                        // satisfied by multiple Networks.  Instead the request is added to
+                        // each satisfying Network and notified about each.
+                        network.addRequest(nri.request);
+                        notifyNetworkCallback(network, nri);
+                    } else {
+                        bestNetwork = network;
+                    }
                 }
             }
         }
         if (bestNetwork != null) {
             if (VDBG) log("using " + bestNetwork.name());
-            if (nri.isRequest && bestNetwork.networkInfo.isConnected()) {
+            if (bestNetwork.networkInfo.isConnected()) {
                 // Cancel any lingering so the linger timeout doesn't teardown this network
                 // even though we have a request for it.
                 bestNetwork.networkLingered.clear();
@@ -2105,7 +2143,7 @@
             mNetworkForRequestId.put(nri.request.requestId, bestNetwork);
             notifyNetworkCallback(bestNetwork, nri);
             score = bestNetwork.currentScore;
-            if (nri.isRequest && nri.request.legacyType != TYPE_NONE) {
+            if (nri.request.legacyType != TYPE_NONE) {
                 mLegacyTypeTracker.add(nri.request.legacyType, bestNetwork);
             }
         }
@@ -2663,6 +2701,20 @@
     }
 
     /**
+     * Set whether the current VPN package has the ability to launch VPNs without
+     * user intervention. This method is used by system UIs and not available
+     * in ConnectivityManager. Permissions are checked in Vpn class.
+     * @hide
+     */
+    @Override
+    public void setVpnPackageAuthorization(boolean authorized) {
+        int user = UserHandle.getUserId(Binder.getCallingUid());
+        synchronized(mVpns) {
+            mVpns.get(user).setPackageAuthorization(authorized);
+        }
+    }
+
+    /**
      * Configure a TUN interface and return its file descriptor. Parameters
      * are encoded and opaque to this class. This method is used by VpnBuilder
      * and not available in ConnectivityManager. Permissions are checked in
@@ -3490,10 +3542,13 @@
             mIsProvisioningNetwork.set(false);
             // Check for  apps that can handle provisioning first
             Intent provisioningIntent = new Intent(TelephonyIntents.ACTION_CARRIER_SETUP);
-            provisioningIntent.addCategory(TelephonyIntents.CATEGORY_MCCMNC_PREFIX
-                    + mTelephonyManager.getSimOperator());
-            if (mContext.getPackageManager().resolveActivity(provisioningIntent, 0 /* flags */)
-                    != null) {
+            List<String> carrierPackages =
+                    mTelephonyManager.getCarrierPackageNamesForBroadcastIntent(provisioningIntent);
+            if (carrierPackages != null && !carrierPackages.isEmpty()) {
+                if (carrierPackages.size() != 1) {
+                    if (DBG) log("Multiple matching carrier apps found, launching the first.");
+                }
+                provisioningIntent.setPackage(carrierPackages.get(0));
                 provisioningIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT |
                         Intent.FLAG_ACTIVITY_NEW_TASK);
                 mContext.startActivity(provisioningIntent);
@@ -4104,7 +4159,7 @@
             int currentScore, NetworkMisc networkMisc) {
         enforceConnectivityInternalPermission();
 
-        NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(), nextNetId(),
+        NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(),
             new NetworkInfo(networkInfo), new LinkProperties(linkProperties),
             new NetworkCapabilities(networkCapabilities), currentScore, mContext, mTrackerHandler,
             networkMisc);
@@ -4118,9 +4173,7 @@
     private void handleRegisterNetworkAgent(NetworkAgentInfo na) {
         if (VDBG) log("Got NetworkAgent Messenger");
         mNetworkAgentInfos.put(na.messenger, na);
-        synchronized (mNetworkForNetId) {
-            mNetworkForNetId.put(na.network.netId, na);
-        }
+        assignNextNetId(na);
         na.asyncChannel.connect(mContext, mTrackerHandler, na.messenger);
         NetworkInfo networkInfo = na.networkInfo;
         na.networkInfo = null;
@@ -4386,6 +4439,10 @@
             if (VDBG) log("  checking if request is satisfied: " + nri.request);
             if (nri.request.networkCapabilities.satisfiedByNetworkCapabilities(
                     newNetwork.networkCapabilities)) {
+                if (!nri.isRequest) {
+                    newNetwork.addRequest(nri.request);
+                    continue;
+                }
                 // next check if it's better than any current network we're using for
                 // this request
                 if (VDBG) {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 931f448..8aec392 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -3189,7 +3189,11 @@
 
     void endLockTaskModeIfTaskEnding(TaskRecord task) {
         if (mLockTaskModeTask != null && mLockTaskModeTask == task) {
+            final Message lockTaskMsg = Message.obtain();
+            lockTaskMsg.arg1 = mLockTaskModeTask.userId;
+            lockTaskMsg.what = LOCK_TASK_END_MSG;
             mLockTaskModeTask = null;
+            mHandler.sendMessage(lockTaskMsg);
         }
     }
 
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 4eb2ef1..5a97aee 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -40,7 +40,7 @@
  */
 public class NetworkAgentInfo {
     public NetworkInfo networkInfo;
-    public final Network network;
+    public Network network;
     public LinkProperties linkProperties;
     public NetworkCapabilities networkCapabilities;
     public int currentScore;
@@ -55,12 +55,12 @@
     public final Messenger messenger;
     public final AsyncChannel asyncChannel;
 
-    public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, int netId, NetworkInfo info,
+    public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, NetworkInfo info,
             LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler,
             NetworkMisc misc) {
         this.messenger = messenger;
         asyncChannel = ac;
-        network = new Network(netId);
+        network = null;
         networkInfo = info;
         linkProperties = lp;
         networkCapabilities = nc;
@@ -87,6 +87,7 @@
 
     public String name() {
         return "NetworkAgentInfo [" + networkInfo.getTypeName() + " (" +
-                networkInfo.getSubtypeName() + ") - " + network.toString() + "]";
+                networkInfo.getSubtypeName() + ") - " +
+                (network == null ? "null" : network.toString()) + "]";
     }
 }
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index 5057994..b09298c 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -272,23 +272,22 @@
                 case CMD_NETWORK_LINGER:
                     if (DBG) log("Lingering");
                     transitionTo(mLingeringState);
-                    break;
+                    return HANDLED;
                 case CMD_NETWORK_CONNECTED:
                     if (DBG) log("Connected");
                     transitionTo(mEvaluatingState);
-                    break;
+                    return HANDLED;
                 case CMD_NETWORK_DISCONNECTED:
                     if (DBG) log("Disconnected - quitting");
                     quit();
-                    break;
+                    return HANDLED;
                 case CMD_FORCE_REEVALUATION:
                     if (DBG) log("Forcing reevaluation");
                     transitionTo(mEvaluatingState);
-                    break;
+                    return HANDLED;
                 default:
-                    break;
+                    return HANDLED;
             }
-            return HANDLED;
         }
     }
 
@@ -314,11 +313,10 @@
             switch (message.what) {
                 case CMD_NETWORK_CONNECTED:
                     transitionTo(mValidatedState);
-                    break;
+                    return HANDLED;
                 default:
                     return NOT_HANDLED;
             }
-            return HANDLED;
         }
     }
 
@@ -347,23 +345,25 @@
             switch (message.what) {
                 case CMD_REEVALUATE:
                     if (message.arg1 != mReevaluateToken)
-                        break;
+                        return HANDLED;
                     if (mNetworkAgentInfo.isVPN()) {
                         transitionTo(mValidatedState);
+                        return HANDLED;
                     }
                     // If network provides no internet connectivity adjust evaluation.
                     if (!mNetworkAgentInfo.networkCapabilities.hasCapability(
                             NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
                         // TODO: Try to verify something works.  Do all gateways respond to pings?
                         transitionTo(mValidatedState);
+                        return HANDLED;
                     }
                     // Kick off a thread to perform internet connectivity evaluation.
                     Thread thread = new EvaluateInternetConnectivity(mReevaluateToken);
                     thread.run();
-                    break;
+                    return HANDLED;
                 case EVENT_REEVALUATION_COMPLETE:
                     if (message.arg1 != mReevaluateToken)
-                        break;
+                        return HANDLED;
                     int httpResponseCode = message.arg2;
                     if (httpResponseCode == 204) {
                         transitionTo(mValidatedState);
@@ -375,11 +375,10 @@
                         Message msg = obtainMessage(CMD_REEVALUATE, ++mReevaluateToken, 0);
                         sendMessageDelayed(msg, mReevaluateDelayMs);
                     }
-                    break;
+                    return HANDLED;
                 default:
                     return NOT_HANDLED;
             }
-            return HANDLED;
         }
     }
 
@@ -432,17 +431,16 @@
             switch (message.what) {
                 case EVENT_APP_BYPASSED_CAPTIVE_PORTAL:
                     transitionTo(mValidatedState);
-                    break;
+                    return HANDLED;
                 case EVENT_APP_INDICATES_SIGN_IN_IMPOSSIBLE:
                     transitionTo(mOfflineState);
-                    break;
+                    return HANDLED;
                 case EVENT_NO_APP_RESPONSE:
                     transitionTo(mUserPromptedState);
-                    break;
+                    return HANDLED;
                 default:
                     return NOT_HANDLED;
             }
-            return HANDLED;
         }
         public void exit() {
             mReceiver.cancel();
@@ -488,13 +486,12 @@
             switch (message.what) {
                 case CMD_USER_WANTS_SIGN_IN:
                     if (message.arg1 != mUserPromptedToken)
-                        break;
+                        return HANDLED;
                     transitionTo(mInteractiveAppsPromptedState);
-                    break;
+                    return HANDLED;
                 default:
                     return NOT_HANDLED;
             }
-            return HANDLED;
         }
 
         @Override
@@ -520,17 +517,16 @@
             switch (message.what) {
                 case EVENT_APP_BYPASSED_CAPTIVE_PORTAL:
                     transitionTo(mValidatedState);
-                    break;
+                    return HANDLED;
                 case EVENT_APP_INDICATES_SIGN_IN_IMPOSSIBLE:
                     transitionTo(mOfflineState);
-                    break;
+                    return HANDLED;
                 case EVENT_NO_APP_RESPONSE:
                     transitionTo(mCaptivePortalState);
-                    break;
+                    return HANDLED;
                 default:
                     return NOT_HANDLED;
             }
-            return HANDLED;
         }
         public void exit() {
             mReceiver.cancel();
@@ -581,18 +577,17 @@
             switch (message.what) {
                 case CMD_CAPTIVE_PORTAL_LOGGED_IN:
                     if (message.arg1 != mCaptivePortalLoggedInToken)
-                        break;
+                        return HANDLED;
                     if (message.arg2 == 0) {
                         // TODO: Should teardown network.
                         transitionTo(mOfflineState);
                     } else {
                         transitionTo(mValidatedState);
                     }
-                    break;
+                    return HANDLED;
                 default:
                     return NOT_HANDLED;
             }
-            return HANDLED;
         }
 
         @Override
@@ -616,17 +611,16 @@
                 case CMD_NETWORK_CONNECTED:
                     // Go straight to active as we've already evaluated.
                     transitionTo(mValidatedState);
-                    break;
+                    return HANDLED;
                 case CMD_LINGER_EXPIRED:
                     if (message.arg1 != mLingerToken)
-                        break;
+                        return HANDLED;
                     mConnectivityServiceHandler.sendMessage(
                             obtainMessage(EVENT_NETWORK_LINGER_COMPLETE, mNetworkAgentInfo));
-                    break;
+                    return HANDLED;
                 default:
                     return NOT_HANDLED;
             }
-            return HANDLED;
         }
     }
 
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 0f6b3ad..69caab9 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -21,9 +21,7 @@
 import static android.system.OsConstants.AF_INET6;
 
 import android.app.AppGlobals;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
+import android.app.AppOpsManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -35,10 +33,6 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
 import android.content.pm.UserInfo;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.drawable.Drawable;
-import android.net.BaseNetworkStateTracker;
 import android.net.ConnectivityManager;
 import android.net.IConnectivityManager;
 import android.net.INetworkManagementEventObserver;
@@ -51,7 +45,6 @@
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkMisc;
-import android.net.NetworkUtils;
 import android.net.RouteInfo;
 import android.net.UidRange;
 import android.os.Binder;
@@ -70,15 +63,15 @@
 import android.security.Credentials;
 import android.security.KeyStore;
 import android.util.Log;
-import android.util.SparseBooleanArray;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.R;
 import com.android.internal.net.LegacyVpnInfo;
 import com.android.internal.net.VpnConfig;
 import com.android.internal.net.VpnProfile;
 import com.android.server.net.BaseNetworkObserver;
 
+import libcore.io.IoUtils;
+
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
@@ -92,8 +85,6 @@
 import java.util.List;
 import java.util.concurrent.atomic.AtomicInteger;
 
-import libcore.io.IoUtils;
-
 /**
  * @hide
  */
@@ -114,8 +105,6 @@
     private boolean mAllowIPv6;
     private Connection mConnection;
     private LegacyVpnRunner mLegacyVpnRunner;
-    private PendingIntent mStatusIntent;
-    private volatile boolean mEnableNotif = true;
     private volatile boolean mEnableTeardown = true;
     private final IConnectivityManager mConnService;
     private final INetworkManagementService mNetd;
@@ -180,14 +169,6 @@
     }
 
     /**
-     * Set if this object is responsible for showing its own notifications. When
-     * {@code false}, notifications are handled externally by someone else.
-     */
-    public void setEnableNotifications(boolean enableNotif) {
-        mEnableNotif = enableNotif;
-    }
-
-    /**
      * Set if this object is responsible for watching for {@link NetworkInfo}
      * teardown. When {@code false}, teardown is handled externally by someone
      * else.
@@ -228,6 +209,20 @@
     public synchronized boolean prepare(String oldPackage, String newPackage) {
         // Return false if the package does not match.
         if (oldPackage != null && !oldPackage.equals(mPackage)) {
+            // The package doesn't match. If this VPN was not previously authorized, return false
+            // to force user authorization. Otherwise, revoke the VPN anyway.
+            if (!oldPackage.equals(VpnConfig.LEGACY_VPN) && isVpnUserPreConsented(oldPackage)) {
+                long token = Binder.clearCallingIdentity();
+                try {
+                    // This looks bizarre, but it is what ConfirmDialog in VpnDialogs is doing when
+                    // the user clicks through to allow the VPN to consent. So we are emulating the
+                    // action of the dialog without actually showing it.
+                    prepare(null, oldPackage);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+                return true;
+            }
             return false;
         }
 
@@ -240,11 +235,8 @@
         // Check if the caller is authorized.
         enforceControlPermission();
 
-        // Reset the interface and hide the notification.
+        // Reset the interface.
         if (mInterface != null) {
-            for (UidRange uidRange : mVpnUsers) {
-                hideNotification(uidRange.getStartUser());
-            }
             agentDisconnect();
             jniReset(mInterface);
             mInterface = null;
@@ -287,12 +279,46 @@
             Binder.restoreCallingIdentity(token);
         }
         mConfig = null;
+
         updateState(DetailedState.IDLE, "prepare");
         return true;
     }
 
+    /**
+     * Set whether the current package has the ability to launch VPNs without user intervention.
+     */
+    public void setPackageAuthorization(boolean authorized) {
+        // Check if the caller is authorized.
+        enforceControlPermission();
+
+        if (mPackage == null || VpnConfig.LEGACY_VPN.equals(mPackage)) {
+            return;
+        }
+
+        long token = Binder.clearCallingIdentity();
+        try {
+            AppOpsManager appOps =
+                    (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+            appOps.setMode(AppOpsManager.OP_ACTIVATE_VPN, mOwnerUID, mPackage,
+                    authorized ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
+        } catch (Exception e) {
+            Log.wtf(TAG, "Failed to set app ops for package " + mPackage, e);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    private boolean isVpnUserPreConsented(String packageName) {
+        AppOpsManager appOps =
+                (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+
+        // Verify that the caller matches the given package and has permission to activate VPNs.
+        return appOps.noteOpNoThrow(AppOpsManager.OP_ACTIVATE_VPN, Binder.getCallingUid(),
+                packageName) == AppOpsManager.MODE_ALLOWED;
+    }
+
     private int getAppUid(String app) {
-        if (app == VpnConfig.LEGACY_VPN) {
+        if (VpnConfig.LEGACY_VPN.equals(app)) {
             return Process.myUid();
         }
         PackageManager pm = mContext.getPackageManager();
@@ -355,9 +381,10 @@
         try {
             mNetworkAgent = new NetworkAgent(mLooper, mContext, NETWORKTYPE,
                     mNetworkInfo, mNetworkCapabilities, lp, 0, networkMisc) {
+                            @Override
                             public void unwanted() {
                                 // We are user controlled, not driven by NetworkRequest.
-                            };
+                            }
                         };
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -540,39 +567,6 @@
 
         // add the user
         mVpnUsers.add(UidRange.createForUser(user));
-
-        // show the notification
-        if (!mPackage.equals(VpnConfig.LEGACY_VPN)) {
-            // Load everything for the user's notification
-            PackageManager pm = mContext.getPackageManager();
-            ApplicationInfo app = null;
-            final long token = Binder.clearCallingIdentity();
-            try {
-                app = AppGlobals.getPackageManager().getApplicationInfo(mPackage, 0, mUserId);
-            } catch (RemoteException e) {
-                throw new IllegalStateException("Invalid application");
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-            String label = app.loadLabel(pm).toString();
-            // Load the icon and convert it into a bitmap.
-            Drawable icon = app.loadIcon(pm);
-            Bitmap bitmap = null;
-            if (icon.getIntrinsicWidth() > 0 && icon.getIntrinsicHeight() > 0) {
-                int width = mContext.getResources().getDimensionPixelSize(
-                        android.R.dimen.notification_large_icon_width);
-                int height = mContext.getResources().getDimensionPixelSize(
-                        android.R.dimen.notification_large_icon_height);
-                icon.setBounds(0, 0, width, height);
-                bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
-                Canvas c = new Canvas(bitmap);
-                icon.draw(c);
-                c.setBitmap(null);
-            }
-            showNotification(label, bitmap, user);
-        } else {
-            showNotification(null, null, user);
-        }
     }
 
     private void removeVpnUserLocked(int user) {
@@ -584,7 +578,6 @@
                 mNetworkAgent.removeUidRanges(new UidRange[] { uidRange });
             }
             mVpnUsers.remove(uidRange);
-            hideNotification(user);
     }
 
     private void onUserAdded(int userId) {
@@ -652,9 +645,6 @@
         public void interfaceRemoved(String interfaze) {
             synchronized (Vpn.this) {
                 if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) {
-                    for (UidRange uidRange : mVpnUsers) {
-                        hideNotification(uidRange.getStartUser());
-                    }
                     mVpnUsers = null;
                     mInterface = null;
                     if (mConnection != null) {
@@ -712,56 +702,6 @@
         }
     }
 
-    private void showNotification(String label, Bitmap icon, int user) {
-        if (!mEnableNotif) return;
-        final long token = Binder.clearCallingIdentity();
-        try {
-            mStatusIntent = VpnConfig.getIntentForStatusPanel(mContext);
-
-            NotificationManager nm = (NotificationManager)
-                    mContext.getSystemService(Context.NOTIFICATION_SERVICE);
-
-            if (nm != null) {
-                String title = (label == null) ? mContext.getString(R.string.vpn_title) :
-                        mContext.getString(R.string.vpn_title_long, label);
-                String text = (mConfig.session == null) ? mContext.getString(R.string.vpn_text) :
-                        mContext.getString(R.string.vpn_text_long, mConfig.session);
-
-                Notification notification = new Notification.Builder(mContext)
-                        .setSmallIcon(R.drawable.vpn_connected)
-                        .setLargeIcon(icon)
-                        .setContentTitle(title)
-                        .setContentText(text)
-                        .setContentIntent(mStatusIntent)
-                        .setDefaults(0)
-                        .setOngoing(true)
-                        .setColor(mContext.getResources().getColor(
-                                com.android.internal.R.color.system_notification_accent_color))
-                        .build();
-                nm.notifyAsUser(null, R.drawable.vpn_connected, notification, new UserHandle(user));
-            }
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    private void hideNotification(int user) {
-        if (!mEnableNotif) return;
-        mStatusIntent = null;
-
-        NotificationManager nm = (NotificationManager)
-                mContext.getSystemService(Context.NOTIFICATION_SERVICE);
-
-        if (nm != null) {
-            final long token = Binder.clearCallingIdentity();
-            try {
-                nm.cancelAsUser(null, R.drawable.vpn_connected, new UserHandle(user));
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-    }
-
     public synchronized boolean addAddress(String address, int prefixLength) {
         if (Binder.getCallingUid() != mOwnerUID || mInterface == null || mNetworkAgent == null) {
             return false;
@@ -971,9 +911,6 @@
         final LegacyVpnInfo info = new LegacyVpnInfo();
         info.key = mConfig.user;
         info.state = LegacyVpnInfo.stateFromNetworkInfo(mNetworkInfo);
-        if (mNetworkInfo.isConnected()) {
-            info.intent = mStatusIntent;
-        }
         return info;
     }
 
@@ -1263,6 +1200,7 @@
                 }
             } catch (Exception e) {
                 Log.i(TAG, "Aborting", e);
+                updateState(DetailedState.FAILED, e.getMessage());
                 exit();
             } finally {
                 // Kill the daemons if they fail to stop.
diff --git a/services/core/java/com/android/server/net/LockdownVpnTracker.java b/services/core/java/com/android/server/net/LockdownVpnTracker.java
index 52e741b..04df3e7 100644
--- a/services/core/java/com/android/server/net/LockdownVpnTracker.java
+++ b/services/core/java/com/android/server/net/LockdownVpnTracker.java
@@ -205,7 +205,6 @@
     private void initLocked() {
         Slog.d(TAG, "initLocked()");
 
-        mVpn.setEnableNotifications(false);
         mVpn.setEnableTeardown(false);
 
         final IntentFilter resetFilter = new IntentFilter(ACTION_LOCKDOWN_RESET);
@@ -249,7 +248,6 @@
         hideNotification();
 
         mContext.unregisterReceiver(mResetReceiver);
-        mVpn.setEnableNotifications(true);
         mVpn.setEnableTeardown(true);
     }
 
diff --git a/telecomm/java/android/telecomm/AudioState.java b/telecomm/java/android/telecomm/AudioState.java
index 491af14..a5fda79 100644
--- a/telecomm/java/android/telecomm/AudioState.java
+++ b/telecomm/java/android/telecomm/AudioState.java
@@ -26,25 +26,25 @@
  */
 public final class AudioState implements Parcelable {
     /** Direct the audio stream through the device's earpiece. */
-    public static int ROUTE_EARPIECE      = 0x00000001;
+    public static final int ROUTE_EARPIECE      = 0x00000001;
 
     /** Direct the audio stream through Bluetooth. */
-    public static int ROUTE_BLUETOOTH     = 0x00000002;
+    public static final int ROUTE_BLUETOOTH     = 0x00000002;
 
     /** Direct the audio stream through a wired headset. */
-    public static int ROUTE_WIRED_HEADSET = 0x00000004;
+    public static final int ROUTE_WIRED_HEADSET = 0x00000004;
 
     /** Direct the audio stream through the device's spakerphone. */
-    public static int ROUTE_SPEAKER       = 0x00000008;
+    public static final int ROUTE_SPEAKER       = 0x00000008;
 
     /**
      * Direct the audio stream through the device's earpiece or wired headset if one is
      * connected.
      */
-    public static int ROUTE_WIRED_OR_EARPIECE = ROUTE_EARPIECE | ROUTE_WIRED_HEADSET;
+    public static final int ROUTE_WIRED_OR_EARPIECE = ROUTE_EARPIECE | ROUTE_WIRED_HEADSET;
 
     /** Bit mask of all possible audio routes. */
-    public static int ROUTE_ALL = ROUTE_EARPIECE | ROUTE_BLUETOOTH | ROUTE_WIRED_HEADSET |
+    public static final int ROUTE_ALL = ROUTE_EARPIECE | ROUTE_BLUETOOTH | ROUTE_WIRED_HEADSET |
             ROUTE_SPEAKER;
 
     /** True if the call is muted, false otherwise. */
diff --git a/telecomm/java/android/telecomm/Connection.java b/telecomm/java/android/telecomm/Connection.java
index c1d5715..c307a25 100644
--- a/telecomm/java/android/telecomm/Connection.java
+++ b/telecomm/java/android/telecomm/Connection.java
@@ -65,8 +65,6 @@
     // Flag controlling whether PII is emitted into the logs
     private static final boolean PII_DEBUG = Log.isLoggable(android.util.Log.DEBUG);
 
-    private static Connection sNullConnection;
-
     /** @hide */
     public abstract static class Listener {
         public void onStateChanged(Connection c, int state) {}
@@ -476,9 +474,8 @@
     private boolean mAudioModeIsVoip;
     private StatusHints mStatusHints;
     private int mVideoState;
-    private int mFailureCode;
-    private String mFailureMessage;
-    private boolean mIsCanceled;
+    private int mDisconnectCause;
+    private String mDisconnectMessage;
     private Conference mConference;
     private ConnectionService mConnectionService;
 
@@ -604,17 +601,17 @@
     }
 
     /**
-     * @return The failure code ({@see DisconnectCause}) associated with this failed connection.
+     * @return The {@link DisconnectCause} for this connection.
      */
-    public final int getFailureCode() {
-        return mFailureCode;
+    public final int getDisconnectCause() {
+        return mDisconnectCause;
     }
 
     /**
-     * @return The reason for the connection failure. This will not be displayed to the user.
+     * @return The disconnect message for this connection.
      */
-    public final String getFailureMessage() {
-        return mFailureMessage;
+    public final String getDisconnectMessage() {
+        return mDisconnectMessage;
     }
 
     /**
@@ -778,6 +775,8 @@
      * @param message Optional call-service-provided message about the disconnect.
      */
     public final void setDisconnected(int cause, String message) {
+        mDisconnectCause = cause;
+        mDisconnectMessage = message;
         setState(STATE_DISCONNECTED);
         Log.d(this, "Disconnected with cause %d message %s", cause, message);
         for (Listener l : mListeners) {
@@ -1060,13 +1059,6 @@
         return builder.toString();
     }
 
-    static synchronized Connection getNullConnection() {
-        if (sNullConnection == null) {
-            sNullConnection = new Connection() {};
-        }
-        return sNullConnection;
-    }
-
     private void setState(int state) {
         if (mState == STATE_DISCONNECTED && mState != state) {
             Log.d(this, "Connection already DISCONNECTED; cannot transition out of this state.");
@@ -1082,31 +1074,29 @@
         }
     }
 
-    static class FailureSignalingConnection extends Connection {
-        public FailureSignalingConnection(int code, String message) {
-            setDisconnected(code, message);
+    private static class FailureSignalingConnection extends Connection {
+        public FailureSignalingConnection(int cause, String message) {
+            setDisconnected(cause, message);
         }
     }
 
     /**
      * Return a {@code Connection} which represents a failed connection attempt. The returned
-     * {@code Connection} will have a {@link #getFailureCode()} and {@link #getFailureMessage()}
-     * as specified, a {@link #getState()} of {@link #STATE_DISCONNECTED}.
+     * {@code Connection} will have a {@link #getDisconnectCause()} and
+     * {@link #getDisconnectMessage()} as specified, and a {@link #getState()} of
+     * {@link #STATE_DISCONNECTED}.
      * <p>
      * The returned {@code Connection} can be assumed to {@link #destroy()} itself when appropriate,
      * so users of this method need not maintain a reference to its return value to destroy it.
      *
-     * @param code The failure code ({@see DisconnectCause}).
+     * @param cause The disconnect cause, ({@see DisconnectCause}).
      * @param message A reason for why the connection failed (not intended to be shown to the user).
      * @return A {@code Connection} which indicates failure.
      */
-    public static Connection createFailedConnection(final int code, final String message) {
-        return new FailureSignalingConnection(code, message);
+    public static Connection createFailedConnection(int cause, String message) {
+        return new FailureSignalingConnection(cause, message);
     }
 
-    private static final Connection CANCELED_CONNECTION =
-            new FailureSignalingConnection(DisconnectCause.OUTGOING_CANCELED, null);
-
     /**
      * Return a {@code Connection} which represents a canceled connection attempt. The returned
      * {@code Connection} will have state {@link #STATE_DISCONNECTED}, and cannot be moved out of
@@ -1119,7 +1109,7 @@
      * @return A {@code Connection} which indicates that the underlying call should be canceled.
      */
     public static Connection createCanceledConnection() {
-        return CANCELED_CONNECTION;
+        return new FailureSignalingConnection(DisconnectCause.OUTGOING_CANCELED, null);
     }
 
     private final void  fireOnConferenceableConnectionsChanged() {
diff --git a/telecomm/java/android/telecomm/ConnectionService.java b/telecomm/java/android/telecomm/ConnectionService.java
index 03b6c7b..c805978 100644
--- a/telecomm/java/android/telecomm/ConnectionService.java
+++ b/telecomm/java/android/telecomm/ConnectionService.java
@@ -73,6 +73,8 @@
     private static final int MSG_ON_PHONE_ACCOUNT_CLICKED = 15;
     private static final int MSG_REMOVE_CONNECTION_SERVICE_ADAPTER = 16;
 
+    private static Connection sNullConnection;
+
     private final Map<String, Connection> mConnectionById = new HashMap<>();
     private final Map<Connection, String> mIdByConnection = new HashMap<>();
     private final Map<String, Conference> mConferenceById = new HashMap<>();
@@ -498,36 +500,29 @@
             final String callId,
             final ConnectionRequest request,
             boolean isIncoming) {
-        Log.d(this, "call %s", request);
+        Log.d(this, "createConnection, callManagerAccount: %s, callId: %s, request: %s, " +
+                "isIncoming: %b", callManagerAccount, callId, request, isIncoming);
 
-        Connection createdConnection = isIncoming
+        Connection connection = isIncoming
                 ? onCreateIncomingConnection(callManagerAccount, request)
                 : onCreateOutgoingConnection(callManagerAccount, request);
-
-        if (createdConnection == null) {
-            Log.d(this, "adapter handleCreateConnectionComplete CANCELED %s", callId);
-            // Tell telecomm to try a different service.
-            createdConnection = Connection.createCanceledConnection();
+        Log.d(this, "createConnection, connection: %s", connection);
+        if (connection == null) {
+            connection = Connection.createFailedConnection(DisconnectCause.OUTGOING_FAILURE, null);
         }
-        connectionCreated(callId, request, createdConnection);
-    }
 
-    private void connectionCreated(
-            String callId,
-            ConnectionRequest request,
-            Connection connection) {
-        if (!(connection instanceof Connection.FailureSignalingConnection)) {
+        if (connection.getState() != Connection.STATE_DISCONNECTED) {
             addConnection(callId, connection);
         }
 
         Uri handle = connection.getHandle();
         String number = handle == null ? "null" : handle.getSchemeSpecificPart();
-        Log.v(this, "connectionCreated, parcelableconnection: %s, %d, %s",
+        Log.v(this, "createConnection, number: %s, state: %s, capabilities: %s",
                 Connection.toLogSafePhoneNumber(number),
-                connection.getState(),
+                Connection.stateToString(connection.getState()),
                 PhoneCapabilities.toString(connection.getCallCapabilities()));
 
-        Log.d(this, "adapter handleCreateConnectionSuccessful %s", callId);
+        Log.d(this, "createConnection, calling handleCreateConnectionSuccessful %s", callId);
         mAdapter.handleCreateConnectionComplete(
                 callId,
                 request,
@@ -545,8 +540,8 @@
                         connection.isRequestingRingback(),
                         connection.getAudioModeIsVoip(),
                         connection.getStatusHints(),
-                        connection.getFailureCode(),
-                        connection.getFailureMessage()));
+                        connection.getDisconnectCause(),
+                        connection.getDisconnectMessage()));
     }
 
     private void abort(String callId) {
@@ -598,13 +593,13 @@
         Log.d(this, "conference %s, %s", callId1, callId2);
 
         Connection connection1 = findConnectionForAction(callId1, "conference");
-        if (connection1 == Connection.getNullConnection()) {
+        if (connection1 == getNullConnection()) {
             Log.w(this, "Connection1 missing in conference request %s.", callId1);
             return;
         }
 
         Connection connection2 = findConnectionForAction(callId2, "conference");
-        if (connection2 == Connection.getNullConnection()) {
+        if (connection2 == getNullConnection()) {
             Log.w(this, "Connection2 missing in conference request %s.", callId2);
             return;
         }
@@ -616,7 +611,7 @@
         Log.d(this, "splitFromConference(%s)", callId);
 
         Connection connection = findConnectionForAction(callId, "splitFromConference");
-        if (connection == Connection.getNullConnection()) {
+        if (connection == getNullConnection()) {
             Log.w(this, "Connection missing in conference request %s.", callId);
             return;
         }
@@ -881,6 +876,13 @@
             return mConnectionById.get(callId);
         }
         Log.w(this, "%s - Cannot find Connection %s", action, callId);
-        return Connection.getNullConnection();
+        return getNullConnection();
+    }
+
+    static synchronized Connection getNullConnection() {
+        if (sNullConnection == null) {
+            sNullConnection = new Connection() {};
+        }
+        return sNullConnection;
     }
 }
diff --git a/telecomm/java/android/telecomm/ParcelableConnection.java b/telecomm/java/android/telecomm/ParcelableConnection.java
index 30ff5be..812ee55 100644
--- a/telecomm/java/android/telecomm/ParcelableConnection.java
+++ b/telecomm/java/android/telecomm/ParcelableConnection.java
@@ -41,8 +41,8 @@
     private boolean mRequestingRingback;
     private boolean mAudioModeIsVoip;
     private StatusHints mStatusHints;
-    private int mFailureCode;
-    private String mFailureMessage;
+    private int mDisconnectCause;
+    private String mDisconnectMessage;
 
     /** @hide */
     public ParcelableConnection(
@@ -58,8 +58,8 @@
             boolean requestingRingback,
             boolean audioModeIsVoip,
             StatusHints statusHints,
-            int failureCode,
-            String failureMessage) {
+            int disconnectCause,
+            String disconnectMessage) {
         mPhoneAccount = phoneAccount;
         mState = state;
         mCapabilities = capabilities;
@@ -72,8 +72,8 @@
         mRequestingRingback = requestingRingback;
         mAudioModeIsVoip = audioModeIsVoip;
         mStatusHints = statusHints;
-        mFailureCode = failureCode;
-        mFailureMessage = failureMessage;
+        mDisconnectCause = disconnectCause;
+        mDisconnectMessage = disconnectMessage;
     }
 
     public PhoneAccountHandle getPhoneAccount() {
@@ -125,12 +125,12 @@
         return mStatusHints;
     }
 
-    public final int getFailureCode() {
-        return mFailureCode;
+    public final int getDisconnectCause() {
+        return mDisconnectCause;
     }
 
-    public final String getFailureMessage() {
-        return mFailureMessage;
+    public final String getDisconnectMessage() {
+        return mDisconnectMessage;
     }
 
     @Override
@@ -212,7 +212,7 @@
         destination.writeByte((byte) (mRequestingRingback ? 1 : 0));
         destination.writeByte((byte) (mAudioModeIsVoip ? 1 : 0));
         destination.writeParcelable(mStatusHints, 0);
-        destination.writeInt(mFailureCode);
-        destination.writeString(mFailureMessage);
+        destination.writeInt(mDisconnectCause);
+        destination.writeString(mDisconnectMessage);
     }
 }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index d4f8362..0eb170c 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -617,6 +617,37 @@
     }
 
     /**
+     * Returns the IMEI. Return null if IMEI is not available.
+     *
+     * <p>Requires Permission:
+     *   {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     */
+    /** {@hide} */
+    public String getImei() {
+        return getImei(getDefaultSim());
+    }
+
+    /**
+     * Returns the IMEI. Return null if IMEI is not available.
+     *
+     * <p>Requires Permission:
+     *   {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     *
+     * @param slotId of which deviceID is returned
+     */
+    /** {@hide} */
+    public String getImei(int slotId) {
+        long[] subId = SubscriptionManager.getSubId(slotId);
+        try {
+            return getSubscriberInfo().getImeiUsingSubId(subId[0]);
+        } catch (RemoteException ex) {
+            return null;
+        } catch (NullPointerException ex) {
+            return null;
+        }
+    }
+
+    /**
      * Returns the current location of the device.
      *<p>
      * If there is only one radio in the device and that radio has an LTE connection,
diff --git a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
index 4734965..552abaf 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
@@ -33,6 +33,10 @@
      */
     String getDeviceIdUsingSubId(long subId);
 
+    /**
+     * Retrieves the IMEI.
+     */
+    String getImeiUsingSubId(long subId);
 
     /**
      * Retrieves the software version number for the device, e.g., IMEI/SV
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index a561dc2..a01a6b6 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -18,8 +18,8 @@
 
 import android.net.wifi.passpoint.WifiPasspointInfo;
 import android.net.wifi.passpoint.WifiPasspointManager;
-import android.os.Parcelable;
 import android.os.Parcel;
+import android.os.Parcelable;
 
 /**
  * Describes information about a detected access point. In addition
@@ -48,7 +48,10 @@
      */
     public String capabilities;
     /**
-     * The detected signal level in dBm.
+     * The detected signal level in dBm, also known as the RSSI.
+     *
+     * <p>Use {@link android.net.wifi.WifiManager#calculateSignalLevel} to convert this number into
+     * an absolute signal level which can be displayed to a user.
      */
     public int level;
     /**
diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java
index e808136..44a7108 100644
--- a/wifi/java/android/net/wifi/WifiInfo.java
+++ b/wifi/java/android/net/wifi/WifiInfo.java
@@ -323,7 +323,11 @@
     /**
      * Returns the received signal strength indicator of the current 802.11
      * network, in dBm.
-     * @return the RSSI, in the range -127 to 200
+     *
+     * <p>Use {@link android.net.wifi.WifiManager#calculateSignalLevel} to convert this number into
+     * an absolute signal level which can be displayed to a user.
+     *
+     * @return the RSSI.
      */
     public int getRssi() {
         return mRssi;