Merge "Fix typo that caused excessive display config updates"
diff --git a/api/current.txt b/api/current.txt
index d11dd84..9f6d358b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -39452,6 +39452,7 @@
     method public android.text.StaticLayout.Builder setHyphenationFrequency(int);
     method public android.text.StaticLayout.Builder setIncludePad(boolean);
     method public android.text.StaticLayout.Builder setIndents(int[], int[]);
+    method public android.text.StaticLayout.Builder setJustify(boolean);
     method public android.text.StaticLayout.Builder setLineSpacing(float, float);
     method public android.text.StaticLayout.Builder setMaxLines(int);
     method public android.text.StaticLayout.Builder setText(java.lang.CharSequence);
@@ -49061,6 +49062,7 @@
     method public boolean getIncludeFontPadding();
     method public android.os.Bundle getInputExtras(boolean);
     method public int getInputType();
+    method public boolean getJustify();
     method public final android.text.method.KeyListener getKeyListener();
     method public final android.text.Layout getLayout();
     method public float getLetterSpacing();
@@ -49169,6 +49171,7 @@
     method public void setIncludeFontPadding(boolean);
     method public void setInputExtras(int) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
     method public void setInputType(int);
+    method public void setJustify(boolean);
     method public void setKeyListener(android.text.method.KeyListener);
     method public void setLetterSpacing(float);
     method public void setLineSpacing(float, float);
diff --git a/api/system-current.txt b/api/system-current.txt
index 3d7c60a..4c9cada 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -42670,6 +42670,7 @@
     method public android.text.StaticLayout.Builder setHyphenationFrequency(int);
     method public android.text.StaticLayout.Builder setIncludePad(boolean);
     method public android.text.StaticLayout.Builder setIndents(int[], int[]);
+    method public android.text.StaticLayout.Builder setJustify(boolean);
     method public android.text.StaticLayout.Builder setLineSpacing(float, float);
     method public android.text.StaticLayout.Builder setMaxLines(int);
     method public android.text.StaticLayout.Builder setText(java.lang.CharSequence);
@@ -52639,6 +52640,7 @@
     method public boolean getIncludeFontPadding();
     method public android.os.Bundle getInputExtras(boolean);
     method public int getInputType();
+    method public boolean getJustify();
     method public final android.text.method.KeyListener getKeyListener();
     method public final android.text.Layout getLayout();
     method public float getLetterSpacing();
@@ -52747,6 +52749,7 @@
     method public void setIncludeFontPadding(boolean);
     method public void setInputExtras(int) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
     method public void setInputType(int);
+    method public void setJustify(boolean);
     method public void setKeyListener(android.text.method.KeyListener);
     method public void setLetterSpacing(float);
     method public void setLineSpacing(float, float);
diff --git a/api/test-current.txt b/api/test-current.txt
index d2c1749..c1ec21d 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -39572,6 +39572,7 @@
     method public android.text.StaticLayout.Builder setHyphenationFrequency(int);
     method public android.text.StaticLayout.Builder setIncludePad(boolean);
     method public android.text.StaticLayout.Builder setIndents(int[], int[]);
+    method public android.text.StaticLayout.Builder setJustify(boolean);
     method public android.text.StaticLayout.Builder setLineSpacing(float, float);
     method public android.text.StaticLayout.Builder setMaxLines(int);
     method public android.text.StaticLayout.Builder setText(java.lang.CharSequence);
@@ -49363,6 +49364,7 @@
     method public boolean getIncludeFontPadding();
     method public android.os.Bundle getInputExtras(boolean);
     method public int getInputType();
+    method public boolean getJustify();
     method public final android.text.method.KeyListener getKeyListener();
     method public final android.text.Layout getLayout();
     method public float getLetterSpacing();
@@ -49471,6 +49473,7 @@
     method public void setIncludeFontPadding(boolean);
     method public void setInputExtras(int) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
     method public void setInputType(int);
+    method public void setJustify(boolean);
     method public void setKeyListener(android.text.method.KeyListener);
     method public void setLetterSpacing(float);
     method public void setLineSpacing(float, float);
diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl
index e454ae1..ef997c9 100644
--- a/core/java/android/app/ITaskStackListener.aidl
+++ b/core/java/android/app/ITaskStackListener.aidl
@@ -95,4 +95,11 @@
      * perform relevant animations before the window disappears.
      */
     void onTaskRemovalStarted(int taskId);
+
+    /**
+     * Called when the task has been put in a locked state because one or more of the
+     * activities inside it belong to a managed profile user, and that user has just
+     * been locked.
+     */
+    void onTaskProfileLocked(int taskId, int userId);
 }
diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java
index 0639552..ad5e69b 100644
--- a/core/java/android/app/TaskStackListener.java
+++ b/core/java/android/app/TaskStackListener.java
@@ -74,4 +74,8 @@
     public void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation)
             throws RemoteException {
     }
+
+    @Override
+    public void onTaskProfileLocked(int taskId, int userId) {
+    }
 }
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index 3189681..31e779f 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -1094,8 +1094,9 @@
      * <p>Only apps with a foreground activity or a foreground service can call it.  Otherwise
      * it'll throw {@link IllegalStateException}.
      *
-     * <p>When an app calls this API when a previous request is still waiting for a response,
-     * the previous request will be canceled.
+     * <p>It's up to the launcher how to handle previous pending requests when the same package
+     * calls this API multiple times in a row.  It may ignore the previous requests,
+     * for example.
      *
      * @param provider The {@link ComponentName} for the {@link
      *    android.content.BroadcastReceiver BroadcastReceiver} provider for your AppWidget.
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index b686938..98a5341 100644
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -243,7 +243,7 @@
         }
 
         as.mPfd = new ParcelFileDescriptor(fds[0]);
-        as.mSocket = new LocalSocket(fds[0]);
+        as.mSocket = LocalSocket.createConnectedLocalSocket(fds[0]);
         as.mSocketIS = as.mSocket.getInputStream();
         as.mSocketOS = as.mSocket.getOutputStream();
         as.mAddress = RemoteAddr;
@@ -367,7 +367,7 @@
                 if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed");
                 if (mPfd == null) throw new IOException("bt socket connect failed");
                 FileDescriptor fd = mPfd.getFileDescriptor();
-                mSocket = new LocalSocket(fd);
+                mSocket = LocalSocket.createConnectedLocalSocket(fd);
                 mSocketIS = mSocket.getInputStream();
                 mSocketOS = mSocket.getOutputStream();
             }
@@ -416,9 +416,9 @@
                 if(mSocketState != SocketState.INIT) return EBADFD;
                 if(mPfd == null) return -1;
                 FileDescriptor fd = mPfd.getFileDescriptor();
-                if (DBG) Log.d(TAG, "bindListen(), new LocalSocket ");
-                mSocket = new LocalSocket(fd);
-                if (DBG) Log.d(TAG, "bindListen(), new LocalSocket.getInputStream() ");
+                if (DBG) Log.d(TAG, "bindListen(), Create LocalSocket");
+                mSocket = LocalSocket.createConnectedLocalSocket(fd);
+                if (DBG) Log.d(TAG, "bindListen(), new LocalSocket.getInputStream()");
                 mSocketIS = mSocket.getInputStream();
                 mSocketOS = mSocket.getOutputStream();
             }
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 4b5b995..afd7578 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -1200,7 +1200,7 @@
 
         /**
          * Return {@code TRUE} if a request is valid -- i.e. {@link #accept(Bundle)} has not been
-         * called, and it has not been canceled.
+         * called yet.
          */
         public boolean isValid() {
             try {
diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java
index c8fb3d1..3853400 100644
--- a/core/java/android/content/pm/ShortcutManager.java
+++ b/core/java/android/content/pm/ShortcutManager.java
@@ -846,8 +846,9 @@
      * <p>Only apps with a foreground activity or a foreground service can call it.  Otherwise
      * it'll throw {@link IllegalStateException}.
      *
-     * <p>When an app calls this API when a previous request is still waiting for a response,
-     * the previous request will be canceled.
+     * <p>It's up to the launcher how to handle previous pending requests when the same package
+     * calls this API multiple times in a row.  It may ignore the previous requests,
+     * for example.
      *
      * @param shortcut New shortcut to pin.  If an app wants to pin an existing (either dynamic
      *     or manifest) shortcut, then it only needs to have an ID, and other fields don't have to
diff --git a/core/java/android/net/LocalServerSocket.java b/core/java/android/net/LocalServerSocket.java
index e1eaf00..3fcde330 100644
--- a/core/java/android/net/LocalServerSocket.java
+++ b/core/java/android/net/LocalServerSocket.java
@@ -89,7 +89,7 @@
 
         impl.accept(acceptedImpl);
 
-        return LocalSocket.createLocalSocketForAccept(acceptedImpl, LocalSocket.SOCKET_UNKNOWN);
+        return LocalSocket.createLocalSocketForAccept(acceptedImpl);
     }
 
     /**
diff --git a/core/java/android/net/LocalSocket.java b/core/java/android/net/LocalSocket.java
index d9ad74b..8afa1ed 100644
--- a/core/java/android/net/LocalSocket.java
+++ b/core/java/android/net/LocalSocket.java
@@ -31,6 +31,7 @@
 public class LocalSocket implements Closeable {
 
     private final LocalSocketImpl impl;
+    /** false if impl.create() needs to be called */
     private volatile boolean implCreated;
     private LocalSocketAddress localAddress;
     private boolean isBound;
@@ -61,19 +62,6 @@
      */
     public LocalSocket(int sockType) {
         this(new LocalSocketImpl(), sockType);
-        isBound = false;
-        isConnected = false;
-    }
-
-    /**
-     * Creates a AF_LOCAL/UNIX domain stream socket with FileDescriptor.
-     * @hide
-     */
-    public LocalSocket(FileDescriptor fd) throws IOException {
-        this(new LocalSocketImpl(fd), SOCKET_UNKNOWN);
-        isBound = true;
-        isConnected = true;
-        implCreated = true;
     }
 
     private LocalSocket(LocalSocketImpl impl, int sockType) {
@@ -84,9 +72,27 @@
     }
 
     /**
+     * Creates a LocalSocket instances using the FileDescriptor for an already-connected
+     * AF_LOCAL/UNIX domain stream socket. Note: the FileDescriptor must be closed by the caller:
+     * closing the LocalSocket will not close it.
+     *
+     * @hide - used by BluetoothSocket.
+     */
+    public static LocalSocket createConnectedLocalSocket(FileDescriptor fd) {
+        return createConnectedLocalSocket(new LocalSocketImpl(fd), SOCKET_UNKNOWN);
+    }
+
+    /**
      * for use with LocalServerSocket.accept()
      */
-    static LocalSocket createLocalSocketForAccept(LocalSocketImpl impl, int sockType) {
+    static LocalSocket createLocalSocketForAccept(LocalSocketImpl impl) {
+        return createConnectedLocalSocket(impl, SOCKET_UNKNOWN);
+    }
+
+    /**
+     * Creates a LocalSocket from an existing LocalSocketImpl that is already connected.
+     */
+    private static LocalSocket createConnectedLocalSocket(LocalSocketImpl impl, int sockType) {
         LocalSocket socket = new LocalSocket(impl, sockType);
         socket.isConnected = true;
         socket.isBound = true;
diff --git a/core/java/android/net/LocalSocketImpl.java b/core/java/android/net/LocalSocketImpl.java
index d8f7821..05c8afb 100644
--- a/core/java/android/net/LocalSocketImpl.java
+++ b/core/java/android/net/LocalSocketImpl.java
@@ -218,7 +218,7 @@
      *
      * @param fd non-null; bound file descriptor
      */
-    /*package*/ LocalSocketImpl(FileDescriptor fd) throws IOException
+    /*package*/ LocalSocketImpl(FileDescriptor fd)
     {
         this.fd = fd;
     }
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index 9ac8996..1e9deeb 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -85,7 +85,7 @@
         this(base, display, paint, width, align, TextDirectionHeuristics.FIRSTSTRONG_LTR,
                 spacingmult, spacingadd, includepad,
                 StaticLayout.BREAK_STRATEGY_SIMPLE, StaticLayout.HYPHENATION_FREQUENCY_NONE,
-                ellipsize, ellipsizedWidth);
+                false /* justify */, ellipsize, ellipsizedWidth);
     }
 
     /**
@@ -102,7 +102,8 @@
                          int width, Alignment align, TextDirectionHeuristic textDir,
                          float spacingmult, float spacingadd,
                          boolean includepad, int breakStrategy, int hyphenationFrequency,
-                         TextUtils.TruncateAt ellipsize, int ellipsizedWidth) {
+                         boolean justify, TextUtils.TruncateAt ellipsize,
+                         int ellipsizedWidth) {
         super((ellipsize == null)
                 ? display
                 : (display instanceof Spanned)
@@ -127,6 +128,7 @@
 
         mIncludePad = includepad;
         mBreakStrategy = breakStrategy;
+        mJustify = justify;
         mHyphenationFrequency = hyphenationFrequency;
 
         /*
@@ -300,7 +302,8 @@
                 .setEllipsizedWidth(mEllipsizedWidth)
                 .setEllipsize(mEllipsizeAt)
                 .setBreakStrategy(mBreakStrategy)
-                .setHyphenationFrequency(mHyphenationFrequency);
+                .setHyphenationFrequency(mHyphenationFrequency)
+                .setJustify(mJustify);
         reflowed.generate(b, false, true);
         int n = reflowed.getLineCount();
         // If the new layout has a blank line at the end, but it is not
@@ -808,6 +811,7 @@
     private TextUtils.TruncateAt mEllipsizeAt;
     private int mBreakStrategy;
     private int mHyphenationFrequency;
+    private boolean mJustify;
 
     private PackedIntVector mInts;
     private PackedObjectVector<Directions> mObjects;
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 3cb81b0..2fc12d3 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -218,6 +218,11 @@
         mTextDir = textDir;
     }
 
+    /** @hide */
+    protected void setJustify(boolean justify) {
+        mJustify = justify;
+    }
+
     /**
      * Replace constructor properties of this Layout with new ones.  Be careful.
      */
@@ -266,6 +271,99 @@
         drawText(canvas, firstLine, lastLine);
     }
 
+    private boolean isJustificationRequired(int lineNum) {
+        if (!mJustify) return false;
+        final int lineEnd = getLineEnd(lineNum);
+        return lineEnd < mText.length() && mText.charAt(lineEnd - 1) != '\n';
+    }
+
+    private float getJustifyWidth(int lineNum) {
+        Alignment paraAlign = mAlignment;
+        TabStops tabStops = null;
+        boolean tabStopsIsInitialized = false;
+
+        int left = 0;
+        int right = mWidth;
+
+        final int dir = getParagraphDirection(lineNum);
+
+        ParagraphStyle[] spans = NO_PARA_SPANS;
+        if (mSpannedText) {
+            Spanned sp = (Spanned) mText;
+            final int start = getLineStart(lineNum);
+
+            final boolean isFirstParaLine = (start == 0 || mText.charAt(start - 1) == '\n');
+
+            if (isFirstParaLine) {
+                final int spanEnd = sp.nextSpanTransition(start, mText.length(),
+                        ParagraphStyle.class);
+                spans = getParagraphSpans(sp, start, spanEnd, ParagraphStyle.class);
+
+                for (int n = spans.length - 1; n >= 0; n--) {
+                    if (spans[n] instanceof AlignmentSpan) {
+                        paraAlign = ((AlignmentSpan) spans[n]).getAlignment();
+                        break;
+                    }
+                }
+            }
+
+            final int length = spans.length;
+            boolean useFirstLineMargin = isFirstParaLine;
+            for (int n = 0; n < length; n++) {
+                if (spans[n] instanceof LeadingMarginSpan2) {
+                    int count = ((LeadingMarginSpan2) spans[n]).getLeadingMarginLineCount();
+                    int startLine = getLineForOffset(sp.getSpanStart(spans[n]));
+                    if (lineNum < startLine + count) {
+                        useFirstLineMargin = true;
+                        break;
+                    }
+                }
+            }
+            for (int n = 0; n < length; n++) {
+                if (spans[n] instanceof LeadingMarginSpan) {
+                    LeadingMarginSpan margin = (LeadingMarginSpan) spans[n];
+                    if (dir == DIR_RIGHT_TO_LEFT) {
+                        right -= margin.getLeadingMargin(useFirstLineMargin);
+                    } else {
+                        left += margin.getLeadingMargin(useFirstLineMargin);
+                    }
+                }
+            }
+        }
+
+        if (getLineContainsTab(lineNum)) {
+            tabStops = new TabStops(TAB_INCREMENT, spans);
+        }
+
+        final Alignment align;
+        if (paraAlign == Alignment.ALIGN_LEFT) {
+            align = (dir == DIR_LEFT_TO_RIGHT) ?  Alignment.ALIGN_NORMAL : Alignment.ALIGN_OPPOSITE;
+        } else if (paraAlign == Alignment.ALIGN_RIGHT) {
+            align = (dir == DIR_LEFT_TO_RIGHT) ?  Alignment.ALIGN_OPPOSITE : Alignment.ALIGN_NORMAL;
+        } else {
+            align = paraAlign;
+        }
+
+        final int indentWidth;
+        if (align == Alignment.ALIGN_NORMAL) {
+            if (dir == DIR_LEFT_TO_RIGHT) {
+                indentWidth = getIndentAdjust(lineNum, Alignment.ALIGN_LEFT);
+            } else {
+                indentWidth = -getIndentAdjust(lineNum, Alignment.ALIGN_RIGHT);
+            }
+        } else if (align == Alignment.ALIGN_OPPOSITE) {
+            if (dir == DIR_LEFT_TO_RIGHT) {
+                indentWidth = -getIndentAdjust(lineNum, Alignment.ALIGN_RIGHT);
+            } else {
+                indentWidth = getIndentAdjust(lineNum, Alignment.ALIGN_LEFT);
+            }
+        } else { // Alignment.ALIGN_CENTER
+            indentWidth = getIndentAdjust(lineNum, Alignment.ALIGN_CENTER);
+        }
+
+        return right - left - indentWidth;
+    }
+
     /**
      * @hide
      */
@@ -274,7 +372,7 @@
         int previousLineEnd = getLineStart(firstLine);
         ParagraphStyle[] spans = NO_PARA_SPANS;
         int spanEnd = 0;
-        TextPaint paint = mPaint;
+        final TextPaint paint = mPaint;
         CharSequence buf = mText;
 
         Alignment paraAlign = mAlignment;
@@ -288,6 +386,7 @@
         for (int lineNum = firstLine; lineNum <= lastLine; lineNum++) {
             int start = previousLineEnd;
             previousLineEnd = getLineStart(lineNum + 1);
+            final boolean justify = isJustificationRequired(lineNum);
             int end = getLineVisibleEnd(lineNum, start, previousLineEnd);
 
             int ltop = previousLineBottom;
@@ -386,34 +485,42 @@
             }
 
             int x;
+            final int indentWidth;
             if (align == Alignment.ALIGN_NORMAL) {
                 if (dir == DIR_LEFT_TO_RIGHT) {
-                    x = left + getIndentAdjust(lineNum, Alignment.ALIGN_LEFT);
+                    indentWidth = getIndentAdjust(lineNum, Alignment.ALIGN_LEFT);
+                    x = left + indentWidth;
                 } else {
-                    x = right + getIndentAdjust(lineNum, Alignment.ALIGN_RIGHT);
+                    indentWidth = -getIndentAdjust(lineNum, Alignment.ALIGN_RIGHT);
+                    x = right - indentWidth;
                 }
             } else {
                 int max = (int)getLineExtent(lineNum, tabStops, false);
                 if (align == Alignment.ALIGN_OPPOSITE) {
                     if (dir == DIR_LEFT_TO_RIGHT) {
-                        x = right - max + getIndentAdjust(lineNum, Alignment.ALIGN_RIGHT);
+                        indentWidth = -getIndentAdjust(lineNum, Alignment.ALIGN_RIGHT);
+                        x = right - max - indentWidth;
                     } else {
-                        x = left - max + getIndentAdjust(lineNum, Alignment.ALIGN_LEFT);
+                        indentWidth = getIndentAdjust(lineNum, Alignment.ALIGN_LEFT);
+                        x = left - max + indentWidth;
                     }
                 } else { // Alignment.ALIGN_CENTER
+                    indentWidth = getIndentAdjust(lineNum, Alignment.ALIGN_CENTER);
                     max = max & ~1;
-                    x = ((right + left - max) >> 1) +
-                            getIndentAdjust(lineNum, Alignment.ALIGN_CENTER);
+                    x = ((right + left - max) >> 1) + indentWidth;
                 }
             }
 
             paint.setHyphenEdit(getHyphen(lineNum));
             Directions directions = getLineDirections(lineNum);
-            if (directions == DIRS_ALL_LEFT_TO_RIGHT && !mSpannedText && !hasTab) {
+            if (directions == DIRS_ALL_LEFT_TO_RIGHT && !mSpannedText && !hasTab && !justify) {
                 // XXX: assumes there's nothing additional to be done
                 canvas.drawText(buf, start, end, x, lbaseline, paint);
             } else {
                 tl.set(paint, buf, start, end, dir, directions, hasTab, tabStops);
+                if (justify) {
+                    tl.justify(right - left - indentWidth);
+                }
                 tl.draw(canvas, x, ltop, lbaseline, lbottom);
             }
             paint.setHyphenEdit(0);
@@ -1094,6 +1201,9 @@
         TextLine tl = TextLine.obtain();
         mPaint.setHyphenEdit(getHyphen(line));
         tl.set(mPaint, mText, start, end, dir, directions, hasTabs, tabStops);
+        if (isJustificationRequired(line)) {
+            tl.justify(getJustifyWidth(line));
+        }
         float width = tl.metrics(null);
         mPaint.setHyphenEdit(0);
         TextLine.recycle(tl);
@@ -1118,6 +1228,9 @@
         TextLine tl = TextLine.obtain();
         mPaint.setHyphenEdit(getHyphen(line));
         tl.set(mPaint, mText, start, end, dir, directions, hasTabs, tabStops);
+        if (isJustificationRequired(line)) {
+            tl.justify(getJustifyWidth(line));
+        }
         float width = tl.metrics(null);
         mPaint.setHyphenEdit(0);
         TextLine.recycle(tl);
@@ -1303,10 +1416,7 @@
                 return end - 1;
             }
 
-            // Note: keep this in sync with Minikin LineBreaker::isLineEndSpace()
-            if (!(ch == ' ' || ch == '\t' || ch == 0x1680 ||
-                    (0x2000 <= ch && ch <= 0x200A && ch != 0x2007) ||
-                    ch == 0x205F || ch == 0x3000)) {
+            if (!TextLine.isLineEndSpace(ch)) {
                 break;
             }
 
@@ -2086,6 +2196,7 @@
     private boolean mSpannedText;
     private TextDirectionHeuristic mTextDir;
     private SpanSet<LineBackgroundSpan> mLineBackgroundSpans;
+    private boolean mJustify;
 
     public static final int DIR_LEFT_TO_RIGHT = 1;
     public static final int DIR_RIGHT_TO_LEFT = -1;
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 081be3a..cb5b073 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -94,6 +94,7 @@
             b.mMaxLines = Integer.MAX_VALUE;
             b.mBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE;
             b.mHyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE;
+            b.mJustify = false;
 
             b.mMeasuredText = MeasuredText.obtain();
             return b;
@@ -320,6 +321,17 @@
         }
 
         /**
+         * Enables or disables paragraph justification. The default value is disabled (false).
+         *
+         * @param justify true for enabling and false for disabling paragraph justification.
+         * @return this builder, useful for chaining.
+         */
+        public Builder setJustify(boolean justify) {
+            mJustify = justify;
+            return this;
+        }
+
+        /**
          * Measurement and break iteration is done in native code. The protocol for using
          * the native code is as follows.
          *
@@ -404,6 +416,7 @@
         int mHyphenationFrequency;
         int[] mLeftIndents;
         int[] mRightIndents;
+        boolean mJustify;
 
         Paint.FontMetricsInt mFontMetricsInt = new Paint.FontMetricsInt();
 
@@ -557,6 +570,7 @@
 
         mLeftIndents = b.mLeftIndents;
         mRightIndents = b.mRightIndents;
+        setJustify(b.mJustify);
 
         generate(b, b.mIncludePad, b.mIncludePad);
     }
@@ -676,7 +690,8 @@
 
             nSetupParagraph(b.mNativePtr, chs, paraEnd - paraStart,
                     firstWidth, firstWidthLineCount, restWidth,
-                    variableTabStops, TAB_INCREMENT, b.mBreakStrategy, b.mHyphenationFrequency);
+                    variableTabStops, TAB_INCREMENT, b.mBreakStrategy, b.mHyphenationFrequency,
+                    b.mJustify);
             if (mLeftIndents != null || mRightIndents != null) {
                 // TODO(raph) performance: it would be better to do this once per layout rather
                 // than once per paragraph, but that would require a change to the native
@@ -1284,7 +1299,8 @@
     // Set up paragraph text and settings; done as one big method to minimize jni crossings
     private static native void nSetupParagraph(long nativePtr, char[] text, int length,
             float firstWidth, int firstWidthLineCount, float restWidth,
-            int[] variableTabStops, int defaultTabStop, int breakStrategy, int hyphenationFrequency);
+            int[] variableTabStops, int defaultTabStop, int breakStrategy, int hyphenationFrequency,
+            boolean isJustified);
 
     private static native float nAddStyleRun(long nativePtr, long nativePaint,
             long nativeTypeface, int start, int end, boolean isRtl);
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index c411860..fcff9a2 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -54,6 +54,10 @@
     private char[] mChars;
     private boolean mCharsValid;
     private Spanned mSpanned;
+
+    // Additional width of whitespace for justification. This value is per whitespace, thus
+    // the line width will increase by mAddedWidth x (number of stretchable whitespaces).
+    private float mAddedWidth;
     private final TextPaint mWorkPaint = new TextPaint();
     private final SpanSet<MetricAffectingSpan> mMetricAffectingSpanSpanSet =
             new SpanSet<MetricAffectingSpan>(MetricAffectingSpan.class);
@@ -177,6 +181,25 @@
             }
         }
         mTabs = tabStops;
+        mAddedWidth = 0;
+    }
+
+    /**
+     * Justify the line to the given width.
+     */
+    void justify(float justifyWidth) {
+        int end = mLen;
+        while (end > 0 && isLineEndSpace(mText.charAt(mStart + end - 1))) {
+            end--;
+        }
+        final int spaces = countStretchableSpaces(0, end);
+        if (spaces == 0) {
+            // There are no stretchable spaces, so we can't help the justification by adding any
+            // width.
+            return;
+        }
+        final float width = Math.abs(measure(end, false, null));
+        mAddedWidth = (justifyWidth - width) / spaces;
     }
 
     /**
@@ -595,6 +618,7 @@
 
         TextPaint wp = mWorkPaint;
         wp.set(mPaint);
+        wp.setWordSpacing(mAddedWidth);
 
         int spanStart = runStart;
         int spanLimit;
@@ -695,6 +719,7 @@
             Canvas c, float x, int top, int y, int bottom,
             FontMetricsInt fmi, boolean needWidth, int offset) {
 
+        wp.setWordSpacing(mAddedWidth);
         // Get metrics first (even for empty strings or "0" width runs)
         if (fmi != null) {
             expandMetricsFromPaint(fmi, wp);
@@ -981,5 +1006,34 @@
         return TabStops.nextDefaultStop(h, TAB_INCREMENT);
     }
 
+    private boolean isStretchableWhitespace(int ch) {
+        // TODO: Support other stretchable whitespace. (Bug: 34013491)
+        return ch == 0x0020 || ch == 0x00A0;
+    }
+
+    private int nextStretchableSpace(int start, int end) {
+        for (int i = start; i < end; i++) {
+            final char c = mCharsValid ? mChars[i] : mText.charAt(i + mStart);
+            if (isStretchableWhitespace(c)) return i;
+        }
+        return end;
+    }
+
+    /* Return the number of spaces in the text line, for the purpose of justification */
+    private int countStretchableSpaces(int start, int end) {
+        int count = 0;
+        for (int i = start; i < end; i = nextStretchableSpace(i + 1, end)) {
+            count++;
+        }
+        return count;
+    }
+
+    // Note: keep this in sync with Minikin LineBreaker::isLineEndSpace()
+    public static boolean isLineEndSpace(char ch) {
+        return ch == ' ' || ch == '\t' || ch == 0x1680
+                || (0x2000 <= ch && ch <= 0x200A && ch != 0x2007)
+                || ch == 0x205F || ch == 0x3000;
+    }
+
     private static final int TAB_INCREMENT = 20;
 }
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 58bc9a7..bb952ab 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -29,6 +29,8 @@
 import android.os.SystemProperties;
 import android.provider.Settings;
 import android.text.style.AbsoluteSizeSpan;
+import android.text.style.AccessibilityClickableSpan;
+import android.text.style.AccessibilityURLSpan;
 import android.text.style.AlignmentSpan;
 import android.text.style.BackgroundColorSpan;
 import android.text.style.BulletSpan;
@@ -621,7 +623,11 @@
     /** @hide */
     public static final int TTS_SPAN = 24;
     /** @hide */
-    public static final int LAST_SPAN = TTS_SPAN;
+    public static final int ACCESSIBILITY_CLICKABLE_SPAN = 25;
+    /** @hide */
+    public static final int ACCESSIBILITY_URL_SPAN = 26;
+    /** @hide */
+    public static final int LAST_SPAN = ACCESSIBILITY_URL_SPAN;
 
     /**
      * Flatten a CharSequence and whatever styles can be copied across processes
@@ -803,6 +809,14 @@
                     readSpan(p, sp, new TtsSpan(p));
                     break;
 
+                case ACCESSIBILITY_CLICKABLE_SPAN:
+                    readSpan(p, sp, new AccessibilityClickableSpan(p));
+                    break;
+
+                case ACCESSIBILITY_URL_SPAN:
+                    readSpan(p, sp, new AccessibilityURLSpan(p));
+                    break;
+
                 default:
                     throw new RuntimeException("bogus span encoding " + kind);
                 }
diff --git a/core/java/android/text/style/AccessibilityClickableSpan.java b/core/java/android/text/style/AccessibilityClickableSpan.java
new file mode 100644
index 0000000..9c305ff
--- /dev/null
+++ b/core/java/android/text/style/AccessibilityClickableSpan.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2016 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.text.style;
+
+import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ARGUMENT_ACCESSIBLE_CLICKABLE_SPAN;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.ParcelableSpan;
+import android.text.Spanned;
+import android.text.TextUtils;
+import android.view.View;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import com.android.internal.R;
+
+import java.lang.ref.WeakReference;
+
+
+/**
+ * {@link ClickableSpan} cannot be parceled, but accessibility services need to be able to cause
+ * their callback handlers to be called. This class serves as a parcelable placeholder for the
+ * real spans.
+ *
+ * This span is also passed back to an app's process when an accessibility service tries to click
+ * it. It contains enough information to track down the original clickable span so it can be
+ * called.
+ *
+ * @hide
+ */
+public class AccessibilityClickableSpan extends ClickableSpan
+        implements ParcelableSpan {
+    // The id of the span this one replaces
+    private final int mOriginalClickableSpanId;
+
+    // Only retain a weak reference to the node to avoid referencing cycles that could create memory
+    // leaks.
+    private WeakReference<AccessibilityNodeInfo> mAccessibilityNodeInfoRef;
+
+
+    /**
+     * @param originalClickableSpanId The id of the span this one replaces
+     */
+    public AccessibilityClickableSpan(int originalClickableSpanId) {
+        mOriginalClickableSpanId = originalClickableSpanId;
+    }
+
+    public AccessibilityClickableSpan(Parcel p) {
+        mOriginalClickableSpanId = p.readInt();
+    }
+
+    @Override
+    public int getSpanTypeId() {
+        return getSpanTypeIdInternal();
+    }
+
+    @Override
+    public int getSpanTypeIdInternal() {
+        return TextUtils.ACCESSIBILITY_CLICKABLE_SPAN;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        writeToParcelInternal(dest, flags);
+    }
+
+    @Override
+    public void writeToParcelInternal(Parcel dest, int flags) {
+        dest.writeInt(mOriginalClickableSpanId);
+    }
+
+    /**
+     * Find the ClickableSpan that matches the one used to create this object.
+     *
+     * @param text The text that contains the original ClickableSpan.
+     * @return The ClickableSpan that matches this object, or {@code null} if no such object
+     * can be found.
+     */
+    public ClickableSpan findClickableSpan(CharSequence text) {
+        if (!(text instanceof Spanned)) {
+            return null;
+        }
+        Spanned sp = (Spanned) text;
+        ClickableSpan[] os = sp.getSpans(0, text.length(), ClickableSpan.class);
+        for (int i = 0; i < os.length; i++) {
+            if (os[i].getId() == mOriginalClickableSpanId) {
+                return os[i];
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Set the accessibilityNodeInfo that this placeholder belongs to. This node is not
+     * included in the parceling logic, and must be set to allow the onClick handler to function.
+     *
+     * @param accessibilityNodeInfo The info this span is part of
+     */
+    public void setAccessibilityNodeInfo(AccessibilityNodeInfo accessibilityNodeInfo) {
+        mAccessibilityNodeInfoRef = new WeakReference<>(accessibilityNodeInfo);
+    }
+
+    /**
+     * Perform the click from an accessibility service. Will not work unless
+     * setAccessibilityNodeInfo is called with a properly initialized node.
+     *
+     * @param unused This argument is required by the superclass but is unused. The real view will
+     * be determined by the AccessibilityNodeInfo.
+     */
+    @Override
+    public void onClick(View unused) {
+        if (mAccessibilityNodeInfoRef == null) {
+            return;
+        }
+        AccessibilityNodeInfo info = mAccessibilityNodeInfoRef.get();
+        if (info == null) {
+            return;
+        }
+        Bundle arguments = new Bundle();
+        arguments.putParcelable(ACTION_ARGUMENT_ACCESSIBLE_CLICKABLE_SPAN, this);
+
+        info.performAction(R.id.accessibilityActionClickOnClickableSpan, arguments);
+    }
+
+    public static final Parcelable.Creator<AccessibilityClickableSpan> CREATOR =
+            new Parcelable.Creator<AccessibilityClickableSpan>() {
+                @Override
+                public AccessibilityClickableSpan createFromParcel(Parcel parcel) {
+                    return new AccessibilityClickableSpan(parcel);
+                }
+
+                @Override
+                public AccessibilityClickableSpan[] newArray(int size) {
+                    return new AccessibilityClickableSpan[size];
+                }
+            };
+}
diff --git a/core/java/android/text/style/AccessibilityURLSpan.java b/core/java/android/text/style/AccessibilityURLSpan.java
new file mode 100644
index 0000000..0147009
--- /dev/null
+++ b/core/java/android/text/style/AccessibilityURLSpan.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2016 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.text.style;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.view.View;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+/**
+ * URLSpan's onClick method does not work from an accessibility service. This version of it does.
+ * It is used to replace URLSpans in {@link AccessibilityNodeInfo#setText(CharSequence)}
+ * @hide
+ */
+public class AccessibilityURLSpan extends URLSpan implements Parcelable {
+    final AccessibilityClickableSpan mAccessibilityClickableSpan;
+
+    /**
+     * @param spanToReplace The original span
+     */
+    public AccessibilityURLSpan(URLSpan spanToReplace) {
+        super(spanToReplace.getURL());
+        mAccessibilityClickableSpan =
+                new AccessibilityClickableSpan(spanToReplace.getId());
+    }
+
+    public AccessibilityURLSpan(Parcel p) {
+        super(p);
+        mAccessibilityClickableSpan = new AccessibilityClickableSpan(p);
+    }
+
+    @Override
+    public int getSpanTypeId() {
+        return getSpanTypeIdInternal();
+    }
+
+    @Override
+    public int getSpanTypeIdInternal() {
+        return TextUtils.ACCESSIBILITY_URL_SPAN;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        writeToParcelInternal(dest, flags);
+    }
+
+    @Override
+    public void writeToParcelInternal(Parcel dest, int flags) {
+        super.writeToParcelInternal(dest, flags);
+        mAccessibilityClickableSpan.writeToParcel(dest, flags);
+    }
+
+    @Override
+    public void onClick(View unused) {
+        mAccessibilityClickableSpan.onClick(unused);
+    }
+
+    /**
+     * Delegated to AccessibilityClickableSpan
+     * @param accessibilityNodeInfo
+     */
+    public void setAccessibilityNodeInfo(AccessibilityNodeInfo accessibilityNodeInfo) {
+        mAccessibilityClickableSpan.setAccessibilityNodeInfo(accessibilityNodeInfo);
+    }
+}
diff --git a/core/java/android/text/style/ClickableSpan.java b/core/java/android/text/style/ClickableSpan.java
index 989ef54..a183427 100644
--- a/core/java/android/text/style/ClickableSpan.java
+++ b/core/java/android/text/style/ClickableSpan.java
@@ -26,12 +26,15 @@
  * be called.
  */
 public abstract class ClickableSpan extends CharacterStyle implements UpdateAppearance {
+    private static int sIdCounter = 0;
+
+    private int mId = sIdCounter++;
 
     /**
      * Performs the click action associated with this span.
      */
     public abstract void onClick(View widget);
-   
+
     /**
      * Makes the text underlined and in the link color.
      */
@@ -40,4 +43,14 @@
         ds.setColor(ds.linkColor);
         ds.setUnderlineText(true);
     }
+
+    /**
+     * Get the unique ID for this span.
+     *
+     * @return The unique ID.
+     * @hide
+     */
+    public int getId() {
+        return mId;
+    }
 }
diff --git a/core/java/android/util/EventLog.java b/core/java/android/util/EventLog.java
index 99f6c2a9..79d16fb 100644
--- a/core/java/android/util/EventLog.java
+++ b/core/java/android/util/EventLog.java
@@ -59,7 +59,7 @@
         private Exception mLastWtf;
 
         // Layout of event log entry received from Android logger.
-        //  see system/core/include/log/logger.h
+        //  see system/core/include/log/log.h
         private static final int LENGTH_OFFSET = 0;
         private static final int HEADER_SIZE_OFFSET = 2;
         private static final int PROCESS_OFFSET = 4;
diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java
index c7b1d03..13ee48e 100644
--- a/core/java/android/view/AccessibilityInteractionController.java
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -16,6 +16,8 @@
 
 package android.view;
 
+import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ARGUMENT_ACCESSIBLE_CLICKABLE_SPAN;
+
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
@@ -24,8 +26,12 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.Parcelable;
 import android.os.Process;
 import android.os.RemoteException;
+import android.text.TextUtils;
+import android.text.style.AccessibilityClickableSpan;
+import android.text.style.ClickableSpan;
 import android.util.LongSparseArray;
 import android.view.View.AttachInfo;
 import android.view.accessibility.AccessibilityInteractionClient;
@@ -33,6 +39,7 @@
 import android.view.accessibility.AccessibilityNodeProvider;
 import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
 
+import com.android.internal.R;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.util.Predicate;
 
@@ -655,17 +662,23 @@
                 target = mViewRootImpl.mView;
             }
             if (target != null && isShown(target)) {
-                AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
-                if (provider != null) {
-                    if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
-                        succeeded = provider.performAction(virtualDescendantId, action,
-                                arguments);
-                    } else {
-                        succeeded = provider.performAction(AccessibilityNodeProvider.HOST_VIEW_ID,
-                                action, arguments);
+                if (action == R.id.accessibilityActionClickOnClickableSpan) {
+                    // Handle this hidden action separately
+                    succeeded = handleClickableSpanActionUiThread(
+                            target, virtualDescendantId, arguments);
+                } else {
+                    AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
+                    if (provider != null) {
+                        if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
+                            succeeded = provider.performAction(virtualDescendantId, action,
+                                    arguments);
+                        } else {
+                            succeeded = provider.performAction(
+                                    AccessibilityNodeProvider.HOST_VIEW_ID, action, arguments);
+                        }
+                    } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
+                        succeeded = target.performAccessibilityAction(action, arguments);
                     }
-                } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
-                    succeeded = target.performAccessibilityAction(action, arguments);
                 }
             }
         } finally {
@@ -816,6 +829,37 @@
         return (appScale != 1.0f || (spec != null && !spec.isNop()));
     }
 
+    private boolean handleClickableSpanActionUiThread(
+            View view, int virtualDescendantId, Bundle arguments) {
+        Parcelable span = arguments.getParcelable(ACTION_ARGUMENT_ACCESSIBLE_CLICKABLE_SPAN);
+        if (!(span instanceof AccessibilityClickableSpan)) {
+            return false;
+        }
+
+        // Find the original ClickableSpan if it's still on the screen
+        AccessibilityNodeInfo infoWithSpan = null;
+        AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
+        if (provider != null) {
+            int idForNode = (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID)
+                    ? AccessibilityNodeProvider.HOST_VIEW_ID : virtualDescendantId;
+            infoWithSpan = provider.createAccessibilityNodeInfo(idForNode);
+        } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
+            infoWithSpan = view.createAccessibilityNodeInfo();
+        }
+        if (infoWithSpan == null) {
+            return false;
+        }
+
+        // Click on the corresponding span
+        ClickableSpan clickableSpan = ((AccessibilityClickableSpan) span).findClickableSpan(
+                infoWithSpan.getOriginalText());
+        if (clickableSpan != null) {
+            clickableSpan.onClick(view);
+            return true;
+        }
+        return false;
+    }
+
     /**
      * This class encapsulates a prefetching strategy for the accessibility APIs for
      * querying window content. It is responsible to prefetch a batch of
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index caa09cc..dd85256 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -437,6 +437,15 @@
     }
 
     /**
+     * Holds the contents of a starting window. {@link #addSplashScreen} needs to wrap the
+     * contents of the starting window into an class implementing this interface, which then will be
+     * held by WM and passed into {@link #removeSplashScreen} when the starting window is no
+     * longer needed.
+     */
+    interface StartingSurface {
+    }
+
+    /**
      * Interface for calling back in to the window manager that is private
      * between it and the policy.
      */
@@ -738,17 +747,17 @@
      *        context to for resources.
      *
      * @return Optionally you can return the View that was used to create the
-     *         window, for easy removal in removeStartingWindow.
+     *         window, for easy removal in removeSplashScreen.
      *
-     * @see #removeStartingWindow
+     * @see #removeSplashScreen
      */
-    public View addStartingWindow(IBinder appToken, String packageName,
-            int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel,
-            int labelRes, int icon, int logo, int windowFlags, Configuration overrideConfig);
+    public StartingSurface addSplashScreen(IBinder appToken, String packageName, int theme,
+            CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon,
+            int logo, int windowFlags, Configuration overrideConfig);
 
     /**
      * Called when the first window of an application has been displayed, while
-     * {@link #addStartingWindow} has created a temporary initial window for
+     * {@link #addSplashScreen} has created a temporary initial window for
      * that application.  You should at this point remove the window from the
      * window manager.  This is called without the window manager locked so
      * that you can call back into it.
@@ -759,11 +768,11 @@
      * even if you previously returned one.
      *
      * @param appToken Token of the application that has started.
-     * @param window Window View that was returned by createStartingWindow.
+     * @param surface Surface that was returned by {@link #addSplashScreen}.
      *
-     * @see #addStartingWindow
+     * @see #addSplashScreen
      */
-    public void removeStartingWindow(IBinder appToken, View window);
+    public void removeSplashScreen(IBinder appToken, StartingSurface surface);
 
     /**
      * Prepare for a window being added to the window manager.  You can throw an
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 9127861..91468da 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -25,7 +25,13 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.InputType;
+import android.text.Spannable;
+import android.text.Spanned;
 import android.text.TextUtils;
+import android.text.style.AccessibilityClickableSpan;
+import android.text.style.AccessibilityURLSpan;
+import android.text.style.ClickableSpan;
+import android.text.style.URLSpan;
 import android.util.ArraySet;
 import android.util.LongArray;
 import android.util.Pools.SynchronizedPool;
@@ -464,6 +470,14 @@
     public static final String ACTION_ARGUMENT_PROGRESS_VALUE =
             "android.view.accessibility.action.ARGUMENT_PROGRESS_VALUE";
 
+    /**
+     * Argument to pass the {@link AccessibilityClickableSpan}.
+     * For use with R.id.accessibilityActionClickOnClickableSpan
+     * @hide
+     */
+    public static final String ACTION_ARGUMENT_ACCESSIBLE_CLICKABLE_SPAN =
+            "android.view.accessibility.action.ACTION_ARGUMENT_ACCESSIBLE_CLICKABLE_SPAN";
+
     // Focus types
 
     /**
@@ -628,6 +642,8 @@
 
     private CharSequence mPackageName;
     private CharSequence mClassName;
+    // Hidden, unparceled value used to hold the original value passed to setText
+    private CharSequence mOriginalText;
     private CharSequence mText;
     private CharSequence mError;
     private CharSequence mContentDescription;
@@ -2213,14 +2229,49 @@
 
     /**
      * Gets the text of this node.
+     * <p>
+     *   <strong>Note:</strong> If the text contains {@link ClickableSpan}s or {@link URLSpan}s,
+     *   these spans will have been replaced with ones whose {@link ClickableSpan#onClick(View)}
+     *   can be called from an {@link AccessibilityService}. When called from a service, the
+     *   {@link View} argument is ignored and the corresponding span will be found on the view that
+     *   this {@code AccessibilityNodeInfo} represents and called with that view as its argument.
+     *   <p>
+     *   This treatment of {@link ClickableSpan}s means that the text returned from this method may
+     *   different slightly one passed to {@link #setText(CharSequence)}, although they will be
+     *   equivalent according to {@link TextUtils#equals(CharSequence, CharSequence)}. The
+     *   {@link ClickableSpan#onClick(View)} of any spans, however, will generally not work outside
+     *   of an accessibility service.
+     * </p>
      *
      * @return The text.
      */
     public CharSequence getText() {
+        // Attach this node to any spans that need it
+        if (mText instanceof Spanned) {
+            Spanned spanned = (Spanned) mText;
+            AccessibilityClickableSpan[] clickableSpans =
+                    spanned.getSpans(0, mText.length(), AccessibilityClickableSpan.class);
+            for (int i = 0; i < clickableSpans.length; i++) {
+                clickableSpans[i].setAccessibilityNodeInfo(this);
+            }
+            AccessibilityURLSpan[] urlSpans =
+                    spanned.getSpans(0, mText.length(), AccessibilityURLSpan.class);
+            for (int i = 0; i < urlSpans.length; i++) {
+                urlSpans[i].setAccessibilityNodeInfo(this);
+            }
+        }
         return mText;
     }
 
     /**
+     * Get the text passed to setText before any changes to the spans.
+     * @hide
+     */
+    public CharSequence getOriginalText() {
+        return mOriginalText;
+    }
+
+    /**
      * Sets the text of this node.
      * <p>
      *   <strong>Note:</strong> Cannot be called from an
@@ -2234,6 +2285,34 @@
      */
     public void setText(CharSequence text) {
         enforceNotSealed();
+        mOriginalText = text;
+        // Replace any ClickableSpans in mText with placeholders
+        if (text instanceof Spanned) {
+            ClickableSpan[] spans =
+                    ((Spanned) text).getSpans(0, text.length(), ClickableSpan.class);
+            if (spans.length > 0) {
+                Spannable spannable = Spannable.Factory.getInstance().newSpannable(text);
+                for (int i = 0; i < spans.length; i++) {
+                    ClickableSpan span = spans[i];
+                    if ((span instanceof AccessibilityClickableSpan)
+                            || (span instanceof AccessibilityURLSpan)) {
+                        // We've already done enough
+                        break;
+                    }
+                    int spanToReplaceStart = spannable.getSpanStart(span);
+                    int spanToReplaceEnd = spannable.getSpanEnd(span);
+                    int spanToReplaceFlags = spannable.getSpanFlags(span);
+                    spannable.removeSpan(span);
+                    ClickableSpan replacementSpan = (span instanceof URLSpan)
+                            ? new AccessibilityURLSpan((URLSpan) span)
+                            : new AccessibilityClickableSpan(span.getId());
+                    spannable.setSpan(replacementSpan, spanToReplaceStart, spanToReplaceEnd,
+                            spanToReplaceFlags);
+                }
+                mText = spannable;
+                return;
+            }
+        }
         mText = (text == null) ? null : text.subSequence(0, text.length());
     }
 
diff --git a/core/java/android/view/animation/AnimationSet.java b/core/java/android/view/animation/AnimationSet.java
index 09d4dfc..767024e 100644
--- a/core/java/android/view/animation/AnimationSet.java
+++ b/core/java/android/view/animation/AnimationSet.java
@@ -237,7 +237,7 @@
                 mDuration = a.getStartOffset() + a.getDuration();
                 mLastEnd = mStartOffset + mDuration;
             } else {
-                mLastEnd = Math.max(mLastEnd, a.getStartOffset() + a.getDuration());
+                mLastEnd = Math.max(mLastEnd, mStartOffset + a.getStartOffset() + a.getDuration());
                 mDuration = mLastEnd - mStartOffset;
             }
         }
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 9335811..1ddf53d 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -596,6 +596,7 @@
 
     private int mBreakStrategy;
     private int mHyphenationFrequency;
+    private boolean mJustify;
 
     private int mMaximum = Integer.MAX_VALUE;
     private int mMaxMode = LINES;
@@ -769,6 +770,7 @@
         String fontFeatureSettings = null;
         mBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE;
         mHyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE;
+        mJustify = false;
 
         final Resources.Theme theme = context.getTheme();
 
@@ -3298,6 +3300,29 @@
     }
 
     /**
+     * Enables or disables full justification. The default value is false.
+     *
+     * @see #getJustify()
+     */
+    public void setJustify(boolean justify) {
+        mJustify = justify;
+        if (mLayout != null) {
+            nullLayouts();
+            requestLayout();
+            invalidate();
+        }
+    }
+
+    /**
+     * @return true if currently paragraph justification is enabled.
+     *
+     * @see #setJustify(boolean)
+     */
+    public boolean getJustify() {
+        return mJustify;
+    }
+
+    /**
      * Sets font feature settings. The format is the same as the CSS
      * font-feature-settings attribute:
      * <a href="https://www.w3.org/TR/css-fonts-3/#font-feature-settings-prop">
@@ -7170,6 +7195,7 @@
                         .setIncludePad(mIncludePad)
                         .setBreakStrategy(mBreakStrategy)
                         .setHyphenationFrequency(mHyphenationFrequency)
+                        .setJustify(mJustify)
                         .setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE);
                 if (shouldEllipsize) {
                     builder.setEllipsize(mEllipsize)
@@ -7211,7 +7237,7 @@
         if (mText instanceof Spannable) {
             result = new DynamicLayout(mText, mTransformed, mTextPaint, wantWidth,
                     alignment, mTextDir, mSpacingMult, mSpacingAdd, mIncludePad,
-                    mBreakStrategy, mHyphenationFrequency,
+                    mBreakStrategy, mHyphenationFrequency, mJustify,
                     getKeyListener() == null ? effectiveEllipsize : null, ellipsisWidth);
         } else {
             if (boring == UNKNOWN_BORING) {
@@ -7261,6 +7287,7 @@
                     .setIncludePad(mIncludePad)
                     .setBreakStrategy(mBreakStrategy)
                     .setHyphenationFrequency(mHyphenationFrequency)
+                    .setJustify(mJustify)
                     .setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE);
             if (shouldEllipsize) {
                 builder.setEllipsize(effectiveEllipsize)
diff --git a/core/java/com/android/internal/logging/LogBuilder.java b/core/java/com/android/internal/logging/LogBuilder.java
index 634d061..8e2e114 100644
--- a/core/java/com/android/internal/logging/LogBuilder.java
+++ b/core/java/com/android/internal/logging/LogBuilder.java
@@ -17,8 +17,9 @@
 
     private SparseArray<Object> entries = new SparseArray();
 
-    public LogBuilder() {}
-
+    public LogBuilder(int mainCategory) {
+        setCategory(mainCategory);
+    }
 
     public LogBuilder setView(View view) {
         entries.put(MetricsEvent.RESERVED_FOR_LOGBUILDER_VIEW, view.getId());
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 293de3d..e1e0a21 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -85,6 +85,9 @@
      * file descriptor numbers that are to be closed by the child
      * (and replaced by /dev/null) after forking.  An integer value
      * of -1 in any entry in the array means "ignore this one".
+     * @param fdsToIgnore null-ok an array of ints, either null or holding
+     * one or more POSIX file descriptor numbers that are to be ignored
+     * in the file descriptor table check.
      * @param instructionSet null-ok the instruction set to use.
      * @param appDataDir null-ok the data directory of the app.
      *
@@ -93,11 +96,11 @@
      */
     public static int forkAndSpecialize(int uid, int gid, int[] gids, int debugFlags,
           int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
-          String instructionSet, String appDataDir) {
+          int[] fdsToIgnore, String instructionSet, String appDataDir) {
         VM_HOOKS.preFork();
         int pid = nativeForkAndSpecialize(
                   uid, gid, gids, debugFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
-                  instructionSet, appDataDir);
+                  fdsToIgnore, instructionSet, appDataDir);
         // Enable tracing as soon as possible for the child process.
         if (pid == 0) {
             Trace.setTracingEnabled(true);
@@ -111,7 +114,7 @@
 
     native private static int nativeForkAndSpecialize(int uid, int gid, int[] gids,int debugFlags,
           int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
-          String instructionSet, String appDataDir);
+          int[] fdsToIgnore, String instructionSet, String appDataDir);
 
     /**
      * Special method to start the system server process. In addition to the
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 345350c..83e3cff 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -196,11 +196,14 @@
                 rlimits = parsedArgs.rlimits.toArray(intArray2d);
             }
 
+            int[] fdsToIgnore = null;
+
             if (parsedArgs.invokeWith != null) {
                 FileDescriptor[] pipeFds = Os.pipe2(O_CLOEXEC);
                 childPipeFd = pipeFds[1];
                 serverPipeFd = pipeFds[0];
                 Os.fcntlInt(childPipeFd, F_SETFD, 0);
+                fdsToIgnore = new int[] { childPipeFd.getInt$(), serverPipeFd.getInt$() };
             }
 
             /**
@@ -233,7 +236,7 @@
 
             pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
                     parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
-                    parsedArgs.niceName, fdsToClose, parsedArgs.instructionSet,
+                    parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.instructionSet,
                     parsedArgs.appDataDir);
         } catch (ErrnoException ex) {
             logAndPrintError(newStderr, "Exception creating pipe", ex);
diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java
index 4f7b106..0185e30 100644
--- a/core/java/com/android/internal/view/IInputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java
@@ -562,8 +562,6 @@
             }
             case DO_COMMIT_CONTENT: {
                 final int flags = msg.arg1;
-                final boolean grantUriPermission =
-                        (flags & InputConnection.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0;
                 SomeArgs args = (SomeArgs) msg.obj;
                 try {
                     InputConnection ic = getInputConnection();
@@ -579,22 +577,8 @@
                         args.callback.setCommitContentResult(false, args.seq);
                         return;
                     }
-                    if (grantUriPermission) {
-                        try {
-                            inputContentInfo.requestPermission();
-                        } catch (Exception e) {
-                            Log.e(TAG, "InputConnectionInfo.requestPermission() failed", e);
-                            args.callback.setCommitContentResult(false, args.seq);
-                            return;
-                        }
-                    }
                     final boolean result =
                             ic.commitContent(inputContentInfo, flags, (Bundle) args.arg2);
-                    // If this request is not handled, then there is no reason to keep the URI
-                    // permission.
-                    if (grantUriPermission && !result) {
-                        inputContentInfo.releasePermission();
-                    }
                     args.callback.setCommitContentResult(result, args.seq);
                 } catch (RemoteException e) {
                     Log.w(TAG, "Got RemoteException calling commitContent", e);
diff --git a/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp b/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp
index aa6348e..dcb7874 100644
--- a/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp
+++ b/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp
@@ -33,7 +33,9 @@
 #include "HarfBuzzNGFaceSkia.h"
 
 #include <stdlib.h>
-#include <cutils/log.h>
+
+#include <log/log.h>
+
 #include <SkPaint.h>
 #include <SkPath.h>
 #include <SkPoint.h>
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index aa4570f..14d7e81 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -16,32 +16,32 @@
 
 #define LOG_TAG "android.os.Debug"
 
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
+#include <malloc.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <unistd.h>
-#include <time.h>
 #include <sys/time.h>
-#include <errno.h>
-#include <assert.h>
-#include <ctype.h>
-#include <malloc.h>
+#include <time.h>
+#include <unistd.h>
 
 #include <iomanip>
 #include <string>
 
-#include "jni.h"
+#include <android-base/stringprintf.h>
+#include <cutils/debugger.h>
+#include <log/log.h>
+#include <utils/misc.h>
+#include <utils/String8.h>
 
-#include "android-base/stringprintf.h"
-#include "cutils/debugger.h"
-#include "cutils/log.h"
 #include "JNIHelp.h"
-#include "memtrack/memtrack.h"
-#include "memunreachable/memunreachable.h"
-#include "utils/misc.h"
-#include "utils/String8.h"
+#include "jni.h"
+#include <memtrack/memtrack.h>
+#include <memunreachable/memunreachable.h>
 
 namespace android
 {
diff --git a/core/jni/android_os_Trace.cpp b/core/jni/android_os_Trace.cpp
index ea893f0..de91f70 100644
--- a/core/jni/android_os_Trace.cpp
+++ b/core/jni/android_os_Trace.cpp
@@ -19,15 +19,14 @@
 
 #include <inttypes.h>
 
+#include <cutils/trace.h>
+#include <utils/String8.h>
+#include <log/log.h>
+
 #include <JNIHelp.h>
 #include <ScopedUtfChars.h>
 #include <ScopedStringChars.h>
 
-#include <utils/String8.h>
-
-#include <cutils/trace.h>
-#include <cutils/log.h>
-
 namespace android {
 
 static void sanitizeString(String8& utf8Chars) {
diff --git a/core/jni/android_text_StaticLayout.cpp b/core/jni/android_text_StaticLayout.cpp
index 02fa872..c05ef26 100644
--- a/core/jni/android_text_StaticLayout.cpp
+++ b/core/jni/android_text_StaticLayout.cpp
@@ -54,7 +54,8 @@
 // hyphenFrequency)
 static void nSetupParagraph(JNIEnv* env, jclass, jlong nativePtr, jcharArray text, jint length,
         jfloat firstWidth, jint firstWidthLineLimit, jfloat restWidth,
-        jintArray variableTabStops, jint defaultTabStop, jint strategy, jint hyphenFrequency) {
+        jintArray variableTabStops, jint defaultTabStop, jint strategy, jint hyphenFrequency,
+        jboolean isJustified) {
     minikin::LineBreaker* b = reinterpret_cast<minikin::LineBreaker*>(nativePtr);
     b->resize(length);
     env->GetCharArrayRegion(text, 0, length, b->buffer());
@@ -68,6 +69,7 @@
     }
     b->setStrategy(static_cast<minikin::BreakStrategy>(strategy));
     b->setHyphenationFrequency(static_cast<minikin::HyphenationFrequency>(hyphenFrequency));
+    b->setJustified(isJustified);
 }
 
 static void recycleCopy(JNIEnv* env, jobject recycle, jintArray recycleBreaks,
@@ -190,7 +192,7 @@
     {"nFinishBuilder", "(J)V", (void*) nFinishBuilder},
     {"nLoadHyphenator", "(Ljava/nio/ByteBuffer;I)J", (void*) nLoadHyphenator},
     {"nSetLocale", "(JLjava/lang/String;J)V", (void*) nSetLocale},
-    {"nSetupParagraph", "(J[CIFIF[IIII)V", (void*) nSetupParagraph},
+    {"nSetupParagraph", "(J[CIFIF[IIIIZ)V", (void*) nSetupParagraph},
     {"nSetIndents", "(J[I)V", (void*) nSetIndents},
     {"nAddStyleRun", "(JJJIIZ)F", (void*) nAddStyleRun},
     {"nAddMeasuredRun", "(JII[F)V", (void*) nAddMeasuredRun},
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 5559d48..abcd1e7 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -29,19 +29,19 @@
 #include <sys/types.h>
 #include <unistd.h>
 
-#include <utils/Atomic.h>
 #include <binder/IInterface.h>
+#include <binder/IServiceManager.h>
 #include <binder/IPCThreadState.h>
-#include <utils/Log.h>
-#include <utils/SystemClock.h>
-#include <utils/List.h>
-#include <utils/KeyedVector.h>
-#include <log/logger.h>
 #include <binder/Parcel.h>
 #include <binder/ProcessState.h>
-#include <binder/IServiceManager.h>
-#include <utils/threads.h>
+#include <log/log.h>
+#include <utils/Atomic.h>
+#include <utils/KeyedVector.h>
+#include <utils/List.h>
+#include <utils/Log.h>
 #include <utils/String8.h>
+#include <utils/SystemClock.h>
+#include <utils/threads.h>
 
 #include <ScopedUtfChars.h>
 #include <ScopedLocalRef.h>
diff --git a/core/jni/android_util_Log.cpp b/core/jni/android_util_Log.cpp
index 7719e31..20dfe78 100644
--- a/core/jni/android_util_Log.cpp
+++ b/core/jni/android_util_Log.cpp
@@ -21,7 +21,7 @@
 #include <android-base/macros.h>
 #include <assert.h>
 #include <cutils/properties.h>
-#include <log/logger.h>               // For LOGGER_ENTRY_MAX_PAYLOAD.
+#include <log/log.h>               // For LOGGER_ENTRY_MAX_PAYLOAD.
 #include <utils/Log.h>
 #include <utils/String8.h>
 
diff --git a/core/jni/android_util_jar_StrictJarFile.cpp b/core/jni/android_util_jar_StrictJarFile.cpp
index 2e31c8b..4f1f926 100644
--- a/core/jni/android_util_jar_StrictJarFile.cpp
+++ b/core/jni/android_util_jar_StrictJarFile.cpp
@@ -20,13 +20,14 @@
 #include <memory>
 #include <string>
 
+#include <log/log.h>
+
 #include "JNIHelp.h"
 #include "JniConstants.h"
 #include "ScopedLocalRef.h"
 #include "ScopedUtfChars.h"
 #include "jni.h"
 #include "ziparchive/zip_archive.h"
-#include "cutils/log.h"
 
 namespace android {
 
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index cc7b958..070a2d9 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -43,6 +43,7 @@
 #include <sys/wait.h>
 #include <unistd.h>
 
+#include "android-base/logging.h"
 #include <cutils/fs.h>
 #include <cutils/multiuser.h>
 #include <cutils/sched_policy.h>
@@ -440,6 +441,22 @@
 // The list of open zygote file descriptors.
 static FileDescriptorTable* gOpenFdTable = NULL;
 
+static void FillFileDescriptorVector(JNIEnv* env,
+                                     jintArray java_fds,
+                                     std::vector<int>* fds) {
+  CHECK(fds != nullptr);
+  if (java_fds != nullptr) {
+    ScopedIntArrayRO ar(env, java_fds);
+    if (ar.get() == nullptr) {
+      RuntimeAbort(env, __LINE__, "Bad fd array");
+    }
+    fds->reserve(ar.size());
+    for (size_t i = 0; i < ar.size(); ++i) {
+      fds->push_back(ar[i]);
+    }
+  }
+}
+
 // Utility routine to fork zygote and specialize the child process.
 static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGids,
                                      jint debug_flags, jobjectArray javaRlimits,
@@ -447,6 +464,7 @@
                                      jint mount_external,
                                      jstring java_se_info, jstring java_se_name,
                                      bool is_system_server, jintArray fdsToClose,
+                                     jintArray fdsToIgnore,
                                      jstring instructionSet, jstring dataDir) {
   SetSigChldHandler();
 
@@ -471,12 +489,14 @@
   // If this is the first fork for this zygote, create the open FD table.
   // If it isn't, we just need to check whether the list of open files has
   // changed (and it shouldn't in the normal case).
+  std::vector<int> fds_to_ignore;
+  FillFileDescriptorVector(env, fdsToIgnore, &fds_to_ignore);
   if (gOpenFdTable == NULL) {
-    gOpenFdTable = FileDescriptorTable::Create();
+    gOpenFdTable = FileDescriptorTable::Create(fds_to_ignore);
     if (gOpenFdTable == NULL) {
       RuntimeAbort(env, __LINE__, "Unable to construct file descriptor table.");
     }
-  } else if (!gOpenFdTable->Restat()) {
+  } else if (!gOpenFdTable->Restat(fds_to_ignore)) {
     RuntimeAbort(env, __LINE__, "Unable to restat file descriptor table.");
   }
 
@@ -646,7 +666,9 @@
         JNIEnv* env, jclass, jint uid, jint gid, jintArray gids,
         jint debug_flags, jobjectArray rlimits,
         jint mount_external, jstring se_info, jstring se_name,
-        jintArray fdsToClose, jstring instructionSet, jstring appDataDir) {
+        jintArray fdsToClose,
+        jintArray fdsToIgnore,
+        jstring instructionSet, jstring appDataDir) {
     jlong capabilities = 0;
 
     // Grant CAP_WAKE_ALARM to the Bluetooth process.
@@ -681,7 +703,7 @@
 
     return ForkAndSpecializeCommon(env, uid, gid, gids, debug_flags,
             rlimits, capabilities, capabilities, mount_external, se_info,
-            se_name, false, fdsToClose, instructionSet, appDataDir);
+            se_name, false, fdsToClose, fdsToIgnore, instructionSet, appDataDir);
 }
 
 static jint com_android_internal_os_Zygote_nativeForkSystemServer(
@@ -692,7 +714,7 @@
                                       debug_flags, rlimits,
                                       permittedCapabilities, effectiveCapabilities,
                                       MOUNT_EXTERNAL_DEFAULT, NULL, NULL, true, NULL,
-                                      NULL, NULL);
+                                      NULL, NULL, NULL);
   if (pid > 0) {
       // The zygote process checks whether the child process has died or not.
       ALOGI("System server process %d has been created", pid);
@@ -759,7 +781,7 @@
 
 static const JNINativeMethod gMethods[] = {
     { "nativeForkAndSpecialize",
-      "(II[II[[IILjava/lang/String;Ljava/lang/String;[ILjava/lang/String;Ljava/lang/String;)I",
+      "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[ILjava/lang/String;Ljava/lang/String;)I",
       (void *) com_android_internal_os_Zygote_nativeForkAndSpecialize },
     { "nativeForkSystemServer", "(II[II[[IJJ)I",
       (void *) com_android_internal_os_Zygote_nativeForkSystemServer },
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index 969d336f3..59a536b 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -381,7 +381,7 @@
 }
 
 // static
-FileDescriptorTable* FileDescriptorTable::Create() {
+FileDescriptorTable* FileDescriptorTable::Create(const std::vector<int>& fds_to_ignore) {
   DIR* d = opendir(kFdPath);
   if (d == NULL) {
     ALOGE("Unable to open directory %s: %s", kFdPath, strerror(errno));
@@ -396,6 +396,10 @@
     if (fd == -1) {
       continue;
     }
+    if (std::find(fds_to_ignore.begin(), fds_to_ignore.end(), fd) != fds_to_ignore.end()) {
+      ALOGI("Ignoring open file descriptor %d", fd);
+      continue;
+    }
 
     FileDescriptorInfo* info = FileDescriptorInfo::CreateFromFd(fd);
     if (info == NULL) {
@@ -414,7 +418,7 @@
   return new FileDescriptorTable(open_fd_map);
 }
 
-bool FileDescriptorTable::Restat() {
+bool FileDescriptorTable::Restat(const std::vector<int>& fds_to_ignore) {
   std::set<int> open_fds;
 
   // First get the list of open descriptors.
@@ -431,6 +435,10 @@
     if (fd == -1) {
       continue;
     }
+    if (std::find(fds_to_ignore.begin(), fds_to_ignore.end(), fd) != fds_to_ignore.end()) {
+      ALOGI("Ignoring open file descriptor %d", fd);
+      continue;
+    }
 
     open_fds.insert(fd);
   }
diff --git a/core/jni/fd_utils.h b/core/jni/fd_utils.h
index 9e3afd9..03298c3 100644
--- a/core/jni/fd_utils.h
+++ b/core/jni/fd_utils.h
@@ -122,9 +122,9 @@
   // Creates a new FileDescriptorTable. This function scans
   // /proc/self/fd for the list of open file descriptors and collects
   // information about them. Returns NULL if an error occurs.
-  static FileDescriptorTable* Create();
+  static FileDescriptorTable* Create(const std::vector<int>& fds_to_ignore);
 
-  bool Restat();
+  bool Restat(const std::vector<int>& fds_to_ignore);
 
   // Reopens all file descriptors that are contained in the table. Returns true
   // if all descriptors were successfully re-opened or detached, and false if an
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index 5c165e6..5547706 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -129,4 +129,6 @@
   <item type="id" name="remote_input_tag" />
 
   <item type="id" name="cross_task_transition" />
+
+  <item type="id" name="accessibilityActionClickOnClickableSpan" />
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 8639004..f44b039 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -213,6 +213,7 @@
   <java-symbol type="id" name="selection_start_handle" />
   <java-symbol type="id" name="selection_end_handle" />
   <java-symbol type="id" name="insertion_handle" />
+  <java-symbol type="id" name="accessibilityActionClickOnClickableSpan" />
 
   <java-symbol type="attr" name="actionModeShareDrawable" />
   <java-symbol type="attr" name="alertDialogCenterButtons" />
diff --git a/core/tests/coretests/src/com/android/internal/logging/LogBuilderTest.java b/core/tests/coretests/src/com/android/internal/logging/LogBuilderTest.java
index e7d23a8..e3f754c 100644
--- a/core/tests/coretests/src/com/android/internal/logging/LogBuilderTest.java
+++ b/core/tests/coretests/src/com/android/internal/logging/LogBuilderTest.java
@@ -5,7 +5,7 @@
 public class LogBuilderTest extends TestCase {
 
     public void testSerialize() {
-        LogBuilder builder = new LogBuilder();
+        LogBuilder builder = new LogBuilder(0);
         builder.addTaggedData(1, "one");
         builder.addTaggedData(2, "two");
         Object[] out = builder.serialize();
@@ -16,7 +16,7 @@
     }
 
     public void testInvalidInputThrows() {
-        LogBuilder builder = new LogBuilder();
+        LogBuilder builder = new LogBuilder(0);
         boolean threw = false;
         try {
             builder.addTaggedData(0, new Object());
@@ -28,7 +28,7 @@
     }
 
     public void testValidInputTypes() {
-        LogBuilder builder = new LogBuilder();
+        LogBuilder builder = new LogBuilder(0);
         builder.addTaggedData(1, "onetwothree");
         builder.addTaggedData(2, 123);
         builder.addTaggedData(3, 123L);
diff --git a/libs/androidfw/BackupData.cpp b/libs/androidfw/BackupData.cpp
index ba4a4ff..76a430e 100644
--- a/libs/androidfw/BackupData.cpp
+++ b/libs/androidfw/BackupData.cpp
@@ -16,14 +16,13 @@
 
 #define LOG_TAG "backup_data"
 
-#include <androidfw/BackupHelpers.h>
-#include <utils/ByteOrder.h>
-
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
 
-#include <cutils/log.h>
+#include <androidfw/BackupHelpers.h>
+#include <log/log.h>
+#include <utils/ByteOrder.h>
 
 namespace android {
 
diff --git a/libs/androidfw/BackupHelpers.cpp b/libs/androidfw/BackupHelpers.cpp
index 78e9d91..8bfe2b6 100644
--- a/libs/androidfw/BackupHelpers.cpp
+++ b/libs/androidfw/BackupHelpers.cpp
@@ -18,23 +18,22 @@
 
 #include <androidfw/BackupHelpers.h>
 
-#include <utils/KeyedVector.h>
-#include <utils/ByteOrder.h>
-#include <utils/String8.h>
-
 #include <errno.h>
-#include <sys/types.h>
-#include <sys/uio.h>
-#include <sys/stat.h>
-#include <sys/time.h>  // for utimes
+#include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/time.h>  // for utimes
+#include <sys/uio.h>
 #include <unistd.h>
 #include <utime.h>
-#include <fcntl.h>
 #include <zlib.h>
 
-#include <cutils/log.h>
+#include <log/log.h>
+#include <utils/ByteOrder.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
 
 namespace android {
 
diff --git a/libs/androidfw/include/androidfw/CursorWindow.h b/libs/androidfw/include/androidfw/CursorWindow.h
index 8a2979a..f543565 100644
--- a/libs/androidfw/include/androidfw/CursorWindow.h
+++ b/libs/androidfw/include/androidfw/CursorWindow.h
@@ -17,11 +17,11 @@
 #ifndef _ANDROID__DATABASE_WINDOW_H
 #define _ANDROID__DATABASE_WINDOW_H
 
-#include <cutils/log.h>
 #include <stddef.h>
 #include <stdint.h>
 
 #include <binder/Parcel.h>
+#include <log/log.h>
 #include <utils/String8.h>
 
 #if LOG_NDEBUG
diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp
index 6d5833b..2b4fe17 100644
--- a/libs/hwui/DamageAccumulator.cpp
+++ b/libs/hwui/DamageAccumulator.cpp
@@ -16,7 +16,7 @@
 
 #include "DamageAccumulator.h"
 
-#include <cutils/log.h>
+#include <log/log.h>
 
 #include "RenderNode.h"
 #include "utils/MathUtils.h"
diff --git a/libs/hwui/DeviceInfo.cpp b/libs/hwui/DeviceInfo.cpp
index 700642e..d180ba5 100644
--- a/libs/hwui/DeviceInfo.cpp
+++ b/libs/hwui/DeviceInfo.cpp
@@ -13,16 +13,18 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 #include <DeviceInfo.h>
 
 #include "Extensions.h"
 
-#include <GLES2/gl2.h>
-#include <log/log.h>
-
 #include <thread>
 #include <mutex>
 
+#include <log/log.h>
+
+#include <GLES2/gl2.h>
+
 namespace android {
 namespace uirenderer {
 
diff --git a/libs/hwui/GpuMemoryTracker.h b/libs/hwui/GpuMemoryTracker.h
index bfb1bf1..352f3d7 100644
--- a/libs/hwui/GpuMemoryTracker.h
+++ b/libs/hwui/GpuMemoryTracker.h
@@ -15,10 +15,11 @@
  */
 #pragma once
 
-#include <cutils/log.h>
 #include <pthread.h>
 #include <ostream>
 
+#include <log/log.h>
+
 namespace android {
 namespace uirenderer {
 
diff --git a/libs/hwui/Interpolator.cpp b/libs/hwui/Interpolator.cpp
index f94a22d..d740c03 100644
--- a/libs/hwui/Interpolator.cpp
+++ b/libs/hwui/Interpolator.cpp
@@ -16,10 +16,11 @@
 
 #include "Interpolator.h"
 
-#include "utils/MathUtils.h"
-
 #include <algorithm>
-#include <cutils/log.h>
+
+#include <log/log.h>
+
+#include "utils/MathUtils.h"
 
 namespace android {
 namespace uirenderer {
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index ed6b211..0a9bf54 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -15,19 +15,21 @@
  */
 #include "JankTracker.h"
 
-#include "Properties.h"
-#include "utils/TimeUtils.h"
-
-#include <algorithm>
-#include <cutils/ashmem.h>
-#include <cutils/log.h>
-#include <cstdio>
 #include <errno.h>
 #include <inttypes.h>
-#include <limits>
+
+#include <algorithm>
 #include <cmath>
+#include <cstdio>
+#include <limits>
 #include <sys/mman.h>
 
+#include <cutils/ashmem.h>
+#include <log/log.h>
+
+#include "Properties.h"
+#include "utils/TimeUtils.h"
+
 namespace android {
 namespace uirenderer {
 
diff --git a/libs/hwui/PixelBuffer.h b/libs/hwui/PixelBuffer.h
index 9536bc8..77d5e41 100644
--- a/libs/hwui/PixelBuffer.h
+++ b/libs/hwui/PixelBuffer.h
@@ -18,7 +18,8 @@
 #define ANDROID_HWUI_PIXEL_BUFFER_H
 
 #include <GLES3/gl3.h>
-#include <cutils/log.h>
+
+#include <log/log.h>
 
 namespace android {
 namespace uirenderer {
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 848161e..a766381 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -13,17 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 #include "Properties.h"
-
 #include "Debug.h"
 
-#include <cutils/compiler.h>
-#include <cutils/log.h>
-#include <cutils/properties.h>
-
 #include <algorithm>
 #include <cstdlib>
 
+#include <log/log.h>
+#include <cutils/compiler.h>
+#include <cutils/properties.h>
+
 namespace android {
 namespace uirenderer {
 
diff --git a/libs/hwui/SkiaCanvasProxy.cpp b/libs/hwui/SkiaCanvasProxy.cpp
index c5156cf..f32612d 100644
--- a/libs/hwui/SkiaCanvasProxy.cpp
+++ b/libs/hwui/SkiaCanvasProxy.cpp
@@ -16,9 +16,11 @@
 
 #include "SkiaCanvasProxy.h"
 
-#include "hwui/Bitmap.h"
+#include <memory>
 
-#include <cutils/log.h>
+#include <log/log.h>
+
+#include "hwui/Bitmap.h"
 #include <SkLatticeIter.h>
 #include <SkPatchUtils.h>
 #include <SkPaint.h>
@@ -30,8 +32,6 @@
 #include <SkSurface.h>
 #include <SkTextBlobRunIterator.h>
 
-#include <memory>
-
 namespace android {
 namespace uirenderer {
 
diff --git a/libs/hwui/hwui/MinikinSkia.cpp b/libs/hwui/hwui/MinikinSkia.cpp
index f172473..956f66e 100644
--- a/libs/hwui/hwui/MinikinSkia.cpp
+++ b/libs/hwui/hwui/MinikinSkia.cpp
@@ -16,9 +16,10 @@
 
 #include "MinikinSkia.h"
 
+#include <log/log.h>
+
 #include <SkPaint.h>
 #include <SkTypeface.h>
-#include <cutils/log.h>
 
 namespace android {
 
diff --git a/libs/hwui/hwui/MinikinUtils.cpp b/libs/hwui/hwui/MinikinUtils.cpp
index 8dd165c..713e509 100644
--- a/libs/hwui/hwui/MinikinUtils.cpp
+++ b/libs/hwui/hwui/MinikinUtils.cpp
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 #include "MinikinUtils.h"
 
+#include <string>
+
+#include <log/log.h>
+
 #include "Paint.h"
 #include "SkPathMeasure.h"
 #include "Typeface.h"
 
-#include <cutils/log.h>
-#include <string>
-
 namespace android {
 
 minikin::FontStyle MinikinUtils::prepareMinikinPaint(minikin::MinikinPaint* minikinPaint,
diff --git a/libs/hwui/utils/GLUtils.h b/libs/hwui/utils/GLUtils.h
index 94818b2..c127478 100644
--- a/libs/hwui/utils/GLUtils.h
+++ b/libs/hwui/utils/GLUtils.h
@@ -18,7 +18,7 @@
 
 #include "Debug.h"
 
-#include <cutils/log.h>
+#include <log/log.h>
 
 namespace android {
 namespace uirenderer {
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 0b22ad5..7c60467 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -15,7 +15,6 @@
  */
 
 #define LOG_TAG "PointerController"
-
 //#define LOG_NDEBUG 0
 
 // Log debug messages about pointer updates
@@ -23,8 +22,9 @@
 
 #include "PointerController.h"
 
-#include <cutils/log.h>
+#include <log/log.h>
 
+// ToDo: Fix code to be warning free
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wunused-parameter"
 #include <SkBitmap.h>
diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp
index 18ebd47..4991f04 100644
--- a/libs/input/SpriteController.cpp
+++ b/libs/input/SpriteController.cpp
@@ -15,15 +15,15 @@
  */
 
 #define LOG_TAG "Sprites"
-
 //#define LOG_NDEBUG 0
 
 #include "SpriteController.h"
 
-#include <cutils/log.h>
+#include <log/log.h>
 #include <utils/String8.h>
 #include <gui/Surface.h>
 
+// ToDo: Fix code to be warning free
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wunused-parameter"
 #include <SkBitmap.h>
diff --git a/media/mca/filterpacks/native/base/geometry.cpp b/media/mca/filterpacks/native/base/geometry.cpp
index 7812d502..44b13e4 100644
--- a/media/mca/filterpacks/native/base/geometry.cpp
+++ b/media/mca/filterpacks/native/base/geometry.cpp
@@ -13,10 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#define LOG_TAG "geometry"
 
-#include <cutils/log.h>
 #include <cmath>
 
+#include <log/log.h>
+
 #include "geometry.h"
 
 namespace android {
diff --git a/media/mca/filterpacks/native/base/time_util.cpp b/media/mca/filterpacks/native/base/time_util.cpp
index 1a78a95..7d383df 100644
--- a/media/mca/filterpacks/native/base/time_util.cpp
+++ b/media/mca/filterpacks/native/base/time_util.cpp
@@ -13,14 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#define LOG_TAG "time_util"
 
 #include "time_util.h"
 #include "utilities.h"
 
-#include <cutils/log.h>
 #include <sys/time.h>
 #include <map>
 
+#include <log/log.h>
+
 namespace android {
 namespace filterfw {
 
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 5574753..62f6136 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -483,6 +483,21 @@
             android:exported="true"
             android:enabled="@bool/config_enableKeyguardService" />
 
+        <activity android:name=".keyguard.WorkLockActivity"
+                  android:label="@string/accessibility_desc_work_lock"
+                  android:permission="android.permission.MANAGE_USERS"
+                  android:exported="false"
+                  android:launchMode="singleTop"
+                  android:excludeFromRecents="true"
+                  android:stateNotNeeded="true"
+                  android:resumeWhilePausing="true"
+                  android:theme="@android:style/Theme.Black.NoTitleBar">
+            <intent-filter>
+                <action android:name="android.app.action.CONFIRM_DEVICE_CREDENTIAL_WITH_USER" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
         <activity android:name=".Somnambulator"
             android:label="@string/start_dreams"
             android:icon="@mipmap/ic_launcher_dreams"
diff --git a/packages/SystemUI/res/layout/qs_customize_panel_content.xml b/packages/SystemUI/res/layout/qs_customize_panel_content.xml
index ca0248e..04d0e65 100644
--- a/packages/SystemUI/res/layout/qs_customize_panel_content.xml
+++ b/packages/SystemUI/res/layout/qs_customize_panel_content.xml
@@ -27,7 +27,7 @@
 
     <android.support.v7.widget.RecyclerView
         android:id="@android:id/list"
-        android:layout_width="@dimen/notification_panel_width"
+        android:layout_width="match_parent"
         android:layout_height="0dp"
         android:layout_weight="1"
         android:scrollIndicators="top"
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 34a0397..05963ac 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -460,6 +460,8 @@
     <string name="accessibility_desc_settings">Settings</string>
     <!-- Content description for the recent apps panel (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_desc_recent_apps">Overview.</string>
+    <!-- Content description for the graphic shown instead of an activity window while the activity is locked (not shown on the screen). [CHAR LIMIT=NONE] -->
+    <string name="accessibility_desc_work_lock">Work lock screen</string>
     <!-- Content description for the close button in the zen mode panel introduction message. [CHAR LIMIT=NONE] -->
     <string name="accessibility_desc_close">Close</string>
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 8e5db97..ce89aab 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -326,6 +326,12 @@
      */
     private boolean mPendingLock;
 
+    /**
+     * Controller for showing individual "work challenge" lock screen windows inside managed profile
+     * tasks when the current user has been unlocked but the profile is still locked.
+     */
+    private WorkLockActivityController mWorkLockController;
+
     private boolean mLockLater;
 
     private boolean mWakeAndUnlocking;
@@ -708,6 +714,8 @@
 
         mHideAnimation = AnimationUtils.loadAnimation(mContext,
                 com.android.internal.R.anim.lock_screen_behind_enter);
+
+        mWorkLockController = new WorkLockActivityController(mContext);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java
new file mode 100644
index 0000000..12cf6fa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.keyguard;
+
+import static android.app.ActivityManager.TaskDescription;
+
+import android.annotation.ColorInt;
+import android.annotation.UserIdInt;
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.KeyguardManager;
+import android.app.PendingIntent;
+import android.app.admin.DevicePolicyManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+import android.view.View;
+
+/**
+ * Bouncer between work activities and the activity used to confirm credentials before unlocking
+ * a managed profile.
+ * <p>
+ * Shows a solid color when started, based on the organization color of the user it is supposed to
+ * be blocking. Once focused, it switches to a screen to confirm credentials and auto-dismisses if
+ * credentials are accepted.
+ */
+public class WorkLockActivity extends Activity {
+    private static final String TAG = "WorkLockActivity";
+
+    /**
+     * ID of the locked user that this activity blocks access to.
+     */
+    @UserIdInt
+    private int mUserId;
+
+    /**
+     * {@see KeyguardManager}
+     */
+    private KeyguardManager mKgm;
+
+    /**
+     * {@see DevicePolicyManager}
+     */
+    private DevicePolicyManager mDpm;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mUserId = getIntent().getIntExtra(Intent.EXTRA_USER_ID, UserHandle.myUserId());
+        mDpm = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
+        mKgm = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
+
+        final IntentFilter lockFilter = new IntentFilter();
+        lockFilter.addAction(Intent.ACTION_DEVICE_LOCKED_CHANGED);
+        registerReceiverAsUser(mLockEventReceiver, UserHandle.ALL, lockFilter,
+                /* permission */ null, /* scheduler */ null);
+
+        // Once the receiver is registered, check whether anything happened between now and the time
+        // when this activity was launched. If it did and the user is unlocked now, just quit.
+        if (!mKgm.isDeviceLocked(mUserId)) {
+            finish();
+            return;
+        }
+
+        // Get the organization color; this is a 24-bit integer provided by a DPC, guaranteed to
+        // be completely opaque.
+        final @ColorInt int color = mDpm.getOrganizationColorForUser(mUserId);
+
+        // Draw captions overlaid on the content view, so the whole window is one solid color.
+        setOverlayWithDecorCaptionEnabled(true);
+
+        // Match task description to the task stack we are replacing so it's still recognizably the
+        // original task stack with the same icon and title text.
+        setTaskDescription(new TaskDescription(null, null, color));
+
+        // Blank out the activity. When it is on-screen it will look like a Recents thumbnail with
+        // redaction switched on.
+        final View blankView = new View(this);
+        blankView.setBackgroundColor(color);
+        setContentView(blankView);
+
+        // Respond to input events by showing the prompt to confirm credentials.
+        blankView.setOnClickListener((View v) -> {
+            showConfirmCredentialActivity();
+        });
+    }
+
+    @Override
+    public void onDestroy() {
+        unregisterReceiver(mLockEventReceiver);
+        super.onDestroy();
+    }
+
+    @Override
+    public void onBackPressed() {
+        // Ignore back presses.
+        return;
+    }
+
+    private final BroadcastReceiver mLockEventReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, mUserId);
+            if (userId == mUserId && !mKgm.isDeviceLocked(mUserId)) {
+                finish();
+            }
+        }
+    };
+
+    private void showConfirmCredentialActivity() {
+        if (isFinishing() || !mKgm.isDeviceLocked(mUserId)) {
+            // Don't show the confirm credentials screen if we are already unlocked / unlocking.
+            return;
+        }
+
+        final Intent credential = mKgm.createConfirmDeviceCredentialIntent(null, null, mUserId);
+        if (credential == null) {
+            return;
+        }
+
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchTaskId(getTaskId());
+
+        // Bring this activity back to the foreground after confirming credentials.
+        final PendingIntent target = PendingIntent.getActivity(this, /* request */ -1, getIntent(),
+                PendingIntent.FLAG_CANCEL_CURRENT |
+                PendingIntent.FLAG_ONE_SHOT |
+                PendingIntent.FLAG_IMMUTABLE, options.toBundle());
+
+        credential.putExtra(Intent.EXTRA_INTENT, target.getIntentSender());
+        try {
+            ActivityManager.getService().startConfirmDeviceCredentialIntent(credential);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to start confirm credential intent", e);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
new file mode 100644
index 0000000..22fceff
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.keyguard;
+
+import android.app.Activity;
+import android.app.ActivityOptions;
+import android.app.KeyguardManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.UserHandle;
+
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.misc.SystemServicesProxy.TaskStackListener;
+
+public class WorkLockActivityController {
+    private final Context mContext;
+
+    public WorkLockActivityController(Context context) {
+        mContext = context;
+        EventBus.getDefault().register(this);
+        SystemServicesProxy.getInstance(context).registerTaskStackListener(mLockListener);
+    }
+
+    private void startWorkChallengeInTask(int taskId, int userId) {
+        Intent intent = new Intent(KeyguardManager.ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER)
+                .setComponent(new ComponentName(mContext, WorkLockActivity.class))
+                .putExtra(Intent.EXTRA_USER_ID, userId)
+                .addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
+                        | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT
+                        | Intent.FLAG_ACTIVITY_SINGLE_TOP);
+
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchTaskId(taskId);
+        options.setTaskOverlay(true);
+        mContext.startActivityAsUser(intent, options.toBundle(), UserHandle.CURRENT);
+    }
+
+    private final TaskStackListener mLockListener = new TaskStackListener() {
+        @Override
+        public void onTaskProfileLocked(int taskId, int userId) {
+            startWorkChallengeInTask(taskId, userId);
+        }
+    };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index d86aebf..416c7ce 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -47,13 +47,11 @@
     public FlashlightTile(Host host) {
         super(host);
         mFlashlightController = host.getFlashlightController();
-        mFlashlightController.addCallback(this);
     }
 
     @Override
     protected void handleDestroy() {
         super.handleDestroy();
-        mFlashlightController.removeCallback(this);
     }
 
     @Override
@@ -63,6 +61,11 @@
 
     @Override
     public void setListening(boolean listening) {
+        if (listening) {
+            mFlashlightController.addCallback(this);
+        } else {
+            mFlashlightController.removeCallback(this);
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index ddffea2..05df634 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -154,6 +154,7 @@
         public void onPinnedStackAnimationEnded() { }
         public void onActivityForcedResizable(String packageName, int taskId) { }
         public void onActivityDismissingDockedStack() { }
+        public void onTaskProfileLocked(int taskId, int userId) { }
     }
 
     /**
@@ -197,6 +198,11 @@
         public void onActivityDismissingDockedStack() throws RemoteException {
             mHandler.sendEmptyMessage(H.ON_ACTIVITY_DISMISSING_DOCKED_STACK);
         }
+
+        @Override
+        public void onTaskProfileLocked(int taskId, int userId) {
+            mHandler.obtainMessage(H.ON_TASK_PROFILE_LOCKED, taskId, userId).sendToTarget();
+        }
     };
 
     /**
@@ -1155,6 +1161,7 @@
         private static final int ON_PINNED_STACK_ANIMATION_ENDED = 4;
         private static final int ON_ACTIVITY_FORCED_RESIZABLE = 5;
         private static final int ON_ACTIVITY_DISMISSING_DOCKED_STACK = 6;
+        private static final int ON_TASK_PROFILE_LOCKED = 7;
 
         @Override
         public void handleMessage(Message msg) {
@@ -1196,6 +1203,12 @@
                     }
                     break;
                 }
+                case ON_TASK_PROFILE_LOCKED: {
+                    for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+                        mTaskStackListeners.get(i).onTaskProfileLocked(msg.arg1, msg.arg2);
+                    }
+                    break;
+                }
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java
index 008d837..f0cfa2c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java
@@ -121,6 +121,8 @@
             }
             cleanUpListenersLocked(l);
             mListeners.add(new WeakReference<>(l));
+            l.onFlashlightAvailabilityChanged(mTorchAvailable);
+            l.onFlashlightChanged(mFlashlightEnabled);
         }
     }
 
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index bf3620e..21139da 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -3181,6 +3181,11 @@
     // user accepted
     ACTION_SKIP_DISCLAIMER_SELECTED = 760;
 
+    // Enclosing category for group of APP_TRANSITION_FOO events,
+    // logged when we execute an app transition.
+    APP_TRANSITION = 761;
+
+
     // ---- End O Constants, all O constants go above this line ----
 
     // Add new aosp constants above this line.
diff --git a/services/core/Android.mk b/services/core/Android.mk
index efadbef..1366b3b 100644
--- a/services/core/Android.mk
+++ b/services/core/Android.mk
@@ -24,7 +24,7 @@
     android.hardware.power@1.0-java \
     android.hardware.tv.cec@1.0-java
 
-LOCAL_STATIC_JAVA_LIBRARIES := tzdata_update
+LOCAL_STATIC_JAVA_LIBRARIES := tzdata_update2
 
 ifneq ($(INCREMENTAL_BUILDS),)
     LOCAL_PROGUARD_ENABLED := disabled
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index e7f1d16f..2ca1b4e 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -218,6 +218,11 @@
         @Override
         public void onUserRestrictionsChanged(int userId, Bundle newRestrictions,
                 Bundle prevRestrictions) {
+            if (!newRestrictions.containsKey(UserManager.DISALLOW_BLUETOOTH)
+                    && !prevRestrictions.containsKey(UserManager.DISALLOW_BLUETOOTH)) {
+                // The relevant restriction has not changed - do nothing.
+                return;
+            }
             final boolean bluetoothDisallowed =
                     newRestrictions.getBoolean(UserManager.DISALLOW_BLUETOOTH);
             if ((mEnable || mEnableExternal) && bluetoothDisallowed) {
@@ -228,6 +233,7 @@
                             e);
                 }
             }
+            updateOppLauncherComponentState(bluetoothDisallowed);
         }
     };
 
@@ -953,7 +959,9 @@
         UserManagerInternal userManagerInternal =
                 LocalServices.getService(UserManagerInternal.class);
         userManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener);
-        if (isBluetoothDisallowed()) {
+        final boolean isBluetoothDisallowed = isBluetoothDisallowed();
+        updateOppLauncherComponentState(isBluetoothDisallowed);
+        if (isBluetoothDisallowed) {
             return;
         }
         if (mEnableExternal && isBluetoothPersistedStateOnBluetooth()) {
@@ -2011,6 +2019,24 @@
         }
     }
 
+    /**
+     * Disables BluetoothOppLauncherActivity component, so the Bluetooth sharing option is not
+     * offered to the user if Bluetooth is disallowed. Puts the component to its default state if
+     * Bluetooth is not disallowed.
+     *
+     * @param bluetoothDisallowed whether the {@link UserManager.DISALLOW_BLUETOOTH} user
+     * restriction was set.
+     */
+    private void updateOppLauncherComponentState(boolean bluetoothDisallowed) {
+        final ComponentName oppLauncherComponent = new ComponentName("com.android.bluetooth",
+                "com.android.bluetooth.opp.BluetoothOppLauncherActivity");
+        final int newState = bluetoothDisallowed
+                ? PackageManager.COMPONENT_ENABLED_STATE_DISABLED
+                : PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+        mContext.getPackageManager()
+                .setComponentEnabledSetting(oppLauncherComponent, newState, 0);
+    }
+
     @Override
     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 3ab30f2..3b2041e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -11839,20 +11839,20 @@
         }
 
         synchronized (this) {
-            if (mStackSupervisor.isUserLockedProfile(userId)) {
-                final long ident = Binder.clearCallingIdentity();
-                try {
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                if (mUserController.shouldConfirmCredentials(userId)) {
                     final int currentUserId = mUserController.getCurrentUserIdLocked();
-                    if (mUserController.isLockScreenDisabled(currentUserId)) {
-                        // If there is no device lock, we will show the profile's credential page.
-                        mActivityStarter.showConfirmDeviceCredential(userId);
+                    if (!mKeyguardController.isKeyguardLocked()) {
+                        // If the device is not locked, we will prompt for credentials immediately.
+                        mStackSupervisor.lockAllProfileTasks(userId);
                     } else {
                         // Showing launcher to avoid user entering credential twice.
                         startHomeActivityLocked(currentUserId, "notifyLockedProfile");
                     }
-                } finally {
-                    Binder.restoreCallingIdentity(ident);
                 }
+            } finally {
+                Binder.restoreCallingIdentity(ident);
             }
         }
     }
diff --git a/services/core/java/com/android/server/am/ActivityMetricsLogger.java b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
index 3f166fe..e46d204 100644
--- a/services/core/java/com/android/server/am/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
@@ -172,7 +172,7 @@
         MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_DEVICE_UPTIME_SECONDS,
                 (int) (SystemClock.uptimeMillis() / 1000));
 
-        LogBuilder builder = new LogBuilder();
+        LogBuilder builder = new LogBuilder(MetricsEvent.APP_TRANSITION);
         builder.addTaggedData(MetricsEvent.APP_TRANSITION_COMPONENT_NAME, componentName);
         builder.addTaggedData(MetricsEvent.APP_TRANSITION_PROCESS_RUNNING, processRunning ? 1 : 0);
         builder.addTaggedData(MetricsEvent.APP_TRANSITION_DEVICE_UPTIME_SECONDS,
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 8bd7c90..a935249 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -763,43 +763,52 @@
     }
 
     /**
-     * TODO: Handle freefom mode.
-     * @return true when credential confirmation is needed for the user and there is any
-     *         activity started by the user in any visible stack.
+     * Detects whether we should show a lock screen in front of this task for a locked user.
+     * <p>
+     * We'll do this if either of the following holds:
+     * <ul>
+     *   <li>The top activity explicitly belongs to {@param userId}.</li>
+     *   <li>The top activity returns a result to an activity belonging to {@param userId}.</li>
+     * </ul>
+     *
+     * @return {@code true} if the top activity looks like it belongs to {@param userId}.
      */
-    boolean isUserLockedProfile(@UserIdInt int userId) {
-        if (!mService.mUserController.shouldConfirmCredentials(userId)) {
-            return false;
-        }
-        final ActivityStack fullScreenStack = getStack(FULLSCREEN_WORKSPACE_STACK_ID);
-        final ActivityStack dockedStack = getStack(DOCKED_STACK_ID);
-        final ActivityStack[] activityStacks = new ActivityStack[] {fullScreenStack, dockedStack};
-        for (final ActivityStack activityStack : activityStacks) {
-            if (activityStack == null) {
-                continue;
-            }
-            if (activityStack.topRunningActivityLocked() == null) {
-                continue;
-            }
-            if (activityStack.getStackVisibilityLocked(null) == STACK_INVISIBLE) {
-                continue;
-            }
-            if (activityStack.isDockedStack() && mIsDockMinimized) {
-                continue;
-            }
-            final TaskRecord topTask = activityStack.topTask();
-            if (topTask == null) {
-                continue;
-            }
-            // To handle the case that work app is in the task but just is not the top one.
-            for (int i = topTask.mActivities.size() - 1; i >= 0; i--) {
-                final ActivityRecord activityRecord = topTask.mActivities.get(i);
-                if (activityRecord.userId == userId) {
-                    return true;
+    private boolean taskTopActivityIsUser(TaskRecord task, @UserIdInt int userId) {
+        // To handle the case that work app is in the task but just is not the top one.
+        final ActivityRecord activityRecord = task.getTopActivity();
+        final ActivityRecord resultTo = (activityRecord != null ? activityRecord.resultTo : null);
+
+        return (activityRecord != null && activityRecord.userId == userId)
+                || (resultTo != null && resultTo.userId == userId);
+    }
+
+    /**
+     * Find all visible task stacks containing {@param userId} and intercept them with an activity
+     * to block out the contents and possibly start a credential-confirming intent.
+     *
+     * @param userId user handle for the locked managed profile.
+     */
+    void lockAllProfileTasks(@UserIdInt int userId) {
+        mWindowManager.deferSurfaceLayout();
+        try {
+            final List<ActivityStack> stacks = getStacks();
+            for (int stackNdx = stacks.size() - 1; stackNdx >= 0; stackNdx--) {
+                final List<TaskRecord> tasks = stacks.get(stackNdx).getAllTasks();
+                for (int taskNdx = tasks.size() - 1; taskNdx >= 0; taskNdx--) {
+                    final TaskRecord task = tasks.get(taskNdx);
+
+                    // Check the task for a top activity belonging to userId, or returning a result
+                    // to an activity belonging to userId. Example case: a document picker for
+                    // personal files, opened by a work app, should still get locked.
+                    if (taskTopActivityIsUser(task, userId)) {
+                        mService.mTaskChangeNotificationController.notifyTaskProfileLocked(
+                                task.taskId, userId);
+                    }
                 }
             }
+        } finally {
+            mWindowManager.continueSurfaceLayout();
         }
-        return false;
     }
 
     void setNextTaskIdForUserLocked(int taskId, int userId) {
diff --git a/services/core/java/com/android/server/am/TaskChangeNotificationController.java b/services/core/java/com/android/server/am/TaskChangeNotificationController.java
index fd248c6..fbdbb1b 100644
--- a/services/core/java/com/android/server/am/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/am/TaskChangeNotificationController.java
@@ -39,6 +39,7 @@
     static final int NOTIFY_TASK_DESCRIPTION_CHANGED_LISTENERS_MSG = 11;
     static final int NOTIFY_ACTIVITY_REQUESTED_ORIENTATION_CHANGED_LISTENERS = 12;
     static final int NOTIFY_TASK_REMOVAL_STARTED_LISTENERS = 13;
+    static final int NOTIFY_TASK_PROFILE_LOCKED_LISTENERS_MSG = 14;
 
     // Delay in notifying task stack change listeners (in millis)
     static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100;
@@ -110,6 +111,9 @@
                 case NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG:
                     forAllListeners((listener) -> listener.onActivityDismissingDockedStack());
                     break;
+                case NOTIFY_TASK_PROFILE_LOCKED_LISTENERS_MSG:
+                    forAllListeners((listener) -> listener.onTaskProfileLocked(msg.arg1, msg.arg2));
+                    break;
             }
         }
     }
@@ -228,4 +232,13 @@
         mHandler.obtainMessage(NOTIFY_TASK_REMOVAL_STARTED_LISTENERS, taskId, 0 /* unused */)
                 .sendToTarget();
     }
+
+    /**
+     * Notify listeners that the task has been put in a locked state because one or more of the
+     * activities inside it belong to a managed profile user that has been locked.
+     */
+    void notifyTaskProfileLocked(int taskId, int userId) {
+        mHandler.obtainMessage(NOTIFY_TASK_PROFILE_LOCKED_LISTENERS_MSG, taskId, userId)
+                .sendToTarget();
+    }
 }
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 6d96a10..79567d5 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -199,7 +199,8 @@
         mTetherMasterSM = new TetherMasterSM("TetherMaster", mLooper);
         mTetherMasterSM.start();
 
-        mUpstreamNetworkMonitor = new UpstreamNetworkMonitor();
+        mUpstreamNetworkMonitor = new UpstreamNetworkMonitor(
+                mContext, mTetherMasterSM, TetherMasterSM.EVENT_UPSTREAM_CALLBACK);
 
         mStateReceiver = new StateReceiver();
         IntentFilter filter = new IntentFilter();
@@ -1027,38 +1028,6 @@
     }
 
     /**
-     * A NetworkCallback class that relays information of interest to the
-     * tethering master state machine thread for subsequent processing.
-     */
-    class UpstreamNetworkCallback extends NetworkCallback {
-        @Override
-        public void onAvailable(Network network) {
-            mTetherMasterSM.sendMessage(TetherMasterSM.EVENT_UPSTREAM_CALLBACK,
-                    UpstreamNetworkMonitor.EVENT_ON_AVAILABLE, 0, network);
-        }
-
-        @Override
-        public void onCapabilitiesChanged(Network network, NetworkCapabilities newNc) {
-            mTetherMasterSM.sendMessage(TetherMasterSM.EVENT_UPSTREAM_CALLBACK,
-                    UpstreamNetworkMonitor.EVENT_ON_CAPABILITIES, 0,
-                    new NetworkState(null, null, newNc, network, null, null));
-        }
-
-        @Override
-        public void onLinkPropertiesChanged(Network network, LinkProperties newLp) {
-            mTetherMasterSM.sendMessage(TetherMasterSM.EVENT_UPSTREAM_CALLBACK,
-                    UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES, 0,
-                    new NetworkState(null, newLp, null, network, null, null));
-        }
-
-        @Override
-        public void onLost(Network network) {
-            mTetherMasterSM.sendMessage(TetherMasterSM.EVENT_UPSTREAM_CALLBACK,
-                    UpstreamNetworkMonitor.EVENT_ON_LOST, 0, network);
-        }
-    }
-
-    /**
      * A class to centralize all the network and link properties information
      * pertaining to the current and any potential upstream network.
      *
@@ -1072,21 +1041,31 @@
      * TODO: Investigate whether more "upstream-specific" logic/functionality
      * could/should be moved here.
      */
-    class UpstreamNetworkMonitor {
-        static final int EVENT_ON_AVAILABLE      = 1;
-        static final int EVENT_ON_CAPABILITIES   = 2;
-        static final int EVENT_ON_LINKPROPERTIES = 3;
-        static final int EVENT_ON_LOST           = 4;
+    public class UpstreamNetworkMonitor {
+        public static final int EVENT_ON_AVAILABLE      = 1;
+        public static final int EVENT_ON_CAPABILITIES   = 2;
+        public static final int EVENT_ON_LINKPROPERTIES = 3;
+        public static final int EVENT_ON_LOST           = 4;
 
-        final HashMap<Network, NetworkState> mNetworkMap = new HashMap<>();
-        NetworkCallback mDefaultNetworkCallback;
-        NetworkCallback mDunTetheringCallback;
+        private final Context mContext;
+        private final StateMachine mTarget;
+        private final int mWhat;
+        private final HashMap<Network, NetworkState> mNetworkMap = new HashMap<>();
+        private ConnectivityManager mCM;
+        private NetworkCallback mDefaultNetworkCallback;
+        private NetworkCallback mDunTetheringCallback;
 
-        void start() {
+        public UpstreamNetworkMonitor(Context ctx, StateMachine tgt, int what) {
+            mContext = ctx;
+            mTarget = tgt;
+            mWhat = what;
+        }
+
+        public void start() {
             stop();
 
             mDefaultNetworkCallback = new UpstreamNetworkCallback();
-            getConnectivityManager().registerDefaultNetworkCallback(mDefaultNetworkCallback);
+            cm().registerDefaultNetworkCallback(mDefaultNetworkCallback);
 
             final NetworkRequest dunTetheringRequest = new NetworkRequest.Builder()
                     .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
@@ -1094,29 +1073,28 @@
                     .addCapability(NetworkCapabilities.NET_CAPABILITY_DUN)
                     .build();
             mDunTetheringCallback = new UpstreamNetworkCallback();
-            getConnectivityManager().registerNetworkCallback(
-                    dunTetheringRequest, mDunTetheringCallback);
+            cm().registerNetworkCallback(dunTetheringRequest, mDunTetheringCallback);
         }
 
-        void stop() {
+        public void stop() {
             if (mDefaultNetworkCallback != null) {
-                getConnectivityManager().unregisterNetworkCallback(mDefaultNetworkCallback);
+                cm().unregisterNetworkCallback(mDefaultNetworkCallback);
                 mDefaultNetworkCallback = null;
             }
 
             if (mDunTetheringCallback != null) {
-                getConnectivityManager().unregisterNetworkCallback(mDunTetheringCallback);
+                cm().unregisterNetworkCallback(mDunTetheringCallback);
                 mDunTetheringCallback = null;
             }
 
             mNetworkMap.clear();
         }
 
-        NetworkState lookup(Network network) {
+        public NetworkState lookup(Network network) {
             return (network != null) ? mNetworkMap.get(network) : null;
         }
 
-        NetworkState processCallback(int arg1, Object obj) {
+        public NetworkState processCallback(int arg1, Object obj) {
             switch (arg1) {
                 case EVENT_ON_AVAILABLE: {
                     final Network network = (Network) obj;
@@ -1128,7 +1106,7 @@
                                 new NetworkState(null, null, null, network, null, null));
                     }
 
-                    final ConnectivityManager cm = getConnectivityManager();
+                    final ConnectivityManager cm = cm();
 
                     if (mDefaultNetworkCallback != null) {
                         cm.requestNetworkCapabilities(mDefaultNetworkCallback);
@@ -1199,6 +1177,42 @@
                     return null;
             }
         }
+
+        // Fetch (and cache) a ConnectivityManager only if and when we need one.
+        private ConnectivityManager cm() {
+            if (mCM == null) {
+                mCM = mContext.getSystemService(ConnectivityManager.class);
+            }
+            return mCM;
+        }
+
+        /**
+         * A NetworkCallback class that relays information of interest to the
+         * tethering master state machine thread for subsequent processing.
+         */
+        private class UpstreamNetworkCallback extends NetworkCallback {
+            @Override
+            public void onAvailable(Network network) {
+                mTarget.sendMessage(mWhat, EVENT_ON_AVAILABLE, 0, network);
+            }
+
+            @Override
+            public void onCapabilitiesChanged(Network network, NetworkCapabilities newNc) {
+                mTarget.sendMessage(mWhat, EVENT_ON_CAPABILITIES, 0,
+                        new NetworkState(null, null, newNc, network, null, null));
+            }
+
+            @Override
+            public void onLinkPropertiesChanged(Network network, LinkProperties newLp) {
+                mTarget.sendMessage(mWhat, EVENT_ON_LINKPROPERTIES, 0,
+                        new NetworkState(null, newLp, null, network, null, null));
+            }
+
+            @Override
+            public void onLost(Network network) {
+                mTarget.sendMessage(mWhat, EVENT_ON_LOST, 0, network);
+            }
+        }
     }
 
     // Needed because the canonical source of upstream truth is just the
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 2e0199b..8c4a95c 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -217,26 +217,11 @@
                             dexoptNeeded);
                 }
 
-                final String dexoptType;
-                String oatDir = null;
-                boolean isOdexLocation = (dexoptNeeded < 0);
-                switch (Math.abs(dexoptNeeded)) {
-                    case DexFile.NO_DEXOPT_NEEDED:
-                        continue;
-                    case DexFile.DEX2OAT_FROM_SCRATCH:
-                    case DexFile.DEX2OAT_FOR_BOOT_IMAGE:
-                    case DexFile.DEX2OAT_FOR_FILTER:
-                    case DexFile.DEX2OAT_FOR_RELOCATION:
-                        dexoptType = "dex2oat";
-                        oatDir = createOatDirIfSupported(pkg, dexCodeInstructionSet);
-                        break;
-                    case DexFile.PATCHOAT_FOR_RELOCATION:
-                        dexoptType = "patchoat";
-                        break;
-                    default:
-                        throw new IllegalStateException("Invalid dexopt:" + dexoptNeeded);
+                if (dexoptNeeded == DexFile.NO_DEXOPT_NEEDED) {
+                    continue;
                 }
 
+                String oatDir = createOatDirIfSupported(pkg, dexCodeInstructionSet);
                 String sharedLibrariesPath = null;
                 if (sharedLibraries != null && sharedLibraries.length != 0) {
                     StringBuilder sb = new StringBuilder();
@@ -248,7 +233,7 @@
                     }
                     sharedLibrariesPath = sb.toString();
                 }
-                Log.i(TAG, "Running dexopt (" + dexoptType + ") on: " + path + " pkg="
+                Log.i(TAG, "Running dexopt on: " + path + " pkg="
                         + pkg.applicationInfo.packageName + " isa=" + dexCodeInstructionSet
                         + " vmSafeMode=" + vmSafeMode + " debuggable=" + debuggable
                         + " target-filter=" + targetCompilerFilter + " oatDir=" + oatDir
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index fa1d991..0a312f0 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -251,9 +251,9 @@
     static final boolean DEBUG_INPUT = false;
     static final boolean DEBUG_KEYGUARD = false;
     static final boolean DEBUG_LAYOUT = false;
-    static final boolean DEBUG_STARTING_WINDOW = false;
+    static final boolean DEBUG_SPLASH_SCREEN = false;
     static final boolean DEBUG_WAKEUP = false;
-    static final boolean SHOW_STARTING_ANIMATIONS = true;
+    static final boolean SHOW_SPLASH_SCREENS = true;
 
     // Whether to allow dock apps with METADATA_DOCK_HOME to temporarily take over the Home key.
     // No longer recommended for desk docks;
@@ -2794,10 +2794,10 @@
 
     /** {@inheritDoc} */
     @Override
-    public View addStartingWindow(IBinder appToken, String packageName, int theme,
-            CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
-            int icon, int logo, int windowFlags, Configuration overrideConfig) {
-        if (!SHOW_STARTING_ANIMATIONS) {
+    public StartingSurface addSplashScreen(IBinder appToken, String packageName, int theme,
+            CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon,
+            int logo, int windowFlags, Configuration overrideConfig) {
+        if (!SHOW_SPLASH_SCREENS) {
             return null;
         }
         if (packageName == null) {
@@ -2809,7 +2809,7 @@
 
         try {
             Context context = mContext;
-            if (DEBUG_STARTING_WINDOW) Slog.d(TAG, "addStartingWindow " + packageName
+            if (DEBUG_SPLASH_SCREEN) Slog.d(TAG, "addSplashScreen " + packageName
                     + ": nonLocalizedLabel=" + nonLocalizedLabel + " theme="
                     + Integer.toHexString(theme));
             if (theme != context.getThemeResId() || labelRes != 0) {
@@ -2822,8 +2822,8 @@
             }
 
             if (overrideConfig != null && !overrideConfig.equals(EMPTY)) {
-                if (DEBUG_STARTING_WINDOW) Slog.d(TAG, "addStartingWindow: creating context based"
-                        + " on overrideConfig" + overrideConfig + " for starting window");
+                if (DEBUG_SPLASH_SCREEN) Slog.d(TAG, "addSplashScreen: creating context based"
+                        + " on overrideConfig" + overrideConfig + " for splash screen");
                 final Context overrideContext = context.createConfigurationContext(overrideConfig);
                 overrideContext.setTheme(theme);
                 final TypedArray typedArray = overrideContext.obtainStyledAttributes(
@@ -2833,7 +2833,7 @@
                     // We want to use the windowBackground for the override context if it is
                     // available, otherwise we use the default one to make sure a themed starting
                     // window is displayed for the app.
-                    if (DEBUG_STARTING_WINDOW) Slog.d(TAG, "addStartingWindow: apply overrideConfig"
+                    if (DEBUG_SPLASH_SCREEN) Slog.d(TAG, "addSplashScreen: apply overrideConfig"
                             + overrideConfig + " to starting window resId=" + resId);
                     context = overrideContext;
                 }
@@ -2895,19 +2895,19 @@
                 params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
             }
 
-            params.setTitle("Starting " + packageName);
+            params.setTitle("Splash Screen " + packageName);
 
             wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
             view = win.getDecorView();
 
-            if (DEBUG_STARTING_WINDOW) Slog.d(TAG, "Adding starting window for "
+            if (DEBUG_SPLASH_SCREEN) Slog.d(TAG, "Adding splash screen window for "
                 + packageName + " / " + appToken + ": " + (view.getParent() != null ? view : null));
 
             wm.addView(view, params);
 
             // Only return the view if it was successfully added to the
             // window manager... which we can tell by it having a parent.
-            return view.getParent() != null ? view : null;
+            return view.getParent() != null ? new SplashScreenSurface(view) : null;
         } catch (WindowManager.BadTokenException e) {
             // ignore
             Log.w(TAG, appToken + " already running, starting window not displayed. " +
@@ -2929,13 +2929,13 @@
 
     /** {@inheritDoc} */
     @Override
-    public void removeStartingWindow(IBinder appToken, View window) {
-        if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Removing starting window for " + appToken + ": "
-                + window + " Callers=" + Debug.getCallers(4));
+    public void removeSplashScreen(IBinder appToken, StartingSurface surface) {
+        if (DEBUG_SPLASH_SCREEN) Slog.v(TAG, "Removing splash screen window for " + appToken + ": "
+                + surface + " Callers=" + Debug.getCallers(4));
 
-        if (window != null) {
+        if (surface != null) {
             WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
-            wm.removeView(window);
+            wm.removeView(((SplashScreenSurface) surface).view);
         }
     }
 
diff --git a/services/core/java/com/android/server/policy/SplashScreenSurface.java b/services/core/java/com/android/server/policy/SplashScreenSurface.java
new file mode 100644
index 0000000..d421291
--- /dev/null
+++ b/services/core/java/com/android/server/policy/SplashScreenSurface.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.policy;
+
+import android.view.View;
+import android.view.WindowManagerPolicy;
+import android.view.WindowManagerPolicy.StartingSurface;
+
+import com.android.internal.policy.DecorView;
+import com.android.internal.policy.PhoneWindow;
+
+/**
+ * Holds the contents of a splash screen starting window, i.e. the {@link DecorView} of a
+ * {@link PhoneWindow}. This is just a wrapper such that we can return it from
+ * {@link WindowManagerPolicy#addSplashScreen}.
+ */
+class SplashScreenSurface implements StartingSurface {
+
+    final View view;
+
+    SplashScreenSurface(View view) {
+        this.view = view;
+    }
+}
diff --git a/services/core/java/com/android/server/updates/TzDataInstallReceiver.java b/services/core/java/com/android/server/updates/TzDataInstallReceiver.java
index b260e4e..b704eb1 100644
--- a/services/core/java/com/android/server/updates/TzDataInstallReceiver.java
+++ b/services/core/java/com/android/server/updates/TzDataInstallReceiver.java
@@ -20,7 +20,7 @@
 
 import java.io.File;
 import java.io.IOException;
-import libcore.tzdata.update.TzDataBundleInstaller;
+import libcore.tzdata.update2.TimeZoneBundleInstaller;
 
 /**
  * An install receiver responsible for installing timezone data updates.
@@ -29,18 +29,19 @@
 
     private static final String TAG = "TZDataInstallReceiver";
 
+    private static final File SYSTEM_TZ_DATA_FILE = new File("/system/usr/share/zoneinfo/tzdata");
     private static final File TZ_DATA_DIR = new File("/data/misc/zoneinfo");
     private static final String UPDATE_DIR_NAME = TZ_DATA_DIR.getPath() + "/updates/";
     private static final String UPDATE_METADATA_DIR_NAME = "metadata/";
     private static final String UPDATE_VERSION_FILE_NAME = "version";
     private static final String UPDATE_CONTENT_FILE_NAME = "tzdata_bundle.zip";
 
-    private final TzDataBundleInstaller installer;
+    private final TimeZoneBundleInstaller installer;
 
     public TzDataInstallReceiver() {
         super(UPDATE_DIR_NAME, UPDATE_CONTENT_FILE_NAME, UPDATE_METADATA_DIR_NAME,
                 UPDATE_VERSION_FILE_NAME);
-        installer = new TzDataBundleInstaller(TAG, TZ_DATA_DIR);
+        installer = new TimeZoneBundleInstaller(TAG, SYSTEM_TZ_DATA_FILE, TZ_DATA_DIR);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
index 35004c2..cab39b5 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java
@@ -27,21 +27,21 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.H.ADD_STARTING;
-
-import android.graphics.Bitmap;
-import android.os.Trace;
-import com.android.server.AttributeCache;
 
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
+import android.graphics.Bitmap;
 import android.os.Binder;
 import android.os.Debug;
+import android.os.Handler;
 import android.os.IBinder;
-import android.os.Message;
+import android.os.Looper;
+import android.os.Trace;
 import android.util.Slog;
 import android.view.IApplicationToken;
+import android.view.WindowManagerPolicy.StartingSurface;
 
+import com.android.server.AttributeCache;
 /**
  * Controller for the app window token container. This is created by activity manager to link
  * activity records to the app window token container they use in window manager.
@@ -52,6 +52,7 @@
         extends WindowContainerController<AppWindowToken, AppWindowContainerListener> {
 
     private final IApplicationToken mToken;
+    private final Handler mHandler = new Handler(Looper.getMainLooper());
 
     private final Runnable mOnWindowsDrawn = () -> {
         if (mListener == null) {
@@ -80,6 +81,94 @@
         mListener.onWindowsGone();
     };
 
+    private final Runnable mAddStartingWindow = () -> {
+        final StartingData startingData;
+        final Configuration mergedOverrideConfiguration;
+
+        synchronized (mWindowMap) {
+            startingData = mContainer.startingData;
+            mergedOverrideConfiguration = mContainer.getMergedOverrideConfiguration();
+        }
+
+        if (startingData == null) {
+            // Animation has been canceled... do nothing.
+            return;
+        }
+
+        if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Add starting "
+                + this + ": pkg=" + mContainer.startingData.pkg);
+
+        StartingSurface contents = null;
+        try {
+            contents = mService.mPolicy.addSplashScreen(mContainer.token, startingData.pkg,
+                    startingData.theme, startingData.compatInfo, startingData.nonLocalizedLabel,
+                    startingData.labelRes, startingData.icon, startingData.logo,
+                    startingData.windowFlags, mergedOverrideConfiguration);
+        } catch (Exception e) {
+            Slog.w(TAG_WM, "Exception when adding starting window", e);
+        }
+        if (contents != null) {
+            boolean abort = false;
+
+            synchronized(mWindowMap) {
+                if (mContainer.removed || mContainer.startingData == null) {
+                    // If the window was successfully added, then
+                    // we need to remove it.
+                    if (mContainer.startingWindow != null) {
+                        if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
+                                "Aborted starting " + mContainer
+                                        + ": removed=" + mContainer.removed
+                                        + " startingData=" + mContainer.startingData);
+                        mContainer.startingWindow = null;
+                        mContainer.startingData = null;
+                        abort = true;
+                    }
+                } else {
+                    mContainer.startingSurface = contents;
+                }
+                if (DEBUG_STARTING_WINDOW && !abort) Slog.v(TAG_WM,
+                        "Added starting " + mContainer
+                                + ": startingWindow="
+                                + mContainer.startingWindow + " startingView="
+                                + mContainer.startingSurface);
+            }
+
+            if (abort) {
+                try {
+                    mService.mPolicy.removeSplashScreen(mContainer.token, contents);
+                } catch (Exception e) {
+                    Slog.w(TAG_WM, "Exception when removing starting window", e);
+                }
+            }
+        }
+    };
+
+    private final Runnable mRemoveStartingWindow = () -> {
+        IBinder token = null;
+        StartingSurface contents = null;
+        synchronized (mWindowMap) {
+            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Remove starting "
+                    + mContainer + ": startingWindow="
+                    + mContainer.startingWindow + " startingView="
+                    + mContainer.startingSurface);
+            if (mContainer.startingWindow != null) {
+                contents = mContainer.startingSurface;
+                token = mContainer.token;
+                mContainer.startingData = null;
+                mContainer.startingSurface = null;
+                mContainer.startingWindow = null;
+                mContainer.startingDisplayed = false;
+            }
+        }
+        if (contents != null) {
+            try {
+                mService.mPolicy.removeSplashScreen(token, contents);
+            } catch (Exception e) {
+                Slog.w(TAG_WM, "Exception when removing starting window", e);
+            }
+        }
+    };
+
     public AppWindowContainerController(IApplicationToken token,
             AppWindowContainerListener listener, int taskId, int index, int requestedOrientation,
             boolean fullscreen, boolean showForAllUsers, int configChanges,
@@ -393,19 +482,42 @@
             if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating StartingData");
             mContainer.startingData = new StartingData(pkg, theme, compatInfo, nonLocalizedLabel,
                     labelRes, icon, logo, windowFlags);
-            final Message m = mService.mH.obtainMessage(ADD_STARTING, mContainer);
-            // Note: we really want to do sendMessageAtFrontOfQueue() because we
-            // want to process the message ASAP, before any other queued
-            // messages.
-            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Enqueueing ADD_STARTING");
-            mService.mH.sendMessageAtFrontOfQueue(m);
+            scheduleAddStartingWindow();
         }
         return true;
     }
 
+    void scheduleAddStartingWindow() {
+
+        // Note: we really want to do sendMessageAtFrontOfQueue() because we
+        // want to process the message ASAP, before any other queued
+        // messages.
+        if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Enqueueing ADD_STARTING");
+        mHandler.postAtFrontOfQueue(mAddStartingWindow);
+    }
+
     public void removeStartingWindow() {
         synchronized (mWindowMap) {
-            mService.scheduleRemoveStartingWindowLocked(mContainer);
+            if (mHandler.hasCallbacks(mRemoveStartingWindow)) {
+                // Already scheduled.
+                return;
+            }
+
+            if (mContainer.startingWindow == null) {
+                if (mContainer.startingData != null) {
+                    // Starting window has not been added yet, but it is scheduled to be added.
+                    // Go ahead and cancel the request.
+                    if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
+                            "Clearing startingData for token=" + mContainer);
+                    mContainer.startingData = null;
+                }
+                return;
+            }
+
+            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, Debug.getCallers(1)
+                    + ": Schedule remove starting " + mContainer
+                    + " startingWindow=" + mContainer.startingWindow);
+            mHandler.post(mRemoveStartingWindow);
         }
     }
 
@@ -508,15 +620,15 @@
 
 
     void reportWindowsDrawn() {
-        mService.mH.post(mOnWindowsDrawn);
+        mHandler.post(mOnWindowsDrawn);
     }
 
     void reportWindowsVisible() {
-        mService.mH.post(mOnWindowsVisible);
+        mHandler.post(mOnWindowsVisible);
     }
 
     void reportWindowsGone() {
-        mService.mH.post(mOnWindowsGone);
+        mHandler.post(mOnWindowsGone);
     }
 
     /** Calls directly into activity manager so window manager lock shouldn't held. */
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 0a48758..f4fa220 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -29,9 +29,9 @@
 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 import static com.android.server.wm.AppTransition.TRANSIT_UNSET;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
@@ -47,22 +47,21 @@
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
 import static com.android.server.wm.WindowManagerService.logWithStack;
 
-import android.os.Debug;
-import com.android.internal.util.ToBooleanFunction;
-import com.android.server.input.InputApplicationHandle;
-import com.android.server.wm.WindowManagerService.H;
-
 import android.annotation.NonNull;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Binder;
+import android.os.Debug;
 import android.os.IBinder;
-import android.os.Message;
 import android.os.SystemClock;
 import android.util.Slog;
 import android.view.IApplicationToken;
-import android.view.View;
 import android.view.WindowManager;
+import android.view.WindowManagerPolicy.StartingSurface;
+
+import com.android.internal.util.ToBooleanFunction;
+import com.android.server.input.InputApplicationHandle;
+import com.android.server.wm.WindowManagerService.H;
 
 import java.io.PrintWriter;
 import java.util.ArrayDeque;
@@ -138,7 +137,7 @@
     // Information about an application starting window if displayed.
     StartingData startingData;
     WindowState startingWindow;
-    View startingView;
+    StartingSurface startingSurface;
     boolean startingDisplayed;
     boolean startingMoved;
     boolean firstWindowDrawn;
@@ -213,8 +212,9 @@
             // it from behind the starting window, so there is no need for it to also be doing its
             // own stuff.
             winAnimator.clearAnimation();
-            winAnimator.mService.mFinishedStarting.add(this);
-            winAnimator.mService.mH.sendEmptyMessage(H.FINISHED_STARTING);
+            if (getController() != null) {
+                getController().removeStartingWindow();
+            }
         }
         updateReportedVisibilityLocked();
     }
@@ -439,8 +439,6 @@
     }
 
     void onRemovedFromDisplay() {
-        AppWindowToken startingToken = null;
-
         if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, "Removing app token: " + this);
 
         boolean delayed = setVisibility(null, false, TRANSIT_UNSET, true, mVoiceInteraction);
@@ -461,6 +459,10 @@
         if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG_WM, "removeAppToken: "
                 + this + " delayed=" + delayed + " Callers=" + Debug.getCallers(4));
 
+        if (startingData != null && getController() != null) {
+            getController().removeStartingWindow();
+        }
+
         final TaskStack stack = mTask.mStack;
         if (delayed && !isEmpty()) {
             // set the token aside because it has an active animation to be finished
@@ -477,9 +479,6 @@
         }
 
         removed = true;
-        if (startingData != null) {
-            startingToken = this;
-        }
         stopFreezingScreen(true, true);
         if (mService.mFocusedApp == this) {
             if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "Removing focused app token:" + this);
@@ -491,9 +490,6 @@
         if (!delayed) {
             updateReportedVisibilityLocked();
         }
-
-        // Will only remove if startingToken non null.
-        mService.scheduleRemoveStartingWindowLocked(startingToken);
     }
 
     void clearAnimatingFlags() {
@@ -557,7 +553,9 @@
         mAppStopped = true;
         destroySurfaces();
         // Remove any starting window that was added for this app if they are still around.
-        mTask.mService.scheduleRemoveStartingWindowLocked(this);
+        if (getController() != null) {
+            getController().removeStartingWindow();
+        }
     }
 
     /**
@@ -667,16 +665,20 @@
         // TODO: Something smells about the code below...Is there a better way?
         if (startingWindow == win) {
             if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Notify removed startingWindow " + win);
-            mService.scheduleRemoveStartingWindowLocked(this);
+            if (getController() != null) {
+                getController().removeStartingWindow();
+            }
         } else if (mChildren.size() == 0 && startingData != null) {
             // If this is the last window and we had requested a starting transition window,
             // well there is no point now.
             if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Nulling last startingWindow");
             startingData = null;
-        } else if (mChildren.size() == 1 && startingView != null) {
+        } else if (mChildren.size() == 1 && startingSurface != null) {
             // If this is the last window except for a starting transition window,
             // we need to get rid of the starting transition.
-            mService.scheduleRemoveStartingWindowLocked(this);
+            if (getController() != null) {
+                getController().removeStartingWindow();
+            }
         }
     }
 
@@ -1015,7 +1017,7 @@
         }
 
         final WindowState tStartingWindow = fromToken.startingWindow;
-        if (tStartingWindow != null && fromToken.startingView != null) {
+        if (tStartingWindow != null && fromToken.startingSurface != null) {
             // In this case, the starting icon has already been displayed, so start
             // letting windows get shown immediately without any more transitions.
             mService.mSkipAppTransitionAnimation = true;
@@ -1027,13 +1029,13 @@
 
             // Transfer the starting window over to the new token.
             startingData = fromToken.startingData;
-            startingView = fromToken.startingView;
+            startingSurface = fromToken.startingSurface;
             startingDisplayed = fromToken.startingDisplayed;
             fromToken.startingDisplayed = false;
             startingWindow = tStartingWindow;
             reportedVisible = fromToken.reportedVisible;
             fromToken.startingData = null;
-            fromToken.startingView = null;
+            fromToken.startingSurface = null;
             fromToken.startingWindow = null;
             fromToken.startingMoved = true;
             tStartingWindow.mToken = this;
@@ -1080,10 +1082,9 @@
             startingData = fromToken.startingData;
             fromToken.startingData = null;
             fromToken.startingMoved = true;
-            final Message m = mService.mH.obtainMessage(H.ADD_STARTING, this);
-            // Note: we really want to do sendMessageAtFrontOfQueue() because we want to process the
-            // message ASAP, before any other queued messages.
-            mService.mH.sendMessageAtFrontOfQueue(m);
+            if (getController() != null) {
+                getController().scheduleAddStartingWindow();
+            }
             return true;
         }
 
@@ -1421,10 +1422,10 @@
                     pw.print(" firstWindowDrawn="); pw.print(firstWindowDrawn);
                     pw.print(" mIsExiting="); pw.println(mIsExiting);
         }
-        if (startingWindow != null || startingView != null
+        if (startingWindow != null || startingSurface != null
                 || startingDisplayed || startingMoved) {
             pw.print(prefix); pw.print("startingWindow="); pw.print(startingWindow);
-                    pw.print(" startingView="); pw.print(startingView);
+                    pw.print(" startingSurface="); pw.print(startingSurface);
                     pw.print(" startingDisplayed="); pw.print(startingDisplayed);
                     pw.print(" startingMoved="); pw.println(startingMoved);
         }
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 06b508a..6ac172b 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -498,7 +498,10 @@
                     if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) logSurface(winAnimator.mWin,
                             "RECOVER DESTROY", false);
                     winAnimator.destroySurface();
-                    mService.scheduleRemoveStartingWindowLocked(winAnimator.mWin.mAppToken);
+                    if (winAnimator.mWin.mAppToken != null
+                            && winAnimator.mWin.mAppToken.getController() != null) {
+                        winAnimator.mWin.mAppToken.getController().removeStartingWindow();
+                    }
                 }
 
                 try {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 38cb543..4e25935 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2851,33 +2851,6 @@
         }
     }
 
-    void scheduleRemoveStartingWindowLocked(AppWindowToken wtoken) {
-        if (wtoken == null) {
-            return;
-        }
-        if (mH.hasMessages(H.REMOVE_STARTING, wtoken)) {
-            // Already scheduled.
-            return;
-        }
-
-        if (wtoken.startingWindow == null) {
-            if (wtoken.startingData != null) {
-                // Starting window has not been added yet, but it is scheduled to be added.
-                // Go ahead and cancel the request.
-                if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
-                        "Clearing startingData for token=" + wtoken);
-                wtoken.startingData = null;
-            }
-            return;
-        }
-
-        if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, Debug.getCallers(1) +
-                ": Schedule remove starting " + wtoken + (wtoken != null ?
-                " startingWindow=" + wtoken.startingWindow : ""));
-        Message m = mH.obtainMessage(H.REMOVE_STARTING, wtoken);
-        mH.sendMessage(m);
-    }
-
     public void moveTaskToTop(int taskId) {
         final long origId = Binder.clearCallingIdentity();
         try {
@@ -5578,9 +5551,6 @@
         public static final int REPORT_FOCUS_CHANGE = 2;
         public static final int REPORT_LOSING_FOCUS = 3;
         public static final int DO_TRAVERSAL = 4;
-        public static final int ADD_STARTING = 5;
-        public static final int REMOVE_STARTING = 6;
-        public static final int FINISHED_STARTING = 7;
         public static final int WINDOW_FREEZE_TIMEOUT = 11;
 
         public static final int APP_TRANSITION_TIMEOUT = 13;
@@ -5722,126 +5692,6 @@
                     }
                 } break;
 
-                case ADD_STARTING: {
-                    final AppWindowToken wtoken = (AppWindowToken)msg.obj;
-                    final StartingData sd = wtoken.startingData;
-
-                    if (sd == null) {
-                        // Animation has been canceled... do nothing.
-                        return;
-                    }
-
-                    if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Add starting "
-                            + wtoken + ": pkg=" + sd.pkg);
-
-                    View view = null;
-                    try {
-                        view = mPolicy.addStartingWindow(wtoken.token, sd.pkg, sd.theme,
-                            sd.compatInfo, sd.nonLocalizedLabel, sd.labelRes, sd.icon, sd.logo,
-                            sd.windowFlags, wtoken.getMergedOverrideConfiguration());
-                    } catch (Exception e) {
-                        Slog.w(TAG_WM, "Exception when adding starting window", e);
-                    }
-
-                    if (view != null) {
-                        boolean abort = false;
-
-                        synchronized(mWindowMap) {
-                            if (wtoken.removed || wtoken.startingData == null) {
-                                // If the window was successfully added, then
-                                // we need to remove it.
-                                if (wtoken.startingWindow != null) {
-                                    if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
-                                            "Aborted starting " + wtoken
-                                            + ": removed=" + wtoken.removed
-                                            + " startingData=" + wtoken.startingData);
-                                    wtoken.startingWindow = null;
-                                    wtoken.startingData = null;
-                                    abort = true;
-                                }
-                            } else {
-                                wtoken.startingView = view;
-                            }
-                            if (DEBUG_STARTING_WINDOW && !abort) Slog.v(TAG_WM,
-                                    "Added starting " + wtoken
-                                    + ": startingWindow="
-                                    + wtoken.startingWindow + " startingView="
-                                    + wtoken.startingView);
-                        }
-
-                        if (abort) {
-                            try {
-                                mPolicy.removeStartingWindow(wtoken.token, view);
-                            } catch (Exception e) {
-                                Slog.w(TAG_WM, "Exception when removing starting window", e);
-                            }
-                        }
-                    }
-                } break;
-
-                case REMOVE_STARTING: {
-                    final AppWindowToken wtoken = (AppWindowToken)msg.obj;
-                    IBinder token = null;
-                    View view = null;
-                    synchronized (mWindowMap) {
-                        if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Remove starting "
-                                + wtoken + ": startingWindow="
-                                + wtoken.startingWindow + " startingView="
-                                + wtoken.startingView);
-                        if (wtoken.startingWindow != null) {
-                            view = wtoken.startingView;
-                            token = wtoken.token;
-                            wtoken.startingData = null;
-                            wtoken.startingView = null;
-                            wtoken.startingWindow = null;
-                            wtoken.startingDisplayed = false;
-                        }
-                    }
-                    if (view != null) {
-                        try {
-                            mPolicy.removeStartingWindow(token, view);
-                        } catch (Exception e) {
-                            Slog.w(TAG_WM, "Exception when removing starting window", e);
-                        }
-                    }
-                } break;
-
-                case FINISHED_STARTING: {
-                    IBinder token = null;
-                    View view = null;
-                    while (true) {
-                        synchronized (mWindowMap) {
-                            final int N = mFinishedStarting.size();
-                            if (N <= 0) {
-                                break;
-                            }
-                            AppWindowToken wtoken = mFinishedStarting.remove(N-1);
-
-                            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
-                                    "Finished starting " + wtoken
-                                    + ": startingWindow=" + wtoken.startingWindow
-                                    + " startingView=" + wtoken.startingView);
-
-                            if (wtoken.startingWindow == null) {
-                                continue;
-                            }
-
-                            view = wtoken.startingView;
-                            token = wtoken.token;
-                            wtoken.startingData = null;
-                            wtoken.startingView = null;
-                            wtoken.startingWindow = null;
-                            wtoken.startingDisplayed = false;
-                        }
-
-                        try {
-                            mPolicy.removeStartingWindow(token, view);
-                        } catch (Exception e) {
-                            Slog.w(TAG_WM, "Exception when removing starting window", e);
-                        }
-                    }
-                } break;
-
                 case WINDOW_FREEZE_TIMEOUT: {
                     // TODO(multidisplay): Can non-default displays rotate?
                     synchronized (mWindowMap) {
@@ -7348,21 +7198,6 @@
     private void dumpTokensLocked(PrintWriter pw, boolean dumpAll) {
         pw.println("WINDOW MANAGER TOKENS (dumpsys window tokens)");
         mRoot.dumpTokens(pw, dumpAll);
-        if (!mFinishedStarting.isEmpty()) {
-            pw.println();
-            pw.println("  Finishing start of application tokens:");
-            for (int i=mFinishedStarting.size()-1; i>=0; i--) {
-                WindowToken token = mFinishedStarting.get(i);
-                pw.print("  Finished Starting #"); pw.print(i);
-                        pw.print(' '); pw.print(token);
-                if (dumpAll) {
-                    pw.println(':');
-                    token.dump(pw, "    ");
-                } else {
-                    pw.println();
-                }
-            }
-        }
         if (!mOpeningApps.isEmpty() || !mClosingApps.isEmpty()) {
             pw.println();
             if (mOpeningApps.size() > 0) {
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index e5ed18d..19ef44c 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -461,16 +461,7 @@
         mStackClip = STACK_CLIP_BEFORE_ANIM;
         mWin.checkPolicyVisibilityChange();
         mTransformation.clear();
-        if (mDrawState == HAS_DRAWN
-                && mWin.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING
-                && mWin.mAppToken != null
-                && mWin.mAppToken.firstWindowDrawn
-                && mWin.mAppToken.startingData != null) {
-            if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Finish starting "
-                    + mWin.mToken + ": first real window done animating");
-            mService.mFinishedStarting.add(mWin.mAppToken);
-            mService.mH.sendEmptyMessage(H.FINISHED_STARTING);
-        } else if (mAttrType == LayoutParams.TYPE_STATUS_BAR && mWin.mPolicyVisibility) {
+        if (mAttrType == LayoutParams.TYPE_STATUS_BAR && mWin.mPolicyVisibility) {
             // Upon completion of a not-visible to visible status bar animation a relayout is
             // required.
             if (displayContent != null) {
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 1aabd5e..4df1001 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -441,8 +441,9 @@
             wtoken.deferClearAllDrawn = false;
             // Ensure that apps that are mid-starting are also scheduled to have their
             // starting windows removed after the animation is complete
-            if (wtoken.startingWindow != null && !wtoken.startingWindow.mAnimatingExit) {
-                mService.scheduleRemoveStartingWindowLocked(wtoken);
+            if (wtoken.startingWindow != null && !wtoken.startingWindow.mAnimatingExit
+                    && wtoken.getController() != null) {
+                wtoken.getController().removeStartingWindow();
             }
             mService.mAnimator.mAppWindowAnimating |= appAnimator.isAnimating();
 
diff --git a/services/core/jni/com_android_server_connectivity_Vpn.cpp b/services/core/jni/com_android_server_connectivity_Vpn.cpp
index c54d732..4d85d9a 100644
--- a/services/core/jni/com_android_server_connectivity_Vpn.cpp
+++ b/services/core/jni/com_android_server_connectivity_Vpn.cpp
@@ -17,24 +17,25 @@
 #define LOG_NDEBUG 0
 
 #define LOG_TAG "VpnJni"
-#include <cutils/log.h>
-#include "netutils/ifc.h"
 
-#include <stdio.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
 #include <arpa/inet.h>
 #include <errno.h>
 #include <fcntl.h>
-
 #include <linux/if.h>
 #include <linux/if_tun.h>
 #include <linux/route.h>
 #include <linux/ipv6_route.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <log/log.h>
+
+#include "netutils/ifc.h"
 
 #include "jni.h"
 #include "JNIHelp.h"
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 09886db..fa6405a 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -59,6 +59,12 @@
 static jmethodID method_reportMeasurementData;
 static jmethodID method_reportNavigationMessages;
 
+/*
+ * Save a pointer to JavaVm to attach/detach threads executing
+ * callback methods that need to make JNI calls.
+ */
+static JavaVM* sJvm;
+
 using android::OK;
 using android::sp;
 using android::status_t;
@@ -216,6 +222,62 @@
     }
 }
 
+class ScopedJniThreadAttach {
+public:
+    ScopedJniThreadAttach() {
+        /*
+         * attachResult will also be JNI_OK if the thead was already attached to
+         * JNI before the call to AttachCurrentThread().
+         */
+        jint attachResult = sJvm->AttachCurrentThread(&mEnv, nullptr);
+        LOG_ALWAYS_FATAL_IF(attachResult != JNI_OK, "Unable to attach thread. Error %d",
+                            attachResult);
+    }
+
+    ~ScopedJniThreadAttach() {
+        jint detachResult = sJvm->DetachCurrentThread();
+        /*
+         * Return if the thread was already detached. Log error for any other
+         * failure.
+         */
+        if (detachResult == JNI_EDETACHED) {
+            return;
+        }
+
+        LOG_ALWAYS_FATAL_IF(detachResult != JNI_OK, "Unable to detach thread. Error %d",
+                            detachResult);
+    }
+
+    JNIEnv* getEnv() {
+        /*
+         * Checking validity of mEnv in case the thread was detached elsewhere.
+         */
+        LOG_ALWAYS_FATAL_IF(AndroidRuntime::getJNIEnv() != mEnv);
+        return mEnv;
+    }
+
+private:
+    JNIEnv* mEnv = nullptr;
+};
+
+thread_local std::unique_ptr<ScopedJniThreadAttach> tJniThreadAttacher;
+
+static JNIEnv* getJniEnv() {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+
+    /*
+     * If env is nullptr, the thread is not already attached to
+     * JNI. It is attached below and the destructor for ScopedJniThreadAttach
+     * will detach it on thread exit.
+     */
+    if (env == nullptr) {
+        tJniThreadAttacher.reset(new ScopedJniThreadAttach());
+        env = tJniThreadAttacher->getEnv();
+    }
+
+    return env;
+}
+
 /*
  * GnssCallback class implements the callback methods for IGnss interface.
  */
@@ -247,7 +309,7 @@
 
 Return<void> GnssCallback::gnssLocationCb(
         const ::android::hardware::gnss::V1_0::GnssLocation& location) {
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    JNIEnv* env = getJniEnv();
     env->CallVoidMethod(mCallbacksObj,
                         method_reportLocation,
                         location.gnssLocationFlags,
@@ -263,14 +325,14 @@
 }
 
 Return<void> GnssCallback::gnssStatusCb(const IGnssCallback::GnssStatusValue status) {
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    JNIEnv* env = getJniEnv();
     env->CallVoidMethod(mCallbacksObj, method_reportStatus, status);
     checkAndClearExceptionFromCallback(env, __FUNCTION__);
     return Void();
 }
 
 Return<void> GnssCallback::gnssSvStatusCb(const IGnssCallback::GnssSvStatus& svStatus) {
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    JNIEnv* env = getJniEnv();
 
     sGnssSvListSize = svStatus.numSvs;
     if (sGnssSvListSize > static_cast<uint32_t>(
@@ -292,7 +354,7 @@
 
 Return<void> GnssCallback::gnssNmeaCb(
     int64_t timestamp, const ::android::hardware::hidl_string& nmea) {
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    JNIEnv* env = getJniEnv();
     /*
      * The Java code will call back to read these values.
      * We do this to avoid creating unnecessary String objects.
@@ -308,7 +370,7 @@
 Return<void> GnssCallback::gnssSetCapabilitesCb(uint32_t capabilities) {
     ALOGD("%s: %du\n", __func__, capabilities);
 
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    JNIEnv* env = getJniEnv();
     env->CallVoidMethod(mCallbacksObj, method_setEngineCapabilities, capabilities);
     checkAndClearExceptionFromCallback(env, __FUNCTION__);
     return Void();
@@ -325,7 +387,7 @@
 }
 
 Return<void> GnssCallback::gnssRequestTimeCb() {
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    JNIEnv* env = getJniEnv();
     env->CallVoidMethod(mCallbacksObj, method_requestUtcTime);
     checkAndClearExceptionFromCallback(env, __FUNCTION__);
     return Void();
@@ -334,7 +396,7 @@
 Return<void> GnssCallback::gnssSetSystemInfoCb(const IGnssCallback::GnssSystemInfo& info) {
     ALOGD("%s: yearOfHw=%d\n", __func__, info.yearOfHw);
 
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    JNIEnv* env = getJniEnv();
     env->CallVoidMethod(mCallbacksObj, method_setGnssYearOfHardware,
                         info.yearOfHw);
     checkAndClearExceptionFromCallback(env, __FUNCTION__);
@@ -350,7 +412,7 @@
  * interface.
  */
 Return<void> GnssXtraCallback::downloadRequestCb() {
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    JNIEnv* env = getJniEnv();
     env->CallVoidMethod(mCallbacksObj, method_xtraDownloadRequest);
     checkAndClearExceptionFromCallback(env, __FUNCTION__);
     return Void();
@@ -385,7 +447,7 @@
         const android::hardware::gnss::V1_0::GnssLocation& location,
         GeofenceTransition transition,
         hardware::gnss::V1_0::GnssUtcTime timestamp) {
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    JNIEnv* env = getJniEnv();
 
     env->CallVoidMethod(mCallbacksObj,
                         method_reportGeofenceTransition,
@@ -408,7 +470,7 @@
 Return<void> GnssGeofenceCallback::gnssGeofenceStatusCb(
         GeofenceAvailability status,
         const android::hardware::gnss::V1_0::GnssLocation& location) {
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    JNIEnv* env = getJniEnv();
     env->CallVoidMethod(mCallbacksObj,
                         method_reportGeofenceStatus,
                         status,
@@ -426,7 +488,7 @@
 
 Return<void> GnssGeofenceCallback::gnssGeofenceAddCb(int32_t geofenceId,
                                                     GeofenceStatus status) {
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    JNIEnv* env = getJniEnv();
     if (status != IGnssGeofenceCallback::GeofenceStatus::OPERATION_SUCCESS) {
         ALOGE("%s: Error in adding a Geofence: %d\n", __func__, status);
     }
@@ -441,7 +503,7 @@
 
 Return<void> GnssGeofenceCallback::gnssGeofenceRemoveCb(int32_t geofenceId,
                                                        GeofenceStatus status) {
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    JNIEnv* env = getJniEnv();
     if (status != IGnssGeofenceCallback::GeofenceStatus::OPERATION_SUCCESS) {
         ALOGE("%s: Error in removing a Geofence: %d\n", __func__, status);
     }
@@ -455,7 +517,7 @@
 
 Return<void> GnssGeofenceCallback::gnssGeofencePauseCb(int32_t geofenceId,
                                                       GeofenceStatus status) {
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    JNIEnv* env = getJniEnv();
     if (status != IGnssGeofenceCallback::GeofenceStatus::OPERATION_SUCCESS) {
         ALOGE("%s: Error in pausing Geofence: %d\n", __func__, status);
     }
@@ -469,7 +531,7 @@
 
 Return<void> GnssGeofenceCallback::gnssGeofenceResumeCb(int32_t geofenceId,
                                                        GeofenceStatus status) {
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    JNIEnv* env = getJniEnv();
     if (status != IGnssGeofenceCallback::GeofenceStatus::OPERATION_SUCCESS) {
         ALOGE("%s: Error in resuming Geofence: %d\n", __func__, status);
     }
@@ -496,7 +558,7 @@
 
 Return<void> GnssNavigationMessageCallback::gnssNavigationMessageCb(
         const IGnssNavigationMessageCallback::GnssNavigationMessage& message) {
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    JNIEnv* env = getJniEnv();
 
     size_t dataLength = message.data.size();
 
@@ -545,7 +607,7 @@
 
 Return<void> GnssMeasurementCallback::GnssMeasurementCb(
         const IGnssMeasurementCallback::GnssData& data) {
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    JNIEnv* env = getJniEnv();
 
     jobject clock;
     jobjectArray measurementArray;
@@ -700,7 +762,7 @@
 
 Return<void> GnssNiCallback::niNotifyCb(
         const IGnssNiCallback::GnssNiNotification& notification) {
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    JNIEnv* env = getJniEnv();
     jstring requestorId = env->NewStringUTF(notification.requestorId.c_str());
     jstring text = env->NewStringUTF(notification.notificationMessage.c_str());
 
@@ -742,7 +804,7 @@
 
 Return<void> AGnssCallback::agnssStatusIpV6Cb(
         const IAGnssCallback::AGnssStatusIpV6& agps_status) {
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    JNIEnv* env = getJniEnv();
     jbyteArray byteArray = NULL;
     bool isSupported = false;
 
@@ -778,7 +840,7 @@
 
 Return<void> AGnssCallback::agnssStatusIpV4Cb(
         const IAGnssCallback::AGnssStatusIpV4& agps_status) {
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    JNIEnv* env = getJniEnv();
     jbyteArray byteArray = NULL;
 
     uint32_t ipAddr = agps_status.ipV4Addr;
@@ -813,7 +875,7 @@
         return NULL;
     }
 
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    JNIEnv* env = getJniEnv();
     jbyteArray byteArray = env->NewByteArray(4);
     if (byteArray == NULL) {
         ALOGE("Unable to allocate byte array for IPv4 address");
@@ -832,19 +894,19 @@
  * interface.
  */
 struct AGnssRilCallback : IAGnssRilCallback {
-    Return<void> requestSetIdCb(IAGnssRilCallback::ID setIdFlag) override;
+    Return<void> requestSetIdCb(uint32_t setIdFlag) override;
     Return<void> requestRefLocCb() override;
 };
 
-Return<void> AGnssRilCallback::requestSetIdCb(IAGnssRilCallback::ID setIdFlag) {
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
+Return<void> AGnssRilCallback::requestSetIdCb(uint32_t setIdFlag) {
+    JNIEnv* env = getJniEnv();
     env->CallVoidMethod(mCallbacksObj, method_requestSetID, setIdFlag);
     checkAndClearExceptionFromCallback(env, __FUNCTION__);
     return Void();
 }
 
 Return<void> AGnssRilCallback::requestRefLocCb() {
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    JNIEnv* env = getJniEnv();
     env->CallVoidMethod(mCallbacksObj, method_requestRefLocation);
     checkAndClearExceptionFromCallback(env, __FUNCTION__);
     return Void();
@@ -885,6 +947,14 @@
             "reportNavigationMessage",
             "(Landroid/location/GnssNavigationMessage;)V");
 
+    /*
+     * Save a pointer to JVM.
+     */
+    jint jvmStatus = env->GetJavaVM(&sJvm);
+    if (jvmStatus != JNI_OK) {
+        LOG_ALWAYS_FATAL("Unable to get Java VM. Error: %d", jvmStatus);
+    }
+
     // TODO(b/31632518)
     gnssHal = IGnss::getService("gnss");
     if (gnssHal != nullptr) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 9f66062..2e5b687 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -10097,6 +10097,7 @@
                 .setSmallIcon(R.drawable.ic_qs_network_logging)
                 .setContentTitle(mContext.getString(R.string.network_logging_notification_title))
                 .setContentText(mContext.getString(R.string.network_logging_notification_text))
+                .setTicker(mContext.getString(R.string.network_logging_notification_title))
                 .setShowWhen(true)
                 .setContentIntent(pendingIntent)
                 .build();
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 1853a65..c4fd722 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -16,29 +16,6 @@
 
 package com.android.server.wm;
 
-import com.android.internal.policy.IKeyguardDismissCallback;
-import com.android.internal.policy.IShortcutService;
-import com.android.server.input.InputManagerService;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.res.CompatibilityInfo;
-import android.content.res.Configuration;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-import android.view.Display;
-import android.view.IWindowManager;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.WindowManager;
-import android.view.WindowManagerPolicy;
-import android.view.animation.Animation;
-
-import java.io.PrintWriter;
-
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
@@ -62,8 +39,8 @@
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
 import static android.view.WindowManager.LayoutParams.TYPE_PHONE;
 import static android.view.WindowManager.LayoutParams.TYPE_POINTER;
-import static android.view.WindowManager.LayoutParams.TYPE_PRIORITY_PHONE;
 import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION;
+import static android.view.WindowManager.LayoutParams.TYPE_PRIORITY_PHONE;
 import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION;
 import static android.view.WindowManager.LayoutParams.TYPE_QS_DIALOG;
 import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
@@ -81,9 +58,30 @@
 import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
-
 import static org.mockito.Mockito.mock;
 
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.CompatibilityInfo;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.Display;
+import android.view.IWindowManager;
+import android.view.KeyEvent;
+import android.view.WindowManager;
+import android.view.WindowManagerPolicy;
+import android.view.animation.Animation;
+
+import com.android.internal.policy.IKeyguardDismissCallback;
+import com.android.internal.policy.IShortcutService;
+import com.android.server.input.InputManagerService;
+
+import java.io.PrintWriter;
+
 class TestWindowManagerPolicy implements WindowManagerPolicy {
     private static final String TAG = "TestWindowManagerPolicy";
 
@@ -308,14 +306,14 @@
     }
 
     @Override
-    public View addStartingWindow(IBinder appToken, String packageName, int theme,
+    public StartingSurface addSplashScreen(IBinder appToken, String packageName, int theme,
             CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon,
             int logo, int windowFlags, Configuration overrideConfig) {
         return null;
     }
 
     @Override
-    public void removeStartingWindow(IBinder appToken, View window) {
+    public void removeSplashScreen(IBinder appToken, StartingSurface surface) {
 
     }
 
diff --git a/tools/layoutlib/bridge/src/android/view/AttachInfo_Accessor.java b/tools/layoutlib/bridge/src/android/view/AttachInfo_Accessor.java
index 85584d3..4445a22 100644
--- a/tools/layoutlib/bridge/src/android/view/AttachInfo_Accessor.java
+++ b/tools/layoutlib/bridge/src/android/view/AttachInfo_Accessor.java
@@ -51,4 +51,8 @@
             view.dispatchDetachedFromWindow();
         }
     }
+
+    public static ViewRootImpl getRootView(View view) {
+        return view.mAttachInfo != null ? view.mAttachInfo.mViewRootImpl : null;
+    }
 }
diff --git a/tools/layoutlib/bridge/src/android/view/ViewRootImpl_Accessor.java b/tools/layoutlib/bridge/src/android/view/ViewRootImpl_Accessor.java
new file mode 100644
index 0000000..0e15b97
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/view/ViewRootImpl_Accessor.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2016 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.view;
+
+/**
+ * Accessor to allow layoutlib to call {@link ViewRootImpl#dispatchApplyInsets} directly.
+ */
+public class ViewRootImpl_Accessor {
+    public static void dispatchApplyInsets(ViewRootImpl viewRoot, View host) {
+        viewRoot.dispatchApplyInsets(host);
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java
index 726ff22..2fe3ed5 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java
@@ -38,7 +38,10 @@
 import android.graphics.drawable.Drawable;
 import android.util.DisplayMetrics;
 import android.util.TypedValue;
+import android.view.AttachInfo_Accessor;
 import android.view.View;
+import android.view.ViewRootImpl;
+import android.view.ViewRootImpl_Accessor;
 import android.widget.FrameLayout;
 import android.widget.LinearLayout;
 import android.widget.RelativeLayout;
@@ -302,6 +305,17 @@
         return Bridge.getResourceId(ResourceType.ID, ID_PREFIX + name);
     }
 
+    @Override
+    public void requestFitSystemWindows() {
+        // The framework call would usually bubble up to ViewRootImpl but, in layoutlib, Layout will
+        // act as view root for most purposes. That way, we can also save going through the Handler
+        // to dispatch the new applied insets.
+        ViewRootImpl root = AttachInfo_Accessor.getRootView(this);
+        if (root != null) {
+            ViewRootImpl_Accessor.dispatchApplyInsets(root, this);
+        }
+    }
+
     /**
      * A helper class to help initialize the Layout.
      */
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/myapplication.widgets/InsetsWidget.java b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/myapplication.widgets/InsetsWidget.java
new file mode 100644
index 0000000..36e5c26
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/myapplication.widgets/InsetsWidget.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2016 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.layoutlib.test.myapplication.widgets;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.WindowInsets;
+import android.widget.TextView;
+
+public class InsetsWidget extends TextView {
+    public static boolean sApplyInsetsCalled = false;
+
+    public InsetsWidget(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+
+        requestApplyInsets();
+    }
+
+    @Override
+    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+        sApplyInsetsCalled = true;
+        return super.onApplyWindowInsets(insets);
+    }
+}
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/insets.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/insets.xml
new file mode 100644
index 0000000..ff06d79
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/insets.xml
@@ -0,0 +1,12 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:padding="16dp"
+              android:orientation="horizontal"
+              android:layout_width="wrap_content"
+              android:layout_height="wrap_content">
+
+    <com.android.layoutlib.test.myapplication.widgets.InsetsWidget
+        android:text="Hello world"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:id="@+id/text1"/>
+</LinearLayout>
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
index c813a12..7a436eb 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
@@ -63,6 +63,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.lang.ref.WeakReference;
+import java.lang.reflect.Field;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.net.URLClassLoader;
@@ -87,6 +88,7 @@
 import com.google.android.collect.Lists;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
@@ -434,6 +436,37 @@
         renderAndVerify(params, "simple_activity.png");
     }
 
+    @Test
+    public void testOnApplyInsetsCall()
+            throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
+        // We get the widget via reflection to avoid IntelliJ complaining about the class being
+        // located in the wrong package. (From the Bridge tests point of view, it is)
+        Class insetsWidgetClass = Class.forName("com.android.layoutlib.test.myapplication.widgets" +
+                ".InsetsWidget");
+        Field field = insetsWidgetClass.getDeclaredField("sApplyInsetsCalled");
+        assertFalse((Boolean)field.get(null));
+
+        LayoutPullParser parser = createLayoutPullParser("insets.xml");
+        LayoutLibTestCallback layoutLibCallback =
+                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+        SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
+                layoutLibCallback, "Theme.Material.Light.NoActionBar", false,
+                RenderingMode.NORMAL, 22);
+        try {
+            renderAndVerify(params, "scrolled.png");
+        } catch(AssertionError e) {
+            // In this particular test we do not care about the image similarity.
+            // TODO: Create new render method that allows to not compare images.
+            if (!e.getLocalizedMessage().startsWith("Images differ")) {
+                throw e;
+            }
+        }
+
+        assertTrue((Boolean)field.get(null));
+        field.set(null, false);
+    }
+
     @AfterClass
     public static void tearDown() {
         sLayoutLibLog = null;