Merge "Volume policy updates."
diff --git a/api/current.txt b/api/current.txt
index 2cdae98..16eb4e9 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -15101,6 +15101,7 @@
     field public static final int EVENT_KEY_EXPIRED = 3; // 0x3
     field public static final int EVENT_KEY_REQUIRED = 2; // 0x2
     field public static final int EVENT_PROVISION_REQUIRED = 1; // 0x1
+    field public static final int EVENT_SESSION_RECLAIMED = 5; // 0x5
     field public static final int EVENT_VENDOR_DEFINED = 4; // 0x4
     field public static final int KEY_TYPE_OFFLINE = 2; // 0x2
     field public static final int KEY_TYPE_RELEASE = 3; // 0x3
@@ -34162,6 +34163,7 @@
     method protected float getRightFadingEdgeStrength();
     method protected int getRightPaddingOffset();
     method public android.view.View getRootView();
+    method public android.view.WindowInsets getRootWindowInsets();
     method public float getRotation();
     method public float getRotationX();
     method public float getRotationY();
@@ -37033,6 +37035,7 @@
     method public abstract int getMinimumFontSize();
     method public abstract int getMinimumLogicalFontSize();
     method public abstract int getMixedContentMode();
+    method public abstract boolean getOffscreenPreRaster();
     method public abstract deprecated android.webkit.WebSettings.PluginState getPluginState();
     method public abstract java.lang.String getSansSerifFontFamily();
     method public abstract boolean getSaveFormData();
@@ -37079,6 +37082,7 @@
     method public abstract void setMinimumLogicalFontSize(int);
     method public abstract void setMixedContentMode(int);
     method public abstract void setNeedInitialFocus(boolean);
+    method public abstract void setOffscreenPreRaster(boolean);
     method public abstract deprecated void setPluginState(android.webkit.WebSettings.PluginState);
     method public abstract deprecated void setRenderPriority(android.webkit.WebSettings.RenderPriority);
     method public abstract void setSansSerifFontFamily(java.lang.String);
diff --git a/api/system-current.txt b/api/system-current.txt
index 1b3f1e4..1707897 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -16101,6 +16101,7 @@
     field public static final int EVENT_KEY_EXPIRED = 3; // 0x3
     field public static final int EVENT_KEY_REQUIRED = 2; // 0x2
     field public static final int EVENT_PROVISION_REQUIRED = 1; // 0x1
+    field public static final int EVENT_SESSION_RECLAIMED = 5; // 0x5
     field public static final int EVENT_VENDOR_DEFINED = 4; // 0x4
     field public static final int KEY_TYPE_OFFLINE = 2; // 0x2
     field public static final int KEY_TYPE_RELEASE = 3; // 0x3
@@ -36330,6 +36331,7 @@
     method protected float getRightFadingEdgeStrength();
     method protected int getRightPaddingOffset();
     method public android.view.View getRootView();
+    method public android.view.WindowInsets getRootWindowInsets();
     method public float getRotation();
     method public float getRotationX();
     method public float getRotationY();
@@ -39264,6 +39266,7 @@
     method public abstract int getMinimumLogicalFontSize();
     method public abstract int getMixedContentMode();
     method public abstract deprecated boolean getNavDump();
+    method public abstract boolean getOffscreenPreRaster();
     method public abstract deprecated android.webkit.WebSettings.PluginState getPluginState();
     method public abstract deprecated boolean getPluginsEnabled();
     method public abstract java.lang.String getSansSerifFontFamily();
@@ -39316,6 +39319,7 @@
     method public abstract void setMixedContentMode(int);
     method public abstract deprecated void setNavDump(boolean);
     method public abstract void setNeedInitialFocus(boolean);
+    method public abstract void setOffscreenPreRaster(boolean);
     method public abstract deprecated void setPluginState(android.webkit.WebSettings.PluginState);
     method public abstract deprecated void setPluginsEnabled(boolean);
     method public abstract deprecated void setRenderPriority(android.webkit.WebSettings.RenderPriority);
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 0ef0a29..fd4cf3c 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -17,6 +17,7 @@
 package android.hardware.camera2;
 
 import android.hardware.camera2.params.StreamConfigurationMap;
+import android.hardware.camera2.params.OutputConfiguration;
 import android.os.Handler;
 import android.view.Surface;
 
@@ -380,6 +381,20 @@
             throws CameraAccessException;
 
     /**
+     * <p>Create a new camera capture session by providing the target output set of Surfaces and
+     * its corresponding surface configuration to the camera device.</p>
+     *
+     * @see #createCaptureSession
+     * @see OutputConfiguration
+     *
+     * @hide
+     */
+    public abstract void createCaptureSessionByOutputConfiguration(
+            List<OutputConfiguration> outputConfigurations,
+            CameraCaptureSession.StateCallback callback, Handler handler)
+            throws CameraAccessException;
+
+    /**
      * <p>Create a {@link CaptureRequest.Builder} for new capture requests,
      * initialized with template for a target use case. The settings are chosen
      * to be the best options for the specific camera device, so it is not
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 78aefa5..c5267cb 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -28,6 +28,7 @@
 import android.hardware.camera2.ICameraDeviceCallbacks;
 import android.hardware.camera2.ICameraDeviceUser;
 import android.hardware.camera2.TotalCaptureResult;
+import android.hardware.camera2.params.OutputConfiguration;
 import android.hardware.camera2.utils.CameraBinderDecorator;
 import android.hardware.camera2.utils.CameraRuntimeException;
 import android.hardware.camera2.utils.LongParcelable;
@@ -415,6 +416,18 @@
     public void createCaptureSession(List<Surface> outputs,
             CameraCaptureSession.StateCallback callback, Handler handler)
             throws CameraAccessException {
+        List<OutputConfiguration> outConfigurations = new ArrayList<>(outputs.size());
+        for (Surface surface : outputs) {
+            outConfigurations.add(new OutputConfiguration(surface));
+        }
+        createCaptureSessionByOutputConfiguration(outConfigurations, callback, handler);
+    }
+
+    @Override
+    public void createCaptureSessionByOutputConfiguration(
+            List<OutputConfiguration> outputConfigurations,
+            CameraCaptureSession.StateCallback callback, Handler handler)
+            throws CameraAccessException {
         synchronized(mInterfaceLock) {
             if (DEBUG) {
                 Log.d(TAG, "createCaptureSession");
@@ -431,8 +444,12 @@
             // TODO: dont block for this
             boolean configureSuccess = true;
             CameraAccessException pendingException = null;
+            List<Surface> outSurfaces = new ArrayList<>(outputConfigurations.size());
+            for (OutputConfiguration config : outputConfigurations) {
+                outSurfaces.add(config.getSurface());
+            }
             try {
-                configureSuccess = configureOutputsChecked(outputs); // and then block until IDLE
+                configureSuccess = configureOutputsChecked(outSurfaces); // and then block until IDLE
             } catch (CameraAccessException e) {
                 configureSuccess = false;
                 pendingException = e;
@@ -444,7 +461,7 @@
             // Fire onConfigured if configureOutputs succeeded, fire onConfigureFailed otherwise.
             CameraCaptureSessionImpl newSession =
                     new CameraCaptureSessionImpl(mNextSessionId++,
-                            outputs, callback, handler, this, mDeviceHandler,
+                            outSurfaces, callback, handler, this, mDeviceHandler,
                             configureSuccess);
 
             // TODO: wait until current session closes, then create the new session
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
new file mode 100644
index 0000000..47c784e
--- /dev/null
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -0,0 +1,116 @@
+/*
+ * 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 android.hardware.camera2.params;
+
+import android.hardware.camera2.CameraDevice;
+import android.view.Surface;
+
+import static com.android.internal.util.Preconditions.*;
+
+/**
+ * Immutable class for describing camera output, which contains a {@link Surface} and its specific
+ * configuration for creating capture session.
+ *
+ * @see CameraDevice#createCaptureSession
+ *
+ * @hide
+ */
+public final class OutputConfiguration {
+
+    /**
+     * Rotation constant: 0 degree rotation (no rotation)
+     */
+    public static final int ROTATION_0 = 0;
+
+    /**
+     * Rotation constant: 90 degree counterclockwise rotation.
+     */
+    public static final int ROTATION_90 = 1;
+
+    /**
+     * Rotation constant: 180 degree counterclockwise rotation.
+     */
+    public static final int ROTATION_180 = 2;
+
+    /**
+     * Rotation constant: 270 degree counterclockwise rotation.
+     */
+    public static final int ROTATION_270 = 3;
+
+    /**
+     * Create a new immutable SurfaceConfiguration instance.
+     *
+     * @param surface
+     *          A Surface for camera to output to.
+     *
+     * <p>This constructor creates a default configuration</p>
+     *
+     */
+    public OutputConfiguration(Surface surface) {
+        checkNotNull(surface, "Surface must not be null");
+        mSurface = surface;
+        mRotation = ROTATION_0;
+    }
+
+    /**
+     * Create a new immutable SurfaceConfiguration instance.
+     *
+     * <p>This constructor takes an argument for desired camera rotation</p>
+     *
+     * @param surface
+     *          A Surface for camera to output to.
+     * @param rotation
+     *          The desired rotation to be applied on camera output. Value must be one of
+     *          ROTATION_[0, 90, 180, 270]. Note that when the rotation is 90 or 270 degree,
+     *          application should make sure corresponding surface size has width and height
+     *          transposed corresponding to the width and height without rotation. For example,
+     *          if application needs camera to capture 1280x720 picture and rotate it by 90 degree,
+     *          application should set rotation to {@code ROTATION_90} and make sure the
+     *          corresponding Surface size is 720x1280. Note that {@link CameraDevice} might
+     *          throw {@code IllegalArgumentException} if device cannot perform such rotation.
+     *
+     */
+    public OutputConfiguration(Surface surface, int rotation) {
+        checkNotNull(surface, "Surface must not be null");
+        checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant");
+        mSurface = surface;
+        mRotation = rotation;
+    }
+
+    /**
+     * Get the {@link Surface} associated with this {@link OutputConfiguration}.
+     *
+     * @return the {@link Surface} associated with this {@link OutputConfiguration}.
+     */
+    public Surface getSurface() {
+        return mSurface;
+    }
+
+    /**
+     * Get the rotation associated with this {@link OutputConfiguration}.
+     *
+     * @return the rotation associated with this {@link OutputConfiguration}.
+     *         Value will be one of ROTATION_[0, 90, 180, 270]
+     */
+    public int getRotation() {
+        return mRotation;
+    }
+
+    private final Surface mSurface;
+    private final int mRotation;
+}
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 46af112..da79b1a 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -33,6 +33,7 @@
 
 import com.android.internal.net.LegacyVpnInfo;
 import com.android.internal.net.VpnConfig;
+import com.android.internal.net.VpnInfo;
 import com.android.internal.net.VpnProfile;
 
 /**
@@ -116,6 +117,8 @@
 
     LegacyVpnInfo getLegacyVpnInfo();
 
+    VpnInfo[] getAllVpnInfo();
+
     boolean updateLockdownVpn();
 
     void captivePortalCheckCompleted(in NetworkInfo info, boolean isCaptivePortal);
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 2afe578..0766253 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -19,6 +19,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemClock;
+import android.util.Slog;
 import android.util.SparseBooleanArray;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -42,6 +43,7 @@
  * @hide
  */
 public class NetworkStats implements Parcelable {
+    private static final String TAG = "NetworkStats";
     /** {@link #iface} value when interface details unavailable. */
     public static final String IFACE_ALL = null;
     /** {@link #uid} value when UID details unavailable. */
@@ -783,4 +785,162 @@
         public void foundNonMonotonic(
                 NetworkStats left, int leftIndex, NetworkStats right, int rightIndex, C cookie);
     }
+
+    /**
+     * VPN accounting. Move some VPN's underlying traffic to other UIDs that use tun0 iface.
+     *
+     * This method should only be called on delta NetworkStats. Do not call this method on a
+     * snapshot {@link NetworkStats} object because the tunUid and/or the underlyingIface may
+     * change over time.
+     *
+     * This method performs adjustments for one active VPN package and one VPN iface at a time.
+     *
+     * It is possible for the VPN software to use multiple underlying networks. This method
+     * only migrates traffic for the primary underlying network.
+     *
+     * @param tunUid uid of the VPN application
+     * @param tunIface iface of the vpn tunnel
+     * @param underlyingIface the primary underlying network iface used by the VPN application
+     * @return true if it successfully adjusts the accounting for VPN, false otherwise
+     */
+    public boolean migrateTun(int tunUid, String tunIface, String underlyingIface) {
+        Entry tunIfaceTotal = new Entry();
+        Entry underlyingIfaceTotal = new Entry();
+
+        tunAdjustmentInit(tunUid, tunIface, underlyingIface, tunIfaceTotal, underlyingIfaceTotal);
+
+        // If tunIface < underlyingIface, it leaves the overhead traffic in the VPN app.
+        // If tunIface > underlyingIface, the VPN app doesn't get credit for data compression.
+        // Negative stats should be avoided.
+        Entry pool = tunGetPool(tunIfaceTotal, underlyingIfaceTotal);
+        if (pool.isEmpty()) {
+            return true;
+        }
+        Entry moved = addTrafficToApplications(tunIface,  underlyingIface, tunIfaceTotal, pool);
+        deductTrafficFromVpnApp(tunUid, underlyingIface, moved);
+
+        if (!moved.isEmpty()) {
+            Slog.wtf(TAG, "Failed to deduct underlying network traffic from VPN package. Moved="
+                    + moved);
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Initializes the data used by the migrateTun() method.
+     *
+     * This is the first pass iteration which does the following work:
+     * (1) Adds up all the traffic through tun0.
+     * (2) Adds up all the traffic through the tunUid's underlyingIface
+     *     (both foreground and background).
+     */
+    private void tunAdjustmentInit(int tunUid, String tunIface, String underlyingIface,
+            Entry tunIfaceTotal, Entry underlyingIfaceTotal) {
+        Entry recycle = new Entry();
+        for (int i = 0; i < size; i++) {
+            getValues(i, recycle);
+            if (recycle.uid == UID_ALL) {
+                throw new IllegalStateException(
+                        "Cannot adjust VPN accounting on an iface aggregated NetworkStats.");
+            }
+
+            if (recycle.uid == tunUid && recycle.tag == TAG_NONE
+                    && Objects.equals(underlyingIface, recycle.iface)) {
+                underlyingIfaceTotal.add(recycle);
+            }
+
+            if (recycle.tag == TAG_NONE && Objects.equals(tunIface, recycle.iface)) {
+                // Add up all tunIface traffic.
+                tunIfaceTotal.add(recycle);
+            }
+        }
+    }
+
+    private static Entry tunGetPool(Entry tunIfaceTotal, Entry underlyingIfaceTotal) {
+        Entry pool = new Entry();
+        pool.rxBytes = Math.min(tunIfaceTotal.rxBytes, underlyingIfaceTotal.rxBytes);
+        pool.rxPackets = Math.min(tunIfaceTotal.rxPackets, underlyingIfaceTotal.rxPackets);
+        pool.txBytes = Math.min(tunIfaceTotal.txBytes, underlyingIfaceTotal.txBytes);
+        pool.txPackets = Math.min(tunIfaceTotal.txPackets, underlyingIfaceTotal.txPackets);
+        pool.operations = Math.min(tunIfaceTotal.operations, underlyingIfaceTotal.operations);
+        return pool;
+    }
+
+    private Entry addTrafficToApplications(String tunIface, String underlyingIface,
+            Entry tunIfaceTotal, Entry pool) {
+        Entry moved = new Entry();
+        Entry tmpEntry = new Entry();
+        tmpEntry.iface = underlyingIface;
+        for (int i = 0; i < size; i++) {
+            if (Objects.equals(iface[i], tunIface)) {
+                if (tunIfaceTotal.rxBytes > 0) {
+                    tmpEntry.rxBytes = pool.rxBytes * rxBytes[i] / tunIfaceTotal.rxBytes;
+                } else {
+                    tmpEntry.rxBytes = 0;
+                }
+                if (tunIfaceTotal.rxPackets > 0) {
+                    tmpEntry.rxPackets = pool.rxPackets * rxPackets[i] / tunIfaceTotal.rxPackets;
+                } else {
+                    tmpEntry.rxPackets = 0;
+                }
+                if (tunIfaceTotal.txBytes > 0) {
+                    tmpEntry.txBytes = pool.txBytes * txBytes[i] / tunIfaceTotal.txBytes;
+                } else {
+                    tmpEntry.txBytes = 0;
+                }
+                if (tunIfaceTotal.txPackets > 0) {
+                    tmpEntry.txPackets = pool.txPackets * txPackets[i] / tunIfaceTotal.txPackets;
+                } else {
+                    tmpEntry.txPackets = 0;
+                }
+                if (tunIfaceTotal.operations > 0) {
+                    tmpEntry.operations =
+                            pool.operations * operations[i] / tunIfaceTotal.operations;
+                } else {
+                    tmpEntry.operations = 0;
+                }
+                tmpEntry.uid = uid[i];
+                tmpEntry.tag = tag[i];
+                tmpEntry.set = set[i];
+                combineValues(tmpEntry);
+                if (tag[i] == TAG_NONE) {
+                    moved.add(tmpEntry);
+                }
+            }
+        }
+        return moved;
+    }
+
+    private void deductTrafficFromVpnApp(int tunUid, String underlyingIface, Entry moved) {
+        // Caveat: if the vpn software uses tag, the total tagged traffic may be greater than
+        // the TAG_NONE traffic.
+        int idxVpnBackground = findIndex(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE);
+        if (idxVpnBackground != -1) {
+            tunSubtract(idxVpnBackground, this, moved);
+        }
+
+        int idxVpnForeground = findIndex(underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE);
+        if (idxVpnForeground != -1) {
+            tunSubtract(idxVpnForeground, this, moved);
+        }
+    }
+
+    private static void tunSubtract(int i, NetworkStats left, Entry right) {
+        long rxBytes = Math.min(left.rxBytes[i], right.rxBytes);
+        left.rxBytes[i] -= rxBytes;
+        right.rxBytes -= rxBytes;
+
+        long rxPackets = Math.min(left.rxPackets[i], right.rxPackets);
+        left.rxPackets[i] -= rxPackets;
+        right.rxPackets -= rxPackets;
+
+        long txBytes = Math.min(left.txBytes[i], right.txBytes);
+        left.txBytes[i] -= txBytes;
+        right.txBytes -= txBytes;
+
+        long txPackets = Math.min(left.txPackets[i], right.txPackets);
+        left.txPackets[i] -= txPackets;
+        right.txPackets -= txPackets;
+    }
 }
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index ba1699e..4e8ec890 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -19,13 +19,11 @@
 import static android.system.OsConstants.AF_UNIX;
 import static android.system.OsConstants.SEEK_SET;
 import static android.system.OsConstants.SOCK_STREAM;
-import static android.system.OsConstants.SOCK_SEQPACKET;
 import static android.system.OsConstants.S_ISLNK;
 import static android.system.OsConstants.S_ISREG;
 
 import android.content.BroadcastReceiver;
 import android.content.ContentProvider;
-import android.os.MessageQueue.FileDescriptorCallback;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.OsConstants;
@@ -33,6 +31,7 @@
 import android.util.Log;
 
 import dalvik.system.CloseGuard;
+
 import libcore.io.IoUtils;
 import libcore.io.Memory;
 
@@ -221,8 +220,8 @@
      *             be opened with the requested mode.
      * @see #parseMode(String)
      */
-    public static ParcelFileDescriptor open(File file, int mode, Handler handler,
-            final OnCloseListener listener) throws IOException {
+    public static ParcelFileDescriptor open(
+            File file, int mode, Handler handler, OnCloseListener listener) throws IOException {
         if (handler == null) {
             throw new IllegalArgumentException("Handler must not be null");
         }
@@ -236,25 +235,10 @@
         final FileDescriptor[] comm = createCommSocketPair();
         final ParcelFileDescriptor pfd = new ParcelFileDescriptor(fd, comm[0]);
 
-        handler.getLooper().getQueue().registerFileDescriptorCallback(comm[1],
-                FileDescriptorCallback.EVENT_INPUT, new FileDescriptorCallback() {
-            @Override
-            public int onFileDescriptorEvents(FileDescriptor fd, int events) {
-                Status status = null;
-                if ((events & FileDescriptorCallback.EVENT_INPUT) != 0) {
-                    final byte[] buf = new byte[MAX_STATUS];
-                    status = readCommStatus(fd, buf);
-                } else if ((events & FileDescriptorCallback.EVENT_ERROR) != 0) {
-                    status = new Status(Status.DEAD);
-                }
-                if (status != null) {
-                    IoUtils.closeQuietly(fd);
-                    listener.onClose(status.asIOException());
-                    return 0; // unregister the callback
-                }
-                return EVENT_INPUT;
-            }
-        });
+        // Kick off thread to watch for status updates
+        IoUtils.setBlocking(comm[1], true);
+        final ListenerBridge bridge = new ListenerBridge(comm[1], handler.getLooper(), listener);
+        bridge.start();
 
         return pfd;
     }
@@ -462,12 +446,9 @@
 
     private static FileDescriptor[] createCommSocketPair() throws IOException {
         try {
-            // Use SOCK_SEQPACKET so that we have a guarantee that the status
-            // is written and read atomically as one unit and is not split
-            // across multiple IO operations.
             final FileDescriptor comm1 = new FileDescriptor();
             final FileDescriptor comm2 = new FileDescriptor();
-            Os.socketpair(AF_UNIX, SOCK_SEQPACKET, 0, comm1, comm2);
+            Os.socketpair(AF_UNIX, SOCK_STREAM, 0, comm1, comm2);
             IoUtils.setBlocking(comm1, false);
             IoUtils.setBlocking(comm2, false);
             return new FileDescriptor[] { comm1, comm2 };
@@ -728,7 +709,6 @@
                     writePtr += len;
                 }
 
-                // Must write the entire status as a single operation.
                 Os.write(mCommFd, buf, 0, writePtr);
             } catch (ErrnoException e) {
                 // Reporting status is best-effort
@@ -746,7 +726,6 @@
 
     private static Status readCommStatus(FileDescriptor comm, byte[] buf) {
         try {
-            // Must read the entire status as a single operation.
             final int n = Os.read(comm, buf, 0, buf.length);
             if (n == 0) {
                 // EOF means they're dead
@@ -1035,10 +1014,39 @@
                     return new IOException("Unknown status: " + status);
             }
         }
+    }
+
+    /**
+     * Bridge to watch for remote status, and deliver to listener. Currently
+     * requires that communication socket is <em>blocking</em>.
+     */
+    private static final class ListenerBridge extends Thread {
+        // TODO: switch to using Looper to avoid burning a thread
+
+        private FileDescriptor mCommFd;
+        private final Handler mHandler;
+
+        public ListenerBridge(FileDescriptor comm, Looper looper, final OnCloseListener listener) {
+            mCommFd = comm;
+            mHandler = new Handler(looper) {
+                @Override
+                public void handleMessage(Message msg) {
+                    final Status s = (Status) msg.obj;
+                    listener.onClose(s != null ? s.asIOException() : null);
+                }
+            };
+        }
 
         @Override
-        public String toString() {
-            return "{" + status + ": " + msg + "}";
+        public void run() {
+            try {
+                final byte[] buf = new byte[MAX_STATUS];
+                final Status status = readCommStatus(mCommFd, buf);
+                mHandler.obtainMessage(0, status).sendToTarget();
+            } finally {
+                IoUtils.closeQuietly(mCommFd);
+                mCommFd = null;
+            }
         }
     }
 }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 085a3f2..4f2d4a6 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -6564,6 +6564,19 @@
     }
 
     /**
+     * Provide original WindowInsets that are dispatched to the view hierarchy. The insets are
+     * only available if the view is attached.
+     *
+     * @return WindowInsets from the top of the view hierarchy or null if View is detached
+     */
+    public WindowInsets getRootWindowInsets() {
+        if (mAttachInfo != null) {
+            return mAttachInfo.mViewRootImpl.getWindowInsets(false /* forceConstruct */);
+        }
+        return null;
+    }
+
+    /**
      * @hide Compute the insets that should be consumed by this view and the ones
      * that should propagate to those under it.
      */
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 7cd12d5..f970e88 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -265,6 +265,8 @@
     final Rect mDispatchContentInsets = new Rect();
     final Rect mDispatchStableInsets = new Rect();
 
+    private WindowInsets mLastWindowInsets;
+
     final Configuration mLastConfiguration = new Configuration();
     final Configuration mPendingConfiguration = new Configuration();
 
@@ -550,6 +552,11 @@
                 mPendingContentInsets.set(mAttachInfo.mContentInsets);
                 mPendingStableInsets.set(mAttachInfo.mStableInsets);
                 mPendingVisibleInsets.set(0, 0, 0, 0);
+                try {
+                    relayoutWindow(attrs, getHostVisibility(), false);
+                } catch (RemoteException e) {
+                    if (DEBUG_LAYOUT) Log.e(TAG, "failed to relayoutWindow", e);
+                }
                 if (DEBUG_LAYOUT) Log.v(TAG, "Added window " + mWindow);
                 if (res < WindowManagerGlobal.ADD_OKAY) {
                     mAttachInfo.mRootView = null;
@@ -1227,13 +1234,29 @@
         m.postTranslate(-mAttachInfo.mWindowLeft, -mAttachInfo.mWindowTop);
     }
 
+    /* package */ WindowInsets getWindowInsets(boolean forceConstruct) {
+        if (mLastWindowInsets == null || forceConstruct) {
+            mDispatchContentInsets.set(mAttachInfo.mContentInsets);
+            mDispatchStableInsets.set(mAttachInfo.mStableInsets);
+            Rect contentInsets = mDispatchContentInsets;
+            Rect stableInsets = mDispatchStableInsets;
+            // For dispatch we preserve old logic, but for direct requests from Views we allow to
+            // immediately use pending insets.
+            if (!forceConstruct
+                    && (!mPendingContentInsets.equals(contentInsets) ||
+                        !mPendingStableInsets.equals(stableInsets))) {
+                contentInsets = mPendingContentInsets;
+                stableInsets = mPendingStableInsets;
+            }
+            final boolean isRound = (mIsEmulator && mIsCircularEmulator) || mWindowIsRound;
+            mLastWindowInsets = new WindowInsets(contentInsets,
+                    null /* windowDecorInsets */, stableInsets, isRound);
+        }
+        return mLastWindowInsets;
+    }
+
     void dispatchApplyInsets(View host) {
-        mDispatchContentInsets.set(mAttachInfo.mContentInsets);
-        mDispatchStableInsets.set(mAttachInfo.mStableInsets);
-        final boolean isRound = (mIsEmulator && mIsCircularEmulator) || mWindowIsRound;
-        host.dispatchApplyWindowInsets(new WindowInsets(
-                mDispatchContentInsets, null /* windowDecorInsets */,
-                mDispatchStableInsets, isRound));
+        host.dispatchApplyWindowInsets(getWindowInsets(true /* forceConstruct */));
     }
 
     private void performTraversals() {
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 3df1293..943beb0 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -1330,4 +1330,25 @@
      */
     @SystemApi
     public abstract boolean getVideoOverlayForEmbeddedEncryptedVideoEnabled();
+
+    /**
+     * Sets whether this WebView should raster tiles when it is
+     * offscreen but attached to a window. Turning this on can avoid
+     * rendering artifacts when animating an offscreen WebView on-screen.
+     * Offscreen WebViews in this mode use more memory. The default value is
+     * false.
+     * Please follow these guidelines to limit memory usage:
+     * - WebView size should be not be larger than the device screen size.
+     * - Limit use of this mode to a small number of WebViews. Use it for
+     *   visible WebViews and WebViews about to be animated to visible.
+     */
+    public abstract void setOffscreenPreRaster(boolean enabled);
+
+    /**
+     * Gets whether this WebView should raster tiles when it is
+     * offscreen but attached to a window.
+     * @return true if this WebView will raster tiles when it is
+     * offscreen but attached to a window.
+     */
+    public abstract boolean getOffscreenPreRaster();
 }
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 81d9d56..d93b212 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -3877,7 +3877,7 @@
                     }
                 }
                 mPrevOffset = offset;
-                mTouchWordOffset = trueOffset - offset;
+                mTouchWordOffset = Math.max(trueOffset - offset, 0);
                 mInWord = !isStartBoundary(offset);
                 positionCursor = true;
             } else if (offset - mTouchWordOffset > mPrevOffset) {
@@ -3981,7 +3981,7 @@
                     }
                 }
                 mPrevOffset = offset;
-                mTouchWordOffset = offset - trueOffset;
+                mTouchWordOffset = Math.max(offset - trueOffset, 0);
                 mInWord = !isEndBoundary(offset);
                 positionCursor = true;
             } else if (offset + mTouchWordOffset < mPrevOffset) {
diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java
index 9d6ba57..2671739 100644
--- a/core/java/android/widget/VideoView.java
+++ b/core/java/android/widget/VideoView.java
@@ -302,6 +302,8 @@
             mMediaPlayer = null;
             mCurrentState = STATE_IDLE;
             mTargetState  = STATE_IDLE;
+            AudioManager am = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+            am.abandonAudioFocus(null);
         }
     }
 
@@ -310,12 +312,13 @@
             // not ready for playback just yet, will try again later
             return;
         }
-        AudioManager am = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
-        am.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
-
         // we shouldn't clear the target state, because somebody might have
         // called start() previously
         release(false);
+
+        AudioManager am = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+        am.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
+
         try {
             mMediaPlayer = new MediaPlayer();
             // TODO: create SubtitleController in MediaPlayer, but we need
@@ -641,6 +644,8 @@
             if (cleartargetstate) {
                 mTargetState  = STATE_IDLE;
             }
+            AudioManager am = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+            am.abandonAudioFocus(null);
         }
     }
 
diff --git a/core/java/com/android/internal/net/VpnInfo.aidl b/core/java/com/android/internal/net/VpnInfo.aidl
new file mode 100644
index 0000000..6fc97be
--- /dev/null
+++ b/core/java/com/android/internal/net/VpnInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.internal.net;
+
+parcelable VpnInfo;
diff --git a/core/java/com/android/internal/net/VpnInfo.java b/core/java/com/android/internal/net/VpnInfo.java
new file mode 100644
index 0000000..a676dac
--- /dev/null
+++ b/core/java/com/android/internal/net/VpnInfo.java
@@ -0,0 +1,69 @@
+/*
+ * 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.internal.net;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A lightweight container used to carry information of the ongoing VPN.
+ * Internal use only..
+ *
+ * @hide
+ */
+public class VpnInfo implements Parcelable {
+    public int ownerUid;
+    public String vpnIface;
+    public String primaryUnderlyingIface;
+
+    @Override
+    public String toString() {
+        return "VpnInfo{" +
+                "ownerUid=" + ownerUid +
+                ", vpnIface='" + vpnIface + '\'' +
+                ", primaryUnderlyingIface='" + primaryUnderlyingIface + '\'' +
+                '}';
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(ownerUid);
+        dest.writeString(vpnIface);
+        dest.writeString(primaryUnderlyingIface);
+    }
+
+    public static final Parcelable.Creator<VpnInfo> CREATOR = new Parcelable.Creator<VpnInfo>() {
+        @Override
+        public VpnInfo createFromParcel(Parcel source) {
+            VpnInfo info = new VpnInfo();
+            info.ownerUid = source.readInt();
+            info.vpnIface = source.readString();
+            info.primaryUnderlyingIface = source.readString();
+            return info;
+        }
+
+        @Override
+        public VpnInfo[] newArray(int size) {
+            return new VpnInfo[size];
+        }
+    };
+}
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 3ca88e5..1de93b9 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1434,7 +1434,7 @@
 
          Note that doze dreams are not subject to the same start conditions as ordinary dreams.
          Doze dreams will run whenever the power manager is in a dozing state. -->
-    <string name="config_dozeComponent"></string>
+    <string name="config_dozeComponent" translatable="false"></string>
 
     <!-- If true, the doze component is not started until after the screen has been
          turned off and the screen off animation has been performed. -->
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index e8a249e..6c6d2cc 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -369,6 +369,10 @@
      to 0 -->
      <dimen name="circular_display_mask_offset">0px</dimen>
 
+     <!-- Amount to reduce the size of the circular mask by (to compensate for aliasing
+     effects). This is only used on circular displays. -->
+     <dimen name="circular_display_mask_thickness">1px</dimen>
+
      <dimen name="lock_pattern_dot_line_width">3dp</dimen>
      <dimen name="lock_pattern_dot_size">12dp</dimen>
      <dimen name="lock_pattern_dot_size_activated">28dp</dimen>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index f16c5cf..008d15a 100755
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -419,6 +419,7 @@
   <java-symbol type="dimen" name="immersive_mode_cling_width" />
   <java-symbol type="dimen" name="circular_display_mask_offset" />
   <java-symbol type="dimen" name="accessibility_magnification_indicator_width" />
+  <java-symbol type="dimen" name="circular_display_mask_thickness" />
 
   <java-symbol type="string" name="add_account_button_label" />
   <java-symbol type="string" name="addToDictionary" />
diff --git a/core/tests/coretests/src/android/net/NetworkStatsTest.java b/core/tests/coretests/src/android/net/NetworkStatsTest.java
index 9ee4e20..fd922a2 100644
--- a/core/tests/coretests/src/android/net/NetworkStatsTest.java
+++ b/core/tests/coretests/src/android/net/NetworkStatsTest.java
@@ -320,6 +320,73 @@
         red.combineAllValues(blue);
     }
 
+    public void testMigrateTun() throws Exception {
+        final int tunUid = 10030;
+        final String tunIface = "tun0";
+        final String underlyingIface = "wlan0";
+        final int testTag1 = 8888;
+        NetworkStats delta = new NetworkStats(TEST_START, 17)
+            .addValues(tunIface, 10100, SET_DEFAULT, TAG_NONE, 39605L, 46L, 12259L, 55L, 0L)
+            .addValues(tunIface, 10100, SET_FOREGROUND, TAG_NONE, 0L, 0L, 0L, 0L, 0L)
+            .addValues(tunIface, 10120, SET_DEFAULT, TAG_NONE, 72667L, 197L, 43909L, 241L, 0L)
+            .addValues(tunIface, 10120, SET_FOREGROUND, TAG_NONE, 9297L, 17L, 4128L, 21L, 0L)
+            // VPN package also uses some traffic through unprotected network.
+            .addValues(tunIface, tunUid, SET_DEFAULT, TAG_NONE, 4983L, 10L, 1801L, 12L, 0L)
+            .addValues(tunIface, tunUid, SET_FOREGROUND, TAG_NONE, 0L, 0L, 0L, 0L, 0L)
+            // Tag entries
+            .addValues(tunIface, 10120, SET_DEFAULT, testTag1, 21691L, 41L, 13820L, 51L, 0L)
+            .addValues(tunIface, 10120, SET_FOREGROUND, testTag1, 1281L, 2L, 665L, 2L, 0L)
+            // Irrelevant entries
+            .addValues(TEST_IFACE, 10100, SET_DEFAULT, TAG_NONE, 1685L, 5L, 2070L, 6L, 0L)
+            // Underlying Iface entries
+            .addValues(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, 5178L, 8L, 2139L, 11L, 0L)
+            .addValues(underlyingIface, 10100, SET_FOREGROUND, TAG_NONE, 0L, 0L, 0L, 0L, 0L)
+            .addValues(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, 149873L, 287L,
+                    59217L /* smaller than sum(tun0) */, 299L /* smaller than sum(tun0) */, 0L)
+            .addValues(underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, 0L, 0L, 0L, 0L, 0L);
+
+        assertTrue(delta.migrateTun(tunUid, tunIface, underlyingIface));
+        assertEquals(17, delta.size());
+
+        // tunIface and TEST_IFACE entries are not changed.
+        assertValues(delta, 0, tunIface, 10100, SET_DEFAULT, TAG_NONE,
+                39605L, 46L, 12259L, 55L, 0L);
+        assertValues(delta, 1, tunIface, 10100, SET_FOREGROUND, TAG_NONE, 0L, 0L, 0L, 0L, 0L);
+        assertValues(delta, 2, tunIface, 10120, SET_DEFAULT, TAG_NONE,
+                72667L, 197L, 43909L, 241L, 0L);
+        assertValues(delta, 3, tunIface, 10120, SET_FOREGROUND, TAG_NONE,
+                9297L, 17L, 4128L, 21L, 0L);
+        assertValues(delta, 4, tunIface, tunUid, SET_DEFAULT, TAG_NONE,
+                4983L, 10L, 1801L, 12L, 0L);
+        assertValues(delta, 5, tunIface, tunUid, SET_FOREGROUND, TAG_NONE, 0L, 0L, 0L, 0L, 0L);
+        assertValues(delta, 6, tunIface, 10120, SET_DEFAULT, testTag1,
+                21691L, 41L, 13820L, 51L, 0L);
+        assertValues(delta, 7, tunIface, 10120, SET_FOREGROUND, testTag1, 1281L, 2L, 665L, 2L, 0L);
+        assertValues(delta, 8, TEST_IFACE, 10100, SET_DEFAULT, TAG_NONE, 1685L, 5L, 2070L, 6L, 0L);
+
+        // Existing underlying Iface entries are updated
+        assertValues(delta, 9, underlyingIface, 10100, SET_DEFAULT, TAG_NONE,
+                44783L, 54L, 13829L, 60L, 0L);
+        assertValues(delta, 10, underlyingIface, 10100, SET_FOREGROUND, TAG_NONE,
+                0L, 0L, 0L, 0L, 0L);
+
+        // VPN underlying Iface entries are updated
+        assertValues(delta, 11, underlyingIface, tunUid, SET_DEFAULT, TAG_NONE,
+                28304L, 27L, 1719L, 12L, 0L);
+        assertValues(delta, 12, underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE,
+                0L, 0L, 0L, 0L, 0L);
+
+        // New entries are added for new application's underlying Iface traffic
+        assertValues(delta, 13, underlyingIface, 10120, SET_DEFAULT, TAG_NONE,
+                72667L, 197L, 41872l, 219L, 0L);
+        assertValues(delta, 14, underlyingIface, 10120, SET_FOREGROUND, TAG_NONE,
+                9297L, 17L, 3936, 19L, 0L);
+        assertValues(delta, 15, underlyingIface, 10120, SET_DEFAULT, testTag1,
+                21691L, 41L, 13179L, 46L, 0L);
+        assertValues(delta, 16, underlyingIface, 10120, SET_FOREGROUND, testTag1,
+                1281L, 2L, 634L, 1L, 0L);
+    }
+
     private static void assertValues(NetworkStats stats, int index, String iface, int uid, int set,
             int tag, long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) {
         final NetworkStats.Entry entry = stats.getValues(index, null);
diff --git a/docs/html/sdk/installing/installing-adt.jd b/docs/html/sdk/installing/installing-adt.jd
index 0114848..5559d1a 100644
--- a/docs/html/sdk/installing/installing-adt.jd
+++ b/docs/html/sdk/installing/installing-adt.jd
@@ -1,8 +1,8 @@
 page.title=Installing the Eclipse Plugin
-adt.zip.version=23.0.4
-adt.zip.download=ADT-23.0.4.zip
-adt.zip.bytes=103336810
-adt.zip.checksum=91a43dcf686ab73dec2c08b77243492b
+adt.zip.version=23.0.6
+adt.zip.download=ADT-23.0.6.zip
+adt.zip.bytes=103344298
+adt.zip.checksum=f64b7e50c84799f41c642218c35f1bbe
 
 @jd:body
 
@@ -142,7 +142,7 @@
 
 <p>Note that there are features of ADT that require some optional
 Eclipse packages (for example, WST). If you encounter an error when
-installing ADT, your Eclipse installion might not include these packages.
+installing ADT, your Eclipse installation might not include these packages.
 For information about how to quickly add the necessary packages to your
 Eclipse installation, see the troubleshooting topic
 <a href="{@docRoot}resources/faq/troubleshooting.html#installeclipsecomponents">ADT
diff --git a/docs/html/tools/sdk/eclipse-adt.jd b/docs/html/tools/sdk/eclipse-adt.jd
index c3a4dea..08634da 100644
--- a/docs/html/tools/sdk/eclipse-adt.jd
+++ b/docs/html/tools/sdk/eclipse-adt.jd
@@ -53,6 +53,40 @@
 <div class="toggle-content opened">
   <p><a href="#" onclick="return toggleContent(this)">
     <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-content-img"
+      alt=""/>ADT 23.0.6</a> <em>(March 2015)</em>
+  </p>
+
+  <div class="toggle-content-toggleme">
+<dl>
+  <dt>Dependencies:</dt>
+
+  <dd>
+    <ul>
+      <li>Java 7 or higher is required if you are targeting Android 5.0 and higher.</li>
+      <li>Java 1.6 or higher is required if you are targeting other releases.</li>
+      <li>Eclipse Indigo (Version 3.7.2) or higher is required.</li>
+      <li>This version of ADT is designed for use with
+        <a href="{@docRoot}tools/sdk/tools-notes.html">SDK Tools r24.1.2</a>.
+        If you haven't already installed SDK Tools r24.1.2 into your SDK, use the
+        Android SDK Manager to do so.</li>
+    </ul>
+  </dd>
+
+  <dt>General Notes:</dt>
+  <dd>
+    <ul>
+        <li>Fixed issues with the rendering library.</li>
+    </ul>
+  </dd>
+</dl>
+</div>
+</div>
+
+
+
+<div class="toggle-content closed">
+  <p><a href="#" onclick="return toggleContent(this)">
+    <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-content-img"
       alt=""/>ADT 23.0.4</a> <em>(October 2014)</em>
   </p>
 
@@ -62,12 +96,12 @@
 
   <dd>
     <ul>
-      <li>Java 7 or higher is required if you are targeting the L Developer Preview.</li>
+      <li>Java 7 or higher is required if you are targeting Android 5.0 and higher.</li>
       <li>Java 1.6 or higher is required if you are targeting other releases.</li>
       <li>Eclipse Indigo (Version 3.7.2) or higher is required.</li>
       <li>This version of ADT is designed for use with
-        <a href="{@docRoot}tools/sdk/tools-notes.html">SDK Tools r23.0.2</a>.
-        If you haven't already installed SDK Tools r23.0.2 into your SDK, use the
+        <a href="{@docRoot}tools/sdk/tools-notes.html">SDK Tools r23.0.4</a>.
+        If you haven't already installed SDK Tools r23.0.4 into your SDK, use the
         Android SDK Manager to do so.</li>
     </ul>
   </dd>
@@ -96,7 +130,7 @@
 
   <dd>
     <ul>
-      <li>Java 7 or higher is required if you are targeting the L Developer Preview.</li>
+      <li>Java 7 or higher is required if you are targeting Android 5.0 and higher.</li>
       <li>Java 1.6 or higher is required if you are targeting other releases.</li>
       <li>Eclipse Indigo (Version 3.7.2) or higher is required.</li>
       <li>This version of ADT is designed for use with
@@ -132,7 +166,7 @@
 
   <dd>
     <ul>
-      <li>Java 7 or higher is required if you are targeting the L Developer Preview.</li>
+      <li>Java 7 or higher is required if you are targeting Android 5.0 and higher.</li>
       <li>Java 1.6 or higher is required if you are targeting other releases.</li>
       <li>Eclipse Indigo (Version 3.7.2) or higher is required.</li>
       <li>This version of ADT is designed for use with
@@ -167,7 +201,7 @@
 
   <dd>
     <ul>
-      <li>Java 7 or higher is required if you are targeting the L Developer Preview.</li>
+      <li>Java 7 or higher is required if you are targeting Android 5.0 and higher.</li>
       <li>Java 1.6 or higher is required if you are targeting other releases.</li>
       <li>Eclipse Indigo (Version 3.7.2) or higher is required.</li>
       <li>This version of ADT is designed for use with
@@ -2274,7 +2308,7 @@
 <dt>DDMS integration:</dt>
 <dd>
 <ul>
-<li>Includes the improvements from the standlone DDMS, revision 3.</li>
+<li>Includes the improvements from the standalone DDMS, revision 3.</li>
 <li>Adds an option to open HPROF files into eclipse instead of writing them on
 disk. If a profiler such as MAT (<a href="http://eclipse.org/mat">Memory Analyzer
 Tool</a>) is installed, it'll open the file.</li>
diff --git a/docs/html/training/auto/messaging/index.jd b/docs/html/training/auto/messaging/index.jd
index 2405d83..70ac205 100644
--- a/docs/html/training/auto/messaging/index.jd
+++ b/docs/html/training/auto/messaging/index.jd
@@ -48,7 +48,7 @@
 </a>
 
 <p>
-  Staying connected through text messages is important to many drivers. Chat apps can let users
+  Staying connected through messages is important to many drivers. Chat apps can let users
   know if a child need to be picked up, or if a dinner location has been changed. Apps that provide
   sports information might tell the user who just won the big game, and let the user ask questions
   about other games being played. The Android framework enables messaging apps to extend their
@@ -95,6 +95,90 @@
     has read or replied to a message.
 </ul>
 
+<h3 id="#concepts">Concepts and Objects</h3>
+
+<p>Before you start designing your app, it's helpful to understand how Auto
+handles messaging.</p>
+
+<p>Each individual chunk of communication is a <em>message</em>. A message is a
+short length of text, suitable for the Auto device to read aloud. In a chat app,
+this might be a single message from one person to another: <code>"Fitzy -- Jane
+can't come to the ball, her youngest has the croup. :-( --Liz"</code> In a
+sports app, a message might be a single bit of news about a game: <code>"Granger
+scores for Harpies at 7 minutes in."</code></p>
+
+<p>A <em>conversation</em> is a group of messages that are all grouped together
+in some way. Auto uses the conversation information to group the messages
+together when presenting them to the user. In a chat app, a conversation might
+be all the messages between the user and another person (for example, all
+the messages back and forth between Darcy and Elizabeth). In a sports app, a
+conversation might be all the messages about a particular game. Every message
+belongs to a conversation, even if it's the only message in that conversation.
+Each conversation has a <em>conversation name</em>.
+The conversation name is used by Android Auto to
+present the messages; it's up to your app to choose an appropriate conversation
+name. In a chat app, the conversation name is usually the person your user is
+talking to.
+In a sports app, this might be the name of the teams playing in the game.</p>
+
+<p>The v4 support library defines an {@link
+android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation
+UnreadConversation} object. This object holds all messages in a conversation
+which have not yet been heard by the user. To give those messages to the user,
+you attach that {@link
+android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation
+UnreadConversation} to a notification. However, you do not attach messages to
+the {@link
+android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation
+UnreadConversation} directly. Instead, you must first set up an {@link
+android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation.Builder
+UnreadConversation.Builder} object for the conversation. The messages are added to the builder,
+then when you are ready to send the messages, you use the builder to create the
+actual {@link
+android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation
+UnreadConversation} and attach the {@link
+android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation
+UnreadConversation} to the notification.</p>
+
+<p class="note"><strong>Note:</strong> When Auto presents messages to the
+user, it uses the notification <em>tag</em> and <em>ID</em> to determine which conversation the
+messages belong to. It is important to use the same tag and ID for all messages in
+a conversation, and to not use that tag for other conversations.</p>
+
+<h3 id="#workflow">Workflow</h3>
+
+<p>This section describes how the mobile device interacts with Auto to present
+messages to the user.</p>
+
+<ol>
+
+<li>The app receives a message that it wants to pass on to the user. It attaches
+the message to an  {@link
+android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation
+UnreadConversation} object and attaches it to a notification. That notification
+is associated with a {@link
+android.support.v4.app.NotificationCompat.CarExtender CarExtender} object, which
+indicates that the notification can be handled by Android Auto.</li>
+
+<li>The app posts the notification. The Android notification framework passes the
+message to Auto. Auto uses the notification tag and ID to determine which conversation
+the message belongs to, and presents the message to the user in an appropriate
+way.</li>
+
+<li>When the user listens to the message, Auto triggers the app's message heard
+pending intent. The app should discard the {@link
+android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation
+UnreadConversation} object and its builder at this time, since the messages
+contained in those objects have been heard by the user.</li>
+
+<li>If the user sends a reply, Auto triggers the app's "message reply" intent and
+attaches a transcript of the user's response.  The app can take appropriate
+action, based on the app's logic. For example, a chat app might interpret the
+reply as a message to go to the other conversation participants, while a sports
+app might try to interpret the "reply" as a request for other information
+("What's the score in the Sharks game?").</li>
+
+</ol>
 
 <h2 id="#manifest">Configure Your Manifest</h2>
 
@@ -104,7 +188,6 @@
   section describes what changes to make to your manifest to support messaging for Auto devices.
 </p>
 
-
 <h3 id="manifest-messaging">Declare Auto messaging support</h3>
 
 <p>
@@ -159,26 +242,28 @@
 <pre>
 &lt;application&gt;
     ...
-    &lt;receiver android:name="<em>.MyMessageReadReceiver</em>"&gt;
+    &lt;receiver android:name=".MyMessageHeardReceiver"&gt;
         &lt;intent-filter&gt;
-          &lt;action android:name="<em>com.myapp.messagingservice.ACTION_MESSAGE_HEARD</em>"/&gt;
+          &lt;action android:name="com.myapp.messagingservice.MY_ACTION_MESSAGE_HEARD"/&gt;
         &lt;/intent-filter&gt;
     &lt;/receiver&gt;
 
-    &lt;receiver android:name="<em>.MyMessageReplyReceiver</em>"&gt;
+    &lt;receiver android:name=".MyMessageReplyReceiver"&gt;
         &lt;intent-filter&gt;
-          &lt;action android:name="<em>com.myapp.messagingservice.ACTION_MESSAGE_REPLY</em>"/&gt;
+          &lt;action android:name="com.myapp.messagingservice.MY_ACTION_MESSAGE_REPLY"/&gt;
         &lt;/intent-filter&gt;
     &lt;/receiver&gt;
     ...
 &lt;/application&gt;
 </pre>
 
-<p>
-  The definition of the {@link android.content.BroadcastReceiver} classes shown in this example
-  is discussed in <a href="#handle_actions">Handle User Actions</a>.
-</p>
-
+<p>   In this example, <code>"MyMessageReadReceiver"</code> and
+<code>"MyMessageReplyReceiver"</code> are the names of the {@link
+android.content.BroadcastReceiver} subclasses you define to handle the
+intents. You can choose whatever you like   as the action names, but it's best
+to prepend your package name to ensure that   the action names are unique. For
+more information about handling actions, see <a href="#handle_actions">Handle
+User Actions</a>. </p>
 
 <h2 id="support-lib">Import Support Library for Messaging</h3>
 
@@ -199,7 +284,7 @@
 <pre>
 dependencies {
     ...
-    compile 'com.android.support:support-v4:21.0.+'
+    compile 'com.android.support:support-v4:21.0.2'
 }
 </pre>
 
@@ -240,79 +325,6 @@
   provide the content of messages in those conversations.
 </p>
 
-
-<h3 id="build_conversation">Build message conversations</h4>
-
-<p>
-  Messaging notifications for Auto organize messages into conversations using the {@link
-  android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation} class,
-  that represents an unread or new
-  portion of a conversation from a particular sender. It contains a list of messages from the
-  sender.
-</p>
-
-<p>
-  Use the {@link android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation.Builder} class to create an unread conversation object,
-  as shown in the following example code:
-</p>
-
-<pre>
-// Build a RemoteInput for receiving voice input in a Car Notification
-RemoteInput remoteInput = new RemoteInput.Builder(EXTRA_VOICE_REPLY)
-        .setLabel(getApplicationContext().getString(R.string.notification_reply))
-        .build();
-
-// Create an unread conversation object to organize a group of messages
-// from a particular sender.
-UnreadConversation.Builder unreadConvBuilder =
-    new UnreadConversation.Builder(participantName)
-        .setReadPendingIntent(msgHeardPendingIntent)
-        .setReplyAction(replyPendingIntent, remoteInput);
-</pre>
-
-<p>
-  This conversation object includes a {@link android.app.PendingIntent}, which allows the Auto
-  device to signal your app that the conversation has been read by the Auto user. The construction
-  of this intent is discussed in the <a href="#conversation-intents">Creating conversation read and
-  reply intents</a> section.
-</p>
-
-<p>
-  If your app supports replying to a conversation, you must call the {@link android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation.Builder#setReplyAction setReplyAction()}
-  method and provide a pending intent to pass that user action back to your app. The
-  {@link android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation}
-  object you create must also include a {@link
-  android.support.v4.app.RemoteInput} object. When the Auto user
-  receiving this conversation speaks a reply, the remote input objects lets your app get a text
-  version of the voice reply.
-</p>
-
-
-<h4 id="conversation-messages">Associate messages with conversations</h4>
-
-<p>
-  Messages provided for Auto must be associated with a conversation using the
-  {@link android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation}
-  class. The following code example shows how
-  to associate individual messages with a conversation object.
-</p>
-
-<pre>
-// Note: Add messages from oldest to newest to the UnreadConversation.Builder
-for (Iterator&lt;String&gt; messages = conversation.getMessages().iterator();
-     messages.hasNext(); ) {
-    String message = messages.next();
-    unreadConvBuilder.addMessage(message);
-}
-</pre>
-
-<p>
-  When a new message arrives in a particular conversation, your app should check if there is
-  already a conversation object for that particular conversation. If there is, associate the new
-  message with the existing conversation instead of building a new one.
-</p>
-
-
 <h4 id="conversation-intents">Create conversation read and reply intents</h4>
 
 <p>
@@ -323,28 +335,27 @@
 
 <p>
   The following example code demonstrates how to define a {@link android.app.PendingIntent} to let
-  your app know if a conversation was listened to by the Auto user:
+  your app know if a conversation was read to the Auto user:
 </p>
 
 <pre>
 Intent msgHeardIntent = new Intent()
     .addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
-    .setAction(<em>com.myapp.messagingservice.ACTION_MESSAGE_HEARD</em>)
-    .putExtra("conversation_id", <em>conversationId</em>);
+    .setAction("com.myapp.messagingservice.MY_ACTION_MESSAGE_HEARD")
+    .putExtra("conversation_id", thisConversationId);
 
 PendingIntent msgHeardPendingIntent =
     PendingIntent.getBroadcast(getApplicationContext(),
-        <em>conversationId</em>,
+        thisConversationId,
         msgHeardIntent,
         PendingIntent.FLAG_UPDATE_CURRENT);
 </pre>
 
-<p>
-  In this example, {@code conversationId} is an integer that identifies the current conversation.
-  The value of {@link android.content.Intent#setAction setAction()} is an intent filter identifier for heard messages which is
-  defined in your app manifest, as shown in <a href="#manifest-intent">Define read and reply intent
-  filters</a>.
-</p>
+<p>In this example, {@code thisConversationId} is an integer that identifies the
+current conversation.   The value of {@link android.content.Intent#setAction
+Intent.setAction()} is the intent filter identifier for heard messages which you
+defined in your app manifest, as shown in <a href="#manifest-intent">Define read
+and reply intent filters</a>. </p>
 
 <p>
   If your app supports replying to conversations, you also create a {@link
@@ -356,22 +367,79 @@
 <pre>
 Intent msgReplyIntent = new Intent()
     .addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
-    .setAction(<em>com.myapp.messagingservice.ACTION_MESSAGE_REPLY</em>)
-    .putExtra("conversation_id", <em>conversationId</em>);
+    .setAction("com.myapp.messagingservice.MY_ACTION_MESSAGE_REPLY")
+    .putExtra("conversation_id", thisConversationId);
 
 PendingIntent msgReplyPendingIntent = PendingIntent.getBroadcast(
     getApplicationContext(),
-    <em>conversationId</em>,
+    thisConversationId,
     msgReplyIntent,
     PendingIntent.FLAG_UPDATE_CURRENT);
 </pre>
 
+<p>   Once again, {@code thisConversationId} is an integer that uniquely identifies
+this conversation, and    the value you pass to {@link
+android.content.Intent#setAction Intent.setAction()} is the intent filter
+identifier you defined for replies in your   app manifest. </p>
+
+<h3 id="build_conversation">Set up the conversation builder</h4>
+
 <p>
-  Once again, {@code conversationId} is an integer that uniquely identifies this conversation. The
-  value of {@link android.content.Intent#setAction setAction()} is an intent filter identifier for replies which is defined in your
-  app manifest, as shown in <a href="#manifest-intent">Define read and reply intent filters</a>.
+  Messaging notifications for Auto organize messages into conversations using the {@link
+  android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation} class,
+  that represents an unread or new
+  portion of a conversation from a particular sender. It contains a list of messages from the
+  sender.
 </p>
 
+<p>
+  You generally do not configure the {@link
+  android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation UnreadConversation}
+  directly. Instead, you configure an
+  {@link android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation.Builder
+  UnreadConversation.Builder} with the information about the conversation,
+  as shown in the following example code.
+</p>
+
+<pre>
+// Build a RemoteInput for receiving voice input in a Car Notification
+RemoteInput remoteInput = new RemoteInput.Builder(MY_VOICE_REPLY_KEY)
+        .setLabel(getApplicationContext().getString(R.string.notification_reply))
+        .build();
+
+// Create an unread conversation object to organize a group of messages
+// from a particular sender.
+UnreadConversation.Builder unreadConvBuilder =
+    new UnreadConversation.Builder(conversationName)
+        .setReadPendingIntent(msgHeardPendingIntent)
+        .setReplyAction(msgReplyPendingIntent, remoteInput);
+</pre>
+
+<p class="note">
+  <strong>Note:</strong> You won't actually create the {@link
+  android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation
+  UnreadConversation} until you are almost ready to send the message.
+</p>
+
+<p>
+  This conversation object includes a {@link android.app.PendingIntent}, which allows the Auto
+  device to signal your app that the conversation has been read by the Auto user. The construction
+  of this intent is discussed in the <a href="#conversation-intents">Creating conversation read and
+  reply intents</a> section.
+</p>
+
+<p>
+  If your app supports replying to a conversation, you must call the
+  {@link android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation.Builder#setReplyAction
+  UnreadConversation.Builder.setReplyAction()}
+  method and provide a pending intent to pass that user action back to your app. The
+  {@link android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation
+  UnreadConversation}
+  object you create must also include a {@link
+  android.support.v4.app.RemoteInput} object. When the Auto user
+  receiving this conversation speaks a reply, the remote input objects lets your app get a text
+  version of the voice reply.
+</p>
 
 <h3 id="sending_messages">Sending Messages</h4>
 
@@ -381,49 +449,99 @@
 </p>
 
 <p>First, add the message to the {@link
-android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation.Builder}
-for this conversation, and update its timestamp:</p>
+android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation.Builder
+UnreadConversation.Builder} for this conversation, and update its timestamp:</p>
 
 <pre>
-unreadConvBuilder.addMessage(<em>messageString</em>)
-    .setLatestTimestamp(<em>currentTimestamp</em>);
+unreadConvBuilder.addMessage(messageString)
+    .setLatestTimestamp(currentTimestamp);
 </pre>
 
-<p>Then create a {@link android.support.v4.app.NotificationCompat.Builder}
-object that you'll use to build the actual notification. You'll need to use the
+<p class="note"><strong>Note:</strong> If you are sending several messages at
+once, add them to the {@link
+android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation.Builder
+UnreadConversation.Builder} in order, from oldest to newest.</p>
+
+<p>Then create the {@link android.support.v4.app.NotificationCompat.Builder
+NotificationCompat.Builder}
+object that builds the actual notification. You need to use the
 pending intents you created in the previous step.</p>
 
 <pre>
 NotificationCompat.Builder notificationBuilder =
     new NotificationCompat.Builder(getApplicationContext())
-        .setSmallIcon(R.drawable.<em>notification_icon</em>)
-        .setLargeIcon(<em>icon_bitmap</em>)
-        .setContentText(<em>messageString</em>)
-        .setWhen(<em>currentTimestamp</em>)
-        .setContentTitle(<em>participant_name</em>)
-        .setContentIntent(msgHeardPendingIntent);
-
+        .setSmallIcon(smallIconResourceID)
+        .setLargeIcon(largeIconBitmap);
 </pre>
 
+<dl>
+  <dt><code>smallIconResourceID</code></dt>
+  <dd>The resource ID of a small icon to use for the conversation. This is
+    typically a generic icon for the messaging app.</dd>
+
+  <dt><code>largeIconBitmap</code></dt>
+  <dd>A {@link android.graphics.Bitmap} of a large version of the icon. This
+    is typically a conversation-specific graphic. For example, if this is a
+    chat app, the large icon would be a picture of the person the user is
+    chatting with.</dd>
+
+  <dt><code>messageString</code></dt>
+  <dd>The text of the message you want to send. (If you are sending several
+    messages at once, concatenate them into a single string, with the oldest
+    message first.)</dd>
+
+  <dt><code>currentTimestamp</code></dt>
+  <dd>The message timestamp. (If you are sending several messages at once,
+    use the timestamp of the most recent message.)</dd>
+
+  <dt><code>conversationName</code></dt>
+
+  <dd>The name you chose for this conversation (for example, the name of the
+    person the user is chatting with). This should be the same conversation
+    name you used when you created the {@link
+android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation.Builder
+    UnreadConversation.Builder}.</dd>
+
+  <dt><code>msgHeardPendingIntent</code></dt>
+  <dd>The pending intent object you created in
+    <a href="#conversation-intents">Create conversation read and reply
+      intents</a>.</dd>
+</dl>
+
+
 <p>You'll also need to extend the  {@link
-android.support.v4.app.NotificationCompat.Builder} with the {@link
-android.support.v4.app.NotificationCompat.CarExtender}. This is where you
-actually create the {@link android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation} object using the builder you
+android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder} with the {@link
+android.support.v4.app.NotificationCompat.CarExtender CarExtender}. This is where you
+actually create the
+{@link android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation
+UnreadConversation} object using the builder you
 just created, and attach it to the {@link
-android.support.v4.app.NotificationCompat.CarExtender}:</p>
+android.support.v4.app.NotificationCompat.CarExtender CarExtender}:</p>
 
 <pre>
 notificationBuilder.extend(new CarExtender()
     .setUnreadConversation(unreadConvBuilder.build());
 </pre>
 
+<p class="note"><strong>Note:</strong> If you wish, you can set an override icon
+or color for the {@link android.support.v4.app.NotificationCompat.CarExtender
+CarExtender} by calling {@link
+android.support.v4.app.NotificationCompat.CarExtender#setLargeIcon
+setLargeIcon()} or {@link
+android.support.v4.app.NotificationCompat.CarExtender#setColor setColor()}. The
+override icon or color is used when the notification is handled by a car, and
+has no effect if the notification is handled on the Android device. This is
+useful if the notification's default icon or color are not suitable for the
+car's display.</p>
+
 <p>Once you've done all this, you use your app's {@link
 android.support.v4.app.NotificationManagerCompat} to send the notification:</p>
 
 <pre>
 NotificationManagerCompat msgNotificationManager =
     NotificationManagerCompat.from(context);
-msgNotificationManager.notify(<em>notificationId</em>, notificationBuilder.build());
+msgNotificationManager.notify(notificationTag,
+    notificationId, notificationBuilder.build());
 </pre>
 
 <h2 id="handle_actions">Handle User Actions</h2>
@@ -431,7 +549,7 @@
 <p>
   When your create and dispatch a notification for messaging, you specify intents to be triggered
   when the Auto user hears the message and when the user dictates a reply. Your app indicates to
-  the Android framework that it handles these intends by registering them through it's manifest, as
+  the Android framework that it handles these intends by registering them through its manifest, as
   discussed in <a href="#manifest-intent">Define read and reply intent filters</a>.
 </p>
 
@@ -461,7 +579,7 @@
 </p>
 
 <pre>
-public class MessageHeardReceiver extends BroadcastReceiver {
+public class MyMessageHeardReceiver extends BroadcastReceiver {
 
     &#64;Override
     public void onReceive(Context context, Intent intent) {
@@ -469,7 +587,7 @@
         // If you set up the intent as described in
         // "Create conversation read and reply intents",
         // you can get the conversation ID by calling:
-        int conversationId = intent.getIntExtra("conversation_id", -1);
+        int thisConversationId = intent.getIntExtra("conversation_id", -1);
 
         // Remove the notification to indicate it has been read
         // and update the list of unread conversations in your app.
@@ -479,7 +597,8 @@
 
 <p>
   Once a notification is read, your app can remove it by calling
-  {@link android.support.v4.app.NotificationManagerCompat#cancel} with the notification ID.
+  {@link android.support.v4.app.NotificationManagerCompat#cancel
+  NotificationManagerCompat.cancel()} with the notification ID.
   Within your app, you should mark the messages provided in the notification as read.
 </p>
 
@@ -505,7 +624,7 @@
 </p>
 
 <pre>
-  public class MessageReplyReceiver extends BroadcastReceiver {
+  public class MyMessageReplyReceiver extends BroadcastReceiver {
 
 
     &#64;Override
@@ -513,7 +632,7 @@
         // If you set up the intent as described in
         // "Create conversation read and reply intents",
         // you can get the conversation ID by calling:
-        int conversationId = intent.getIntExtra("conversation_id", -1).
+        int thisConversationId = intent.getIntExtra("conversation_id", -1).
 
     }
 
@@ -527,7 +646,7 @@
         Bundle remoteInput =
             RemoteInput.getResultsFromIntent(intent);
         if (remoteInput != null) {
-            return remoteInput.getCharSequence("extra_voice_reply");
+            return remoteInput.getCharSequence(MY_VOICE_REPLY_KEY);
         }
         return null;
     }
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 6f93c820..d5d583c 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -701,6 +701,12 @@
 
                 *u16len = decodeLength(&str);
                 if ((uint32_t)(str+*u16len-strings) < mStringPoolSize) {
+                    // Reject malformed (non null-terminated) strings
+                    if (str[*u16len] != 0x0000) {
+                        ALOGW("Bad string block: string #%d is not null-terminated",
+                              (int)idx);
+                        return NULL;
+                    }
                     return reinterpret_cast<const char16_t*>(str);
                 } else {
                     ALOGW("Bad string block: string #%d extends to %d, past end at %d\n",
@@ -748,6 +754,13 @@
                         return NULL;
                     }
 
+                    // Reject malformed (non null-terminated) strings
+                    if (u8str[u8len] != 0x00) {
+                        ALOGW("Bad string block: string #%d is not null-terminated",
+                              (int)idx);
+                        return NULL;
+                    }
+
                     char16_t *u16str = (char16_t *)calloc(*u16len+1, sizeof(char16_t));
                     if (!u16str) {
                         ALOGW("No memory when trying to allocate decode cache for string #%d\n",
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 3fc3646..4fdcfa4 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -56,7 +56,7 @@
  */
 public class AudioManager {
 
-    private final Context mContext;
+    private final Context mApplicationContext;
     private long mVolumeKeyUpTime;
     private final boolean mUseVolumeKeySounds;
     private final boolean mUseFixedVolume;
@@ -575,10 +575,10 @@
      * @hide
      */
     public AudioManager(Context context) {
-        mContext = context;
-        mUseVolumeKeySounds = mContext.getResources().getBoolean(
+        mApplicationContext = context;
+        mUseVolumeKeySounds = mApplicationContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_useVolumeKeySounds);
-        mUseFixedVolume = mContext.getResources().getBoolean(
+        mUseFixedVolume = mApplicationContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_useFixedVolume);
         sAudioPortEventHandler.init();
     }
@@ -617,7 +617,7 @@
      *     or {@link KeyEvent#KEYCODE_MEDIA_AUDIO_TRACK}.
      */
     public void dispatchMediaKeyEvent(KeyEvent keyEvent) {
-        MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext);
+        MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mApplicationContext);
         helper.sendMediaButtonEvent(keyEvent, false);
     }
 
@@ -663,7 +663,8 @@
                 break;
             case KeyEvent.KEYCODE_VOLUME_MUTE:
                 if (event.getRepeatCount() == 0) {
-                    MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent(event, false);
+                    MediaSessionLegacyHelper.getHelper(mApplicationContext)
+                            .sendVolumeKeyEvent(event, false);
                 }
                 break;
         }
@@ -690,7 +691,8 @@
                 mVolumeKeyUpTime = SystemClock.uptimeMillis();
                 break;
             case KeyEvent.KEYCODE_VOLUME_MUTE:
-                MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent(event, false);
+                MediaSessionLegacyHelper.getHelper(mApplicationContext)
+                        .sendVolumeKeyEvent(event, false);
                 break;
         }
     }
@@ -735,7 +737,7 @@
         IAudioService service = getService();
         try {
             service.adjustStreamVolume(streamType, direction, flags,
-                    mContext.getOpPackageName());
+                    mApplicationContext.getOpPackageName());
         } catch (RemoteException e) {
             Log.e(TAG, "Dead object in adjustStreamVolume", e);
         }
@@ -765,7 +767,7 @@
      * @see #isVolumeFixed()
      */
     public void adjustVolume(int direction, int flags) {
-        MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext);
+        MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mApplicationContext);
         helper.sendAdjustVolumeBy(USE_DEFAULT_STREAM_TYPE, direction, flags);
     }
 
@@ -794,7 +796,7 @@
      * @see #isVolumeFixed()
      */
     public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) {
-        MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext);
+        MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mApplicationContext);
         helper.sendAdjustVolumeBy(suggestedStreamType, direction, flags);
     }
 
@@ -802,7 +804,7 @@
     public void setMasterMute(boolean mute, int flags) {
         IAudioService service = getService();
         try {
-            service.setMasterMute(mute, flags, mContext.getOpPackageName());
+            service.setMasterMute(mute, flags, mApplicationContext.getOpPackageName());
         } catch (RemoteException e) {
             Log.e(TAG, "Dead object in setMasterMute", e);
         }
@@ -931,7 +933,7 @@
         }
         IAudioService service = getService();
         try {
-            service.setRingerModeExternal(ringerMode, mContext.getOpPackageName());
+            service.setRingerModeExternal(ringerMode, mApplicationContext.getOpPackageName());
         } catch (RemoteException e) {
             Log.e(TAG, "Dead object in setRingerMode", e);
         }
@@ -952,7 +954,7 @@
     public void setStreamVolume(int streamType, int index, int flags) {
         IAudioService service = getService();
         try {
-            service.setStreamVolume(streamType, index, flags, mContext.getOpPackageName());
+            service.setStreamVolume(streamType, index, flags, mApplicationContext.getOpPackageName());
         } catch (RemoteException e) {
             Log.e(TAG, "Dead object in setStreamVolume", e);
         }
@@ -1265,7 +1267,7 @@
      * @see #startBluetoothSco()
     */
     public boolean isBluetoothScoAvailableOffCall() {
-        return mContext.getResources().getBoolean(
+        return mApplicationContext.getResources().getBoolean(
                com.android.internal.R.bool.config_bluetooth_sco_off_call);
     }
 
@@ -1317,7 +1319,8 @@
     public void startBluetoothSco(){
         IAudioService service = getService();
         try {
-            service.startBluetoothSco(mICallBack, mContext.getApplicationInfo().targetSdkVersion);
+            service.startBluetoothSco(mICallBack,
+                    mApplicationContext.getApplicationInfo().targetSdkVersion);
         } catch (RemoteException e) {
             Log.e(TAG, "Dead object in startBluetoothSco", e);
         }
@@ -1465,7 +1468,7 @@
     public void setMicrophoneMute(boolean on){
         IAudioService service = getService();
         try {
-            service.setMicrophoneMute(on, mContext.getOpPackageName());
+            service.setMicrophoneMute(on, mApplicationContext.getOpPackageName());
         } catch (RemoteException e) {
             Log.e(TAG, "Dead object in setMicrophoneMute", e);
         }
@@ -1498,7 +1501,7 @@
     public void setMode(int mode) {
         IAudioService service = getService();
         try {
-            service.setMode(mode, mICallBack, mContext.getOpPackageName());
+            service.setMode(mode, mICallBack, mApplicationContext.getOpPackageName());
         } catch (RemoteException e) {
             Log.e(TAG, "Dead object in setMode", e);
         }
@@ -1896,7 +1899,7 @@
      * Settings has an in memory cache, so this is fast.
      */
     private boolean querySoundEffectsEnabled(int user) {
-        return Settings.System.getIntForUser(mContext.getContentResolver(),
+        return Settings.System.getIntForUser(mApplicationContext.getContentResolver(),
                 Settings.System.SOUND_EFFECTS_ENABLED, 0, user) != 0;
     }
 
@@ -2308,7 +2311,7 @@
         try {
             status = service.requestAudioFocus(requestAttributes, durationHint, mICallBack,
                     mAudioFocusDispatcher, getIdForAudioFocusListener(l),
-                    mContext.getOpPackageName() /* package name */, flags,
+                    mApplicationContext.getOpPackageName() /* package name */, flags,
                     ap != null ? ap.cb() : null);
         } catch (RemoteException e) {
             Log.e(TAG, "Can't call requestAudioFocus() on AudioService:", e);
@@ -2333,7 +2336,7 @@
                         .setInternalLegacyStreamType(streamType).build(),
                     durationHint, mICallBack, null,
                     AudioSystem.IN_VOICE_COMM_FOCUS_ID,
-                    mContext.getOpPackageName(),
+                    mApplicationContext.getOpPackageName(),
                     AUDIOFOCUS_FLAG_LOCK,
                     null /* policy token */);
         } catch (RemoteException e) {
@@ -2402,7 +2405,7 @@
         if (eventReceiver == null) {
             return;
         }
-        if (!eventReceiver.getPackageName().equals(mContext.getPackageName())) {
+        if (!eventReceiver.getPackageName().equals(mApplicationContext.getPackageName())) {
             Log.e(TAG, "registerMediaButtonEventReceiver() error: " +
                     "receiver and context package names don't match");
             return;
@@ -2411,7 +2414,7 @@
         Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
         //     the associated intent will be handled by the component being registered
         mediaButtonIntent.setComponent(eventReceiver);
-        PendingIntent pi = PendingIntent.getBroadcast(mContext,
+        PendingIntent pi = PendingIntent.getBroadcast(mApplicationContext,
                 0/*requestCode, ignored*/, mediaButtonIntent, 0/*flags*/);
         registerMediaButtonIntent(pi, eventReceiver);
     }
@@ -2445,8 +2448,8 @@
             Log.e(TAG, "Cannot call registerMediaButtonIntent() with a null parameter");
             return;
         }
-        MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext);
-        helper.addMediaButtonListener(pi, eventReceiver, mContext);
+        MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mApplicationContext);
+        helper.addMediaButtonListener(pi, eventReceiver, mApplicationContext);
     }
 
     /**
@@ -2464,7 +2467,7 @@
         Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
         //     the associated intent will be handled by the component being registered
         mediaButtonIntent.setComponent(eventReceiver);
-        PendingIntent pi = PendingIntent.getBroadcast(mContext,
+        PendingIntent pi = PendingIntent.getBroadcast(mApplicationContext,
                 0/*requestCode, ignored*/, mediaButtonIntent, 0/*flags*/);
         unregisterMediaButtonIntent(pi);
     }
@@ -2487,7 +2490,7 @@
      * @hide
      */
     public void unregisterMediaButtonIntent(PendingIntent pi) {
-        MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext);
+        MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mApplicationContext);
         helper.removeMediaButtonListener(pi);
     }
 
@@ -2504,7 +2507,7 @@
         if ((rcClient == null) || (rcClient.getRcMediaIntent() == null)) {
             return;
         }
-        rcClient.registerWithSession(MediaSessionLegacyHelper.getHelper(mContext));
+        rcClient.registerWithSession(MediaSessionLegacyHelper.getHelper(mApplicationContext));
     }
 
     /**
@@ -2519,7 +2522,7 @@
         if ((rcClient == null) || (rcClient.getRcMediaIntent() == null)) {
             return;
         }
-        rcClient.unregisterWithSession(MediaSessionLegacyHelper.getHelper(mContext));
+        rcClient.unregisterWithSession(MediaSessionLegacyHelper.getHelper(mApplicationContext));
     }
 
     /**
@@ -3038,7 +3041,7 @@
         IAudioService service = getService();
         try {
             service.setWiredDeviceConnectionState(type, state, address, name,
-                    mContext.getOpPackageName());
+                    mApplicationContext.getOpPackageName());
         } catch (RemoteException e) {
             Log.e(TAG, "Dead object in setWiredDeviceConnectionState "+e);
         }
@@ -3182,7 +3185,7 @@
      */
     public void disableSafeMediaVolume() {
         try {
-            getService().disableSafeMediaVolume(mContext.getOpPackageName());
+            getService().disableSafeMediaVolume(mApplicationContext.getOpPackageName());
         } catch (RemoteException e) {
             Log.w(TAG, "Error disabling safe media volume", e);
         }
@@ -3194,7 +3197,7 @@
      */
     public void setRingerModeInternal(int ringerMode) {
         try {
-            getService().setRingerModeInternal(ringerMode, mContext.getOpPackageName());
+            getService().setRingerModeInternal(ringerMode, mApplicationContext.getOpPackageName());
         } catch (RemoteException e) {
             Log.w(TAG, "Error calling setRingerModeInternal", e);
         }
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 7a35a31..d7752b9 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -275,6 +275,12 @@
      */
     public static final int EVENT_VENDOR_DEFINED = 4;
 
+    /**
+     * This event indicates that a session opened by the app has been reclaimed by the resource
+     * manager.
+     */
+    public static final int EVENT_SESSION_RECLAIMED = 5;
+
     private static final int DRM_EVENT = 200;
 
     private class EventHandler extends Handler
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index d9de7a9..a3447b8 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -92,6 +92,7 @@
     jint kEventKeyRequired;
     jint kEventKeyExpired;
     jint kEventVendorDefined;
+    jint kEventSessionReclaimed;
 } gEventTypes;
 
 struct KeyTypes {
@@ -194,6 +195,9 @@
         case DrmPlugin::kDrmPluginEventVendorDefined:
             jeventType = gEventTypes.kEventVendorDefined;
             break;
+        case DrmPlugin::kDrmPluginEventSessionReclaimed:
+            jeventType = gEventTypes.kEventSessionReclaimed;
+            break;
         default:
             ALOGE("Invalid event DrmPlugin::EventType %d, ignored", (int)eventType);
             return;
@@ -565,6 +569,8 @@
     gEventTypes.kEventKeyExpired = env->GetStaticIntField(clazz, field);
     GET_STATIC_FIELD_ID(field, clazz, "EVENT_VENDOR_DEFINED", "I");
     gEventTypes.kEventVendorDefined = env->GetStaticIntField(clazz, field);
+    GET_STATIC_FIELD_ID(field, clazz, "EVENT_SESSION_RECLAIMED", "I");
+    gEventTypes.kEventSessionReclaimed = env->GetStaticIntField(clazz, field);
 
     GET_STATIC_FIELD_ID(field, clazz, "KEY_TYPE_STREAMING", "I");
     gKeyTypes.kKeyTypeStreaming = env->GetStaticIntField(clazz, field);
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 7a9bbd6..8330e88 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -26,6 +26,7 @@
 import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
 import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
 
+import android.annotation.Nullable;
 import android.app.AlarmManager;
 import android.app.Notification;
 import android.app.NotificationManager;
@@ -100,6 +101,7 @@
 import com.android.internal.net.LegacyVpnInfo;
 import com.android.internal.net.NetworkStatsFactory;
 import com.android.internal.net.VpnConfig;
+import com.android.internal.net.VpnInfo;
 import com.android.internal.net.VpnProfile;
 import com.android.internal.telephony.DctConstants;
 import com.android.internal.util.AsyncChannel;
@@ -2978,7 +2980,6 @@
      * Return the information of the ongoing legacy VPN. This method is used
      * by VpnSettings and not available in ConnectivityManager. Permissions
      * are checked in Vpn class.
-     * @hide
      */
     @Override
     public LegacyVpnInfo getLegacyVpnInfo() {
@@ -2990,6 +2991,56 @@
     }
 
     /**
+     * Return the information of all ongoing VPNs. This method is used by NetworkStatsService
+     * and not available in ConnectivityManager.
+     */
+    @Override
+    public VpnInfo[] getAllVpnInfo() {
+        enforceConnectivityInternalPermission();
+        if (mLockdownEnabled) {
+            return new VpnInfo[0];
+        }
+
+        synchronized(mVpns) {
+            List<VpnInfo> infoList = new ArrayList<>();
+            for (int i = 0; i < mVpns.size(); i++) {
+                VpnInfo info = createVpnInfo(mVpns.valueAt(i));
+                if (info != null) {
+                    infoList.add(info);
+                }
+            }
+            return infoList.toArray(new VpnInfo[infoList.size()]);
+        }
+    }
+
+    /**
+     * @return VPN information for accounting, or null if we can't retrieve all required
+     *         information, e.g primary underlying iface.
+     */
+    @Nullable
+    private VpnInfo createVpnInfo(Vpn vpn) {
+        VpnInfo info = vpn.getVpnInfo();
+        if (info == null) {
+            return null;
+        }
+        Network[] underlyingNetworks = vpn.getUnderlyingNetworks();
+        // see VpnService.setUnderlyingNetworks()'s javadoc about how to interpret
+        // the underlyingNetworks list.
+        if (underlyingNetworks == null) {
+            NetworkAgentInfo defaultNetwork = getDefaultNetwork();
+            if (defaultNetwork != null && defaultNetwork.linkProperties != null) {
+                info.primaryUnderlyingIface = getDefaultNetwork().linkProperties.getInterfaceName();
+            }
+        } else if (underlyingNetworks.length > 0) {
+            LinkProperties linkProperties = getLinkProperties(underlyingNetworks[0]);
+            if (linkProperties != null) {
+                info.primaryUnderlyingIface = linkProperties.getInterfaceName();
+            }
+        }
+        return info.primaryUnderlyingIface == null ? null : info;
+    }
+
+    /**
      * Returns the information of the ongoing VPN. This method is used by VpnDialogs and
      * not available in ConnectivityManager.
      * Permissions are checked in Vpn class.
@@ -4512,8 +4563,13 @@
     public boolean setUnderlyingNetworksForVpn(Network[] networks) {
         throwIfLockdownEnabled();
         int user = UserHandle.getUserId(Binder.getCallingUid());
+        boolean success;
         synchronized (mVpns) {
-            return mVpns.get(user).setUnderlyingNetworks(networks);
+            success = mVpns.get(user).setUnderlyingNetworks(networks);
         }
+        if (success) {
+            notifyIfacesChanged();
+        }
+        return success;
     }
 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index c4122e8..7f44f4e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1757,7 +1757,7 @@
             }
             case ENTER_ANIMATION_COMPLETE_MSG: {
                 synchronized (ActivityManagerService.this) {
-                    ActivityRecord r = ActivityRecord.forToken((IBinder) msg.obj);
+                    ActivityRecord r = ActivityRecord.forTokenLocked((IBinder) msg.obj);
                     if (r != null && r.app != null && r.app.thread != null) {
                         try {
                             r.app.thread.scheduleEnterAnimationComplete(r.appToken);
@@ -2520,7 +2520,7 @@
     public void notifyActivityDrawn(IBinder token) {
         if (DEBUG_VISBILITY) Slog.d(TAG, "notifyActivityDrawn: token=" + token);
         synchronized (this) {
-            ActivityRecord r= mStackSupervisor.isInAnyStackLocked(token);
+            ActivityRecord r = mStackSupervisor.isInAnyStackLocked(token);
             if (r != null) {
                 r.task.stack.notifyActivityDrawnLocked(r);
             }
@@ -3955,11 +3955,10 @@
                 return;
             }
 
-            ArrayList<ActivityRecord> activities = new ArrayList<ActivityRecord>(
-                    mHeavyWeightProcess.activities);
-            for (int i=0; i<activities.size(); i++) {
+            ArrayList<ActivityRecord> activities = new ArrayList<>(mHeavyWeightProcess.activities);
+            for (int i = 0; i < activities.size(); i++) {
                 ActivityRecord r = activities.get(i);
-                if (!r.finishing) {
+                if (!r.finishing && r.isInStackLocked()) {
                     r.task.stack.finishActivityLocked(r, Activity.RESULT_CANCELED,
                             null, "finish-heavy", true);
                 }
@@ -4086,7 +4085,7 @@
             final long origId = Binder.clearCallingIdentity();
             try {
                 ActivityRecord r = ActivityRecord.isInStackLocked(token);
-                if (r.task == null || r.task.stack == null) {
+                if (r == null) {
                     return false;
                 }
                 return r.task.stack.safelyDestroyActivityLocked(r, "app-req");
@@ -7797,20 +7796,19 @@
                     android.Manifest.permission.GET_DETAILED_TASKS)
                     == PackageManager.PERMISSION_GRANTED;
 
-            final int N = mRecentTasks.size();
-            ArrayList<ActivityManager.RecentTaskInfo> res
-                    = new ArrayList<ActivityManager.RecentTaskInfo>(
-                            maxNum < N ? maxNum : N);
+            final int recentsCount = mRecentTasks.size();
+            ArrayList<ActivityManager.RecentTaskInfo> res =
+                    new ArrayList<>(maxNum < recentsCount ? maxNum : recentsCount);
 
             final Set<Integer> includedUsers;
             if (includeProfiles) {
                 includedUsers = getProfileIdsLocked(userId);
             } else {
-                includedUsers = new HashSet<Integer>();
+                includedUsers = new HashSet<>();
             }
             includedUsers.add(Integer.valueOf(userId));
 
-            for (int i=0; i<N && maxNum > 0; i++) {
+            for (int i = 0; i < recentsCount && maxNum > 0; i++) {
                 TaskRecord tr = mRecentTasks.get(i);
                 // Only add calling user or related users recent tasks
                 if (!includedUsers.contains(Integer.valueOf(tr.userId))) {
@@ -7873,7 +7871,7 @@
         synchronized (this) {
             enforceCallingPermission(android.Manifest.permission.READ_FRAME_BUFFER,
                     "getTaskThumbnail()");
-            TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(id);
+            TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(id, false);
             if (tr != null) {
                 return tr.getTaskThumbnailLocked();
             }
@@ -7986,7 +7984,7 @@
     @Override
     public void setTaskResizeable(int taskId, boolean resizeable) {
         synchronized (this) {
-            TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+            TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId, false);
             if (task == null) {
                 Slog.w(TAG, "setTaskResizeable: taskId=" + taskId + " not found");
                 return;
@@ -8153,7 +8151,7 @@
      * @return Returns true if the given task was found and removed.
      */
     private boolean removeTaskByIdLocked(int taskId, boolean killProcess) {
-        TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(taskId);
+        TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(taskId, false);
         if (tr != null) {
             tr.removeTaskActivitiesLocked();
             cleanUpRemovedTaskLocked(tr, killProcess);
@@ -8292,7 +8290,7 @@
             if (parentActivityToken == null) {
                 throw new IllegalArgumentException("parent token must not be null");
             }
-            ActivityRecord r = ActivityRecord.forToken(parentActivityToken);
+            ActivityRecord r = ActivityRecord.forTokenLocked(parentActivityToken);
             if (r == null) {
                 return null;
             }
@@ -8406,7 +8404,7 @@
         long ident = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
-                TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(taskId);
+                TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(taskId, false);
                 return tr != null && tr.stack != null && tr.stack.isHomeStack();
             }
         } finally {
@@ -8494,7 +8492,7 @@
         long ident = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
-                final ActivityRecord r = ActivityRecord.forToken(token);
+                final ActivityRecord r = ActivityRecord.forTokenLocked(token);
                 if (r == null) {
                     return;
                 }
@@ -16573,8 +16571,8 @@
     @Override
     public boolean shouldUpRecreateTask(IBinder token, String destAffinity) {
         synchronized (this) {
-            ActivityRecord srec = ActivityRecord.forToken(token);
-            if (srec.task != null && srec.task.stack != null) {
+            ActivityRecord srec = ActivityRecord.forTokenLocked(token);
+            if (srec != null) {
                 return srec.task.stack.shouldUpRecreateTaskLocked(srec, destAffinity);
             }
         }
@@ -16585,16 +16583,19 @@
             Intent resultData) {
 
         synchronized (this) {
-            final ActivityStack stack = ActivityRecord.getStackLocked(token);
-            if (stack != null) {
-                return stack.navigateUpToLocked(token, destIntent, resultCode, resultData);
+            final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+            if (r != null) {
+                return r.task.stack.navigateUpToLocked(r, destIntent, resultCode, resultData);
             }
             return false;
         }
     }
 
     public int getLaunchedFromUid(IBinder activityToken) {
-        ActivityRecord srec = ActivityRecord.forToken(activityToken);
+        ActivityRecord srec;
+        synchronized (this) {
+            srec = ActivityRecord.forTokenLocked(activityToken);
+        }
         if (srec == null) {
             return -1;
         }
@@ -16602,7 +16603,10 @@
     }
 
     public String getLaunchedFromPackage(IBinder activityToken) {
-        ActivityRecord srec = ActivityRecord.forToken(activityToken);
+        ActivityRecord srec;
+        synchronized (this) {
+            srec = ActivityRecord.forTokenLocked(activityToken);
+        }
         if (srec == null) {
             return null;
         }
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 09dc426..ca2721c 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -17,6 +17,7 @@
 package com.android.server.am;
 
 import static com.android.server.am.ActivityManagerDebugConfig.*;
+import static com.android.server.am.ActivityManagerService.DEBUG_SWITCH;
 import static com.android.server.am.ActivityManagerService.DEBUG_THUMBNAILS;
 import static com.android.server.am.TaskPersister.DEBUG_PERSISTER;
 import static com.android.server.am.TaskPersister.DEBUG_RESTORER;
@@ -319,44 +320,83 @@
     }
 
     static class Token extends IApplicationToken.Stub {
-        final WeakReference<ActivityRecord> weakActivity;
+        private final WeakReference<ActivityRecord> weakActivity;
+        private final ActivityManagerService mService;
 
-        Token(ActivityRecord activity) {
+        Token(ActivityRecord activity, ActivityManagerService service) {
             weakActivity = new WeakReference<>(activity);
+            mService = service;
         }
 
-        @Override public void windowsDrawn() {
-            ActivityRecord activity = weakActivity.get();
-            if (activity != null) {
-                activity.windowsDrawn();
+        @Override
+        public void windowsDrawn() {
+            synchronized (mService) {
+                ActivityRecord r = tokenToActivityRecordLocked(this);
+                if (r != null) {
+                    r.windowsDrawnLocked();
+                }
             }
         }
 
-        @Override public void windowsVisible() {
-            ActivityRecord activity = weakActivity.get();
-            if (activity != null) {
-                activity.windowsVisible();
+        @Override
+        public void windowsVisible() {
+            synchronized (mService) {
+                ActivityRecord r = tokenToActivityRecordLocked(this);
+                if (r != null) {
+                    r.windowsVisibleLocked();
+                }
             }
         }
 
-        @Override public void windowsGone() {
-            ActivityRecord activity = weakActivity.get();
-            if (activity != null) {
-                activity.windowsGone();
+        @Override
+        public void windowsGone() {
+            synchronized (mService) {
+                ActivityRecord r = tokenToActivityRecordLocked(this);
+                if (r != null) {
+                    if (DEBUG_SWITCH) Log.v(TAG, "windowsGone(): " + r);
+                    r.nowVisible = false;
+                    return;
+                }
             }
         }
 
-        @Override public boolean keyDispatchingTimedOut(String reason) {
-            ActivityRecord activity = weakActivity.get();
-            return activity != null && activity.keyDispatchingTimedOut(reason);
+        @Override
+        public boolean keyDispatchingTimedOut(String reason) {
+            ActivityRecord r;
+            ActivityRecord anrActivity;
+            ProcessRecord anrApp;
+            synchronized (mService) {
+                r = tokenToActivityRecordLocked(this);
+                if (r == null) {
+                    return false;
+                }
+                anrActivity = r.getWaitingHistoryRecordLocked();
+                anrApp = r != null ? r.app : null;
+            }
+            return mService.inputDispatchingTimedOut(anrApp, anrActivity, r, false, reason);
         }
 
-        @Override public long getKeyDispatchingTimeout() {
-            ActivityRecord activity = weakActivity.get();
-            if (activity != null) {
-                return activity.getKeyDispatchingTimeout();
+        @Override
+        public long getKeyDispatchingTimeout() {
+            synchronized (mService) {
+                ActivityRecord r = tokenToActivityRecordLocked(this);
+                if (r == null) {
+                    return 0;
+                }
+                r = r.getWaitingHistoryRecordLocked();
+                return ActivityManagerService.getInputDispatchingTimeoutLocked(r);
             }
-            return 0;
+        }
+
+        private static final ActivityRecord tokenToActivityRecordLocked(Token token) {
+            if (token == null) {
+                return null;
+            }
+            ActivityRecord r = token.weakActivity.get();
+            if (r == null || r.task == null || r.task.stack == null) {
+                return null;
+            }
+            return r;
         }
 
         @Override
@@ -371,9 +411,9 @@
         }
     }
 
-    static ActivityRecord forToken(IBinder token) {
+    static ActivityRecord forTokenLocked(IBinder token) {
         try {
-            return token != null ? ((Token)token).weakActivity.get() : null;
+            return Token.tokenToActivityRecordLocked((Token)token);
         } catch (ClassCastException e) {
             Slog.w(TAG, "Bad activity token: " + token, e);
             return null;
@@ -391,7 +431,7 @@
             boolean _componentSpecified, ActivityStackSupervisor supervisor,
             ActivityContainer container, Bundle options) {
         service = _service;
-        appToken = new Token(this);
+        appToken = new Token(this, service);
         info = aInfo;
         launchedFromUid = _launchedFromUid;
         launchedFromPackage = _launchedFromPackage;
@@ -528,13 +568,8 @@
     }
 
     void setTask(TaskRecord newTask, TaskRecord taskToAffiliateWith) {
-        if (task != null && task.removeActivity(this)) {
-            if (task != newTask) {
-                task.stack.removeTask(task, "setTask");
-            } else {
-                Slog.d(TAG, "!!! REMOVE THIS LOG !!! setTask: nearly removed stack=" +
-                        (newTask == null ? null : newTask.stack));
-            }
+        if (task != null && task.removeActivity(this) && task != newTask && task.stack != null) {
+            task.stack.removeTask(task, "setTask");
         }
         task = newTask;
         setTaskToAffiliateWith(taskToAffiliateWith);
@@ -580,6 +615,10 @@
         return inHistory;
     }
 
+    boolean isInStackLocked() {
+        return task != null && task.stack != null && task.stack.isInStackLocked(this) != null;
+    }
+
     boolean isHomeActivity() {
         return mActivityType == HOME_ACTIVITY_TYPE;
     }
@@ -599,9 +638,10 @@
                         (intent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) == 0);
     }
 
-    void makeFinishing() {
+    void makeFinishingLocked() {
         if (!finishing) {
-            if (this == task.stack.getVisibleBehindActivity()) {
+            if (task != null && task.stack != null
+                    && this == task.stack.getVisibleBehindActivity()) {
                 // A finishing activity should not remain as visible in the background
                 mStackSupervisor.requestVisibleBehindLocked(this, false);
             }
@@ -670,8 +710,9 @@
         // stack.
         final ReferrerIntent rintent = new ReferrerIntent(intent, referrer);
         boolean unsent = true;
-        if ((state == ActivityState.RESUMED || (service.isSleeping()
-                        && task.stack.topRunningActivityLocked(null) == this))
+        if ((state == ActivityState.RESUMED
+                || (service.isSleeping() && task.stack != null
+                    && task.stack.topRunningActivityLocked(null) == this))
                 && app != null && app.thread != null) {
             try {
                 ArrayList<ReferrerIntent> ar = new ArrayList<>(1);
@@ -842,19 +883,27 @@
     }
 
     boolean continueLaunchTickingLocked() {
-        if (launchTickTime != 0) {
-            final ActivityStack stack = task.stack;
-            Message msg = stack.mHandler.obtainMessage(ActivityStack.LAUNCH_TICK_MSG, this);
-            stack.mHandler.removeMessages(ActivityStack.LAUNCH_TICK_MSG);
-            stack.mHandler.sendMessageDelayed(msg, ActivityStack.LAUNCH_TICK);
-            return true;
+        if (launchTickTime == 0) {
+            return false;
         }
-        return false;
+
+        final ActivityStack stack = task.stack;
+        if (stack == null) {
+            return false;
+        }
+
+        Message msg = stack.mHandler.obtainMessage(ActivityStack.LAUNCH_TICK_MSG, this);
+        stack.mHandler.removeMessages(ActivityStack.LAUNCH_TICK_MSG);
+        stack.mHandler.sendMessageDelayed(msg, ActivityStack.LAUNCH_TICK);
+        return true;
     }
 
     void finishLaunchTickingLocked() {
         launchTickTime = 0;
-        task.stack.mHandler.removeMessages(ActivityStack.LAUNCH_TICK_MSG);
+        final ActivityStack stack = task.stack;
+        if (stack != null) {
+            stack.mHandler.removeMessages(ActivityStack.LAUNCH_TICK_MSG);
+        }
     }
 
     // IApplicationToken
@@ -885,8 +934,8 @@
         if (displayStartTime != 0) {
             reportLaunchTimeLocked(curTime);
         }
-        if (fullyDrawnStartTime != 0) {
-            final ActivityStack stack = task.stack;
+        final ActivityStack stack = task.stack;
+        if (fullyDrawnStartTime != 0 && stack != null) {
             final long thisTime = curTime - fullyDrawnStartTime;
             final long totalTime = stack.mFullyDrawnStartTime != 0
                     ? (curTime - stack.mFullyDrawnStartTime) : thisTime;
@@ -911,13 +960,16 @@
             if (totalTime > 0) {
                 //service.mUsageStatsService.noteFullyDrawnTime(realActivity, (int) totalTime);
             }
-            fullyDrawnStartTime = 0;
             stack.mFullyDrawnStartTime = 0;
         }
+        fullyDrawnStartTime = 0;
     }
 
     private void reportLaunchTimeLocked(final long curTime) {
         final ActivityStack stack = task.stack;
+        if (stack == null) {
+            return;
+        }
         final long thisTime = curTime - displayStartTime;
         final long totalTime = stack.mLaunchStartTime != 0
                 ? (curTime - stack.mLaunchStartTime) : thisTime;
@@ -947,60 +999,47 @@
         stack.mLaunchStartTime = 0;
     }
 
-    public void windowsDrawn() {
-        synchronized(service) {
-            if (displayStartTime != 0) {
-                reportLaunchTimeLocked(SystemClock.uptimeMillis());
-            }
-            mStackSupervisor.sendWaitingVisibleReportLocked(this);
-            startTime = 0;
-            finishLaunchTickingLocked();
-            if (task != null) {
-                task.hasBeenVisible = true;
-            }
+    void windowsDrawnLocked() {
+        if (displayStartTime != 0) {
+            reportLaunchTimeLocked(SystemClock.uptimeMillis());
+        }
+        mStackSupervisor.sendWaitingVisibleReportLocked(this);
+        startTime = 0;
+        finishLaunchTickingLocked();
+        if (task != null) {
+            task.hasBeenVisible = true;
         }
     }
 
-    public void windowsVisible() {
-        synchronized(service) {
-            mStackSupervisor.reportActivityVisibleLocked(this);
-            if (ActivityManagerService.DEBUG_SWITCH) Log.v(TAG, "windowsVisible(): " + this);
-            if (!nowVisible) {
-                nowVisible = true;
-                lastVisibleTime = SystemClock.uptimeMillis();
-                if (!idle) {
-                    // Instead of doing the full stop routine here, let's just
-                    // hide any activities we now can, and let them stop when
-                    // the normal idle happens.
-                    mStackSupervisor.processStoppingActivitiesLocked(false);
-                } else {
-                    // If this activity was already idle, then we now need to
-                    // make sure we perform the full stop of any activities
-                    // that are waiting to do so.  This is because we won't
-                    // do that while they are still waiting for this one to
-                    // become visible.
-                    final int N = mStackSupervisor.mWaitingVisibleActivities.size();
-                    if (N > 0) {
-                        for (int i=0; i<N; i++) {
-                            ActivityRecord r = mStackSupervisor.mWaitingVisibleActivities.get(i);
-                            if (ActivityManagerService.DEBUG_SWITCH) Log.v(TAG,
-                                    "Was waiting for visible: " + r);
-                        }
-                        mStackSupervisor.mWaitingVisibleActivities.clear();
-                        mStackSupervisor.scheduleIdleLocked();
+    void windowsVisibleLocked() {
+        mStackSupervisor.reportActivityVisibleLocked(this);
+        if (DEBUG_SWITCH) Log.v(TAG, "windowsVisibleLocked(): " + this);
+        if (!nowVisible) {
+            nowVisible = true;
+            lastVisibleTime = SystemClock.uptimeMillis();
+            if (!idle) {
+                // Instead of doing the full stop routine here, let's just hide any activities
+                // we now can, and let them stop when the normal idle happens.
+                mStackSupervisor.processStoppingActivitiesLocked(false);
+            } else {
+                // If this activity was already idle, then we now need to make sure we perform
+                // the full stop of any activities that are waiting to do so. This is because
+                // we won't do that while they are still waiting for this one to become visible.
+                final int size = mStackSupervisor.mWaitingVisibleActivities.size();
+                if (size > 0) {
+                    for (int i = 0; i < size; i++) {
+                        ActivityRecord r = mStackSupervisor.mWaitingVisibleActivities.get(i);
+                        if (DEBUG_SWITCH) Log.v(TAG, "Was waiting for visible: " + r);
                     }
+                    mStackSupervisor.mWaitingVisibleActivities.clear();
+                    mStackSupervisor.scheduleIdleLocked();
                 }
-                service.scheduleAppGcsLocked();
             }
+            service.scheduleAppGcsLocked();
         }
     }
 
-    public void windowsGone() {
-        if (ActivityManagerService.DEBUG_SWITCH) Log.v(TAG, "windowsGone(): " + this);
-        nowVisible = false;
-    }
-
-    private ActivityRecord getWaitingHistoryRecordLocked() {
+    ActivityRecord getWaitingHistoryRecordLocked() {
         // First find the real culprit...  if we are waiting
         // for another app to start, then we have paused dispatching
         // for this activity.
@@ -1021,24 +1060,6 @@
         return r;
     }
 
-    public boolean keyDispatchingTimedOut(String reason) {
-        ActivityRecord r;
-        ProcessRecord anrApp;
-        synchronized(service) {
-            r = getWaitingHistoryRecordLocked();
-            anrApp = r != null ? r.app : null;
-        }
-        return service.inputDispatchingTimedOut(anrApp, r, this, false, reason);
-    }
-
-    /** Returns the key dispatching timeout for this application token. */
-    public long getKeyDispatchingTimeout() {
-        synchronized(service) {
-            ActivityRecord r = getWaitingHistoryRecordLocked();
-            return ActivityManagerService.getInputDispatchingTimeoutLocked(r);
-        }
-    }
-
     /**
      * This method will return true if the activity is either visible, is becoming visible, is
      * currently pausing, or is resumed.
@@ -1066,14 +1087,14 @@
     }
 
     static void activityResumedLocked(IBinder token) {
-        final ActivityRecord r = ActivityRecord.forToken(token);
+        final ActivityRecord r = ActivityRecord.forTokenLocked(token);
         if (DEBUG_SAVED_STATE) Slog.i(TAG, "Resumed activity; dropping state of: " + r);
         r.icicle = null;
         r.haveState = false;
     }
 
     static int getTaskForActivityLocked(IBinder token, boolean onlyRoot) {
-        final ActivityRecord r = ActivityRecord.forToken(token);
+        final ActivityRecord r = ActivityRecord.forTokenLocked(token);
         if (r == null) {
             return INVALID_TASK_ID;
         }
@@ -1086,7 +1107,7 @@
     }
 
     static ActivityRecord isInStackLocked(IBinder token) {
-        final ActivityRecord r = ActivityRecord.forToken(token);
+        final ActivityRecord r = ActivityRecord.forTokenLocked(token);
         return (r != null) ? r.task.stack.isInStackLocked(r) : null;
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 7d7520a..8a7e3d3 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -461,7 +461,7 @@
     }
 
     ActivityRecord isInStackLocked(IBinder token) {
-        final ActivityRecord r = ActivityRecord.forToken(token);
+        final ActivityRecord r = ActivityRecord.forTokenLocked(token);
         return isInStackLocked(r);
     }
 
@@ -470,7 +470,8 @@
             return null;
         }
         final TaskRecord task = r.task;
-        if (task != null && task.mActivities.contains(r) && mTaskHistory.contains(task)) {
+        if (task != null && task.stack != null
+                && task.mActivities.contains(r) && mTaskHistory.contains(task)) {
             if (task.stack != this) Slog.w(TAG,
                     "Illegal state! task does not point to stack it is in.");
             return r;
@@ -2797,7 +2798,7 @@
             return false;
         }
 
-        r.makeFinishing();
+        r.makeFinishingLocked();
         final TaskRecord task = r.task;
         EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY,
                 r.userId, System.identityHashCode(r),
@@ -2900,7 +2901,7 @@
                 || prevState == ActivityState.INITIALIZING) {
             // If this activity is already stopped, we can just finish
             // it right now.
-            r.makeFinishing();
+            r.makeFinishingLocked();
             boolean activityRemoved = destroyActivityLocked(r, true, "finish-imm");
             if (activityRemoved) {
                 mStackSupervisor.resumeTopActivitiesLocked();
@@ -2976,9 +2977,8 @@
         return false;
     }
 
-    final boolean navigateUpToLocked(IBinder token, Intent destIntent, int resultCode,
+    final boolean navigateUpToLocked(ActivityRecord srec, Intent destIntent, int resultCode,
             Intent resultData) {
-        final ActivityRecord srec = ActivityRecord.forToken(token);
         final TaskRecord task = srec.task;
         final ArrayList<ActivityRecord> activities = task.mActivities;
         final int start = activities.indexOf(srec);
@@ -3125,7 +3125,7 @@
     private void removeActivityFromHistoryLocked(ActivityRecord r, String reason) {
         mStackSupervisor.removeChildActivityContainers(r);
         finishActivityResultsLocked(r, Activity.RESULT_CANCELED, null);
-        r.makeFinishing();
+        r.makeFinishingLocked();
         if (DEBUG_ADD_REMOVE) {
             RuntimeException here = new RuntimeException("here");
             here.fillInStackTrace();
@@ -3366,7 +3366,7 @@
     final void activityDestroyedLocked(IBinder token, String reason) {
         final long origId = Binder.clearCallingIdentity();
         try {
-            ActivityRecord r = ActivityRecord.forToken(token);
+            ActivityRecord r = ActivityRecord.forTokenLocked(token);
             if (r != null) {
                 mHandler.removeMessages(DESTROY_TIMEOUT_MSG, r);
             }
@@ -3947,7 +3947,7 @@
                 }
             }
         }
-        final ActivityRecord r = ActivityRecord.forToken(token);
+        final ActivityRecord r = ActivityRecord.forTokenLocked(token);
         if (r == null) {
             return false;
         }
@@ -4247,6 +4247,8 @@
                 mActivityContainer.onTaskListEmptyLocked();
             }
         }
+
+        task.stack = null;
     }
 
     TaskRecord createTaskRecord(int taskId, ActivityInfo info, Intent intent,
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 05297d4..cbbb11a8 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -394,6 +394,10 @@
      * Use {@link ActivityStack#isStackVisibleLocked} to determine if a specific
      * stack is visible or not. */
     boolean isFrontStack(ActivityStack stack) {
+        if (stack == null) {
+            return false;
+        }
+
         final ActivityRecord parent = stack.mActivityContainer.mParentActivity;
         if (parent != null) {
             stack = parent.task.stack;
@@ -471,6 +475,16 @@
     }
 
     TaskRecord anyTaskForIdLocked(int id) {
+        return anyTaskForIdLocked(id, true);
+    }
+
+    /**
+     * Returns a {@link TaskRecord} for the input id if available. Null otherwise.
+     * @param id Id of the task we would like returned.
+     * @param restoreFromRecents If the id was not in the active list, but was found in recents,
+     *                           restore the task from recents to the active list.
+     */
+    TaskRecord anyTaskForIdLocked(int id, boolean restoreFromRecents) {
         int numDisplays = mActivityDisplays.size();
         for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
             ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
@@ -491,6 +505,10 @@
             return null;
         }
 
+        if (!restoreFromRecents) {
+            return task;
+        }
+
         if (!restoreRecentTaskLocked(task)) {
             if (DEBUG_RECENTS) Slog.w(TAG, "Couldn't restore task id=" + id + " found in recents");
             return null;
@@ -525,7 +543,7 @@
             if (mCurTaskId <= 0) {
                 mCurTaskId = 1;
             }
-        } while (anyTaskForIdLocked(mCurTaskId) != null);
+        } while (anyTaskForIdLocked(mCurTaskId, false) != null);
         return mCurTaskId;
     }
 
@@ -1366,6 +1384,9 @@
                 return ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT;
             }
             resultRecord = sourceRecord.resultTo;
+            if (resultRecord != null && !resultRecord.isInStackLocked()) {
+                resultRecord = null;
+            }
             resultWho = sourceRecord.resultWho;
             requestCode = sourceRecord.requestCode;
             sourceRecord.resultTo = null;
@@ -1550,7 +1571,7 @@
 
             ActivityStack stack;
 
-            if (task != null) {
+            if (task != null && task.stack != null) {
                 stack = task.stack;
                 if (stack.isOnHomeDisplay()) {
                     if (mFocusedStack != stack) {
@@ -1658,7 +1679,8 @@
                 && !launchSingleTask && !launchSingleInstance
                 && (launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0;
 
-        if (r.resultTo != null && (launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
+        if (r.resultTo != null && (launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0
+                && r.resultTo.task.stack != null) {
             // For whatever reason this activity is being launched into a new
             // task...  yet the caller has requested a result back.  Well, that
             // is pretty messed up, so instead immediately send back a cancel
@@ -2060,7 +2082,7 @@
             }
 
         } else {
-            if (r.resultTo != null) {
+            if (r.resultTo != null && r.resultTo.task.stack != null) {
                 r.resultTo.task.stack.sendActivityResultLocked(-1, r.resultTo, r.resultWho,
                         r.requestCode, Activity.RESULT_CANCELED, null);
             }
@@ -2293,7 +2315,7 @@
         boolean booting = false;
         boolean activityRemoved = false;
 
-        ActivityRecord r = ActivityRecord.forToken(token);
+        ActivityRecord r = ActivityRecord.forTokenLocked(token);
         if (r != null) {
             if (DEBUG_IDLE) Slog.d(TAG, "activityIdleInternalLocked: Callers=" +
                     Debug.getCallers(4));
@@ -2341,13 +2363,13 @@
         // Atomically retrieve all of the other things to do.
         stops = processStoppingActivitiesLocked(true);
         NS = stops != null ? stops.size() : 0;
-        if ((NF=mFinishingActivities.size()) > 0) {
-            finishes = new ArrayList<ActivityRecord>(mFinishingActivities);
+        if ((NF = mFinishingActivities.size()) > 0) {
+            finishes = new ArrayList<>(mFinishingActivities);
             mFinishingActivities.clear();
         }
 
         if (mStartingUsers.size() > 0) {
-            startingUsers = new ArrayList<UserStartedState>(mStartingUsers);
+            startingUsers = new ArrayList<>(mStartingUsers);
             mStartingUsers.clear();
         }
 
@@ -2356,10 +2378,12 @@
         for (int i = 0; i < NS; i++) {
             r = stops.get(i);
             final ActivityStack stack = r.task.stack;
-            if (r.finishing) {
-                stack.finishCurrentActivityLocked(r, ActivityStack.FINISH_IMMEDIATELY, false);
-            } else {
-                stack.stopActivityLocked(r);
+            if (stack != null) {
+                if (r.finishing) {
+                    stack.finishCurrentActivityLocked(r, ActivityStack.FINISH_IMMEDIATELY, false);
+                } else {
+                    stack.stopActivityLocked(r);
+                }
             }
         }
 
@@ -2367,7 +2391,10 @@
         // waiting for the next one to start.
         for (int i = 0; i < NF; i++) {
             r = finishes.get(i);
-            activityRemoved |= r.task.stack.destroyActivityLocked(r, true, "finish-idle");
+            final ActivityStack stack = r.task.stack;
+            if (stack != null) {
+                activityRemoved |= stack.destroyActivityLocked(r, true, "finish-idle");
+            }
         }
 
         if (!booting) {
@@ -2536,6 +2563,11 @@
             // we'll just indicate that this task returns to the home task.
             task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
         }
+        if (task.stack == null) {
+            Slog.e(TAG, "findTaskToMoveToFrontLocked: can't move task="
+                    + task + " to front. Stack is null");
+            return;
+        }
         task.stack.moveTaskToFrontLocked(task, false /* noAnimation */, options, reason);
         if (DEBUG_STACK) Slog.d(TAG, "findTaskToMoveToFront: moved to front of stack="
                 + task.stack);
@@ -2717,7 +2749,7 @@
             final ArrayList<ActivityStack> homeDisplayStacks = mHomeStack.mStacks;
             for (int stackNdx = homeDisplayStacks.size() - 1; stackNdx >= 0; --stackNdx) {
                 final ActivityStack tmpStack = homeDisplayStacks.get(stackNdx);
-                if (!tmpStack.isHomeStack()) {
+                if (!tmpStack.isHomeStack() && tmpStack.mFullscreen) {
                     stack = tmpStack;
                     break;
                 }
@@ -3755,7 +3787,7 @@
                 } break;
                 case LAUNCH_TASK_BEHIND_COMPLETE: {
                     synchronized (mService) {
-                        ActivityRecord r = ActivityRecord.forToken((IBinder) msg.obj);
+                        ActivityRecord r = ActivityRecord.forTokenLocked((IBinder) msg.obj);
                         if (r != null) {
                             handleLaunchTaskBehindCompleteLocked(r);
                         }
@@ -3979,6 +4011,10 @@
         }
 
         void onTaskListEmptyLocked() {
+            mHandler.removeMessages(CONTAINER_TASK_LIST_EMPTY_TIMEOUT, this);
+            detachLocked();
+            deleteActivityContainer(this);
+            mHandler.obtainMessage(CONTAINER_CALLBACK_TASK_LIST_EMPTY, this).sendToTarget();
         }
 
         @Override
@@ -4067,13 +4103,6 @@
             return false;
         }
 
-        void onTaskListEmptyLocked() {
-            mHandler.removeMessages(CONTAINER_TASK_LIST_EMPTY_TIMEOUT, this);
-            detachLocked();
-            deleteActivityContainer(this);
-            mHandler.obtainMessage(CONTAINER_CALLBACK_TASK_LIST_EMPTY, this).sendToTarget();
-        }
-
         private void setSurfaceIfReadyLocked() {
             if (DEBUG_STACK) Slog.v(TAG, "setSurfaceIfReadyLocked: mDrawn=" + mDrawn +
                     " mContainerState=" + mContainerState + " mSurface=" + mSurface);
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 432abd2..7f2cae4 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -275,8 +275,10 @@
                         }
                         break;
                     case ActivityManager.INTENT_SENDER_ACTIVITY_RESULT:
-                        key.activity.task.stack.sendActivityResultLocked(-1, key.activity,
-                                key.who, key.requestCode, code, finalIntent);
+                        if (key.activity.task.stack != null) {
+                            key.activity.task.stack.sendActivityResultLocked(-1, key.activity,
+                                    key.who, key.requestCode, code, finalIntent);
+                        }
                         break;
                     case ActivityManager.INTENT_SENDER_BROADCAST:
                         try {
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 398f0b3..d8d361b 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -500,10 +500,12 @@
     }
 
     ActivityRecord topRunningActivityLocked(ActivityRecord notTop) {
-        for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
-            ActivityRecord r = mActivities.get(activityNdx);
-            if (!r.finishing && r != notTop && stack.okToShowLocked(r)) {
-                return r;
+        if (stack != null) {
+            for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
+                ActivityRecord r = mActivities.get(activityNdx);
+                if (!r.finishing && r != notTop && stack.okToShowLocked(r)) {
+                    return r;
+                }
             }
         }
         return null;
@@ -666,7 +668,7 @@
                     if (opts != null) {
                         ret.updateOptionsLocked(opts);
                     }
-                    if (stack.finishActivityLocked(
+                    if (stack != null && stack.finishActivityLocked(
                             r, Activity.RESULT_CANCELED, null, "clear-task-stack", false)) {
                         --activityNdx;
                         --numActivities;
@@ -679,8 +681,10 @@
                 if (ret.launchMode == ActivityInfo.LAUNCH_MULTIPLE
                         && (launchFlags & Intent.FLAG_ACTIVITY_SINGLE_TOP) == 0) {
                     if (!ret.finishing) {
-                        stack.finishActivityLocked(
-                                ret, Activity.RESULT_CANCELED, null, "clear-task-top", false);
+                        if (stack != null) {
+                            stack.finishActivityLocked(
+                                    ret, Activity.RESULT_CANCELED, null, "clear-task-top", false);
+                        }
                         return null;
                     }
                 }
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index ad38ab8..7f47678 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -69,6 +69,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.net.LegacyVpnInfo;
 import com.android.internal.net.VpnConfig;
+import com.android.internal.net.VpnInfo;
 import com.android.internal.net.VpnProfile;
 import com.android.server.net.BaseNetworkObserver;
 
@@ -808,6 +809,21 @@
         return mConfig.underlyingNetworks;
     }
 
+    /**
+     * This method should only be called by ConnectivityService. Because it doesn't
+     * have enough data to fill VpnInfo.primaryUnderlyingIface field.
+     */
+    public synchronized VpnInfo getVpnInfo() {
+        if (!isRunningLocked()) {
+            return null;
+        }
+
+        VpnInfo info = new VpnInfo();
+        info.ownerUid = mOwnerUID;
+        info.vpnIface = mInterface;
+        return info;
+    }
+
     public synchronized boolean appliesToUid(int uid) {
         if (!isRunningLocked()) {
             return false;
diff --git a/services/core/java/com/android/server/net/NetworkStatsRecorder.java b/services/core/java/com/android/server/net/NetworkStatsRecorder.java
index d5d7667..6490865 100644
--- a/services/core/java/com/android/server/net/NetworkStatsRecorder.java
+++ b/services/core/java/com/android/server/net/NetworkStatsRecorder.java
@@ -31,6 +31,7 @@
 import android.util.MathUtils;
 import android.util.Slog;
 
+import com.android.internal.net.VpnInfo;
 import com.android.internal.util.FileRotator;
 import com.android.internal.util.IndentingPrintWriter;
 import com.google.android.collect.Sets;
@@ -163,7 +164,8 @@
      * snapshot is considered bootstrap, and is not counted as delta.
      */
     public void recordSnapshotLocked(NetworkStats snapshot,
-            Map<String, NetworkIdentitySet> ifaceIdent, long currentTimeMillis) {
+            Map<String, NetworkIdentitySet> ifaceIdent, VpnInfo[] vpnArray,
+            long currentTimeMillis) {
         final HashSet<String> unknownIfaces = Sets.newHashSet();
 
         // skip recording when snapshot missing
@@ -182,6 +184,12 @@
         final long end = currentTimeMillis;
         final long start = end - delta.getElapsedRealtime();
 
+        if (vpnArray != null) {
+            for (VpnInfo info : vpnArray) {
+                delta.migrateTun(info.ownerUid, info.vpnIface, info.primaryUnderlyingIface);
+            }
+        }
+
         NetworkStats.Entry entry = null;
         for (int i = 0; i < delta.size(); i++) {
             entry = delta.getValues(i, entry);
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 856a076..0b596aa 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -111,6 +111,7 @@
 import android.util.TrustedTime;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.net.VpnInfo;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FileRotator;
 import com.android.internal.util.IndentingPrintWriter;
@@ -855,6 +856,20 @@
         return ident;
     }
 
+    private void recordSnapshotLocked(long currentTime) throws RemoteException {
+        // snapshot and record current counters; read UID stats first to
+        // avoid overcounting dev stats.
+        final NetworkStats uidSnapshot = getNetworkStatsUidDetail();
+        final NetworkStats xtSnapshot = mNetworkManager.getNetworkStatsSummaryXt();
+        final NetworkStats devSnapshot = mNetworkManager.getNetworkStatsSummaryDev();
+
+        VpnInfo[] vpnArray = mConnManager.getAllVpnInfo();
+        mDevRecorder.recordSnapshotLocked(devSnapshot, mActiveIfaces, null, currentTime);
+        mXtRecorder.recordSnapshotLocked(xtSnapshot, mActiveIfaces, null, currentTime);
+        mUidRecorder.recordSnapshotLocked(uidSnapshot, mActiveUidIfaces, vpnArray, currentTime);
+        mUidTagRecorder.recordSnapshotLocked(uidSnapshot, mActiveUidIfaces, vpnArray, currentTime);
+    }
+
     /**
      * Bootstrap initial stats snapshot, usually during {@link #systemReady()}
      * so we have baseline values without double-counting.
@@ -864,17 +879,7 @@
                 : System.currentTimeMillis();
 
         try {
-            // snapshot and record current counters; read UID stats first to
-            // avoid overcounting dev stats.
-            final NetworkStats uidSnapshot = getNetworkStatsUidDetail();
-            final NetworkStats xtSnapshot = mNetworkManager.getNetworkStatsSummaryXt();
-            final NetworkStats devSnapshot = mNetworkManager.getNetworkStatsSummaryDev();
-
-            mDevRecorder.recordSnapshotLocked(devSnapshot, mActiveIfaces, currentTime);
-            mXtRecorder.recordSnapshotLocked(xtSnapshot, mActiveIfaces, currentTime);
-            mUidRecorder.recordSnapshotLocked(uidSnapshot, mActiveUidIfaces, currentTime);
-            mUidTagRecorder.recordSnapshotLocked(uidSnapshot, mActiveUidIfaces, currentTime);
-
+            recordSnapshotLocked(currentTime);
         } catch (IllegalStateException e) {
             Slog.w(TAG, "problem reading network stats: " + e);
         } catch (RemoteException e) {
@@ -918,17 +923,7 @@
                 : System.currentTimeMillis();
 
         try {
-            // snapshot and record current counters; read UID stats first to
-            // avoid overcounting dev stats.
-            final NetworkStats uidSnapshot = getNetworkStatsUidDetail();
-            final NetworkStats xtSnapshot = mNetworkManager.getNetworkStatsSummaryXt();
-            final NetworkStats devSnapshot = mNetworkManager.getNetworkStatsSummaryDev();
-
-            mDevRecorder.recordSnapshotLocked(devSnapshot, mActiveIfaces, currentTime);
-            mXtRecorder.recordSnapshotLocked(xtSnapshot, mActiveIfaces, currentTime);
-            mUidRecorder.recordSnapshotLocked(uidSnapshot, mActiveUidIfaces, currentTime);
-            mUidTagRecorder.recordSnapshotLocked(uidSnapshot, mActiveUidIfaces, currentTime);
-
+            recordSnapshotLocked(currentTime);
         } catch (IllegalStateException e) {
             Log.wtf(TAG, "problem reading network stats", e);
             return;
diff --git a/services/core/java/com/android/server/wm/CircularDisplayMask.java b/services/core/java/com/android/server/wm/CircularDisplayMask.java
index 9fdfc47..7c2da2d 100644
--- a/services/core/java/com/android/server/wm/CircularDisplayMask.java
+++ b/services/core/java/com/android/server/wm/CircularDisplayMask.java
@@ -50,9 +50,10 @@
     private int mRotation;
     private boolean mVisible;
     private boolean mDimensionsUnequal = false;
+    private int mMaskThickness;
 
     public CircularDisplayMask(Display display, SurfaceSession session, int zOrder,
-            int screenOffset) {
+            int screenOffset, int maskThickness) {
         mScreenSize = new Point();
         display.getSize(mScreenSize);
         if (mScreenSize.x != mScreenSize.y) {
@@ -84,6 +85,7 @@
         mPaint.setAntiAlias(true);
         mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
         mScreenOffset = screenOffset;
+        mMaskThickness = maskThickness;
     }
 
     private void drawIfNeeded() {
@@ -121,8 +123,8 @@
         int circleRadius = mScreenSize.x / 2;
         c.drawColor(Color.BLACK);
 
-        // The radius is reduced by 1 to provide an anti aliasing effect on the display edges.
-        c.drawCircle(circleRadius, circleRadius, circleRadius - 1, mPaint);
+        // The radius is reduced by mMaskThickness to provide an anti aliasing effect on the display edges.
+        c.drawCircle(circleRadius, circleRadius, circleRadius - mMaskThickness, mPaint);
         mSurface.unlockCanvasAndPost(c);
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index a4f3f23..cfee5a5 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -5857,13 +5857,15 @@
                     if (mCircularDisplayMask == null) {
                         int screenOffset = mContext.getResources().getDimensionPixelSize(
                                 com.android.internal.R.dimen.circular_display_mask_offset);
+                        int maskThickness = mContext.getResources().getDimensionPixelSize(
+                                com.android.internal.R.dimen.circular_display_mask_thickness);
 
                         mCircularDisplayMask = new CircularDisplayMask(
                                 getDefaultDisplayContentLocked().getDisplay(),
                                 mFxSession,
                                 mPolicy.windowTypeToLayerLw(
                                         WindowManager.LayoutParams.TYPE_POINTER)
-                                        * TYPE_LAYER_MULTIPLIER + 10, screenOffset);
+                                        * TYPE_LAYER_MULTIPLIER + 10, screenOffset, maskThickness);
                     }
                     mCircularDisplayMask.setVisibility(true);
                 } else if (mCircularDisplayMask != null) {