Merge "Expand scrim all the way to the right" into mnc-dev
diff --git a/api/current.txt b/api/current.txt
index 32af4c1..f73d56f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3449,6 +3449,7 @@
     method public boolean onPreparePanel(int, android.view.View, android.view.Menu);
     method public void onProvideAssistContent(android.app.assist.AssistContent);
     method public void onProvideAssistData(android.os.Bundle);
+    method public android.net.Uri onProvideReferrer();
     method public void onRequestPermissionsResult(int, java.lang.String[], int[]);
     method protected void onRestart();
     method protected void onRestoreInstanceState(android.os.Bundle);
@@ -4469,6 +4470,7 @@
     method public boolean onHasView();
     method public boolean onHasWindowAnimations();
     method public void onInvalidateOptionsMenu();
+    method public void onRequestPermissionsFromFragment(android.app.Fragment, java.lang.String[], int);
     method public boolean onShouldSaveFragmentState(android.app.Fragment);
     method public void onStartActivityFromFragment(android.app.Fragment, android.content.Intent, int, android.os.Bundle);
     method public boolean onUseFragmentManagerInflaterFactory();
@@ -5489,8 +5491,11 @@
     field public static final int MODE_NIGHT_YES = 2; // 0x2
   }
 
-  public class VoiceInteractor {
+  public final class VoiceInteractor {
+    method public android.app.VoiceInteractor.Request getActiveRequest(java.lang.String);
+    method public android.app.VoiceInteractor.Request[] getActiveRequests();
     method public boolean submitRequest(android.app.VoiceInteractor.Request);
+    method public boolean submitRequest(android.app.VoiceInteractor.Request, java.lang.String);
     method public boolean[] supportsCommands(java.lang.String[]);
   }
 
@@ -5553,6 +5558,7 @@
     method public void cancel();
     method public android.app.Activity getActivity();
     method public android.content.Context getContext();
+    method public java.lang.String getName();
     method public void onAttached(android.app.Activity);
     method public void onCancel();
     method public void onDetached();
@@ -39236,8 +39242,8 @@
     method public deprecated void onGlobalFocusChanged(android.view.View, android.view.View);
     method public void onPause();
     method public void onResume();
-    method public boolean overlayHorizontalScrollbar();
-    method public boolean overlayVerticalScrollbar();
+    method public deprecated boolean overlayHorizontalScrollbar();
+    method public deprecated boolean overlayVerticalScrollbar();
     method public boolean pageDown(boolean);
     method public boolean pageUp(boolean);
     method public void pauseTimers();
@@ -39257,13 +39263,13 @@
     method public deprecated void setCertificate(android.net.http.SslCertificate);
     method public void setDownloadListener(android.webkit.DownloadListener);
     method public void setFindListener(android.webkit.WebView.FindListener);
-    method public void setHorizontalScrollbarOverlay(boolean);
+    method public deprecated void setHorizontalScrollbarOverlay(boolean);
     method public void setHttpAuthUsernamePassword(java.lang.String, java.lang.String, java.lang.String, java.lang.String);
     method public void setInitialScale(int);
     method public deprecated void setMapTrackballToArrowKeys(boolean);
     method public void setNetworkAvailable(boolean);
     method public deprecated void setPictureListener(android.webkit.WebView.PictureListener);
-    method public void setVerticalScrollbarOverlay(boolean);
+    method public deprecated void setVerticalScrollbarOverlay(boolean);
     method public void setWebChromeClient(android.webkit.WebChromeClient);
     method public static void setWebContentsDebuggingEnabled(boolean);
     method public void setWebViewClient(android.webkit.WebViewClient);
diff --git a/api/system-current.txt b/api/system-current.txt
index cff5c99..e2d89a0 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3535,6 +3535,7 @@
     method public boolean onPreparePanel(int, android.view.View, android.view.Menu);
     method public void onProvideAssistContent(android.app.assist.AssistContent);
     method public void onProvideAssistData(android.os.Bundle);
+    method public android.net.Uri onProvideReferrer();
     method public void onRequestPermissionsResult(int, java.lang.String[], int[]);
     method protected void onRestart();
     method protected void onRestoreInstanceState(android.os.Bundle);
@@ -4565,6 +4566,7 @@
     method public boolean onHasView();
     method public boolean onHasWindowAnimations();
     method public void onInvalidateOptionsMenu();
+    method public void onRequestPermissionsFromFragment(android.app.Fragment, java.lang.String[], int);
     method public boolean onShouldSaveFragmentState(android.app.Fragment);
     method public void onStartActivityFromFragment(android.app.Fragment, android.content.Intent, int, android.os.Bundle);
     method public boolean onUseFragmentManagerInflaterFactory();
@@ -5585,8 +5587,11 @@
     field public static final int MODE_NIGHT_YES = 2; // 0x2
   }
 
-  public class VoiceInteractor {
+  public final class VoiceInteractor {
+    method public android.app.VoiceInteractor.Request getActiveRequest(java.lang.String);
+    method public android.app.VoiceInteractor.Request[] getActiveRequests();
     method public boolean submitRequest(android.app.VoiceInteractor.Request);
+    method public boolean submitRequest(android.app.VoiceInteractor.Request, java.lang.String);
     method public boolean[] supportsCommands(java.lang.String[]);
   }
 
@@ -5649,6 +5654,7 @@
     method public void cancel();
     method public android.app.Activity getActivity();
     method public android.content.Context getContext();
+    method public java.lang.String getName();
     method public void onAttached(android.app.Activity);
     method public void onCancel();
     method public void onDetached();
@@ -41581,8 +41587,8 @@
     method public deprecated void onGlobalFocusChanged(android.view.View, android.view.View);
     method public void onPause();
     method public void onResume();
-    method public boolean overlayHorizontalScrollbar();
-    method public boolean overlayVerticalScrollbar();
+    method public deprecated boolean overlayHorizontalScrollbar();
+    method public deprecated boolean overlayVerticalScrollbar();
     method public boolean pageDown(boolean);
     method public boolean pageUp(boolean);
     method public void pauseTimers();
@@ -41602,13 +41608,13 @@
     method public deprecated void setCertificate(android.net.http.SslCertificate);
     method public void setDownloadListener(android.webkit.DownloadListener);
     method public void setFindListener(android.webkit.WebView.FindListener);
-    method public void setHorizontalScrollbarOverlay(boolean);
+    method public deprecated void setHorizontalScrollbarOverlay(boolean);
     method public void setHttpAuthUsernamePassword(java.lang.String, java.lang.String, java.lang.String, java.lang.String);
     method public void setInitialScale(int);
     method public deprecated void setMapTrackballToArrowKeys(boolean);
     method public void setNetworkAvailable(boolean);
     method public deprecated void setPictureListener(android.webkit.WebView.PictureListener);
-    method public void setVerticalScrollbarOverlay(boolean);
+    method public deprecated void setVerticalScrollbarOverlay(boolean);
     method public void setWebChromeClient(android.webkit.WebChromeClient);
     method public static void setWebContentsDebuggingEnabled(boolean);
     method public void setWebViewClient(android.webkit.WebViewClient);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 90567c7..1b4ee2e 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -690,6 +690,8 @@
     private static final String SAVED_DIALOG_KEY_PREFIX = "android:dialog_";
     private static final String SAVED_DIALOG_ARGS_KEY_PREFIX = "android:dialog_args_";
 
+    private static final String REQUEST_PERMISSIONS_WHO_PREFIX = "@android:requestPermissions:";
+
     private static class ManagedDialog {
         Dialog mDialog;
         Bundle mArgs;
@@ -3751,7 +3753,7 @@
      */
     public final void requestPermissions(@NonNull String[] permissions, int requestCode) {
         Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions);
-        startActivityForResult(intent, requestCode);
+        startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null);
     }
 
     /**
@@ -4279,6 +4281,10 @@
         if (mParent == null) {
             int result = ActivityManager.START_RETURN_INTENT_TO_CALLER;
             try {
+                Uri referrer = onProvideReferrer();
+                if (referrer != null) {
+                    intent.putExtra(Intent.EXTRA_REFERRER, referrer);
+                }
                 intent.migrateExtraStreamToClipData();
                 intent.prepareToLeaveProcess();
                 result = ActivityManagerNative.getDefault()
@@ -4463,6 +4469,10 @@
     @Override
     public void startActivityForResult(
             String who, Intent intent, int requestCode, @Nullable Bundle options) {
+        Uri referrer = onProvideReferrer();
+        if (referrer != null) {
+            intent.putExtra(Intent.EXTRA_REFERRER, referrer);
+        }
         Instrumentation.ActivityResult ar =
             mInstrumentation.execStartActivity(
                 this, mMainThread.getApplicationThread(), mToken, who,
@@ -4616,6 +4626,16 @@
     }
 
     /**
+     * Override to generate the desired referrer for the content currently being shown
+     * by the app.  The default implementation returns null, meaning the referrer will simply
+     * be the android-app: of the package name of this activity.  Return a non-null Uri to
+     * have that supplied as the {@link Intent#EXTRA_REFERRER} of any activities started from it.
+     */
+    public Uri onProvideReferrer() {
+        return null;
+    }
+
+    /**
      * Return the name of the package that invoked this activity.  This is who
      * the data in {@link #setResult setResult()} will be sent to.  You can
      * use this information to validate that the recipient is allowed to
@@ -6330,32 +6350,32 @@
             + ", resCode=" + resultCode + ", data=" + data);
         mFragments.noteStateNotSaved();
         if (who == null) {
-            if (isRequestPermissionResult(data)) {
+            onActivityResult(requestCode, resultCode, data);
+        } else if (who.startsWith(REQUEST_PERMISSIONS_WHO_PREFIX)) {
+            who = who.substring(REQUEST_PERMISSIONS_WHO_PREFIX.length());
+            if (TextUtils.isEmpty(who)) {
                 dispatchRequestPermissionsResult(requestCode, data);
             } else {
-                onActivityResult(requestCode, resultCode, data);
-            }
-        } else {
-            if (who.startsWith("@android:view:")) {
-                ArrayList<ViewRootImpl> views = WindowManagerGlobal.getInstance().getRootViews(
-                        getActivityToken());
-                for (ViewRootImpl viewRoot : views) {
-                    if (viewRoot.getView() != null
-                            && viewRoot.getView().dispatchActivityResult(
-                                    who, requestCode, resultCode, data)) {
-                        return;
-                    }
-                }
-            } else {
                 Fragment frag = mFragments.findFragmentByWho(who);
                 if (frag != null) {
-                    if (isRequestPermissionResult(data)) {
-                        dispatchRequestPermissionsResultToFragment(requestCode, data, frag);
-                    } else {
-                        frag.onActivityResult(requestCode, resultCode, data);
-                    }
+                    dispatchRequestPermissionsResultToFragment(requestCode, data, frag);
                 }
             }
+        } else if (who.startsWith("@android:view:")) {
+            ArrayList<ViewRootImpl> views = WindowManagerGlobal.getInstance().getRootViews(
+                    getActivityToken());
+            for (ViewRootImpl viewRoot : views) {
+                if (viewRoot.getView() != null
+                        && viewRoot.getView().dispatchActivityResult(
+                                who, requestCode, resultCode, data)) {
+                    return;
+                }
+            }
+        } else {
+            Fragment frag = mFragments.findFragmentByWho(who);
+            if (frag != null) {
+                frag.onActivityResult(requestCode, resultCode, data);
+            }
         }
     }
 
@@ -6466,11 +6486,6 @@
         fragement.onRequestPermissionsResult(requestCode, permissions, grantResults);
     }
 
-    private static boolean isRequestPermissionResult(Intent intent) {
-        return intent != null
-                && PackageManager.ACTION_REQUEST_PERMISSIONS.equals(intent.getAction());
-    }
-
     class HostCallbacks extends FragmentHostCallback<Activity> {
         public HostCallbacks() {
             super(Activity.this /*activity*/);
@@ -6518,6 +6533,14 @@
         }
 
         @Override
+        public void onRequestPermissionsFromFragment(Fragment fragment, String[] permissions,
+                int requestCode) {
+            String who = REQUEST_PERMISSIONS_WHO_PREFIX + fragment.mWho;
+            Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions);
+            startActivityForResult(who, intent, requestCode, null);
+        }
+
+        @Override
         public boolean onHasWindowAnimations() {
             return getWindow() != null;
         }
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 3892dd9..da345a6 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -279,51 +279,54 @@
      * all activities that are visible to the user. */
     public static final int PROCESS_STATE_TOP = 2;
 
+    /** @hide Process is hosting a foreground service due to a system binding. */
+    public static final int PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 3;
+
     /** @hide Process is hosting a foreground service. */
-    public static final int PROCESS_STATE_FOREGROUND_SERVICE = 3;
+    public static final int PROCESS_STATE_FOREGROUND_SERVICE = 4;
 
     /** @hide Same as {@link #PROCESS_STATE_TOP} but while device is sleeping. */
-    public static final int PROCESS_STATE_TOP_SLEEPING = 4;
+    public static final int PROCESS_STATE_TOP_SLEEPING = 5;
 
     /** @hide Process is important to the user, and something they are aware of. */
-    public static final int PROCESS_STATE_IMPORTANT_FOREGROUND = 5;
+    public static final int PROCESS_STATE_IMPORTANT_FOREGROUND = 6;
 
     /** @hide Process is important to the user, but not something they are aware of. */
-    public static final int PROCESS_STATE_IMPORTANT_BACKGROUND = 6;
+    public static final int PROCESS_STATE_IMPORTANT_BACKGROUND = 7;
 
     /** @hide Process is in the background running a backup/restore operation. */
-    public static final int PROCESS_STATE_BACKUP = 7;
+    public static final int PROCESS_STATE_BACKUP = 8;
 
     /** @hide Process is in the background, but it can't restore its state so we want
      * to try to avoid killing it. */
-    public static final int PROCESS_STATE_HEAVY_WEIGHT = 8;
+    public static final int PROCESS_STATE_HEAVY_WEIGHT = 9;
 
     /** @hide Process is in the background running a service.  Unlike oom_adj, this level
      * is used for both the normal running in background state and the executing
      * operations state. */
-    public static final int PROCESS_STATE_SERVICE = 9;
+    public static final int PROCESS_STATE_SERVICE = 10;
 
     /** @hide Process is in the background running a receiver.   Note that from the
      * perspective of oom_adj receivers run at a higher foreground level, but for our
      * prioritization here that is not necessary and putting them below services means
      * many fewer changes in some process states as they receive broadcasts. */
-    public static final int PROCESS_STATE_RECEIVER = 10;
+    public static final int PROCESS_STATE_RECEIVER = 11;
 
     /** @hide Process is in the background but hosts the home activity. */
-    public static final int PROCESS_STATE_HOME = 11;
+    public static final int PROCESS_STATE_HOME = 12;
 
     /** @hide Process is in the background but hosts the last shown activity. */
-    public static final int PROCESS_STATE_LAST_ACTIVITY = 12;
+    public static final int PROCESS_STATE_LAST_ACTIVITY = 13;
 
     /** @hide Process is being cached for later use and contains activities. */
-    public static final int PROCESS_STATE_CACHED_ACTIVITY = 13;
+    public static final int PROCESS_STATE_CACHED_ACTIVITY = 14;
 
     /** @hide Process is being cached for later use and is a client of another cached
      * process that contains activities. */
-    public static final int PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 14;
+    public static final int PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 15;
 
     /** @hide Process is being cached for later use and is empty. */
-    public static final int PROCESS_STATE_CACHED_EMPTY = 15;
+    public static final int PROCESS_STATE_CACHED_EMPTY = 16;
 
     /** @hide requestType for assist context: only basic information. */
     public static final int ASSIST_CONTEXT_BASIC = 0;
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index dabcc50c..6ae21eb 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2197,7 +2197,8 @@
             Bundle extras = data.readBundle();
             AssistStructure structure = AssistStructure.CREATOR.createFromParcel(data);
             AssistContent content = AssistContent.CREATOR.createFromParcel(data);
-            reportAssistContextExtras(token, extras, structure, content);
+            Uri referrer = data.readInt() != 0 ? Uri.CREATOR.createFromParcel(data) : null;
+            reportAssistContextExtras(token, extras, structure, content, referrer);
             reply.writeNoException();
             return true;
         }
@@ -5367,7 +5368,7 @@
     }
 
     public void reportAssistContextExtras(IBinder token, Bundle extras, AssistStructure structure,
-            AssistContent content) throws RemoteException {
+            AssistContent content, Uri referrer) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
@@ -5375,6 +5376,12 @@
         data.writeBundle(extras);
         structure.writeToParcel(data, 0);
         content.writeToParcel(data, 0);
+        if (referrer != null) {
+            data.writeInt(1);
+            referrer.writeToParcel(data, 0);
+        } else {
+            data.writeInt(0);
+        }
         mRemote.transact(REPORT_ASSIST_CONTEXT_EXTRAS_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index ffb3fb8..e21c04a 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2572,9 +2572,11 @@
         AssistStructure structure = null;
         AssistContent content = new AssistContent();
         ActivityClientRecord r = mActivities.get(cmd.activityToken);
+        Uri referrer = null;
         if (r != null) {
             r.activity.getApplication().dispatchOnProvideAssistData(r.activity, data);
             r.activity.onProvideAssistData(data);
+            referrer = r.activity.onProvideReferrer();
             if (cmd.requestType == ActivityManager.ASSIST_CONTEXT_FULL) {
                 structure = new AssistStructure(r.activity);
                 Intent activityIntent = r.activity.getIntent();
@@ -2597,7 +2599,7 @@
         }
         IActivityManager mgr = ActivityManagerNative.getDefault();
         try {
-            mgr.reportAssistContextExtras(cmd.requestToken, data, structure, content);
+            mgr.reportAssistContextExtras(cmd.requestToken, data, structure, content, referrer);
         } catch (RemoteException e) {
         }
     }
diff --git a/core/java/android/app/AssistContent.java b/core/java/android/app/AssistContent.java
index d23e73d..173b237 100644
--- a/core/java/android/app/AssistContent.java
+++ b/core/java/android/app/AssistContent.java
@@ -68,8 +68,10 @@
         setWebUri(null);
         if (intent != null && Intent.ACTION_VIEW.equals(intent.getAction())) {
             Uri uri = intent.getData();
-            if ("http".equals(uri.getScheme()) || "https".equals(uri.getScheme())) {
-                setWebUri(uri);
+            if (uri != null) {
+                if ("http".equals(uri.getScheme()) || "https".equals(uri.getScheme())) {
+                    setWebUri(uri);
+                }
             }
         }
     }
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 829b098..be36af7 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1745,7 +1745,8 @@
         mResourcesManager = ResourcesManager.getInstance();
 
         final int displayId = (createDisplayWithId != Display.INVALID_DISPLAY)
-                ? createDisplayWithId : getDisplayId();
+                ? createDisplayWithId
+                : (display != null) ? display.getDisplayId() : Display.DEFAULT_DISPLAY;
 
         CompatibilityInfo compatInfo = null;
         if (container != null) {
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index 26d4fd4..95b3b8e 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -1200,9 +1200,7 @@
         if (mHost == null) {
             throw new IllegalStateException("Fragment " + this + " not attached to Activity");
         }
-        Intent intent =
-                mHost.getContext().getPackageManager().buildRequestPermissionsIntent(permissions);
-        mHost.onStartActivityFromFragment(this, intent, requestCode, null);
+        mHost.onRequestPermissionsFromFragment(this, permissions,requestCode);
     }
 
     /**
diff --git a/core/java/android/app/FragmentHostCallback.java b/core/java/android/app/FragmentHostCallback.java
index 3e753f0..7b01307 100644
--- a/core/java/android/app/FragmentHostCallback.java
+++ b/core/java/android/app/FragmentHostCallback.java
@@ -16,8 +16,8 @@
 
 package android.app;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
@@ -126,6 +126,14 @@
     }
 
     /**
+     * Requests permissions from the given fragment.
+     * See {@link Activity#requestPermissions(String[], int)}
+     */
+    public void onRequestPermissionsFromFragment(@NonNull Fragment fragment,
+            @NonNull String[] permissions, int requestCode) {
+    }
+
+    /**
      * Return {@code true} if there are window animations.
      */
     public boolean onHasWindowAnimations() {
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 0d5e1c7..9311e5e 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -436,7 +436,7 @@
             throws RemoteException;
 
     public void reportAssistContextExtras(IBinder token, Bundle extras,
-            AssistStructure structure, AssistContent content) throws RemoteException;
+            AssistStructure structure, AssistContent content, Uri referrer) throws RemoteException;
 
     public boolean launchAssistIntent(Intent intent, int requestType, String hint, int userHandle,
             Bundle args) throws RemoteException;
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 0cc57ba..653f1b6 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -24,6 +24,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
 import android.hardware.input.InputManager;
+import android.net.Uri;
 import android.os.Bundle;
 import android.os.Debug;
 import android.os.IBinder;
@@ -1439,7 +1440,7 @@
      * objects and dispatches this call to the system activity manager; you can
      * override this to watch for the application to start an activity, and 
      * modify what happens when it does. 
-     *  
+     *
      * <p>This method returns an {@link ActivityResult} object, which you can 
      * use when intercepting application calls to avoid performing the start 
      * activity action but still return the result the application is 
@@ -1448,10 +1449,10 @@
      * you would like the application to see, and don't call up to the super 
      * class.  Note that an application is only expecting a result if 
      * <var>requestCode</var> is &gt;= 0.
-     *  
+     *
      * <p>This method throws {@link android.content.ActivityNotFoundException}
      * if there was no Activity found to run the given Intent.
-     * 
+     *
      * @param who The Context from which the activity is being started.
      * @param contextThread The main thread of the Context from which the activity
      *                      is being started.
@@ -1464,23 +1465,27 @@
      * @param requestCode Identifier for this request's result; less than zero 
      *                    if the caller is not expecting a result.
      * @param options Addition options.
-     * 
+     *
      * @return To force the return of a particular result, return an 
      *         ActivityResult object containing the desired data; otherwise
      *         return null.  The default implementation always returns null.
-     *  
+     *
      * @throws android.content.ActivityNotFoundException
-     * 
+     *
      * @see Activity#startActivity(Intent)
      * @see Activity#startActivityForResult(Intent, int)
      * @see Activity#startActivityFromChild
-     * 
+     *
      * {@hide}
      */
     public ActivityResult execStartActivity(
             Context who, IBinder contextThread, IBinder token, Activity target,
             Intent intent, int requestCode, Bundle options) {
         IApplicationThread whoThread = (IApplicationThread) contextThread;
+        Uri referrer = target != null ? target.onProvideReferrer() : null;
+        if (referrer != null) {
+            intent.putExtra(Intent.EXTRA_REFERRER, referrer);
+        }
         if (mActivityMonitors != null) {
             synchronized (mSync) {
                 final int N = mActivityMonitors.size();
diff --git a/core/java/android/app/VoiceInteractor.java b/core/java/android/app/VoiceInteractor.java
index eccd9dc..9cc399d 100644
--- a/core/java/android/app/VoiceInteractor.java
+++ b/core/java/android/app/VoiceInteractor.java
@@ -58,9 +58,11 @@
  * request, rather than holding on to the activity instance yourself, either explicitly
  * or implicitly through a non-static inner class.
  */
-public class VoiceInteractor {
+public final class VoiceInteractor {
     static final String TAG = "VoiceInteractor";
-    static final boolean DEBUG = true;
+    static final boolean DEBUG = false;
+
+    static final Request[] NO_REQUESTS = new Request[0];
 
     final IVoiceInteractor mInteractor;
 
@@ -189,7 +191,7 @@
         }
     };
 
-    final ArrayMap<IBinder, Request> mActiveRequests = new ArrayMap<IBinder, Request>();
+    final ArrayMap<IBinder, Request> mActiveRequests = new ArrayMap<>();
 
     static final int MSG_CONFIRMATION_RESULT = 1;
     static final int MSG_PICK_OPTION_RESULT = 2;
@@ -206,10 +208,22 @@
         IVoiceInteractorRequest mRequestInterface;
         Context mContext;
         Activity mActivity;
+        String mName;
 
         Request() {
         }
 
+        /**
+         * Return the name this request was submitted through
+         * {@link #submitRequest(android.app.VoiceInteractor.Request, String)}.
+         */
+        public String getName() {
+            return mName;
+        }
+
+        /**
+         * Cancel this active request.
+         */
         public void cancel() {
             try {
                 mRequestInterface.cancel();
@@ -218,20 +232,39 @@
             }
         }
 
+        /**
+         * Return the current {@link Context} this request is associated with.  May change
+         * if the activity hosting it goes through a configuration change.
+         */
         public Context getContext() {
             return mContext;
         }
 
+        /**
+         * Return the current {@link Activity} this request is associated with.  Will change
+         * if the activity is restarted such as through a configuration change.
+         */
         public Activity getActivity() {
             return mActivity;
         }
 
+        /**
+         * Report from voice interaction service: this operation has been canceled, typically
+         * as a completion of a previous call to {@link #cancel}.
+         */
         public void onCancel() {
         }
 
+        /**
+         * The request is now attached to an activity, or being re-attached to a new activity
+         * after a configuration change.
+         */
         public void onAttached(Activity activity) {
         }
 
+        /**
+         * The request is being detached from an activity.
+         */
         public void onDetached() {
         }
 
@@ -239,6 +272,7 @@
             mRequestInterface = null;
             mContext = null;
             mActivity = null;
+            mName = null;
         }
 
         abstract IVoiceInteractorRequest submit(IVoiceInteractor interactor,
@@ -761,12 +795,31 @@
     }
 
     public boolean submitRequest(Request request) {
+        return submitRequest(request, null);
+    }
+
+    /**
+     * Submit a new {@link Request} to the voice interaction service.  The request must be
+     * one of the available subclasses -- {@link ConfirmationRequest}, {@link PickOptionRequest},
+     * {@link CompleteVoiceRequest}, {@link AbortVoiceRequest}, or {@link CommandRequest}.
+     *
+     * @param request The desired request to submit.
+     * @param name An optional name for this request, or null. This can be used later with
+     * {@link #getActiveRequests} and {@link #getActiveRequest} to find the request.
+     *
+     * @return Returns true of the request was successfully submitted, else false.
+     */
+    public boolean submitRequest(Request request, String name) {
         try {
+            if (request.mRequestInterface != null) {
+                throw new IllegalStateException("Given " + request + " is already active");
+            }
             IVoiceInteractorRequest ireq = request.submit(mInteractor,
                     mContext.getOpPackageName(), mCallback);
             request.mRequestInterface = ireq;
             request.mContext = mContext;
             request.mActivity = mActivity;
+            request.mName = name;
             synchronized (mActiveRequests) {
                 mActiveRequests.put(ireq.asBinder(), request);
             }
@@ -778,6 +831,43 @@
     }
 
     /**
+     * Return all currently active requests.
+     */
+    public Request[] getActiveRequests() {
+        synchronized (mActiveRequests) {
+            final int N = mActiveRequests.size();
+            if (N <= 0) {
+                return NO_REQUESTS;
+            }
+            Request[] requests = new Request[N];
+            for (int i=0; i<N; i++) {
+                requests[i] = mActiveRequests.valueAt(i);
+            }
+            return requests;
+        }
+    }
+
+    /**
+     * Return any currently active request that was submitted with the given name.
+     *
+     * @param name The name used to submit the request, as per
+     * {@link #submitRequest(android.app.VoiceInteractor.Request, String)}.
+     * @return Returns the active request with that name, or null if there was none.
+     */
+    public Request getActiveRequest(String name) {
+        synchronized (mActiveRequests) {
+            final int N = mActiveRequests.size();
+            for (int i=0; i<N; i++) {
+                Request req = mActiveRequests.valueAt(i);
+                if (name == req.getName() || (name != null && name.equals(req.getName()))) {
+                    return req;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
      * Queries the supported commands available from the VoiceInteractionService.
      * The command is a string that describes the generic operation to be performed.
      * An example might be "org.example.commands.PICK_DATE" to ask the user to pick
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index ab3f7bc..97afafa 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -1467,7 +1467,7 @@
      * @hide
      */
     public BluetoothServerSocket listenUsingRfcommOn(int channel) throws IOException {
-        return listenUsingRfcommOn(channel, false);
+        return listenUsingRfcommOn(channel, false, false);
     }
 
     /**
@@ -1482,14 +1482,17 @@
      * {@link SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as channel number.
      * @param channel RFCOMM channel to listen on
      * @param mitm    enforce man-in-the-middle protection for authentication.
+     * @param min16DigitPin enforce a pin key length og minimum 16 digit for sec mode 2 connections.
      * @return a listening RFCOMM BluetoothServerSocket
      * @throws IOException on error, for example Bluetooth not available, or
      *                     insufficient permissions, or channel in use.
      * @hide
      */
-    public BluetoothServerSocket listenUsingRfcommOn(int channel, boolean mitm) throws IOException {
+    public BluetoothServerSocket listenUsingRfcommOn(int channel, boolean mitm,
+            boolean min16DigitPin)
+            throws IOException {
         BluetoothServerSocket socket = new BluetoothServerSocket(
-                BluetoothSocket.TYPE_RFCOMM, true, true, channel, mitm);
+                BluetoothSocket.TYPE_RFCOMM, true, true, channel, mitm, min16DigitPin);
         int errno = socket.mSocket.bindListen();
         if (channel == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
             socket.setChannel(socket.mSocket.getPort());
@@ -1694,14 +1697,16 @@
      * {@link SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number.
      * @param port    the PSM to listen on
      * @param mitm    enforce man-in-the-middle protection for authentication.
+     * @param min16DigitPin enforce a pin key length og minimum 16 digit for sec mode 2 connections.
      * @return An L2CAP BluetoothServerSocket
      * @throws IOException On error, for example Bluetooth not available, or
      *                     insufficient permissions.
      * @hide
      */
-    public BluetoothServerSocket listenUsingL2capOn(int port, boolean mitm) throws IOException {
+    public BluetoothServerSocket listenUsingL2capOn(int port, boolean mitm, boolean min16DigitPin)
+            throws IOException {
         BluetoothServerSocket socket = new BluetoothServerSocket(
-                BluetoothSocket.TYPE_L2CAP, true, true, port, mitm);
+                BluetoothSocket.TYPE_L2CAP, true, true, port, mitm, min16DigitPin);
         int errno = socket.mSocket.bindListen();
         if(port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
             socket.setChannel(socket.mSocket.getPort());
@@ -1727,7 +1732,7 @@
      * @hide
      */
     public BluetoothServerSocket listenUsingL2capOn(int port) throws IOException {
-        return listenUsingL2capOn(port, false);
+        return listenUsingL2capOn(port, false, false);
     }
 
     /**
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index dcf06d8..c96fe71 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -531,6 +531,13 @@
     public static final int PAIRING_VARIANT_OOB_CONSENT = 6;
 
     /**
+     * The user will be prompted to enter a 16 digit pin or
+     * an app will enter a 16 digit pin for user.
+     * @hide
+     */
+    public static final int PAIRING_VARIANT_PIN_16_DIGITS = 7;
+
+    /**
      * Used as an extra field in {@link #ACTION_UUID} intents,
      * Contains the {@link android.os.ParcelUuid}s of the remote device which
      * is a parcelable version of {@link UUID}.
@@ -1315,8 +1322,8 @@
             Log.e(TAG, "", e);
         }
         return false;
-    }    
-    
+    }
+
     /**
      * Create an RFCOMM {@link BluetoothSocket} ready to start a secure
      * outgoing connection to this remote device on given channel.
diff --git a/core/java/android/bluetooth/BluetoothServerSocket.java b/core/java/android/bluetooth/BluetoothServerSocket.java
index a80f55c..c15852d 100644
--- a/core/java/android/bluetooth/BluetoothServerSocket.java
+++ b/core/java/android/bluetooth/BluetoothServerSocket.java
@@ -98,14 +98,16 @@
      * @param encrypt require the connection to be encrypted
      * @param port    remote port
      * @param mitm    enforce man-in-the-middle protection for authentication.
+     * @param min16DigitPin enforce a minimum length of 16 digits for a sec mode 2 connection
      * @throws IOException On error, for example Bluetooth not available, or
      *                     insufficient privileges
      */
     /*package*/ BluetoothServerSocket(int type, boolean auth, boolean encrypt, int port,
-            boolean mitm)
+            boolean mitm, boolean min16DigitPin)
             throws IOException {
         mChannel = port;
-        mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, port, null, mitm);
+        mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, port, null, mitm,
+                min16DigitPin);
         if(port == BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
             mSocket.setExcludeSdp(true);
         }
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index 6ca6976..6302521 100644
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -107,6 +107,7 @@
     /*package*/ static final int SEC_FLAG_AUTH = 1 << 1;
     /*package*/ static final int BTSOCK_FLAG_NO_SDP  = 1 << 2;
     /*package*/ static final int SEC_FLAG_AUTH_MITM  = 1 << 3;
+    /*package*/ static final int SEC_FLAG_AUTH_16_DIGIT  = 1 << 4;
 
     private final int mType;  /* one of TYPE_RFCOMM etc */
     private BluetoothDevice mDevice;    /* remote device */
@@ -118,6 +119,7 @@
     private final ParcelUuid mUuid;
     private boolean mExcludeSdp = false; /* when true no SPP SDP record will be created */
     private boolean mAuthMitm = false;   /* when true Man-in-the-middle protection will be enabled*/
+    private boolean mMin16DigitPin = false; /* Minimum 16 digit pin for sec mode 2 connections */
     private ParcelFileDescriptor mPfd;
     private LocalSocket mSocket;
     private InputStream mSocketIS;
@@ -160,7 +162,7 @@
      */
     /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt,
             BluetoothDevice device, int port, ParcelUuid uuid) throws IOException {
-        this(type, fd, auth, encrypt, device, port, uuid, false);
+        this(type, fd, auth, encrypt, device, port, uuid, false, false);
     }
 
     /**
@@ -173,11 +175,13 @@
      * @param port    remote port
      * @param uuid    SDP uuid
      * @param mitm    enforce man-in-the-middle protection.
+     * @param min16DigitPin enforce a minimum length of 16 digits for a sec mode 2 connection
      * @throws IOException On error, for example Bluetooth not available, or
      *                     insufficient privileges
      */
     /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt,
-            BluetoothDevice device, int port, ParcelUuid uuid, boolean mitm) throws IOException {
+            BluetoothDevice device, int port, ParcelUuid uuid, boolean mitm, boolean min16DigitPin)
+                    throws IOException {
         if (VDBG) Log.d(TAG, "Creating new BluetoothSocket of type: " + type);
         if (type == BluetoothSocket.TYPE_RFCOMM && uuid == null && fd == -1
                 && port != BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
@@ -191,6 +195,7 @@
         mType = type;
         mAuth = auth;
         mAuthMitm = mitm;
+        mMin16DigitPin = min16DigitPin;
         mEncrypt = encrypt;
         mDevice = device;
         mPort = port;
@@ -223,6 +228,7 @@
         mServiceName = s.mServiceName;
         mExcludeSdp = s.mExcludeSdp;
         mAuthMitm = s.mAuthMitm;
+        mMin16DigitPin = s.mMin16DigitPin;
     }
     private BluetoothSocket acceptSocket(String RemoteAddr) throws IOException {
         BluetoothSocket as = new BluetoothSocket(this);
@@ -254,7 +260,7 @@
      */
     private BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, String address,
             int port) throws IOException {
-        this(type, fd, auth, encrypt, new BluetoothDevice(address), port, null, false);
+        this(type, fd, auth, encrypt, new BluetoothDevice(address), port, null, false, false);
     }
 
     /** @hide */
@@ -276,6 +282,8 @@
             flags |= BTSOCK_FLAG_NO_SDP;
         if(mAuthMitm)
             flags |= SEC_FLAG_AUTH_MITM;
+        if(mMin16DigitPin)
+            flags |= SEC_FLAG_AUTH_16_DIGIT;
         return flags;
     }
 
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 5c23204..25be96a 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1226,7 +1226,8 @@
      * <p>
      * Input: {@link #EXTRA_ASSIST_PACKAGE}, {@link #EXTRA_ASSIST_CONTEXT}, can provide
      * additional optional contextual information about where the user was when they
-     * requested the assist.
+     * requested the assist; {@link #EXTRA_REFERRER} may be set with additional referrer
+     * information.
      * Output: nothing.
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index e61813c..82d3e0a 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -396,6 +396,11 @@
      * @param flags optional flags; should be 0
      * @param callback an object to receive authentication events
      * @param handler an optional handler to handle callback events
+     *
+     * @throws IllegalArgumentException if the crypto operation is not supported or is not backed
+     *         by <a href="{@docRoot}training/articles/keystore.html">Android Keystore
+     *         facility</a>.
+     * @throws IllegalStateException if the crypto primitive is not initialized.
      */
     @RequiresPermission(USE_FINGERPRINT)
     public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index cac4a53..640f434 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5549,6 +5549,15 @@
         public static final String DOUBLE_TAP_TO_WAKE = "double_tap_to_wake";
 
         /**
+         * The current assistant component. It could be a voice interaction service,
+         * or an activity that handles ACTION_ASSIST, or empty which means using the default
+         * handling.
+         *
+         * @hide
+         */
+        public static final String ASSISTANT = "assistant";
+
+        /**
          * This are the settings to be backed up.
          *
          * NOTE: Settings are backed up and restored in the order they appear
diff --git a/core/java/android/util/Log.java b/core/java/android/util/Log.java
index 121485a..fe41932 100644
--- a/core/java/android/util/Log.java
+++ b/core/java/android/util/Log.java
@@ -289,7 +289,10 @@
     static int wtf(int logId, String tag, String msg, Throwable tr, boolean localStack,
             boolean system) {
         TerribleFailure what = new TerribleFailure(msg, tr);
-        int bytes = println_native(logId, ASSERT, tag, msg + '\n'
+        // Only mark this as ERROR, do not use ASSERT since that should be
+        // reserved for cases where the system is guaranteed to abort.
+        // The onTerribleFailure call does not always cause a crash.
+        int bytes = println_native(logId, ERROR, tag, msg + '\n'
                 + getStackTraceString(localStack ? what : tr));
         sWtfHandler.onTerribleFailure(tag, what, system);
         return bytes;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 5dd5ab8..be372d0 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -16070,23 +16070,23 @@
             }
         } else if (cache != null) {
             mPrivateFlags &= ~PFLAG_DIRTY_MASK;
-            Paint cachePaint;
-            int restoreAlpha = 0;
-
             if (layerType == LAYER_TYPE_NONE) {
-                cachePaint = parent.mCachePaint;
+                // no layer paint, use temporary paint to draw bitmap
+                Paint cachePaint = parent.mCachePaint;
                 if (cachePaint == null) {
                     cachePaint = new Paint();
                     cachePaint.setDither(false);
                     parent.mCachePaint = cachePaint;
                 }
+                cachePaint.setAlpha((int) (alpha * 255));
+                canvas.drawBitmap(cache, 0.0f, 0.0f, cachePaint);
             } else {
-                cachePaint = mLayerPaint;
-                restoreAlpha = mLayerPaint.getAlpha();
+                // use layer paint to draw the bitmap, merging the two alphas, but also restore
+                int layerPaintAlpha = mLayerPaint.getAlpha();
+                mLayerPaint.setAlpha((int) (alpha * layerPaintAlpha));
+                canvas.drawBitmap(cache, 0.0f, 0.0f, mLayerPaint);
+                mLayerPaint.setAlpha(layerPaintAlpha);
             }
-            cachePaint.setAlpha((int) (alpha * 255));
-            canvas.drawBitmap(cache, 0.0f, 0.0f, cachePaint);
-            cachePaint.setAlpha(restoreAlpha);
         }
 
         if (restoreTo >= 0) {
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 5080fcc..aa72eb3 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -611,41 +611,45 @@
     /**
      * Specifies whether the horizontal scrollbar has overlay style.
      *
+     * @deprecated This method has no effect.
      * @param overlay true if horizontal scrollbar should have overlay style
      */
+    @Deprecated
     public void setHorizontalScrollbarOverlay(boolean overlay) {
-        checkThread();
-        mProvider.setHorizontalScrollbarOverlay(overlay);
     }
 
     /**
      * Specifies whether the vertical scrollbar has overlay style.
      *
+     * @deprecated This method has no effect.
      * @param overlay true if vertical scrollbar should have overlay style
      */
+    @Deprecated
     public void setVerticalScrollbarOverlay(boolean overlay) {
-        checkThread();
-        mProvider.setVerticalScrollbarOverlay(overlay);
     }
 
     /**
      * Gets whether horizontal scrollbar has overlay style.
      *
-     * @return true if horizontal scrollbar has overlay style
+     * @deprecated This method is now obsolete.
+     * @return true
      */
+    @Deprecated
     public boolean overlayHorizontalScrollbar() {
-        checkThread();
-        return mProvider.overlayHorizontalScrollbar();
+        // The old implementation defaulted to true, so return true for consistency
+        return true;
     }
 
     /**
      * Gets whether vertical scrollbar has overlay style.
      *
-     * @return true if vertical scrollbar has overlay style
+     * @deprecated This method is now obsolete.
+     * @return false
      */
+    @Deprecated
     public boolean overlayVerticalScrollbar() {
-        checkThread();
-        return mProvider.overlayVerticalScrollbar();
+        // The old implementation defaulted to false, so return false for consistency
+        return false;
     }
 
     /**
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index 09afcf1..27033ad 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -70,12 +70,16 @@
     public void init(Map<String, Object> javaScriptInterfaces,
             boolean privateBrowsing);
 
+    // Deprecated - should never be called
     public void setHorizontalScrollbarOverlay(boolean overlay);
 
+    // Deprecated - should never be called
     public void setVerticalScrollbarOverlay(boolean overlay);
 
+    // Deprecated - should never be called
     public boolean overlayHorizontalScrollbar();
 
+    // Deprecated - should never be called
     public boolean overlayVerticalScrollbar();
 
     public int getVisibleTitleHeight();
diff --git a/core/java/com/android/internal/app/ProcessStats.java b/core/java/com/android/internal/app/ProcessStats.java
index fe79eff..27aec4e 100644
--- a/core/java/com/android/internal/app/ProcessStats.java
+++ b/core/java/com/android/internal/app/ProcessStats.java
@@ -140,6 +140,7 @@
             STATE_PERSISTENT,               // ActivityManager.PROCESS_STATE_PERSISTENT
             STATE_PERSISTENT,               // ActivityManager.PROCESS_STATE_PERSISTENT_UI
             STATE_TOP,                      // ActivityManager.PROCESS_STATE_TOP
+            STATE_IMPORTANT_FOREGROUND,     // ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
             STATE_IMPORTANT_FOREGROUND,     // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
             STATE_TOP,                      // ActivityManager.PROCESS_STATE_TOP_SLEEPING
             STATE_IMPORTANT_FOREGROUND,     // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
index 8fbd214..0d9980a 100644
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
+++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
@@ -170,6 +170,7 @@
                 assertNotSame(-1, varient);
                 switch (varient) {
                     case BluetoothDevice.PAIRING_VARIANT_PIN:
+                    case BluetoothDevice.PAIRING_VARIANT_PIN_16_DIGITS:
                         mDevice.setPin(mPin);
                         break;
                     case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
diff --git a/docs/html/training/wearables/watch-faces/drawing.jd b/docs/html/training/wearables/watch-faces/drawing.jd
index 60da5d5..8b6de76 100644
--- a/docs/html/training/wearables/watch-faces/drawing.jd
+++ b/docs/html/training/wearables/watch-faces/drawing.jd
@@ -16,17 +16,20 @@
 <ul>
   <li><a href="{@docRoot}design/wear/watchfaces.html">Watch Faces for Android Wear</a></li>
 </ul>
+<h2>Related Samples</h2>
+  <ul>
+    <li><a href="{@docRoot}samples/WatchFace/index.html">WatchFace</a></li>
+  </ul>
 </div>
 </div>
 
 <p>After you have configured your project and added a class that implements the watch
 face service, you can start writing code to initialize and draw your custom watch face.</p>
 
-<p>This lesson explains how the system invokes the methods in the
-watch face service using examples from the <em>WatchFace</em> sample
-included in the Android SDK. This sample is located in the
-<code>android-sdk/samples/android-21/wearable/WatchFace</code> directory. Many aspects of the
-service implementations described here (such as initialization and detecting device features)
+<p>This lesson includes examples from the
+<a href="{@docRoot}samples/WatchFace/index.html">WatchFace</a> sample to show how the system uses
+the watch face service. Many aspects of the
+service implementations described here (such as initialization and device features detection)
 apply to any watch face, so you can reuse some of the code in your own watch faces.</p>
 
 
@@ -36,7 +39,8 @@
      width="180" height="180" alt="" style="margin-left:25px;margin-top:12px"/>
 <p class="img-caption">
 <strong>Figure 1.</strong> The analog and digital watch faces in
-the <em>WatchFace</em> sample.</p>
+the
+<a href="{@docRoot}samples/WatchFace/index.html">WatchFace</a> sample.</p>
 
 
 <h2 id="Initialize">Initialize Your Watch Face</h2>
@@ -51,8 +55,12 @@
 
 <ol>
 <li>Declare variables for a custom timer, graphic objects, and other elements.</li>
-<li>Initialize the watch face elements in the <code>Engine.onCreate()</code> method.</li>
-<li>Initialize the custom timer in the <code>Engine.onVisibilityChanged()</code> method.</li>
+<li>Initialize the watch face elements in the
+<a href="{@docRoot}reference/android/support/wearable/watchface/WatchFaceService.Engine.html#onCreate(android.view.SurfaceHolder)"><code>Engine.onCreate()</code></a>
+method.</li>
+<li>Initialize the custom timer in the
+<a href="{@docRoot}reference/android/support/wearable/watchface/WatchFaceService.Engine.html#onVisibilityChanged(boolean)"><code>Engine.onVisibilityChanged()</code></a>
+method.</li>
 </ol>
 
 <p>The following sections describe these steps in detail.</p>
@@ -61,7 +69,8 @@
 
 <p>The resources that you intialize when the system loads your service need to be accessible
 at different points throughout your implementation, so you can reuse them. You achieve this
-by declaring member variables for these resources in your <code>WatchFaceService.Engine</code>
+by declaring member variables for these resources in your
+<a href="{@docRoot}reference/android/support/wearable/watchface/WatchFaceService.Engine.html"><code>WatchFaceService.Engine</code></a>
 implementation.</p>
 
 <p>Declare variables for the following elements:</p>
@@ -83,31 +92,25 @@
 zone changes and update the time accordingly.</dd>
 </dl>
 
-<p>The <code>AnalogWatchFaceService.Engine</code> class in the <em>WatchFace</em> sample defines
-these variables as shown in the snippet below. The custom timer is implemented as a
-{@link android.os.Handler} instance that sends and processes delayed messages using the thread's
-message queue. For this particular watch face, the custom timer ticks once every second. When the
-timer ticks, the handler calls the <code>invalidate()</code> method and the system then calls
-the <code>onDraw()</code> method to redraw the watch face.</p>
+<p>The following snippet shows how to define these variables:</p>
 
 <pre>
 private class Engine extends CanvasWatchFaceService.Engine {
     static final int MSG_UPDATE_TIME = 0;
 
-    /* a time object */
-    Time mTime;
+    Calendar mCalendar;
 
-    /* device features */
+    // device features
     boolean mLowBitAmbient;
 
-    /* graphic objects */
+    // graphic objects
     Bitmap mBackgroundBitmap;
     Bitmap mBackgroundScaledBitmap;
     Paint mHourPaint;
     Paint mMinutePaint;
     ...
 
-    /* handler to update the time once a second in interactive mode */
+    // handler to update the time once a second in interactive mode
     final Handler mUpdateTimeHandler = new Handler() {
         &#64;Override
         public void handleMessage(Message message) {
@@ -126,53 +129,63 @@
         }
     };
 
-    /* receiver to update the time zone */
+    // receiver to update the time zone
     final BroadcastReceiver mTimeZoneReceiver = new BroadcastReceiver() {
         &#64;Override
         public void onReceive(Context context, Intent intent) {
-            mTime.clear(intent.getStringExtra("time-zone"));
-            mTime.setToNow();
+            mCalendar.setTimeZone(TimeZone.getDefault());
+            invalidate();
         }
     };
 
-    /* service methods (see other sections) */
+    // service methods (see other sections)
     ...
 }
 </pre>
 
+<p>In the example above, the custom timer is implemented as a
+{@link android.os.Handler} instance that sends and processes delayed messages using the thread's
+message queue. For this particular watch face, the custom timer ticks once every second. When the
+timer ticks, the handler calls the
+<a href="{@docRoot}reference/android/support/wearable/watchface/CanvasWatchFaceService.Engine.html#invalidate()"><code>invalidate()</code></a>
+method and the system then calls the
+<a href="{@docRoot}reference/android/support/wearable/watchface/CanvasWatchFaceService.Engine.html#onDraw(android.graphics.Canvas, android.graphics.Rect)"<code>onDraw()</code></a>
+method to redraw the watch face.</p>
+
 <h3 id="InitializeElements">Initialize watch face elements</h3>
 
-<p>After you have declared member variables for bitmap resources, paint styles, and other
-elements that you reuse every time your redraw your watch face, initialize them when the system
+<p>After declaring member variables for bitmap resources, paint styles, and other
+elements that you reuse every time you redraw your watch face, initialize them when the system
 loads your service. Initializing these elements only once and reusing them improves performance
 and battery life.</p>
 
-<p>In the <code>Engine.onCreate()</code> method, initialize the following elements:</p>
+<p>In the
+<a href="{@docRoot}reference/android/support/wearable/watchface/WatchFaceService.Engine.html#onCreate(android.view.SurfaceHolder)"><code>Engine.onCreate()</code></a>
+method, initialize the following elements:</p>
 
 <ul>
 <li>Load the background image.</li>
 <li>Create styles and colors to draw graphic objects.</li>
-<li>Allocate an object to hold the time.</li>
+<li>Allocate an object to calculate the time.</li>
 <li>Configure the system UI.</li>
 </ul>
 
-<p>The <code>Engine.onCreate()</code> method in the <code>AnalogWatchFaceService</code> class
-initializes these elements as follows:</p>
+<p>The following snippet shows how to initialize these elements:</p>
 
 <pre>
 &#64;Override
 public void onCreate(SurfaceHolder holder) {
     super.onCreate(holder);
 
-    /* configure the system UI (see next section) */
+    // configure the system UI (see next section)
     ...
 
-    /* load the background image */
+    // load the background image
     Resources resources = AnalogWatchFaceService.this.getResources();
-    Drawable backgroundDrawable = resources.getDrawable(R.drawable.bg);
+    Drawable backgroundDrawable = resources.getDrawable(R.drawable.bg, null);
     mBackgroundBitmap = ((BitmapDrawable) backgroundDrawable).getBitmap();
 
-    /* create graphic styles */
+    // create graphic styles
     mHourPaint = new Paint();
     mHourPaint.setARGB(255, 200, 200, 200);
     mHourPaint.setStrokeWidth(5.0f);
@@ -180,15 +193,16 @@
     mHourPaint.setStrokeCap(Paint.Cap.ROUND);
     ...
 
-    /* allocate an object to hold the time */
-    mTime = new Time();
+    // allocate a Calendar to calculate local time using the UTC time and time zone
+    mCalendar = Calendar.getInstance();
 }
 </pre>
 
 <p>The background bitmap is loaded only once when the system initializes the watch face. The
-graphic styles are instances of the {@link android.graphics.Paint} class. You later use these
-styles to draw the elements of your watch face inside the <code>Engine.onDraw()</code> method,
-as described in <a href="#Drawing">Drawing Your Watch Face</a>.</p>
+graphic styles are instances of the {@link android.graphics.Paint} class. Use these
+styles to draw the elements of your watch face inside the
+<a href="{@docRoot}reference/android/support/wearable/watchface/CanvasWatchFaceService.Engine.html#onDraw(android.graphics.Canvas, android.graphics.Rect)"><code>Engine.onDraw()</code></a>
+method, as described in <a href="#Drawing">Drawing Your Watch Face</a>.</p>
 
 <h3 id="Timer">Initialize the custom timer</h3>
 
@@ -203,8 +217,8 @@
 
 <p>An example timer definition from the <code>AnalogWatchFaceService</code> class that ticks once
 every second is shown in <a href="#Variables">Declare variables</a>. In the
-<code>Engine.onVisibilityChanged()</code> method, start the custom timer if these two
-conditions apply:</p>
+<a href="{@docRoot}reference/android/support/wearable/watchface/WatchFaceService.Engine.html#onVisibilityChanged(boolean)"><code>Engine.onVisibilityChanged()</code></a>
+method, start the custom timer if these two conditions apply:</p>
 
 <ul>
 <li>The watch face is visible.</li>
@@ -230,8 +244,10 @@
 <p>This custom timer ticks once every second, as described in <a href="#Variables">Declare
 variables</a>.</p>
 
-<p>In the <code>Engine.onVisibilityChanged()</code> method, start the timer if required and
-and register the receiver for time zone changes as follows:</p>
+<p>In the
+<a href="{@docRoot}reference/android/support/wearable/watchface/WatchFaceService.Engine.html#onVisibilityChanged(boolean)"><code>onVisibilityChanged()</code></a>
+method, start the timer if required and register the receiver for time zone changes as follows:
+</p>
 
 <pre>
 &#64;Override
@@ -242,23 +258,24 @@
         registerReceiver();
 
         // Update time zone in case it changed while we weren't visible.
-        mTime.clear(TimeZone.getDefault().getID());
-        mTime.setToNow();
+        mCalendar.setTimeZone(TimeZone.getDefault());
     } else {
         unregisterReceiver();
     }
 
     // Whether the timer should be running depends on whether we're visible and
-    // whether we're in ambient mode), so we may need to start or stop the timer
+    // whether we're in ambient mode, so we may need to start or stop the timer
     updateTimer();
 }
 </pre>
 
-<p>When the watch face is visible, the <code>onVisibilityChanged()</code> method registers
-the receiver for time zone changes and starts the custom timer if the device is in interactive
-mode. When the watch face is not visible, this method stops the custom timer and unregisters
-the receiver for time zone changes. The <code>registerReceiver()</code> and
-<code>unregisterReceiver()</code> methods are implemented as follows:</p>
+<p>When the watch face is visible, the
+<a href="{@docRoot}reference/android/support/wearable/watchface/WatchFaceService.Engine.html#onVisibilityChanged(boolean)"><code>onVisibilityChanged()</code></a>
+method registers the receiver for time zone changes. If the device is in interactive mode, this
+method also starts the custom timer. When the watch face is not visible, this
+method stops the custom timer and unregisters the receiver for time zone changes.
+The <code>registerReceiver()</code> and <code>unregisterReceiver()</code> methods are implemented as
+follows:</p>
 
 <pre>
 private void registerReceiver() {
@@ -283,13 +300,16 @@
 
 <h3 id="TimeTick">Update the watch face in ambient mode</h3>
 
-<p>In ambient mode, the system calls the <code>Engine.onTimeTick()</code> method every minute.
-It is usually sufficient to update your watch face once per minute in this mode. To update your
-watch face while in interactive mode, you must provide a custom timer as described in
-<a href="#Timer">Initialize the custom timer</a>.</p>
+<p>In ambient mode, the system calls the
+<a href="{@docRoot}reference/android/support/wearable/watchface/WatchFaceService.Engine.html#onTimeTick()"><code>Engine.onTimeTick()</code></a>
+method every minute. It is usually sufficient to update your watch face once per minute in this
+mode. To update your watch face while in interactive mode, you must provide a custom timer as
+described in <a href="#Timer">Initialize the custom timer</a>.</p>
 
 <p>In ambient mode, most watch face implementations simply invalidate the canvas to redraw the watch
-face in the <code>Engine.onTimeTick()</code> method:</p>
+face in the
+<a href="{@docRoot}reference/android/support/wearable/watchface/WatchFaceService.Engine.html#onTimeTick()"<code>Engine.onTimeTick()</code></a>
+method:</p>
 
 <pre>
 &#64;Override
@@ -320,8 +340,11 @@
 <li>Specify the positioning of the system indicators.</li>
 </ul>
 
-<p>To configure these aspects of the system UI, create a <code>WatchFaceStyle</code> instance
-and pass it to the <code>Engine.setWatchFaceStyle()</code> method.</p>
+<p>To configure these aspects of the system UI, create a
+<a href="{@docRoot}reference/android/support/wearable/watchface/WatchFaceStyle.html"><code>WatchFaceStyle</code></a>
+instance and pass it to the
+<a href="{@docRoot}reference/android/support/wearable/watchface/WatchFaceService.Engine.html#setWatchFaceStyle(android.support.wearable.watchface.WatchFaceStyle)"><code>Engine.setWatchFaceStyle()</code></a>
+method.</p>
 
 <p>The <code>AnalogWatchFaceService</code> class configures the system UI as follows:</p>
 
@@ -330,7 +353,7 @@
 public void onCreate(SurfaceHolder holder) {
     super.onCreate(holder);
 
-    /* configure the system UI */
+    // configure the system UI
     setWatchFaceStyle(new WatchFaceStyle.Builder(AnalogWatchFaceService.this)
             .setCardPeekMode(WatchFaceStyle.PEEK_MODE_SHORT)
             .setBackgroundVisibility(WatchFaceStyle
@@ -350,16 +373,16 @@
 system indicators.</p>
 
 <p>For more details about configuring the system UI, see the
-<a href="{@docRoot}shareables/training/wearable-support-docs.zip">API reference</a> for the
-<code>WatchFaceStyle</code> class.</p>
-
+<a href="{@docRoot}reference/packages-wearable-support.html">Wear API reference documentation</a>.
+</p>
 
 
 <h2 id="Screen">Obtain Information About the Device Screen</h2>
 
-<p>The system calls the <code>Engine.onPropertiesChanged()</code> method when it determines
-the properties of the device screen, such as whether the device uses low-bit ambient mode and
-whether the screen requires burn-in protection.</p>
+<p>The system calls the
+<a href="{@docRoot}reference/android/support/wearable/watchface/WatchFaceService.Engine.html#onPropertiesChanged(android.os.Bundle)"><code>Engine.onPropertiesChanged()</code></a>
+method when it determines the properties of the device screen, such as whether the device uses
+low-bit ambient mode and whether the screen requires burn-in protection.</p>
 
 <p>The following code snippet shows how to obtain these properties:</p>
 
@@ -394,12 +417,13 @@
 <h2 id="Modes">Respond to Changes Between Modes</h2>
 
 <p>When the device switches between ambient and interactive modes, the system calls the
-<code>Engine.onAmbientModeChanged()</code> method. Your service implementation should make
-any necessary adjustments to switch between modes and then call the <code>invalidate()</code>
+<a href="{@docRoot}reference/android/support/wearable/watchface/WatchFaceService.Engine.html#onAmbientModeChanged(boolean)"><code>Engine.onAmbientModeChanged()</code></a>
+method. Your service implementation should make any necessary adjustments to switch between modes
+and then call the
+<a href="{@docRoot}reference/android/support/wearable/watchface/CanvasWatchFaceService.Engine.html#invalidate()"><code>invalidate()</code></a>
 method for the system to redraw the watch face.</p>
 
-<p>The following snippet shows how this method is implemented in the
-<code>AnalogWatchFaceService</code> class inside the <em>WatchFace</em> sample:</p>
+<p>The following snippet shows how to implement this method:</p>
 
 <pre>
 &#64;Override
@@ -426,28 +450,34 @@
 
 <h2 id="Drawing">Draw Your Watch Face</h2>
 
-<p>To draw a custom watch face, the system calls the <code>Engine.onDraw()</code> method with a
-{@link android.graphics.Canvas} instance and the bounds in which you should draw your watch face.
-The bounds account for any inset areas, such as the "chin" on the bottom of some round devices.
-You can use this canvas to draw your watch face directly as follows:</p>
+<p>To draw a custom watch face, the system calls the
+<a href="{@docRoot}reference/android/support/wearable/watchface/CanvasWatchFaceService.Engine.html#onDraw(android.graphics.Canvas, android.graphics.Rect)"><code>Engine.onDraw()</code></a>
+method with a {@link android.graphics.Canvas} instance and the bounds in which you should draw your
+watch face. The bounds take into account any inset areas, such as the "chin" on the bottom of some
+round devices. You can use this canvas to draw your watch face directly as follows:</p>
 
 <ol>
-<li>If this is the first invocation of the <code>onDraw()</code> method, scale your background
-to fit.</li>
+<li>If this is the first invocation of the
+<a href="{@docRoot}reference/android/support/wearable/watchface/CanvasWatchFaceService.Engine.html#onDraw(android.graphics.Canvas, android.graphics.Rect)"><code>onDraw()</code></a>
+method, scale your background to fit.</li>
 <li>Check whether the device is in ambient mode or interactive mode.</li>
 <li>Perform any required graphic computations.</li>
 <li>Draw your background bitmap on the canvas.</li>
 <li>Use the methods in the {@link android.graphics.Canvas} class to draw your watch face.</li>
 </ol>
 
-<p>The <code>AnalogWatchFaceService</code> class in the <em>WatchFace</em> sample follows these
-steps to implement the <code>onDraw()</code> method as follows:</p>
+<p>The following snippet shows how to implement the
+<a href="{@docRoot}reference/android/support/wearable/watchface/CanvasWatchFaceService.Engine.html#onDraw(android.graphics.Canvas, android.graphics.Rect)"><code>onDraw()</code></a>
+method:</p>
 
 <pre>
 &#64;Override
 public void onDraw(Canvas canvas, Rect bounds) {
     // Update the time
-    mTime.setToNow();
+    mCalendar.setTimeInMillis(System.currentTimeMillis());
+
+    // Constant to help calculate clock hand rotations
+    final float TWO_PI = (float) Math.PI * 2f;
 
     int width = bounds.width();
     int height = bounds.height();
@@ -457,7 +487,7 @@
         || mBackgroundScaledBitmap.getWidth() != width
         || mBackgroundScaledBitmap.getHeight() != height) {
         mBackgroundScaledBitmap = Bitmap.createScaledBitmap(mBackgroundBitmap,
-                                         width, height, true /* filter */);
+                                      width, height, true);
     }
     canvas.drawBitmap(mBackgroundScaledBitmap, 0, 0, null);
 
@@ -468,10 +498,13 @@
     float centerY = height / 2f;
 
     // Compute rotations and lengths for the clock hands.
-    float secRot = mTime.second / 30f * (float) Math.PI;
-    int minutes = mTime.minute;
-    float minRot = minutes / 30f * (float) Math.PI;
-    float hrRot = ((mTime.hour + (minutes / 60f)) / 6f ) * (float) Math.PI;
+    float seconds = mCalendar.get(Calendar.SECOND) +
+                    mCalendar.get(Calendar.MILLISECOND) / 1000f;
+    float secRot = seconds / 60f * TWO_PI;
+    float minutes = mCalendar.get(Calendar.MINUTE) + seconds / 60f;
+    float minRot = minutes / 60f * TWO_PI;
+    float hours = mCalendar.get(Calendar.HOUR) + minutes / 60f;
+    float hrRot = hours / 12f * TWO_PI;
 
     float secLength = centerX - 20;
     float minLength = centerX - 40;
@@ -499,11 +532,13 @@
 
 <p>This method computes the required positions for the clock hands based on the current time
 and draws them on top of the background bitmap using the graphic styles initialized in the
-<code>onCreate()</code> method. The second hand is only drawn in interactive mode, not in
-ambient mode.</p>
+<a href="{@docRoot}reference/android/support/wearable/watchface/WatchFaceService.Engine.html#onCreate(android.view.SurfaceHolder)"><code>onCreate()</code></a>
+method. The second hand is only drawn in interactive mode, not in ambient mode.</p>
 
 <p>For more information about drawing on a Canvas instance, see <a
 href="{@docRoot}guide/topics/graphics/2d-graphics.html">Canvas and Drawables</a>.</p>
 
-<p>The <em>WatchFace</em> sample in the Android SDK includes additional watch faces that you
-can refer to as examples of how to implement the <code>onDraw()</code> method.</p>
+<p>The <a href="{@docRoot}samples/WatchFace/index.html">WatchFace</a> sample includes additional
+watch faces that you can refer to as examples of how to implement the
+<a href="{@docRoot}reference/android/support/wearable/watchface/CanvasWatchFaceService.Engine.html#onDraw(android.graphics.Canvas, android.graphics.Rect)"><code>onDraw()</code></a>
+method.</p>
diff --git a/libs/hwui/DisplayListCanvas.cpp b/libs/hwui/DisplayListCanvas.cpp
index cbb6fd5..900a621 100644
--- a/libs/hwui/DisplayListCanvas.cpp
+++ b/libs/hwui/DisplayListCanvas.cpp
@@ -352,6 +352,7 @@
     mDisplayListData->ref(rx);
     mDisplayListData->ref(ry);
     mDisplayListData->ref(paint);
+    refBitmapsInShader(paint->value.getShader());
     addDrawOp(new (alloc()) DrawRoundRectPropsOp(&left->value, &top->value,
             &right->value, &bottom->value, &rx->value, &ry->value, &paint->value));
 }
@@ -366,6 +367,7 @@
     mDisplayListData->ref(y);
     mDisplayListData->ref(radius);
     mDisplayListData->ref(paint);
+    refBitmapsInShader(paint->value.getShader());
     addDrawOp(new (alloc()) DrawCirclePropsOp(&x->value, &y->value,
             &radius->value, &paint->value));
 }
@@ -565,5 +567,24 @@
     return opIndex;
 }
 
+void DisplayListCanvas::refBitmapsInShader(const SkShader* shader) {
+    if (!shader) return;
+
+    // If this paint has an SkShader that has an SkBitmap add
+    // it to the bitmap pile
+    SkBitmap bitmap;
+    SkShader::TileMode xy[2];
+    if (shader->asABitmap(&bitmap, nullptr, xy) == SkShader::kDefault_BitmapType) {
+        refBitmap(bitmap);
+        return;
+    }
+    SkShader::ComposeRec rec;
+    if (shader->asACompose(&rec)) {
+        refBitmapsInShader(rec.fShaderA);
+        refBitmapsInShader(rec.fShaderB);
+        return;
+    }
+}
+
 }; // namespace uirenderer
 }; // namespace android
diff --git a/libs/hwui/DisplayListCanvas.h b/libs/hwui/DisplayListCanvas.h
index d997ef4..edfda62 100644
--- a/libs/hwui/DisplayListCanvas.h
+++ b/libs/hwui/DisplayListCanvas.h
@@ -263,6 +263,7 @@
     size_t addDrawOp(DrawOp* op);
     size_t addRenderNodeOp(DrawRenderNodeOp* op);
 
+    void refBitmapsInShader(const SkShader* shader);
 
     template<class T>
     inline const T* refBuffer(const T* srcBuffer, int32_t count) {
@@ -311,6 +312,7 @@
 
             // replaceValueFor() performs an add if the entry doesn't exist
             mPaintMap.replaceValueFor(key, cachedPaint);
+            refBitmapsInShader(cachedPaint->getShader());
         }
 
         return cachedPaint;
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 29fbf0c..5850dc6 100755
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -524,22 +524,6 @@
     inline float getLayerAlpha(const Layer* layer) const;
 
     /**
-     * Safely retrieves the ColorFilter from the given Paint. If the paint is
-     * null then null is returned.
-     */
-    static inline SkColorFilter* getColorFilter(const SkPaint* paint) {
-        return paint ? paint->getColorFilter() : nullptr;
-    }
-
-    /**
-     * Safely retrieves the Shader from the given Paint. If the paint is
-     * null then null is returned.
-     */
-    static inline const SkShader* getShader(const SkPaint* paint) {
-        return paint ? paint->getShader() : nullptr;
-    }
-
-    /**
      * Set to true to suppress error checks at the end of a frame.
      */
     virtual bool suppressErrorChecks() const {
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index eec4960..a79dd04 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -1455,15 +1455,6 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface BufferFlag {}
 
-    private static class FrameRenderedInfo {
-        public long mPresentationTimeUs;
-        public long mNanoTime;
-        public FrameRenderedInfo(long presentationTimeUs, long nanoTime) {
-            mPresentationTimeUs = presentationTimeUs;
-            mNanoTime = nanoTime;
-        }
-    }
-
     private EventHandler mEventHandler;
     private EventHandler mOnFrameRenderedHandler;
     private EventHandler mCallbackHandler;
@@ -1503,10 +1494,16 @@
                 }
                 case EVENT_FRAME_RENDERED:
                     synchronized (mListenerLock) {
-                        FrameRenderedInfo info = (FrameRenderedInfo)msg.obj;
-                        if (mOnFrameRenderedListener != null) {
+                        Map<String, Object> map = (Map<String, Object>)msg.obj;
+                        for (int i = 0; ; ++i) {
+                            Object mediaTimeUs = map.get(i + "-media-time-us");
+                            Object systemNano = map.get(i + "-system-nano");
+                            if (mediaTimeUs == null || systemNano == null
+                                    || mOnFrameRenderedListener == null) {
+                                break;
+                            }
                             mOnFrameRenderedListener.onFrameRendered(
-                                    mCodec, info.mPresentationTimeUs, info.mNanoTime);
+                                    mCodec, (long)mediaTimeUs, (long)systemNano);
                         }
                         break;
                     }
@@ -2362,26 +2359,9 @@
                 info = mDequeuedOutputInfos.remove(index);
             }
         }
-        // TODO
-        // until codec and libgui supports callback, assume frame is rendered within 50 ms
-        postRenderedCallback(render, info, 50 /* delayMs */);
         releaseOutputBuffer(index, render, false /* updatePTS */, 0 /* dummy */);
     }
 
-    private void postRenderedCallback(boolean render, @Nullable BufferInfo info, long delayMs) {
-        if (render && info != null) {
-            synchronized (mListenerLock) {
-                 if (mOnFrameRenderedListener != null) {
-                     FrameRenderedInfo obj = new FrameRenderedInfo(
-                            info.presentationTimeUs, System.nanoTime() + delayMs * 1000000);
-                     Message msg = mOnFrameRenderedHandler.obtainMessage(
-                            EVENT_FRAME_RENDERED, obj);
-                     mOnFrameRenderedHandler.sendMessageDelayed(msg, delayMs);
-                 }
-            }
-        }
-    }
-
     /**
      * If you are done with a buffer, use this call to update its surface timestamp
      * and return it to the codec to render it on the output surface. If you
@@ -2440,12 +2420,6 @@
                 info = mDequeuedOutputInfos.remove(index);
             }
         }
-        // TODO
-        // until codec and libgui supports callback, assume frame is rendered at the
-        // render time or 16 ms from now, whichever is later.
-        postRenderedCallback(
-                true /* render */, info,
-                Math.max(renderTimestampNs - System.nanoTime(), 16666666) / 1000000);
         releaseOutputBuffer(
                 index, true /* render */, true /* updatePTS */, renderTimestampNs);
     }
@@ -3049,9 +3023,12 @@
             } else if (mOnFrameRenderedHandler != null) {
                 mOnFrameRenderedHandler.removeMessages(EVENT_FRAME_RENDERED);
             }
+            native_enableOnFrameRenderedListener(listener != null);
         }
     }
 
+    private native void native_enableOnFrameRenderedListener(boolean enable);
+
     private EventHandler getEventHandlerOn(
             @Nullable Handler handler, @NonNull EventHandler lastHandler) {
         if (handler == null) {
diff --git a/media/java/android/media/midi/package.html b/media/java/android/media/midi/package.html
index 8b2bd16..673c4ba 100644
--- a/media/java/android/media/midi/package.html
+++ b/media/java/android/media/midi/package.html
@@ -10,27 +10,28 @@
 <p>The Android MIDI package allows users to:</p>
 
 <ul>
-  <li> Connect a MIDI keyboard to Android to play a synthesizer or drive music apps.
-  <li> Connect alternative MIDI controllers to Android.
-  <li> Drive external MIDI synths from Android.
-  <li> Drive external peripherals, lights, show control, etc from Android.
-  <li> Generate music dynamically from games or music creation apps.
-  <li> Generate MIDI messages in one app and send them to a second app.
-  <li> Use an Android device running in <em>peripheral mode</em> as a multitouch controller connected to a laptop.
+  <li> Connect a MIDI keyboard to Android to play a synthesizer or drive music apps.</li>
+  <li> Connect alternative MIDI controllers to Android.</li>
+  <li> Drive external MIDI synths from Android.</li>
+  <li> Drive external peripherals, lights, show control, etc from Android.</li>
+  <li> Generate music dynamically from games or music creation apps.</li>
+  <li> Generate MIDI messages in one app and send them to a second app.</li>
+  <li> Use an Android device running in <em>peripheral mode</em> as a multitouch controller
+  connected to a laptop.</li>
 </ul>
 
 <h2 id=the_api_features_include>The API features include:</h2>
 
-
 <ul>
   <li> Enumeration of currently available devices. Information includes name, vendor,
-capabilities, etc.
-  <li> Provide notification when MIDI devices are plugged in or unplugged.
-  <li> Support efficient transmission of single or multiple short 1-3 byte MIDI
-messages.
-  <li> Support transmission of arbitrary length data for SysEx, etc.
-  <li> Timestamps to avoid jitter.
-  <li> Support direction connection or &ldquo;patching&rdquo; of devices for lower latency.
+capabilities, etc.</li>
+  <li> Provide notification when MIDI devices are plugged in or unplugged.</li>
+  <li> Support efficient transmission of single or multiple short 1-3 byte MIDI messages.</li>
+  <li> Support transmission of arbitrary length data for SysEx, etc.</li>
+  <li> Timestamps to avoid jitter.</li>
+  <li> Support creation of <em>virtual MIDI devices</em> that can be connected to other devices.
+  An example might be a synthesizer app that can be controlled by a composing app.</li>
+  <li> Support direction connection or &ldquo;patching&rdquo; of devices for lower latency.</li>
 </ul>
 
 <h2 id=transports_supported>Transports Supported</h2>
@@ -65,6 +66,14 @@
 
 <h1 id=writing_a_midi_application>Writing a MIDI Application</h1>
 
+<h2 id=manifest_feature>Declare Feature in Manifest</h2>
+
+<p>An app that requires the MIDI API should declare that in the AndroidManifest.xml file.
+Then the app will not appear in the Play Store for old devices that do not support the MIDI API.</p>
+
+<pre class=prettyprint>
+&lt;uses-feature android:name="android.software.midi" android:required="true"/>
+</pre>
 
 <h2 id=the_midimanager>The MidiManager</h2>
 
@@ -132,11 +141,15 @@
 <p>Other properties include PROPERTY_PRODUCT, PROPERTY_NAME,
 PROPERTY_SERIAL_NUMBER</p>
 
-<p>You can get the names of the ports from a PortInfo object.</p>
+<p>You can get the names and types of the ports from a PortInfo object.
+The type will be either TYPE_INPUT or TYPE_OUTPUT.</p>
 
 <pre class=prettyprint>
-PortInfo portInfo = info.getInputPortInfo(i);
-String portName = portInfo.getName();
+MidiDeviceInfo.PortInfo[] portInfos = info.getPorts();
+String portName = portInfos[0].getName();
+if (portInfos[0].getType() == MidiDeviceInfo.PortInfo.TYPE_INPUT) {
+    ...
+}
 </pre>
 
 
@@ -196,8 +209,9 @@
 <p>Here we send a message with a timestamp 2 seconds in the future.</p>
 
 <pre class=prettyprint>
+final long NANOS_PER_SECOND = 1000000000L;
 long now = System.nanoTime();
-long future = now + (2 * 1000000000);
+long future = now + (2 * NANOS_PER_SECOND);
 inputPort.send(buffer, offset, numBytes, future);
 </pre>
 
@@ -263,7 +277,8 @@
 
 
 <p>The details of the resource in this example is stored in
-&ldquo;res/xml/synth_device_info.xml&rdquo;.</p>
+&ldquo;res/xml/synth_device_info.xml&rdquo;. The port names that you
+declare in this file will be available from PortInfo.getName().</p>
 
 <pre class=prettyprint>
 &lt;devices>
@@ -288,6 +303,8 @@
 public class MidiSynthDeviceService extends MidiDeviceService {
     private static final String TAG = "MidiSynthDeviceService";
     private MySynthEngine mSynthEngine = new MySynthEngine();
+    private boolean synthStarted = false;
+
     &#64;Override
     public void onCreate() {
         super.onCreate();
@@ -311,10 +328,12 @@
      */
     &#64;Override
     public void onDeviceStatusChanged(MidiDeviceStatus status) {
-        if (status.isInputPortOpen(0)) {
+        if (status.isInputPortOpen(0) && !synthStarted) {
             mSynthEngine.start();
-        } else {
+            synthStarted = true;
+        } else if (!status.isInputPortOpen(0) && synthStarted){
             mSynthEngine.stop();
+            synthStarted = false;
         }
     }
 }
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 93b8ec7..ce7f7e5 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -56,6 +56,7 @@
 enum {
     EVENT_CALLBACK = 1,
     EVENT_SET_CALLBACK = 2,
+    EVENT_FRAME_RENDERED = 3,
 };
 
 static struct CryptoErrorCodes {
@@ -226,6 +227,18 @@
     mByteBufferLimitMethodID = NULL;
 }
 
+status_t JMediaCodec::enableOnFrameRenderedListener(jboolean enable) {
+    if (enable) {
+        if (mOnFrameRenderedNotification == NULL) {
+            mOnFrameRenderedNotification = new AMessage(kWhatFrameRendered, this);
+        }
+    } else {
+        mOnFrameRenderedNotification.clear();
+    }
+
+    return mCodec->setOnFrameRenderedNotification(mOnFrameRenderedNotification);
+}
+
 status_t JMediaCodec::setCallback(jobject cb) {
     if (cb != NULL) {
         if (mCallbackNotification == NULL) {
@@ -728,6 +741,27 @@
     env->DeleteLocalRef(obj);
 }
 
+void JMediaCodec::handleFrameRenderedNotification(const sp<AMessage> &msg) {
+    int32_t arg1 = 0, arg2 = 0;
+    jobject obj = NULL;
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+
+    sp<AMessage> data;
+    CHECK(msg->findMessage("data", &data));
+
+    status_t err = ConvertMessageToMap(env, data, &obj);
+    if (err != OK) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return;
+    }
+
+    env->CallVoidMethod(
+            mObject, gFields.postEventFromNativeID,
+            EVENT_FRAME_RENDERED, arg1, arg2, obj);
+
+    env->DeleteLocalRef(obj);
+}
+
 void JMediaCodec::onMessageReceived(const sp<AMessage> &msg) {
     switch (msg->what()) {
         case kWhatCallbackNotify:
@@ -735,6 +769,11 @@
             handleCallback(msg);
             break;
         }
+        case kWhatFrameRendered:
+        {
+            handleFrameRenderedNotification(msg);
+            break;
+        }
         default:
             TRESPASS();
     }
@@ -848,6 +887,22 @@
     }
 }
 
+static void android_media_MediaCodec_native_enableOnFrameRenderedListener(
+        JNIEnv *env,
+        jobject thiz,
+        jboolean enabled) {
+    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+
+    if (codec == NULL) {
+        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        return;
+    }
+
+    status_t err = codec->enableOnFrameRenderedListener(enabled);
+
+    throwExceptionAsNecessary(env, err);
+}
+
 static void android_media_MediaCodec_native_setCallback(
         JNIEnv *env,
         jobject thiz,
@@ -1744,6 +1799,9 @@
     { "native_setInputSurface", "(Landroid/view/Surface;)V",
       (void *)android_media_MediaCodec_setInputSurface },
 
+    { "native_enableOnFrameRenderedListener", "(Z)V",
+      (void *)android_media_MediaCodec_native_enableOnFrameRenderedListener },
+
     { "native_setCallback",
       "(Landroid/media/MediaCodec$Callback;)V",
       (void *)android_media_MediaCodec_native_setCallback },
diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h
index a4ed67b..6650cf9 100644
--- a/media/jni/android_media_MediaCodec.h
+++ b/media/jni/android_media_MediaCodec.h
@@ -46,6 +46,8 @@
     void registerSelf();
     void release();
 
+    status_t enableOnFrameRenderedListener(jboolean enable);
+
     status_t setCallback(jobject cb);
 
     status_t configure(
@@ -116,11 +118,11 @@
     virtual ~JMediaCodec();
 
     virtual void onMessageReceived(const sp<AMessage> &msg);
-    void handleCallback(const sp<AMessage> &msg);
 
 private:
     enum {
         kWhatCallbackNotify,
+        kWhatFrameRendered,
     };
 
     jclass mClass;
@@ -139,6 +141,7 @@
     sp<MediaCodec> mCodec;
 
     sp<AMessage> mCallbackNotification;
+    sp<AMessage> mOnFrameRenderedNotification;
 
     status_t mInitStatus;
 
@@ -148,6 +151,8 @@
 
     void cacheJavaObjects(JNIEnv *env);
     void deleteJavaObjects(JNIEnv *env);
+    void handleCallback(const sp<AMessage> &msg);
+    void handleFrameRenderedNotification(const sp<AMessage> &msg);
 
     DISALLOW_EVIL_CONSTRUCTORS(JMediaCodec);
 };
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 14c8262..0e0584f 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -801,6 +801,9 @@
     <!-- Accessibility label for the button that opens the user switcher and announces the current user. -->
     <string name="accessibility_multi_user_switch_switcher_with_current">Switch user, current user <xliff:g id="current_user_name" example="John Doe">%s</xliff:g></string>
 
+    <!-- Accessibility label for the user icon on the lock screen. -->
+    <string name="accessibility_multi_user_switch_inactive">Current user <xliff:g id="current_user_name" example="John Doe">%s</xliff:g></string>
+
     <!-- Accessibility label for the button that opens the quick contact of the user. -->
     <string name="accessibility_multi_user_switch_quick_contact">Show profile</string>
 
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index f129288..3e122da 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -9,14 +9,15 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
+import android.database.ContentObserver;
 import android.graphics.PixelFormat;
 import android.media.AudioAttributes;
 import android.os.AsyncTask;
 import android.os.Bundle;
+import android.os.Handler;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
-import android.os.Vibrator;
 import android.provider.Settings;
 import android.util.Log;
 import android.view.Gravity;
@@ -56,6 +57,8 @@
     private final PhoneStatusBar mBar;
     private final IVoiceInteractionManagerService mVoiceInteractionManagerService;
 
+    private ComponentName mAssistComponent;
+
     private IVoiceInteractionSessionShowCallback mShowCallback =
             new IVoiceInteractionSessionShowCallback.Stub() {
 
@@ -78,12 +81,24 @@
         }
     };
 
+    private final ContentObserver mAssistSettingsObserver = new ContentObserver(new Handler()) {
+        @Override
+        public void onChange(boolean selfChange) {
+            updateAssistInfo();
+        }
+    };
+
     public AssistManager(PhoneStatusBar bar, Context context) {
         mContext = context;
         mBar = bar;
         mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
         mVoiceInteractionManagerService = IVoiceInteractionManagerService.Stub.asInterface(
                 ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE));
+
+        mContext.getContentResolver().registerContentObserver(
+                Settings.Secure.getUriFor(Settings.Secure.ASSISTANT), false,
+                mAssistSettingsObserver);
+        mAssistSettingsObserver.onChange(false);
     }
 
     public void onConfigurationChanged() {
@@ -108,16 +123,17 @@
     }
 
     public void onGestureInvoked(boolean vibrate) {
-        boolean isVoiceInteractorActive = getVoiceInteractorSupportsAssistGesture();
-        if (!isVoiceInteractorActive && !isAssistantIntentAvailable()) {
+        if (mAssistComponent == null) {
             return;
         }
+
         if (vibrate) {
             vibrate();
         }
-        if (!isVoiceInteractorActive || !isVoiceSessionRunning()) {
+        final boolean isService = isAssistantService();
+        if (isService || !isVoiceSessionRunning()) {
             showOrb();
-            mView.postDelayed(mHideRunnable, isVoiceInteractorActive
+            mView.postDelayed(mHideRunnable, isService
                     ? TIMEOUT_SERVICE
                     : TIMEOUT_ACTIVITY);
         }
@@ -157,10 +173,12 @@
     }
 
     private void startAssist() {
-        if (getVoiceInteractorSupportsAssistGesture()) {
-            startVoiceInteractor();
-        } else {
-            startAssistActivity();
+        if (mAssistComponent != null) {
+            if (isAssistantService()) {
+                startVoiceInteractor();
+            } else {
+                startAssistActivity();
+            }
         }
     }
 
@@ -178,6 +196,9 @@
         if (intent == null) {
             return;
         }
+        if (mAssistComponent != null) {
+            intent.setComponent(mAssistComponent);
+        }
 
         try {
             final ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext,
@@ -255,19 +276,9 @@
     }
 
     private void maybeSwapSearchIcon() {
-        Intent intent = ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
-                .getAssistIntent(mContext, false, UserHandle.USER_CURRENT);
-        ComponentName component = null;
-        boolean isService = false;
-        if (getVoiceInteractorSupportsAssistGesture()) {
-            component = getVoiceInteractorComponentName();
-            isService = true;
-        } else if (intent != null) {
-            component = intent.getComponent();
-        }
-        if (component != null) {
-            replaceDrawable(mView.getOrb().getLogo(), component, ASSIST_ICON_METADATA_NAME,
-                    isService);
+        if (mAssistComponent != null) {
+            replaceDrawable(mView.getOrb().getLogo(), mAssistComponent, ASSIST_ICON_METADATA_NAME,
+                    isAssistantService());
         } else {
             mView.getOrb().getLogo().setImageDrawable(null);
         }
@@ -308,8 +319,32 @@
         mView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
     }
 
-    public boolean isAssistantIntentAvailable() {
-        return ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
-                .getAssistIntent(mContext, false, UserHandle.USER_CURRENT) != null;
+    private boolean isAssistantService() {
+        return mAssistComponent == null ?
+                false : mAssistComponent.equals(getVoiceInteractorComponentName());
+    }
+
+    private void updateAssistInfo() {
+        final String setting = Settings.Secure.getStringForUser(mContext.getContentResolver(),
+                Settings.Secure.ASSISTANT, UserHandle.USER_CURRENT);
+        if (setting != null) {
+            mAssistComponent = ComponentName.unflattenFromString(setting);
+            return;
+        }
+
+        // Fallback to keep backward compatible behavior when there is no user setting.
+        if (getVoiceInteractorSupportsAssistGesture()) {
+            mAssistComponent = getVoiceInteractorComponentName();
+            return;
+        }
+
+        Intent intent = ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
+                .getAssistIntent(mContext, false, UserHandle.USER_CURRENT);
+        if (intent != null) {
+            mAssistComponent = intent.getComponent();
+            return;
+        }
+
+        mAssistComponent = null;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
index f59e864..ca38528 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
@@ -110,15 +110,13 @@
     }
 
     private void handleRefreshState() {
-        boolean hasDeviceOwner = mSecurityController.hasDeviceOwner();
-        boolean hasVpn = mSecurityController.isVpnEnabled();
-
-        mIsVisible = (hasVpn || hasDeviceOwner);
-        mIsIconVisible = hasVpn;
-        if (hasDeviceOwner) {
+        mIsIconVisible = mSecurityController.isVpnEnabled();
+        if (mSecurityController.hasDeviceOwner()) {
             mFooterTextId = R.string.device_owned_footer;
+            mIsVisible = true;
         } else {
             mFooterTextId = R.string.vpn_footer;
+            mIsVisible = mIsIconVisible;
         }
         mMainHandler.post(mUpdateDisplayState);
     }
@@ -132,15 +130,17 @@
     }
 
     private void createDialog() {
-        boolean hasDeviceOwner = mSecurityController.hasDeviceOwner();
-        boolean hasProfile = mSecurityController.hasProfileOwner();
-        boolean hasVpn = mSecurityController.isVpnEnabled();
+        String deviceOwner = mSecurityController.getDeviceOwnerName();
+        String profileOwner = mSecurityController.getProfileOwnerName();
+        String primaryVpn = mSecurityController.getPrimaryVpnName();
+        String profileVpn = mSecurityController.getProfileVpnName();
+        boolean managed = mSecurityController.hasProfileOwner();
 
         mDialog = new SystemUIDialog(mContext);
-        mDialog.setTitle(getTitle(hasDeviceOwner, hasProfile));
-        mDialog.setMessage(getMessage(hasDeviceOwner, hasProfile, hasVpn));
+        mDialog.setTitle(getTitle(deviceOwner));
+        mDialog.setMessage(getMessage(deviceOwner, profileOwner, primaryVpn, profileVpn, managed));
         mDialog.setButton(DialogInterface.BUTTON_POSITIVE, getPositiveButton(), this);
-        if (hasVpn) {
+        if (mSecurityController.isVpnEnabled()) {
             mDialog.setButton(DialogInterface.BUTTON_NEGATIVE, getNegativeButton(), this);
         }
         mDialog.show();
@@ -154,31 +154,42 @@
         return mContext.getString(R.string.quick_settings_done);
     }
 
-    private String getMessage(boolean hasDeviceOwner, boolean hasProfile, boolean hasVpn) {
-        if (hasDeviceOwner) {
-            if (hasVpn) {
-                return mContext.getString(R.string.monitoring_description_vpn_device_owned,
-                        mSecurityController.getDeviceOwnerName());
+    private String getMessage(String deviceOwner, String profileOwner, String primaryVpn,
+            String profileVpn, boolean primaryUserIsManaged) {
+        if (deviceOwner != null) {
+            if (primaryVpn != null) {
+                return mContext.getString(R.string.monitoring_description_vpn_app_device_owned,
+                        deviceOwner, primaryVpn);
             } else {
                 return mContext.getString(R.string.monitoring_description_device_owned,
-                        mSecurityController.getDeviceOwnerName());
+                        deviceOwner);
             }
-        } else if (hasProfile) {
-            return mContext.getString(
-                    R.string.monitoring_description_vpn_profile_owned,
-                    mSecurityController.getProfileOwnerName());
+        } else if (primaryVpn != null) {
+            if (profileVpn != null) {
+                return mContext.getString(R.string.monitoring_description_app_personal_work,
+                        profileOwner, profileVpn, primaryVpn);
+            } else {
+                return mContext.getString(R.string.monitoring_description_app_personal,
+                        primaryVpn);
+            }
+        } else if (profileVpn != null) {
+            return mContext.getString(R.string.monitoring_description_app_work,
+                    profileOwner, profileVpn);
+        } else if (profileOwner != null && primaryUserIsManaged) {
+            return mContext.getString(R.string.monitoring_description_device_owned,
+                    profileOwner);
         } else {
-            return mContext.getString(R.string.monitoring_description_vpn);
+            // No device owner, no personal VPN, no work VPN, no user owner. Why are we here?
+            return null;
         }
     }
 
-    private int getTitle(boolean hasDeviceOwner, boolean hasProfile) {
-        if (hasDeviceOwner) {
+    private int getTitle(String deviceOwner) {
+        if (deviceOwner != null) {
             return R.string.monitoring_title_device_owned;
-        } else if (hasProfile) {
-            return R.string.monitoring_title_profile_owned;
+        } else {
+            return R.string.monitoring_title;
         }
-        return R.string.monitoring_title;
     }
 
     private final Runnable mUpdateDisplayState = new Runnable() {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 7d2b5c87..442af90 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -365,7 +365,7 @@
 
     void preloadRecentsInternal() {
         // Preload only the raw task list into a new load plan (which will be consumed by the
-        // RecentsActivity)
+        // RecentsActivity) only if there is a task to animate to.
         ActivityManager.RunningTaskInfo topTask = mSystemServicesProxy.getTopMostTask();
         MutableBoolean topTaskHome = new MutableBoolean(true);
         RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
@@ -374,8 +374,10 @@
             sInstanceLoadPlan.preloadRawTasks(topTaskHome.value);
             loader.preloadTasks(sInstanceLoadPlan, topTaskHome.value);
             TaskStack top = sInstanceLoadPlan.getAllTaskStacks().get(0);
-            preCacheThumbnailTransitionBitmapAsync(topTask, top, mDummyStackView,
-                    topTaskHome.value);
+            if (top.getTaskCount() > 0) {
+                preCacheThumbnailTransitionBitmapAsync(topTask, top, mDummyStackView,
+                        topTaskHome.value);
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index 13b3898..b93fc76 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -34,6 +34,7 @@
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
 import com.android.systemui.statusbar.policy.UserInfoController;
+import com.android.systemui.statusbar.policy.UserSwitcherController;
 
 import java.text.NumberFormat;
 
@@ -140,6 +141,10 @@
         ((BatteryMeterView) findViewById(R.id.battery)).setBatteryController(batteryController);
     }
 
+    public void setUserSwitcherController(UserSwitcherController controller) {
+        mMultiUserSwitch.setUserSwitcherController(controller);
+    }
+
     public void setUserInfoController(UserInfoController userInfoController) {
         userInfoController.addListener(new UserInfoController.OnUserInfoChangedListener() {
             @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
index f11d83c..e70d146 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
@@ -24,7 +24,7 @@
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.View;
-import android.view.accessibility.AccessibilityEvent;
+import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
 import com.android.systemui.R;
@@ -40,10 +40,14 @@
     private QSPanel mQsPanel;
     private KeyguardUserSwitcher mKeyguardUserSwitcher;
     private boolean mKeyguardMode;
+    private UserSwitcherController.BaseUserAdapter mUserListener;
+
     final UserManager mUserManager;
 
     private final int[] mTmpInt2 = new int[2];
 
+    private UserSwitcherController mUserSwitcherController;
+
     public MultiUserSwitch(Context context, AttributeSet attrs) {
         super(context, attrs);
         mUserManager = UserManager.get(getContext());
@@ -53,10 +57,18 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
         setOnClickListener(this);
+        refreshContentDescription();
     }
 
     public void setQsPanel(QSPanel qsPanel) {
         mQsPanel = qsPanel;
+        setUserSwitcherController(qsPanel.getHost().getUserSwitcherController());
+    }
+
+    public void setUserSwitcherController(UserSwitcherController userSwitcherController) {
+        mUserSwitcherController = userSwitcherController;
+        registerListener();
+        refreshContentDescription();
     }
 
     public void setKeyguardUserSwitcher(KeyguardUserSwitcher keyguardUserSwitcher) {
@@ -65,6 +77,28 @@
 
     public void setKeyguardMode(boolean keyguardShowing) {
         mKeyguardMode = keyguardShowing;
+        registerListener();
+    }
+
+    private void registerListener() {
+        if (UserSwitcherController.isUserSwitcherAvailable(mUserManager) && mUserListener == null) {
+
+            final UserSwitcherController controller = mUserSwitcherController;
+            if (controller != null) {
+                mUserListener = new UserSwitcherController.BaseUserAdapter(controller) {
+                    @Override
+                    public void notifyDataSetChanged() {
+                        refreshContentDescription();
+                    }
+
+                    @Override
+                    public View getView(int position, View convertView, ViewGroup parent) {
+                        return null;
+                    }
+                };
+                refreshContentDescription();
+            }
+        }
     }
 
     @Override
@@ -74,22 +108,16 @@
                 if (mKeyguardUserSwitcher != null) {
                     mKeyguardUserSwitcher.show(true /* animate */);
                 }
-            } else {
-                if (mQsPanel != null) {
-                    UserSwitcherController userSwitcherController =
-                            mQsPanel.getHost().getUserSwitcherController();
-                    if (userSwitcherController != null) {
-                        View center = getChildCount() > 0 ? getChildAt(0) : this;
+            } else if (mQsPanel != null && mUserSwitcherController != null) {
+                View center = getChildCount() > 0 ? getChildAt(0) : this;
 
-                        center.getLocationInWindow(mTmpInt2);
-                        mTmpInt2[0] += center.getWidth() / 2;
-                        mTmpInt2[1] += center.getHeight() / 2;
+                center.getLocationInWindow(mTmpInt2);
+                mTmpInt2[0] += center.getWidth() / 2;
+                mTmpInt2[1] += center.getHeight() / 2;
 
-                        mQsPanel.showDetailAdapter(true,
-                                userSwitcherController.userDetailAdapter,
-                                mTmpInt2);
-                    }
-                }
+                mQsPanel.showDetailAdapter(true,
+                        mUserSwitcherController.userDetailAdapter,
+                        mTmpInt2);
             }
         } else {
             Intent intent = ContactsContract.QuickContact.composeQuickContactsIntent(
@@ -100,20 +128,21 @@
     }
 
     @Override
-    public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
-        super.onPopulateAccessibilityEvent(event);
+    public void setClickable(boolean clickable) {
+        super.setClickable(clickable);
+        refreshContentDescription();
+    }
 
+    private void refreshContentDescription() {
+        String currentUser = null;
+        if (UserSwitcherController.isUserSwitcherAvailable(mUserManager)
+                && mUserSwitcherController != null) {
+            currentUser = mUserSwitcherController.getCurrentUserName(mContext);
+        }
+
+        String text = null;
         if (isClickable()) {
-            String text;
             if (UserSwitcherController.isUserSwitcherAvailable(mUserManager)) {
-                String currentUser = null;
-                if (mQsPanel != null) {
-                    UserSwitcherController controller = mQsPanel.getHost()
-                            .getUserSwitcherController();
-                    if (controller != null) {
-                        currentUser = controller.getCurrentUserName(mContext);
-                    }
-                }
                 if (TextUtils.isEmpty(currentUser)) {
                     text = mContext.getString(R.string.accessibility_multi_user_switch_switcher);
                 } else {
@@ -124,11 +153,17 @@
             } else {
                 text = mContext.getString(R.string.accessibility_multi_user_switch_quick_contact);
             }
-            if (!TextUtils.isEmpty(text)) {
-                event.getText().add(text);
+        } else {
+            if (!TextUtils.isEmpty(currentUser)) {
+                text = mContext.getString(
+                        R.string.accessibility_multi_user_switch_inactive,
+                        currentUser);
             }
         }
 
+        if (!TextUtils.equals(getContentDescription(), text)) {
+            setContentDescription(text);
+        }
     }
 
     @Override
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 aa5aba0..a5b18f9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -860,6 +860,7 @@
         // User info. Trigger first load.
         mHeader.setUserInfoController(mUserInfoController);
         mKeyguardStatusBar.setUserInfoController(mUserInfoController);
+        mKeyguardStatusBar.setUserSwitcherController(mUserSwitcherController);
         mUserInfoController.reloadUserInfo();
 
         mHeader.setBatteryController(mBatteryController);
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 e1e022d..40984d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
@@ -22,6 +22,8 @@
     String getDeviceOwnerName();
     String getProfileOwnerName();
     boolean isVpnEnabled();
+    String getPrimaryVpnName();
+    String getProfileVpnName();
     void onUserSwitched(int newUserId);
 
     void addCallback(SecurityControllerCallback 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 4f47cc6..962000a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -36,6 +36,7 @@
 
 import com.android.internal.net.VpnConfig;
 import com.android.internal.net.VpnInfo;
+import com.android.systemui.R;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -61,7 +62,7 @@
     private final ArrayList<SecurityControllerCallback> mCallbacks
             = new ArrayList<SecurityControllerCallback>();
 
-    private SparseArray<Boolean> mCurrentVpnUsers = new SparseArray<>();
+    private SparseArray<VpnConfig> mCurrentVpns = new SparseArray<>();
     private int mCurrentUserId;
 
     public SecurityControllerImpl(Context context) {
@@ -82,7 +83,16 @@
 
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("SecurityController state:");
-        pw.print("  mCurrentVpnUsers=" + mCurrentVpnUsers);
+        pw.print("  mCurrentVpns={");
+        for (int i = 0 ; i < mCurrentVpns.size(); i++) {
+            if (i > 0) {
+                pw.print(", ");
+            }
+            pw.print(mCurrentVpns.keyAt(i));
+            pw.print('=');
+            pw.print(mCurrentVpns.valueAt(i).user);
+        }
+        pw.println("}");
     }
 
     @Override
@@ -97,11 +107,7 @@
 
     @Override
     public boolean hasProfileOwner() {
-        boolean result = false;
-        for (UserInfo profile : mUserManager.getProfiles(mCurrentUserId)) {
-            result |= (mDevicePolicyManager.getProfileOwnerAsUser(profile.id) != null);
-        }
-        return result;
+        return mDevicePolicyManager.getProfileOwnerAsUser(mCurrentUserId) != null;
     }
 
     @Override
@@ -116,8 +122,37 @@
     }
 
     @Override
+    public String getPrimaryVpnName() {
+        VpnConfig cfg = mCurrentVpns.get(mCurrentUserId);
+        if (cfg != null) {
+            return getNameForVpnConfig(cfg, new UserHandle(mCurrentUserId));
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public String getProfileVpnName() {
+        for (UserInfo profile : mUserManager.getProfiles(mCurrentUserId)) {
+            if (profile.id == mCurrentUserId) {
+                continue;
+            }
+            VpnConfig cfg = mCurrentVpns.get(profile.id);
+            if (cfg != null) {
+                return getNameForVpnConfig(cfg, profile.getUserHandle());
+            }
+        }
+        return null;
+    }
+
+    @Override
     public boolean isVpnEnabled() {
-        return mCurrentVpnUsers.get(mCurrentUserId) != null;
+        for (UserInfo profile : mUserManager.getProfiles(mCurrentUserId)) {
+            if (mCurrentVpns.get(profile.id) != null) {
+                return true;
+            }
+        }
+        return false;
     }
 
     @Override
@@ -140,6 +175,22 @@
         fireCallbacks();
     }
 
+    private String getNameForVpnConfig(VpnConfig cfg, UserHandle user) {
+        if (cfg.legacy) {
+            return mContext.getString(R.string.legacy_vpn_name);
+        }
+        // The package name for an active VPN is stored in the 'user' field of its VpnConfig
+        final String vpnPackage = cfg.user;
+        try {
+            Context userContext = mContext.createPackageContextAsUser(mContext.getPackageName(),
+                    0 /* flags */, user);
+            return VpnConfig.getVpnLabel(userContext, vpnPackage).toString();
+        } catch (NameNotFoundException nnfe) {
+            Log.e(TAG, "Package " + vpnPackage + " is not present", nnfe);
+            return null;
+        }
+    }
+
     private void fireCallbacks() {
         for (SecurityControllerCallback callback : mCallbacks) {
             callback.onStateChanged();
@@ -148,21 +199,20 @@
 
     private void updateState() {
         // Find all users with an active VPN
-        SparseArray<Boolean> vpnUsers = new SparseArray<>();
+        SparseArray<VpnConfig> vpns = new SparseArray<>();
         try {
-            for (VpnInfo vpn : mConnectivityManagerService.getAllVpnInfo()) {
-                UserInfo user = mUserManager.getUserInfo(UserHandle.getUserId(vpn.ownerUid));
-                int groupId = (user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID ?
-                        user.profileGroupId : user.id);
-
-                vpnUsers.put(groupId, Boolean.TRUE);
+            for (UserInfo user : mUserManager.getUsers()) {
+                VpnConfig cfg = mConnectivityManagerService.getVpnConfig(user.id);
+                if (cfg != null) {
+                    vpns.put(user.id, cfg);
+                }
             }
         } catch (RemoteException rme) {
             // Roll back to previous state
             Log.e(TAG, "Unable to list active VPNs", rme);
             return;
         }
-        mCurrentVpnUsers = vpnUsers;
+        mCurrentVpns = vpns;
     }
 
     private final NetworkCallback mNetworkCallback = new NetworkCallback() {
@@ -182,5 +232,4 @@
             fireCallbacks();
         };
     };
-
 }
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java b/packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java
index 7472af9..2b76c31 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java
@@ -315,6 +315,16 @@
             }
 
             @Override
+            public String getPrimaryVpnName() {
+                return null;
+            }
+
+            @Override
+            public String getProfileVpnName() {
+                return null;
+            }
+
+            @Override
             public void onUserSwitched(int newUserId) {
             }
 
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 99c4eda..3691a3a 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -112,6 +112,7 @@
 import com.android.internal.util.XmlUtils;
 import com.android.server.am.BatteryStatsService;
 import com.android.server.connectivity.DataConnectionStats;
+import com.android.server.connectivity.NetworkDiagnostics;
 import com.android.server.connectivity.Nat464Xlat;
 import com.android.server.connectivity.NetworkAgentInfo;
 import com.android.server.connectivity.NetworkMonitor;
@@ -1749,6 +1750,15 @@
         return ret;
     }
 
+    private boolean shouldPerformDiagnostics(String[] args) {
+        for (String arg : args) {
+            if (arg.equals("--diag")) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     @Override
     protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
         final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
@@ -1761,6 +1771,26 @@
             return;
         }
 
+        final List<NetworkDiagnostics> netDiags = new ArrayList<NetworkDiagnostics>();
+        if (shouldPerformDiagnostics(args)) {
+            final long DIAG_TIME_MS = 5000;
+            for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
+                // Start gathering diagnostic information.
+                netDiags.add(new NetworkDiagnostics(
+                        nai.network,
+                        new LinkProperties(nai.linkProperties),
+                        DIAG_TIME_MS));
+            }
+
+            for (NetworkDiagnostics netDiag : netDiags) {
+                pw.println();
+                netDiag.waitForMeasurements();
+                netDiag.dump(pw);
+            }
+
+            return;
+        }
+
         pw.print("NetworkFactories for:");
         for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) {
             pw.print(" " + nfi.name);
@@ -2994,7 +3024,12 @@
         throwIfLockdownEnabled();
 
         synchronized(mVpns) {
-            return mVpns.get(userId).prepare(oldPackage, newPackage);
+            Vpn vpn = mVpns.get(userId);
+            if (vpn != null) {
+                return vpn.prepare(oldPackage, newPackage);
+            } else {
+                return false;
+            }
         }
     }
 
@@ -3016,7 +3051,10 @@
         enforceCrossUserPermission(userId);
 
         synchronized(mVpns) {
-            mVpns.get(userId).setPackageAuthorization(packageName, authorized);
+            Vpn vpn = mVpns.get(userId);
+            if (vpn != null) {
+                vpn.setPackageAuthorization(packageName, authorized);
+            }
         }
     }
 
@@ -3127,7 +3165,12 @@
     public VpnConfig getVpnConfig(int userId) {
         enforceCrossUserPermission(userId);
         synchronized(mVpns) {
-            return mVpns.get(userId).getVpnConfig();
+            Vpn vpn = mVpns.get(userId);
+            if (vpn != null) {
+                return vpn.getVpnConfig();
+            } else {
+                return null;
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index bedc729..0d08c2a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -355,6 +355,10 @@
     // giving up on them and unfreezing the screen.
     static final int USER_SWITCH_TIMEOUT = 2*1000;
 
+    // This is the amount of time an app needs to be running a foreground service before
+    // we will consider it to be doing interaction for usage stats.
+    static final int SERVICE_USAGE_INTERACTION_TIME = 30*60*1000;
+
     // Maximum number of users we allow to be running at a time.
     static final int MAX_RUNNING_USERS = 3;
 
@@ -10723,12 +10727,15 @@
     }
 
     public void reportAssistContextExtras(IBinder token, Bundle extras, AssistStructure structure,
-            AssistContent content) {
+            AssistContent content, Uri referrer) {
         PendingAssistExtras pae = (PendingAssistExtras)token;
         synchronized (pae) {
             pae.result = extras;
             pae.structure = structure;
             pae.content = content;
+            if (referrer != null) {
+                pae.extras.putParcelable(Intent.EXTRA_REFERRER, referrer);
+            }
             pae.haveResult = true;
             pae.notifyAll();
             if (pae.intent == null && pae.receiver == null) {
@@ -17752,13 +17759,13 @@
                                     // give them the best state after that.
                                     if ((cr.flags&Context.BIND_FOREGROUND_SERVICE) != 0) {
                                         clientProcState =
-                                                ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
+                                                ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
                                     } else if (mWakefulness
                                                     == PowerManagerInternal.WAKEFULNESS_AWAKE &&
                                             (cr.flags&Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE)
                                                     != 0) {
                                         clientProcState =
-                                                ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
+                                                ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
                                     } else {
                                         clientProcState =
                                                 ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
@@ -17872,7 +17879,7 @@
                         // into the top state, since they are not on top.  Instead
                         // give them the best state after that.
                         clientProcState =
-                                ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
+                                ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
                     }
                 }
                 if (procState > clientProcState) {
@@ -17912,7 +17919,7 @@
                 case ActivityManager.PROCESS_STATE_SERVICE:
                     // These all are longer-term states, so pull them up to the top
                     // of the background states, but not all the way to the top state.
-                    procState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
+                    procState = ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
                     break;
                 default:
                     // Otherwise, top is a better choice, so take it.
@@ -18482,7 +18489,7 @@
             }
             // Inform UsageStats of important process state change
             // Must be called before updating setProcState
-            maybeUpdateUsageStats(app);
+            maybeUpdateUsageStatsLocked(app);
 
             app.setProcState = app.curProcState;
             if (app.setProcState >= ActivityManager.PROCESS_STATE_HOME) {
@@ -18571,7 +18578,7 @@
         uidRec.pendingChange.processState = uidRec.setProcState;
     }
 
-    private void maybeUpdateUsageStats(ProcessRecord app) {
+    private void maybeUpdateUsageStatsLocked(ProcessRecord app) {
         if (DEBUG_USAGE_STATS) {
             Slog.d(TAG, "Checking proc [" + Arrays.toString(app.getPackageList())
                     + "] state changes: old = " + app.setProcState + ", new = "
@@ -18580,9 +18587,32 @@
         if (mUsageStatsService == null) {
             return;
         }
-        if (app.curProcState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
-                && (app.setProcState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
-                        || app.setProcState < 0)) {
+        boolean isInteraction;
+        if (!mSleeping) {
+            isInteraction = app.curProcState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+            app.fgInteractionTime = 0;
+        } else {
+            // If the display is off, we are going to be more restrictive about what we consider
+            // to be an app interaction.  Being the top activity doesn't count, nor do generally
+            // foreground services.
+            if (app.curProcState <= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
+                isInteraction = true;
+                app.fgInteractionTime = 0;
+            } else if (app.curProcState <= ActivityManager.PROCESS_STATE_TOP_SLEEPING) {
+                final long now = SystemClock.elapsedRealtime();
+                if (app.fgInteractionTime == 0) {
+                    app.fgInteractionTime = now;
+                    isInteraction = false;
+                } else {
+                    isInteraction = now > app.fgInteractionTime + SERVICE_USAGE_INTERACTION_TIME;
+                }
+            } else {
+                isInteraction = app.curProcState
+                        <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+                app.fgInteractionTime = 0;
+            }
+        }
+        if (isInteraction && !app.reportedInteraction) {
             String[] packages = app.getPackageList();
             if (packages != null) {
                 for (int i = 0; i < packages.length; i++) {
@@ -18591,6 +18621,7 @@
                 }
             }
         }
+        app.reportedInteraction = isInteraction;
     }
 
     private final void setProcessTrackerStateLocked(ProcessRecord proc, int memFactor, long now) {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 23e62e2..577a4f9 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -2019,22 +2019,15 @@
                                     r, top.task);
                             top.deliverNewIntentLocked(callingUid, r.intent, r.launchedFromPackage);
                         } else {
-                            // A special case: we need to start the activity because it is not
-                            // currently running, and the caller has asked to clear the current
-                            // task to have this activity at the top.
+                            // A special case: we need to
+                            // start the activity because it is not currently
+                            // running, and the caller has asked to clear the
+                            // current task to have this activity at the top.
                             addingToTask = true;
-                            // Now pretend like this activity is being started by the top of its
-                            // task, so it is put in the right place.
+                            // Now pretend like this activity is being started
+                            // by the top of its task, so it is put in the
+                            // right place.
                             sourceRecord = intentActivity;
-                            TaskRecord task = sourceRecord.task;
-                            if (task != null && task.stack == null) {
-                                // Target stack got cleared when we all activities were removed
-                                // above. Go ahead and reset it.
-                                targetStack = computeStackFocus(sourceRecord, false /* newTask */);
-                                targetStack.addTask(
-                                        task, !launchTaskBehind /* toTop */, false /* moving */);
-                            }
-
                         }
                     } else if (r.realActivity.equals(intentActivity.task.realActivity)) {
                         // In this case the top activity on the task is the
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index cdfcd0c..0e24952 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -368,8 +368,11 @@
             case ActivityManager.PROCESS_STATE_TOP:
                 procState = "T ";
                 break;
+            case ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE:
+                procState = "SB";
+                break;
             case ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE:
-                procState = "FS";
+                procState = "SF";
                 break;
             case ActivityManager.PROCESS_STATE_TOP_SLEEPING:
                 procState = "TS";
@@ -481,6 +484,7 @@
         PROC_MEM_PERSISTENT,            // ActivityManager.PROCESS_STATE_PERSISTENT
         PROC_MEM_PERSISTENT,            // ActivityManager.PROCESS_STATE_PERSISTENT_UI
         PROC_MEM_TOP,                   // ActivityManager.PROCESS_STATE_TOP
+        PROC_MEM_IMPORTANT,             // ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
         PROC_MEM_IMPORTANT,             // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
         PROC_MEM_TOP,                   // ActivityManager.PROCESS_STATE_TOP_SLEEPING
         PROC_MEM_IMPORTANT,             // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
@@ -500,6 +504,7 @@
         PSS_SHORT_INTERVAL,             // ActivityManager.PROCESS_STATE_PERSISTENT
         PSS_SHORT_INTERVAL,             // ActivityManager.PROCESS_STATE_PERSISTENT_UI
         PSS_FIRST_TOP_INTERVAL,         // ActivityManager.PROCESS_STATE_TOP
+        PSS_FIRST_BACKGROUND_INTERVAL,  // ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
         PSS_FIRST_BACKGROUND_INTERVAL,  // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
         PSS_FIRST_BACKGROUND_INTERVAL,  // ActivityManager.PROCESS_STATE_TOP_SLEEPING
         PSS_FIRST_BACKGROUND_INTERVAL,  // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
@@ -519,6 +524,7 @@
         PSS_SAME_IMPORTANT_INTERVAL,    // ActivityManager.PROCESS_STATE_PERSISTENT
         PSS_SAME_IMPORTANT_INTERVAL,    // ActivityManager.PROCESS_STATE_PERSISTENT_UI
         PSS_SHORT_INTERVAL,             // ActivityManager.PROCESS_STATE_TOP
+        PSS_SAME_IMPORTANT_INTERVAL,    // ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
         PSS_SAME_IMPORTANT_INTERVAL,    // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
         PSS_SAME_IMPORTANT_INTERVAL,    // ActivityManager.PROCESS_STATE_TOP_SLEEPING
         PSS_SAME_IMPORTANT_INTERVAL,    // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
@@ -538,6 +544,7 @@
         PSS_TEST_FIRST_TOP_INTERVAL,        // ActivityManager.PROCESS_STATE_PERSISTENT
         PSS_TEST_FIRST_TOP_INTERVAL,        // ActivityManager.PROCESS_STATE_PERSISTENT_UI
         PSS_TEST_FIRST_TOP_INTERVAL,        // ActivityManager.PROCESS_STATE_TOP
+        PSS_FIRST_BACKGROUND_INTERVAL,      // ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
         PSS_FIRST_BACKGROUND_INTERVAL,      // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
         PSS_FIRST_BACKGROUND_INTERVAL,      // ActivityManager.PROCESS_STATE_TOP_SLEEPING
         PSS_TEST_FIRST_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
@@ -557,6 +564,7 @@
         PSS_TEST_SAME_BACKGROUND_INTERVAL,  // ActivityManager.PROCESS_STATE_PERSISTENT
         PSS_TEST_SAME_BACKGROUND_INTERVAL,  // ActivityManager.PROCESS_STATE_PERSISTENT_UI
         PSS_TEST_SAME_IMPORTANT_INTERVAL,   // ActivityManager.PROCESS_STATE_TOP
+        PSS_TEST_SAME_IMPORTANT_INTERVAL,   // ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
         PSS_TEST_SAME_IMPORTANT_INTERVAL,   // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
         PSS_TEST_SAME_IMPORTANT_INTERVAL,   // ActivityManager.PROCESS_STATE_TOP_SLEEPING
         PSS_TEST_SAME_IMPORTANT_INTERVAL,   // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 14759c3..3acd3a3 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -112,6 +112,8 @@
     boolean killedByAm;         // True when proc has been killed by activity manager, not for RAM
     boolean killed;             // True once we know the process has been killed
     boolean procStateChanged;   // Keep track of whether we changed 'setAdj'.
+    boolean reportedInteraction;// Whether we have told usage stats about it being an interaction
+    long fgInteractionTime;     // When we became foreground for interaction purposes
     String waitingToKill;       // Process is waiting to be killed when in the bg, and reason
     IBinder forcingToForeground;// Token that is forcing this process to be foreground
     int adjSeq;                 // Sequence id for identifying oom_adj assignment cycles
@@ -291,6 +293,15 @@
                     pw.print(" foregroundServices="); pw.print(foregroundServices);
                     pw.print(" forcingToForeground="); pw.println(forcingToForeground);
         }
+        if (reportedInteraction || fgInteractionTime != 0) {
+            pw.print(prefix); pw.print("reportedInteraction=");
+            pw.print(reportedInteraction);
+            if (fgInteractionTime != 0) {
+                pw.print(" fgInteractionTime=");
+                TimeUtils.formatDuration(fgInteractionTime, SystemClock.elapsedRealtime(), pw);
+            }
+            pw.println();
+        }
         if (persistent || removed) {
             pw.print(prefix); pw.print("persistent="); pw.print(persistent);
                     pw.print(" removed="); pw.println(removed);
diff --git a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
new file mode 100644
index 0000000..5d56d4a
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
@@ -0,0 +1,509 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import static android.system.OsConstants.*;
+
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.RouteInfo;
+import android.os.SystemClock;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.StructTimeval;
+import android.text.TextUtils;
+
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.Closeable;
+import java.io.FileDescriptor;
+import java.io.InterruptedIOException;
+import java.io.IOException;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.NetworkInterface;
+import java.net.SocketAddress;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+
+import libcore.io.IoUtils;
+
+
+/**
+ * NetworkDiagnostics
+ *
+ * A simple class to diagnose network connectivity fundamentals.  Current
+ * checks performed are:
+ *     - ICMPv4/v6 echo requests for all routers
+ *     - ICMPv4/v6 echo requests for all DNS servers
+ *     - DNS UDP queries to all DNS servers
+ *
+ * Currently unimplemented checks include:
+ *     - report ARP/ND data about on-link neighbors
+ *     - DNS TCP queries to all DNS servers
+ *     - HTTP DIRECT and PROXY checks
+ *     - port 443 blocking/TLS intercept checks
+ *     - QUIC reachability checks
+ *     - MTU checks
+ *
+ * The supplied timeout bounds the entire diagnostic process.  Each specific
+ * check class must implement this upper bound on measurements in whichever
+ * manner is most appropriate and effective.
+ *
+ * @hide
+ */
+public class NetworkDiagnostics {
+    private static final String TAG = "NetworkDiagnostics";
+
+    // For brevity elsewhere.
+    private static final long now() {
+        return SystemClock.elapsedRealtime();
+    }
+
+    // Values from RFC 1035 section 4.1.1, names from <arpa/nameser.h>.
+    // Should be a member of DnsUdpCheck, but "compiler says no".
+    public static enum DnsResponseCode { NOERROR, FORMERR, SERVFAIL, NXDOMAIN, NOTIMP, REFUSED };
+
+    private final Network mNetwork;
+    private final LinkProperties mLinkProperties;
+    private final Integer mInterfaceIndex;
+
+    private final long mTimeoutMs;
+    private final long mStartTime;
+    private final long mDeadlineTime;
+
+    // A counter, initialized to the total number of measurements,
+    // so callers can wait for completion.
+    private final CountDownLatch mCountDownLatch;
+
+    private class Measurement {
+        private static final String SUCCEEDED = "SUCCEEDED";
+        private static final String FAILED = "FAILED";
+
+        // TODO: Refactor to make these private for better encapsulation.
+        public String description = "";
+        public long startTime;
+        public long finishTime;
+        public String result = "";
+        public Thread thread;
+
+        public void recordSuccess(String msg) {
+            maybeFixupTimes();
+            if (mCountDownLatch != null) {
+                mCountDownLatch.countDown();
+            }
+            result = SUCCEEDED + ": " + msg;
+        }
+
+        public void recordFailure(String msg) {
+            maybeFixupTimes();
+            if (mCountDownLatch != null) {
+                mCountDownLatch.countDown();
+            }
+            result = FAILED + ": " + msg;
+        }
+
+        private void maybeFixupTimes() {
+            // Allows the caller to just set success/failure and not worry
+            // about also setting the correct finishing time.
+            if (finishTime == 0) { finishTime = now(); }
+
+            // In cases where, for example, a failure has occurred before the
+            // measurement even began, fixup the start time to reflect as much.
+            if (startTime == 0) { startTime = finishTime; }
+        }
+
+        @Override
+        public String toString() {
+            return description + ": " + result + " (" + (finishTime - startTime) + "ms)";
+        }
+    }
+
+    private final Map<InetAddress, Measurement> mIcmpChecks = new HashMap<>();
+    private final Map<InetAddress, Measurement> mDnsUdpChecks = new HashMap<>();
+    private final String mDescription;
+
+
+    public NetworkDiagnostics(Network network, LinkProperties lp, long timeoutMs) {
+        mNetwork = network;
+        mLinkProperties = lp;
+        mInterfaceIndex = getInterfaceIndex(mLinkProperties.getInterfaceName());
+        mTimeoutMs = timeoutMs;
+        mStartTime = now();
+        mDeadlineTime = mStartTime + mTimeoutMs;
+
+        for (RouteInfo route : mLinkProperties.getRoutes()) {
+            if (route.hasGateway()) {
+                prepareIcmpMeasurement(route.getGateway());
+            }
+        }
+        for (InetAddress nameserver : mLinkProperties.getDnsServers()) {
+                prepareIcmpMeasurement(nameserver);
+                prepareDnsMeasurement(nameserver);
+        }
+
+        mCountDownLatch = new CountDownLatch(totalMeasurementCount());
+
+        startMeasurements();
+
+        mDescription = "ifaces{" + TextUtils.join(",", mLinkProperties.getAllInterfaceNames()) + "}"
+                + " index{" + mInterfaceIndex + "}"
+                + " network{" + mNetwork + "}"
+                + " nethandle{" + mNetwork.getNetworkHandle() + "}";
+    }
+
+    private static Integer getInterfaceIndex(String ifname) {
+        try {
+            NetworkInterface ni = NetworkInterface.getByName(ifname);
+            return ni.getIndex();
+        } catch (NullPointerException | SocketException e) {
+            return null;
+        }
+    }
+
+    private void prepareIcmpMeasurement(InetAddress target) {
+        if (!mIcmpChecks.containsKey(target)) {
+            Measurement measurement = new Measurement();
+            measurement.thread = new Thread(new IcmpCheck(target, measurement));
+            mIcmpChecks.put(target, measurement);
+        }
+    }
+
+    private void prepareDnsMeasurement(InetAddress target) {
+        if (!mDnsUdpChecks.containsKey(target)) {
+            Measurement measurement = new Measurement();
+            measurement.thread = new Thread(new DnsUdpCheck(target, measurement));
+            mDnsUdpChecks.put(target, measurement);
+        }
+    }
+
+    private int totalMeasurementCount() {
+        return mIcmpChecks.size() + mDnsUdpChecks.size();
+    }
+
+    private void startMeasurements() {
+        for (Measurement measurement : mIcmpChecks.values()) {
+            measurement.thread.start();
+        }
+        for (Measurement measurement : mDnsUdpChecks.values()) {
+            measurement.thread.start();
+        }
+    }
+
+    public void waitForMeasurements() {
+        try {
+            mCountDownLatch.await(mDeadlineTime - now(), TimeUnit.MILLISECONDS);
+        } catch (InterruptedException ignored) {}
+    }
+
+    public void dump(IndentingPrintWriter pw) {
+        pw.println(TAG + ":" + mDescription);
+        final long unfinished = mCountDownLatch.getCount();
+        if (unfinished > 0) {
+            // This can't happen unless a caller forgets to call waitForMeasurements()
+            // or a measurement isn't implemented to correctly honor the timeout.
+            pw.println("WARNING: countdown wait incomplete: "
+                    + unfinished + " unfinished measurements");
+        }
+
+        pw.increaseIndent();
+        for (Map.Entry<InetAddress, Measurement> entry : mIcmpChecks.entrySet()) {
+            if (entry.getKey() instanceof Inet4Address) {
+                pw.println(entry.getValue().toString());
+            }
+        }
+        for (Map.Entry<InetAddress, Measurement> entry : mIcmpChecks.entrySet()) {
+            if (entry.getKey() instanceof Inet6Address) {
+                pw.println(entry.getValue().toString());
+            }
+        }
+        for (Map.Entry<InetAddress, Measurement> entry : mDnsUdpChecks.entrySet()) {
+            if (entry.getKey() instanceof Inet4Address) {
+                pw.println(entry.getValue().toString());
+            }
+        }
+        for (Map.Entry<InetAddress, Measurement> entry : mDnsUdpChecks.entrySet()) {
+            if (entry.getKey() instanceof Inet6Address) {
+                pw.println(entry.getValue().toString());
+            }
+        }
+        pw.decreaseIndent();
+    }
+
+
+    private class SimpleSocketCheck implements Closeable {
+        protected final InetAddress mTarget;
+        protected final int mAddressFamily;
+        protected final Measurement mMeasurement;
+        protected FileDescriptor mFileDescriptor;
+        protected SocketAddress mSocketAddress;
+
+        protected SimpleSocketCheck(InetAddress target, Measurement measurement) {
+            mMeasurement = measurement;
+
+            if (target instanceof Inet6Address) {
+                Inet6Address targetWithScopeId = null;
+                if (target.isLinkLocalAddress() && mInterfaceIndex != null) {
+                    try {
+                        targetWithScopeId = Inet6Address.getByAddress(
+                                null, target.getAddress(), mInterfaceIndex);
+                    } catch (UnknownHostException e) {
+                        mMeasurement.recordFailure(e.toString());
+                    }
+                }
+                mTarget = (targetWithScopeId != null) ? targetWithScopeId : target;
+                mAddressFamily = AF_INET6;
+            } else {
+                mTarget = target;
+                mAddressFamily = AF_INET;
+            }
+        }
+
+        protected void setupSocket(
+                int sockType, int protocol, long writeTimeout, long readTimeout, int dstPort)
+                throws ErrnoException, IOException {
+            mFileDescriptor = Os.socket(mAddressFamily, sockType, protocol);
+            // Setting SNDTIMEO is purely for defensive purposes.
+            Os.setsockoptTimeval(mFileDescriptor,
+                    SOL_SOCKET, SO_SNDTIMEO, StructTimeval.fromMillis(writeTimeout));
+            Os.setsockoptTimeval(mFileDescriptor,
+                    SOL_SOCKET, SO_RCVTIMEO, StructTimeval.fromMillis(readTimeout));
+            // TODO: Use IP_RECVERR/IPV6_RECVERR, pending OsContants availability.
+            mNetwork.bindSocket(mFileDescriptor);
+            Os.connect(mFileDescriptor, mTarget, dstPort);
+            mSocketAddress = Os.getsockname(mFileDescriptor);
+        }
+
+        protected String getSocketAddressString() {
+            // The default toString() implementation is not the prettiest.
+            InetSocketAddress inetSockAddr = (InetSocketAddress) mSocketAddress;
+            InetAddress localAddr = inetSockAddr.getAddress();
+            return String.format(
+                    (localAddr instanceof Inet6Address ? "[%s]:%d" : "%s:%d"),
+                    localAddr.getHostAddress(), inetSockAddr.getPort());
+        }
+
+        @Override
+        public void close() {
+            IoUtils.closeQuietly(mFileDescriptor);
+        }
+    }
+
+
+    private class IcmpCheck extends SimpleSocketCheck implements Runnable {
+        private static final int TIMEOUT_SEND = 100;
+        private static final int TIMEOUT_RECV = 300;
+        private static final int ICMPV4_ECHO_REQUEST = 8;
+        private static final int ICMPV6_ECHO_REQUEST = 128;
+        private static final int PACKET_BUFSIZE = 512;
+        private final int mProtocol;
+        private final int mIcmpType;
+
+        public IcmpCheck(InetAddress target, Measurement measurement) {
+            super(target, measurement);
+
+            if (mAddressFamily == AF_INET6) {
+                mProtocol = IPPROTO_ICMPV6;
+                mIcmpType = ICMPV6_ECHO_REQUEST;
+                mMeasurement.description = "ICMPv6";
+            } else {
+                mProtocol = IPPROTO_ICMP;
+                mIcmpType = ICMPV4_ECHO_REQUEST;
+                mMeasurement.description = "ICMPv4";
+            }
+
+            mMeasurement.description += " dst{" + mTarget.getHostAddress() + "}";
+        }
+
+        @Override
+        public void run() {
+            // Check if this measurement has already failed during setup.
+            if (mMeasurement.finishTime > 0) {
+                // If the measurement failed during construction it didn't
+                // decrement the countdown latch; do so here.
+                mCountDownLatch.countDown();
+                return;
+            }
+
+            try {
+                setupSocket(SOCK_DGRAM, mProtocol, TIMEOUT_SEND, TIMEOUT_RECV, 0);
+            } catch (ErrnoException | IOException e) {
+                mMeasurement.recordFailure(e.toString());
+                return;
+            }
+            mMeasurement.description += " src{" + getSocketAddressString() + "}";
+
+            // Build a trivial ICMP packet.
+            final byte[] icmpPacket = {
+                    (byte) mIcmpType, 0, 0, 0, 0, 0, 0, 0  // ICMP header
+            };
+
+            int count = 0;
+            mMeasurement.startTime = now();
+            while (now() < mDeadlineTime - (TIMEOUT_SEND + TIMEOUT_RECV)) {
+                count++;
+                icmpPacket[icmpPacket.length - 1] = (byte) count;
+                try {
+                    Os.write(mFileDescriptor, icmpPacket, 0, icmpPacket.length);
+                } catch (ErrnoException | InterruptedIOException e) {
+                    mMeasurement.recordFailure(e.toString());
+                    break;
+                }
+
+                try {
+                    ByteBuffer reply = ByteBuffer.allocate(PACKET_BUFSIZE);
+                    Os.read(mFileDescriptor, reply);
+                    // TODO: send a few pings back to back to guesstimate packet loss.
+                    mMeasurement.recordSuccess("1/" + count);
+                    break;
+                } catch (ErrnoException | InterruptedIOException e) {
+                    continue;
+                }
+            }
+            if (mMeasurement.finishTime == 0) {
+                mMeasurement.recordFailure("0/" + count);
+            }
+
+            close();
+        }
+    }
+
+
+    private class DnsUdpCheck extends SimpleSocketCheck implements Runnable {
+        private static final int TIMEOUT_SEND = 100;
+        private static final int TIMEOUT_RECV = 500;
+        private static final int DNS_SERVER_PORT = 53;
+        private static final int RR_TYPE_A = 1;
+        private static final int RR_TYPE_AAAA = 28;
+        private static final int PACKET_BUFSIZE = 512;
+
+        private final Random mRandom = new Random();
+
+        // Should be static, but the compiler mocks our puny, human attempts at reason.
+        private String responseCodeStr(int rcode) {
+            try {
+                return DnsResponseCode.values()[rcode].toString();
+            } catch (IndexOutOfBoundsException e) {
+                return String.valueOf(rcode);
+            }
+        }
+
+        private final int mQueryType;
+
+        public DnsUdpCheck(InetAddress target, Measurement measurement) {
+            super(target, measurement);
+
+            // TODO: Ideally, query the target for both types regardless of address family.
+            if (mAddressFamily == AF_INET6) {
+                mQueryType = RR_TYPE_AAAA;
+            } else {
+                mQueryType = RR_TYPE_A;
+            }
+
+            mMeasurement.description = "DNS UDP dst{" + mTarget.getHostAddress() + "}";
+        }
+
+        @Override
+        public void run() {
+            // Check if this measurement has already failed during setup.
+            if (mMeasurement.finishTime > 0) {
+                // If the measurement failed during construction it didn't
+                // decrement the countdown latch; do so here.
+                mCountDownLatch.countDown();
+                return;
+            }
+
+            try {
+                setupSocket(SOCK_DGRAM, IPPROTO_UDP, TIMEOUT_SEND, TIMEOUT_RECV, DNS_SERVER_PORT);
+            } catch (ErrnoException | IOException e) {
+                mMeasurement.recordFailure(e.toString());
+                return;
+            }
+            mMeasurement.description += " src{" + getSocketAddressString() + "}";
+
+            // This needs to be fixed length so it can be dropped into the pre-canned packet.
+            final String sixRandomDigits =
+                    Integer.valueOf(mRandom.nextInt(900000) + 100000).toString();
+            mMeasurement.description += " qtype{" + mQueryType + "}"
+                    + " qname{" + sixRandomDigits + "-android-ds.metric.gstatic.com}";
+
+            // Build a trivial DNS packet.
+            final byte[] dnsPacket = getDnsQueryPacket(sixRandomDigits);
+
+            int count = 0;
+            mMeasurement.startTime = now();
+            while (now() < mDeadlineTime - (TIMEOUT_RECV + TIMEOUT_RECV)) {
+                count++;
+                try {
+                    Os.write(mFileDescriptor, dnsPacket, 0, dnsPacket.length);
+                } catch (ErrnoException | InterruptedIOException e) {
+                    mMeasurement.recordFailure(e.toString());
+                    break;
+                }
+
+                try {
+                    ByteBuffer reply = ByteBuffer.allocate(PACKET_BUFSIZE);
+                    Os.read(mFileDescriptor, reply);
+                    // TODO: more correct and detailed evaluation of the response,
+                    // possibly adding the returned IP address(es) to the output.
+                    final String rcodeStr = (reply.limit() > 3)
+                            ? " " + responseCodeStr((int) (reply.get(3)) & 0x0f)
+                            : "";
+                    mMeasurement.recordSuccess("1/" + count + rcodeStr);
+                    break;
+                } catch (ErrnoException | InterruptedIOException e) {
+                    continue;
+                }
+            }
+            if (mMeasurement.finishTime == 0) {
+                mMeasurement.recordFailure("0/" + count);
+            }
+
+            close();
+        }
+
+        private byte[] getDnsQueryPacket(String sixRandomDigits) {
+            byte[] rnd = sixRandomDigits.getBytes(StandardCharsets.US_ASCII);
+            return new byte[] {
+                (byte) mRandom.nextInt(), (byte) mRandom.nextInt(),  // [0-1]   query ID
+                1, 0,  // [2-3]   flags; byte[2] = 1 for recursion desired (RD).
+                0, 1,  // [4-5]   QDCOUNT (number of queries)
+                0, 0,  // [6-7]   ANCOUNT (number of answers)
+                0, 0,  // [8-9]   NSCOUNT (number of name server records)
+                0, 0,  // [10-11] ARCOUNT (number of additional records)
+                17, rnd[0], rnd[1], rnd[2], rnd[3], rnd[4], rnd[5],
+                        '-', 'a', 'n', 'd', 'r', 'o', 'i', 'd', '-', 'd', 's',
+                6, 'm', 'e', 't', 'r', 'i', 'c',
+                7, 'g', 's', 't', 'a', 't', 'i', 'c',
+                3, 'c', 'o', 'm',
+                0,  // null terminator of FQDN (root TLD)
+                0, (byte) mQueryType,  // QTYPE
+                0, 1  // QCLASS, set to 1 = IN (Internet)
+            };
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 604ac97..e2cc3f7 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -278,6 +278,7 @@
     private static final boolean DEBUG_VERIFY = false;
     private static final boolean DEBUG_DEXOPT = false;
     private static final boolean DEBUG_ABI_SELECTION = false;
+    private static final boolean DEBUG_DOMAIN_VERIFICATION = false;
 
     private static final int RADIO_UID = Process.PHONE_UID;
     private static final int LOG_UID = Process.LOG_UID;
@@ -620,7 +621,8 @@
 
             UserHandle user = new UserHandle(userId);
             mContext.sendBroadcastAsUser(verificationIntent, user);
-            Slog.d(TAG, "Sending IntenFilter verification broadcast");
+            if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
+                    "Sending IntenFilter verification broadcast");
         }
 
         public void receiveVerificationResponse(int verificationId) {
@@ -634,8 +636,9 @@
                 PackageParser.ActivityIntentInfo filter = filters.get(n);
                 filter.setVerified(verified);
 
-                Slog.d(TAG, "IntentFilter " + filter.toString() + " verified with result:"
-                        + verified + " and hosts:" + ivs.getHostsString());
+                if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "IntentFilter " + filter.toString()
+                        + " verified with result:" + verified + " and hosts:"
+                        + ivs.getHostsString());
             }
 
             mIntentFilterVerificationStates.remove(verificationId);
@@ -651,8 +654,8 @@
                         + verificationId + " packageName:" + packageName);
                 return;
             }
-            Slog.d(TAG, "Updating IntentFilterVerificationInfo for verificationId:"
-                    + verificationId);
+            if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
+                    "Updating IntentFilterVerificationInfo for verificationId:" + verificationId);
 
             synchronized (mPackages) {
                 if (verified) {
@@ -707,7 +710,8 @@
                     ActivityIntentInfo filter, String packageName) {
             if (!(filter.hasDataScheme(IntentFilter.SCHEME_HTTP) ||
                     filter.hasDataScheme(IntentFilter.SCHEME_HTTPS))) {
-                Slog.d(TAG, "IntentFilter does not contain HTTP nor HTTPS data scheme");
+                if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
+                        "IntentFilter does not contain HTTP nor HTTPS data scheme");
                 return false;
             }
             IntentFilterVerificationState ivs = mIntentFilterVerificationStates.get(verificationId);
@@ -736,16 +740,11 @@
     }
 
     private static boolean hasValidDomains(ActivityIntentInfo filter) {
-        return hasValidDomains(filter, true);
-    }
-
-    private static boolean hasValidDomains(ActivityIntentInfo filter, boolean logging) {
         boolean hasHTTPorHTTPS = filter.hasDataScheme(IntentFilter.SCHEME_HTTP) ||
                 filter.hasDataScheme(IntentFilter.SCHEME_HTTPS);
         if (!hasHTTPorHTTPS) {
-            if (logging) {
-                Slog.d(TAG, "IntentFilter does not contain any HTTP or HTTPS data scheme");
-            }
+            if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
+                    "IntentFilter does not contain any HTTP or HTTPS data scheme");
             return false;
         }
         return true;
@@ -1515,7 +1514,8 @@
 
                     final int userId = state.getUserId();
 
-                    Slog.d(TAG, "Processing IntentFilter verification with token:"
+                    if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
+                            "Processing IntentFilter verification with token:"
                             + verificationId + " and userId:" + userId);
 
                     final IntentFilterVerificationResponse response =
@@ -1523,20 +1523,22 @@
 
                     state.setVerifierResponse(response.callerUid, response.code);
 
-                    Slog.d(TAG, "IntentFilter verification with token:" + verificationId
+                    if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
+                            "IntentFilter verification with token:" + verificationId
                             + " and userId:" + userId
                             + " is settings verifier response with response code:"
                             + response.code);
 
                     if (response.code == PackageManager.INTENT_FILTER_VERIFICATION_FAILURE) {
-                        Slog.d(TAG, "Domains failing verification: "
+                        if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "Domains failing verification: "
                                 + response.getFailedDomainsString());
                     }
 
                     if (state.isVerificationComplete()) {
                         mIntentFilterVerifier.receiveVerificationResponse(verificationId);
                     } else {
-                        Slog.d(TAG, "IntentFilter verification with token:" + verificationId
+                        if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
+                                "IntentFilter verification with token:" + verificationId
                                 + " was not said to be complete");
                     }
 
@@ -2160,7 +2162,7 @@
                 mSettings.mFingerprint = Build.FINGERPRINT;
             }
 
-            primeDomainVerificationsLPw(false);
+            primeDomainVerificationsLPw();
             checkDefaultBrowser();
 
             // All the changes are done during package scanning.
@@ -2268,37 +2270,34 @@
             if (priority < info.priority) {
                 priority = info.priority;
                 verifierComponentName = new ComponentName(packageName, info.activityInfo.name);
-                Slog.d(TAG, "Selecting IntentFilterVerifier: " + verifierComponentName +
-                        " with priority: " + info.priority);
+                if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "Selecting IntentFilterVerifier: "
+                        + verifierComponentName + " with priority: " + info.priority);
             }
         }
 
         return verifierComponentName;
     }
 
-    private void primeDomainVerificationsLPw(boolean logging) {
-        Slog.d(TAG, "Start priming domain verifications");
+    private void primeDomainVerificationsLPw() {
+        if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "Start priming domain verifications");
         boolean updated = false;
         ArraySet<String> allHostsSet = new ArraySet<>();
         for (PackageParser.Package pkg : mPackages.values()) {
             final String packageName = pkg.packageName;
             if (!hasDomainURLs(pkg)) {
-                if (logging) {
-                    Slog.d(TAG, "No priming domain verifications for " +
+                if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "No priming domain verifications for " +
                             "package with no domain URLs: " + packageName);
-                }
                 continue;
             }
             if (!pkg.isSystemApp()) {
-                if (logging) {
-                    Slog.d(TAG, "No priming domain verifications for a non system package : " +
-                            packageName);
-                }
+                if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
+                        "No priming domain verifications for a non system package : " +
+                                packageName);
                 continue;
             }
             for (PackageParser.Activity a : pkg.activities) {
                 for (ActivityIntentInfo filter : a.intents) {
-                    if (hasValidDomains(filter, false)) {
+                    if (hasValidDomains(filter)) {
                         allHostsSet.addAll(filter.getHostsList());
                     }
                 }
@@ -2310,25 +2309,23 @@
             IntentFilterVerificationInfo ivi =
                     mSettings.createIntentFilterVerificationIfNeededLPw(packageName, allHostsList);
             if (ivi != null) {
-                // We will always log this
-                Slog.d(TAG, "Priming domain verifications for package: " + packageName +
+                if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
+                        "Priming domain verifications for package: " + packageName +
                         " with hosts:" + ivi.getDomainsString());
                 ivi.setStatus(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS);
                 updated = true;
             }
             else {
-                if (logging) {
-                    Slog.d(TAG, "No priming domain verifications for package: " + packageName);
-                }
+                if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
+                        "No priming domain verifications for package: " + packageName);
             }
             allHostsSet.clear();
         }
         if (updated) {
-            if (logging) {
-                Slog.d(TAG, "Will need to write primed domain verifications");
-            }
+            if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
+                    "Will need to write primed domain verifications");
         }
-        Slog.d(TAG, "End priming domain verifications");
+        if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "End priming domain verifications");
     }
 
     private void checkDefaultBrowser() {
@@ -10344,7 +10341,7 @@
 
         int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
             if (origin.staged) {
-                Slog.d(TAG, origin.file + " already staged; skipping copy");
+                if (DEBUG_INSTALL) Slog.d(TAG, origin.file + " already staged; skipping copy");
                 codeFile = origin.file;
                 resourceFile = origin.file;
                 return PackageManager.INSTALL_SUCCEEDED;
@@ -10417,16 +10414,16 @@
             final File beforeCodeFile = codeFile;
             final File afterCodeFile = getNextCodePath(targetDir, pkg.packageName);
 
-            Slog.d(TAG, "Renaming " + beforeCodeFile + " to " + afterCodeFile);
+            if (DEBUG_INSTALL) Slog.d(TAG, "Renaming " + beforeCodeFile + " to " + afterCodeFile);
             try {
                 Os.rename(beforeCodeFile.getAbsolutePath(), afterCodeFile.getAbsolutePath());
             } catch (ErrnoException e) {
-                Slog.d(TAG, "Failed to rename", e);
+                Slog.w(TAG, "Failed to rename", e);
                 return false;
             }
 
             if (!SELinux.restoreconRecursive(afterCodeFile)) {
-                Slog.d(TAG, "Failed to restorecon");
+                Slog.w(TAG, "Failed to restorecon");
                 return false;
             }
 
@@ -10589,7 +10586,7 @@
 
         int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
             if (origin.staged) {
-                Slog.d(TAG, origin.cid + " already staged; skipping copy");
+                if (DEBUG_INSTALL) Slog.d(TAG, origin.cid + " already staged; skipping copy");
                 cid = origin.cid;
                 setMountPath(PackageHelper.getSdDir(cid));
                 return PackageManager.INSTALL_SUCCEEDED;
@@ -10849,8 +10846,8 @@
         }
 
         int copyApk(IMediaContainerService imcs, boolean temp) {
-            Slog.d(TAG, "Moving " + move.packageName + " from " + move.fromUuid + " to "
-                    + move.toUuid);
+            if (DEBUG_INSTALL) Slog.d(TAG, "Moving " + move.packageName + " from "
+                    + move.fromUuid + " to " + move.toUuid);
             synchronized (mInstaller) {
                 if (mInstaller.moveCompleteApp(move.fromUuid, move.toUuid, move.packageName,
                         move.dataAppName, move.appId, move.seinfo) != 0) {
@@ -10860,7 +10857,7 @@
 
             codeFile = new File(Environment.getDataAppDirectory(move.toUuid), move.dataAppName);
             resourceFile = codeFile;
-            Slog.d(TAG, "codeFile after move is " + codeFile);
+            if (DEBUG_INSTALL) Slog.d(TAG, "codeFile after move is " + codeFile);
 
             return PackageManager.INSTALL_SUCCEEDED;
         }
@@ -11695,7 +11692,7 @@
 
     private void startIntentFilterVerifications(int userId, PackageParser.Package pkg) {
         if (mIntentFilterVerifierComponent == null) {
-            Slog.d(TAG, "No IntentFilter verification will not be done as "
+            Slog.w(TAG, "No IntentFilter verification will not be done as "
                     + "there is no IntentFilterVerifier available!");
             return;
         }
@@ -11717,17 +11714,20 @@
             PackageParser.Package pkg) {
         int size = pkg.activities.size();
         if (size == 0) {
-            Slog.d(TAG, "No activity, so no need to verify any IntentFilter!");
+            if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
+                    "No activity, so no need to verify any IntentFilter!");
             return;
         }
 
         final boolean hasDomainURLs = hasDomainURLs(pkg);
         if (!hasDomainURLs) {
-            Slog.d(TAG, "No domain URLs, so no need to verify any IntentFilter!");
+            if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
+                    "No domain URLs, so no need to verify any IntentFilter!");
             return;
         }
 
-        Slog.d(TAG, "Checking for userId:" + userId + " if any IntentFilter from the " + size
+        if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "Checking for userId:" + userId
+                + " if any IntentFilter from the " + size
                 + " Activities needs verification ...");
 
         final int verificationId = mIntentFilterVerificationToken++;
@@ -11740,12 +11740,14 @@
                 for (ActivityIntentInfo filter : a.intents) {
                     boolean needsFilterVerification = filter.needsVerification();
                     if (needsFilterVerification && needsNetworkVerificationLPr(filter)) {
-                        Slog.d(TAG, "Verification needed for IntentFilter:" + filter.toString());
+                        if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
+                                "Verification needed for IntentFilter:" + filter.toString());
                         mIntentFilterVerifier.addOneIntentFilterVerification(
                                 verifierUid, userId, verificationId, filter, packageName);
                         count++;
                     } else if (!needsFilterVerification) {
-                        Slog.d(TAG, "No verification needed for IntentFilter:" + filter.toString());
+                        if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
+                                "No verification needed for IntentFilter:" + filter.toString());
                         if (hasValidDomains(filter)) {
                             ArrayList<String> hosts = filter.getHostsList();
                             if (hosts.size() > 0) {
@@ -11757,8 +11759,8 @@
                             }
                         }
                     } else {
-                        Slog.d(TAG, "Verification already done for IntentFilter:"
-                                + filter.toString());
+                        if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
+                                "Verification already done for IntentFilter:" + filter.toString());
                     }
                 }
             }
@@ -11766,10 +11768,12 @@
 
         if (count > 0) {
             mIntentFilterVerifier.startVerifications(userId);
-            Slog.d(TAG, "Started " + count + " IntentFilter verification"
-                    + (count > 1 ? "s" : "") +  " for userId:" + userId + "!");
+            if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "Started " + count
+                    + " IntentFilter verification" + (count > 1 ? "s" : "")
+                    +  " for userId:" + userId + "!");
         } else {
-            Slog.d(TAG, "No need to start any IntentFilter verification!");
+            if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
+                    "No need to start any IntentFilter verification!");
             if (allHosts.size() > 0 && mSettings.createIntentFilterVerificationIfNeededLPw(
                     packageName, allHosts) != null) {
                 scheduleWriteSettingsLocked();
@@ -14558,7 +14562,7 @@
         }
         }
 
-        Slog.d(TAG, "Loaded packages " + loaded);
+        if (DEBUG_INSTALL) Slog.d(TAG, "Loaded packages " + loaded);
         sendResourcesChangedBroadcast(true, false, loaded, null);
     }
 
@@ -14584,7 +14588,7 @@
         }
         }
 
-        Slog.d(TAG, "Unloaded packages " + unloaded);
+        if (DEBUG_INSTALL) Slog.d(TAG, "Unloaded packages " + unloaded);
         sendResourcesChangedBroadcast(false, false, unloaded, null);
     }
 
@@ -14605,7 +14609,7 @@
         try {
             movePackageInternal(packageName, volumeUuid, moveId);
         } catch (PackageManagerException e) {
-            Slog.d(TAG, "Failed to move " + packageName, e);
+            Slog.w(TAG, "Failed to move " + packageName, e);
             mMoveCallbacks.notifyStatusChanged(moveId,
                     PackageManager.MOVE_FAILED_INTERNAL_ERROR);
         }
@@ -14714,7 +14718,8 @@
             }
         }
 
-        Slog.d(TAG, "Measured code size " + stats.codeSize + ", data size " + stats.dataSize);
+        if (DEBUG_INSTALL) Slog.d(TAG, "Measured code size " + stats.codeSize + ", data size "
+                + stats.dataSize);
 
         final long startFreeBytes = measurePath.getFreeSpace();
         final long sizeBytes;
@@ -14742,7 +14747,7 @@
             @Override
             public void onPackageInstalled(String basePackageName, int returnCode, String msg,
                     Bundle extras) throws RemoteException {
-                Slog.d(TAG, "Install result for move: "
+                if (DEBUG_INSTALL) Slog.d(TAG, "Install result for move: "
                         + PackageManager.installStatusToString(returnCode, msg));
 
                 installedLatch.countDown();
@@ -14891,7 +14896,7 @@
             for (VolumeInfo vol : vols) {
                 if (vol.getType() == VolumeInfo.TYPE_PRIVATE && vol.isMountedWritable()) {
                     final String volumeUuid = vol.getFsUuid();
-                    Slog.d(TAG, "Removing user data on volume " + volumeUuid);
+                    if (DEBUG_INSTALL) Slog.d(TAG, "Removing user data on volume " + volumeUuid);
                     mInstaller.removeUserDataDirs(volumeUuid, userHandle);
                 }
             }
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 08d386b..095b7d7 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -589,6 +589,10 @@
         if (ActivityManager.isLowRamDeviceStatic()) {
             return false;
         }
+        if (!mContext.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_MANAGED_USERS)) {
+            return false;
+        }
         synchronized(mPackagesLock) {
             // Limit number of managed profiles that can be created
             if (numberOfUsersOfTypeLocked(UserInfo.FLAG_MANAGED_PROFILE, true)
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 38e2765..4861050 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2734,15 +2734,12 @@
                 }
             }
             final AppWindowToken appToken = win.mAppToken;
-            // Prevent an immediate window exit only for a real animation, ignoring e.g.
-            // dummy animations.
-            final boolean inAnimation = win.mWinAnimator.isWindowAnimatingNow();
             // The starting window is the last window in this app token and it isn't animating.
             // Allow it to be removed now as there is no additional window or animation that will
             // trigger its removal.
             final boolean lastWinStartingNotAnimating = startingWindow && appToken!= null
-                    && appToken.allAppWindows.size() == 1 && !inAnimation;
-            if (!lastWinStartingNotAnimating && (win.mExiting || inAnimation)) {
+                    && appToken.allAppWindows.size() == 1 && !win.mWinAnimator.isAnimating();
+            if (!lastWinStartingNotAnimating && (win.mExiting || win.mWinAnimator.isAnimating())) {
                 // The exit animation is running... wait for it!
                 win.mExiting = true;
                 win.mRemoveOnExit = true;
@@ -4675,7 +4672,12 @@
             // If we are preparing an app transition, then delay changing
             // the visibility of this token until we execute that transition.
             if (okToDisplay() && mAppTransition.isTransitionSet()) {
-                if (!wtoken.startingDisplayed || mSkipAppTransitionAnimation) {
+                // A dummy animation is a placeholder animation which informs others that an
+                // animation is going on (in this case an application transition). If the animation
+                // was transferred from another application/animator, no dummy animator should be
+                // created since an animation is already in progress.
+                if (!wtoken.mAppAnimator.usingTransferredAnimation &&
+                        (!wtoken.startingDisplayed || mSkipAppTransitionAnimation)) {
                     if (DEBUG_APP_TRANSITIONS) Slog.v(
                             TAG, "Setting dummy animation on: " + wtoken);
                     wtoken.mAppAnimator.setDummyAnimation();
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index b42c8eb..5064d8f 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -251,21 +251,11 @@
                 && mAppAnimator.animation == AppWindowAnimator.sDummyAnimation;
     }
 
-    /** Is this window currently set to animate or currently animating?
-     *  NOTE: The method will return true for cases where the window isn't currently animating, but
-     *  is set to animate. i.e. if the window animation is currently set to a dummy placeholder
-     *  animation. Use {@link #isWindowAnimatingNow} to know if the window is currently running a
-     *  real animation. */
+    /** Is this window currently set to animate or currently animating? */
     boolean isWindowAnimating() {
         return mAnimation != null;
     }
 
-    /** Is the window performing a real animation and not a dummy which is only waiting for an
-     * an animation to start? */
-    boolean isWindowAnimatingNow() {
-        return isWindowAnimating() && !isDummyAnimation();
-    }
-
     void cancelExitAnimationForNextAnimationLocked() {
         if (mAnimation != null) {
             mAnimation.cancel();
diff --git a/telecomm/java/android/telecom/VideoProfile.java b/telecomm/java/android/telecom/VideoProfile.java
index 11a4976..dabf706 100644
--- a/telecomm/java/android/telecom/VideoProfile.java
+++ b/telecomm/java/android/telecom/VideoProfile.java
@@ -49,7 +49,32 @@
     public static final int QUALITY_DEFAULT = 4;
 
     /**
-     * Call is currently in an audio-only mode with no video transmission or receipt.
+     * Used when answering or dialing a call to indicate that the call does not have a video
+     * component.
+     * <p>
+     * Should <b>not</b> be used in comparison checks to determine if a video state represents an
+     * audio-only call.
+     * <p>
+     * The following, for example, is not the correct way to check if a call is audio-only:
+     * <pre>
+     * {@code
+     * // This is the incorrect way to check for an audio-only call.
+     * if (videoState == VideoProfile.STATE_AUDIO_ONLY) {
+     *      // Handle audio-only call.
+     * }
+     * }
+     * </pre>
+     * <p>
+     * Instead, use the {@link VideoProfile#isAudioOnly(int)} helper function to check if a
+     * video state represents an audio-only call:
+     * <pre>
+     * {@code
+     * // This is the correct way to check for an audio-only call.
+     * if (VideoProfile.isAudioOnly(videoState)) {
+     *      // Handle audio-only call.
+     * }
+     * }
+     * </pre>
      */
     public static final int STATE_AUDIO_ONLY = 0x0;
 
diff --git a/telephony/java/com/android/ims/ImsCallProfile.java b/telephony/java/com/android/ims/ImsCallProfile.java
index 1c69794..b0b95f6 100644
--- a/telephony/java/com/android/ims/ImsCallProfile.java
+++ b/telephony/java/com/android/ims/ImsCallProfile.java
@@ -331,7 +331,7 @@
                 videostate = VideoProfile.STATE_AUDIO_ONLY;
                 break;
         }
-        if (callProfile.isVideoPaused() && videostate != VideoProfile.STATE_AUDIO_ONLY) {
+        if (callProfile.isVideoPaused() && !VideoProfile.isAudioOnly(videostate)) {
             videostate |= VideoProfile.STATE_PAUSED;
         } else {
             videostate &= ~VideoProfile.STATE_PAUSED;
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
index dae1ac3..3090a11 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
@@ -23,6 +23,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Bitmap;
+import android.net.Uri;
 import android.os.Bundle;
 import android.service.voice.VoiceInteractionSession;
 import android.util.Log;
@@ -79,6 +80,9 @@
         super.onShow(args, showFlags);
         mState = STATE_IDLE;
         mStartIntent = args.getParcelable("intent");
+        if (mStartIntent == null) {
+            mStartIntent = new Intent(getContext(), TestInteractionActivity.class);
+        }
         if (mAssistVisualizer != null) {
             mAssistVisualizer.clearAssistData();
         }
@@ -119,6 +123,7 @@
     }
 
     public void onHandleAssist(Bundle assistBundle) {
+        boolean hasStructure = false;
         if (assistBundle != null) {
             Bundle assistContext = assistBundle.getBundle(Intent.EXTRA_ASSIST_CONTEXT);
             if (assistContext != null) {
@@ -126,6 +131,7 @@
                 if (mAssistStructure != null) {
                     if (mAssistVisualizer != null) {
                         mAssistVisualizer.setAssistStructure(mAssistStructure);
+                        hasStructure = true;
                     }
                 }
                 AssistContent content = AssistContent.getAssistContent(assistContext);
@@ -133,10 +139,13 @@
                     Log.i(TAG, "Assist intent: " + content.getIntent());
                     Log.i(TAG, "Assist clipdata: " + content.getClipData());
                 }
-                return;
+            }
+            Uri referrer = assistBundle.getParcelable(Intent.EXTRA_REFERRER);
+            if (referrer != null) {
+                Log.i(TAG, "Referrer: " + referrer);
             }
         }
-        if (mAssistVisualizer != null) {
+        if (!hasStructure && mAssistVisualizer != null) {
             mAssistVisualizer.clearAssistData();
         }
     }
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java
index 67db289..c038414 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java
@@ -23,15 +23,18 @@
 import android.os.Bundle;
 import android.service.voice.VoiceInteractionService;
 import android.util.Log;
-import android.view.Gravity;
 import android.view.View;
-import android.view.ViewGroup;
 import android.widget.Button;
 import android.widget.TextView;
 
 public class TestInteractionActivity extends Activity implements View.OnClickListener {
     static final String TAG = "TestInteractionActivity";
 
+    static final String REQUEST_ABORT = "abort";
+    static final String REQUEST_COMPLETE = "complete";
+    static final String REQUEST_PICK = "pick";
+    static final String REQUEST_CONFIRM = "confirm";
+
     VoiceInteractor mInteractor;
     VoiceInteractor.Request mCurrentRequest = null;
     TextView mLog;
@@ -72,21 +75,32 @@
         mCancelButton.setOnClickListener(this);
 
         mInteractor = getVoiceInteractor();
-        mCurrentRequest = new VoiceInteractor.ConfirmationRequest(
-                new VoiceInteractor.Prompt("This is a confirmation"), null) {
-            @Override
-            public void onCancel() {
-                Log.i(TAG, "Canceled!");
-                getActivity().finish();
-            }
 
-            @Override
-            public void onConfirmationResult(boolean confirmed, Bundle result) {
-                Log.i(TAG, "Confirmation result: confirmed=" + confirmed + " result=" + result);
-                getActivity().finish();
-            }
-        };
-        mInteractor.submitRequest(mCurrentRequest);
+        VoiceInteractor.Request[] active = mInteractor.getActiveRequests();
+        for (int i=0; i<active.length; i++) {
+            Log.i(TAG, "Active #" + i + " / " + active[i].getName() + ": " + active[i]);
+        }
+
+        mCurrentRequest = mInteractor.getActiveRequest(REQUEST_CONFIRM);
+        if (mCurrentRequest == null) {
+            mCurrentRequest = new VoiceInteractor.ConfirmationRequest(
+                    new VoiceInteractor.Prompt("This is a confirmation"), null) {
+                @Override
+                public void onCancel() {
+                    Log.i(TAG, "Canceled!");
+                    getActivity().finish();
+                }
+
+                @Override
+                public void onConfirmationResult(boolean confirmed, Bundle result) {
+                    Log.i(TAG, "Confirmation result: confirmed=" + confirmed + " result=" + result);
+                    getActivity().finish();
+                }
+            };
+            mInteractor.submitRequest(mCurrentRequest, REQUEST_CONFIRM);
+        } else {
+            Log.i(TAG, "Restarting with active confirmation: " + mCurrentRequest);
+        }
     }
 
     @Override
@@ -112,7 +126,7 @@
                     getActivity().finish();
                 }
             };
-            mInteractor.submitRequest(req);
+            mInteractor.submitRequest(req, REQUEST_ABORT);
         } else if (v == mCompleteButton) {
             VoiceInteractor.CompleteVoiceRequest req = new VoiceInteractor.CompleteVoiceRequest(
                     new VoiceInteractor.Prompt("Woohoo, completed!"), null) {
@@ -129,7 +143,7 @@
                     getActivity().finish();
                 }
             };
-            mInteractor.submitRequest(req);
+            mInteractor.submitRequest(req, REQUEST_COMPLETE);
         } else if (v == mPickButton) {
             VoiceInteractor.PickOptionRequest.Option[] options =
                     new VoiceInteractor.PickOptionRequest.Option[5];
@@ -168,7 +182,7 @@
                     }
                 }
             };
-            mInteractor.submitRequest(req);
+            mInteractor.submitRequest(req, REQUEST_PICK);
         } else if (v == mJumpOutButton) {
             Log.i(TAG, "Jump out");
             Intent intent = new Intent(Intent.ACTION_MAIN);
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/VoiceInteractionMain.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/VoiceInteractionMain.java
index 5d212a4..a7636c3 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/VoiceInteractionMain.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/VoiceInteractionMain.java
@@ -18,6 +18,7 @@
 
 import android.app.Activity;
 import android.content.Intent;
+import android.net.Uri;
 import android.os.Bundle;
 import android.view.View;
 
@@ -41,6 +42,11 @@
         super.onDestroy();
     }
 
+    @Override
+    public Uri onProvideReferrer() {
+        return Uri.parse("http://www.example.com/VoiceInteractionMain");
+    }
+
     View.OnClickListener mStartListener = new View.OnClickListener() {
         public void onClick(View v) {
             startService(new Intent(VoiceInteractionMain.this, MainInteractionService.class));