Merge "Dynamic audio policies: JNI support for UID rules" into nyc-dev
diff --git a/api/current.txt b/api/current.txt
index db77d33..617aac0 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -8512,6 +8512,7 @@
     field public static final java.lang.String ACTION_MANAGED_PROFILE_ADDED = "android.intent.action.MANAGED_PROFILE_ADDED";
     field public static final java.lang.String ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED = "android.intent.action.MANAGED_PROFILE_AVAILABILITY_CHANGED";
     field public static final java.lang.String ACTION_MANAGED_PROFILE_REMOVED = "android.intent.action.MANAGED_PROFILE_REMOVED";
+    field public static final java.lang.String ACTION_MANAGED_PROFILE_UNLOCKED = "android.intent.action.MANAGED_PROFILE_UNLOCKED";
     field public static final java.lang.String ACTION_MANAGE_NETWORK_USAGE = "android.intent.action.MANAGE_NETWORK_USAGE";
     field public static final java.lang.String ACTION_MANAGE_PACKAGE_STORAGE = "android.intent.action.MANAGE_PACKAGE_STORAGE";
     field public static final java.lang.String ACTION_MEDIA_BAD_REMOVAL = "android.intent.action.MEDIA_BAD_REMOVAL";
@@ -19221,7 +19222,6 @@
     method public double getElevationUncertaintyInDeg();
     method public byte getLossOfLock();
     method public byte getMultipathIndicator();
-    method public byte getPrn();
     method public double getPseudorangeInMeters();
     method public double getPseudorangeRateCarrierInMetersPerSec();
     method public double getPseudorangeRateCarrierUncertaintyInMetersPerSec();
@@ -19232,6 +19232,7 @@
     method public long getReceivedGpsTowUncertaintyInNs();
     method public double getSnrInDb();
     method public short getState();
+    method public short getSvid();
     method public short getTimeFromLastBitInMs();
     method public double getTimeOffsetInNs();
     method public boolean hasAzimuthInDeg();
@@ -19291,7 +19292,6 @@
     method public void setElevationUncertaintyInDeg(double);
     method public void setLossOfLock(byte);
     method public void setMultipathIndicator(byte);
-    method public void setPrn(byte);
     method public void setPseudorangeInMeters(double);
     method public void setPseudorangeRateCarrierInMetersPerSec(double);
     method public void setPseudorangeRateCarrierUncertaintyInMetersPerSec(double);
@@ -19302,6 +19302,7 @@
     method public void setReceivedGpsTowUncertaintyInNs(long);
     method public void setSnrInDb(double);
     method public void setState(short);
+    method public void setSvid(short);
     method public void setTimeFromLastBitInMs(short);
     method public void setTimeOffsetInNs(double);
     method public void setUsedInFix(boolean);
@@ -19356,17 +19357,17 @@
     method public int describeContents();
     method public byte[] getData();
     method public short getMessageId();
-    method public byte getPrn();
     method public short getStatus();
     method public short getSubmessageId();
+    method public short getSvid();
     method public byte getType();
     method public void reset();
     method public void set(android.location.GnssNavigationMessage);
     method public void setData(byte[]);
     method public void setMessageId(short);
-    method public void setPrn(byte);
     method public void setStatus(short);
     method public void setSubmessageId(short);
+    method public void setSvid(short);
     method public void setType(byte);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.location.GnssNavigationMessage> CREATOR;
@@ -19412,8 +19413,8 @@
     method public int getConstellationType(int);
     method public float getElevation(int);
     method public int getNumSatellites();
-    method public int getPrn(int);
     method public float getSnr(int);
+    method public int getSvid(int);
     method public boolean hasAlmanac(int);
     method public boolean hasEphemeris(int);
     method public boolean usedInFix(int);
@@ -22950,10 +22951,8 @@
     field public static final int INPUT_STATE_CONNECTED_STANDBY = 1; // 0x1
     field public static final int INPUT_STATE_DISCONNECTED = 2; // 0x2
     field public static final java.lang.String META_DATA_CONTENT_RATING_SYSTEMS = "android.media.tv.metadata.CONTENT_RATING_SYSTEMS";
-    field public static final int RECORDING_ERROR_CONNECTION_FAILED = 1; // 0x1
-    field public static final int RECORDING_ERROR_DISCONNECTED = 2; // 0x2
-    field public static final int RECORDING_ERROR_INSUFFICIENT_SPACE = 3; // 0x3
-    field public static final int RECORDING_ERROR_RESOURCE_BUSY = 4; // 0x4
+    field public static final int RECORDING_ERROR_INSUFFICIENT_SPACE = 1; // 0x1
+    field public static final int RECORDING_ERROR_RESOURCE_BUSY = 2; // 0x2
     field public static final int RECORDING_ERROR_UNKNOWN = 0; // 0x0
     field public static final long TIME_SHIFT_INVALID_TIME = -9223372036854775808L; // 0x8000000000000000L
     field public static final int TIME_SHIFT_STATUS_AVAILABLE = 3; // 0x3
@@ -23053,6 +23052,8 @@
 
   public static abstract class TvRecordingClient.RecordingCallback {
     ctor public TvRecordingClient.RecordingCallback();
+    method public void onConnectionFailed(java.lang.String);
+    method public void onDisconnected(java.lang.String);
     method public void onError(int);
     method public void onRecordingStopped(android.net.Uri);
     method public void onTuned();
@@ -23067,6 +23068,7 @@
     method public final java.lang.String getId();
     method public final java.lang.String getLanguage();
     method public final int getType();
+    method public final byte getVideoActiveFormatDescription();
     method public final float getVideoFrameRate();
     method public final int getVideoHeight();
     method public final float getVideoPixelAspectRatio();
@@ -23086,6 +23088,7 @@
     method public final android.media.tv.TvTrackInfo.Builder setDescription(java.lang.CharSequence);
     method public final android.media.tv.TvTrackInfo.Builder setExtra(android.os.Bundle);
     method public final android.media.tv.TvTrackInfo.Builder setLanguage(java.lang.String);
+    method public final android.media.tv.TvTrackInfo.Builder setVideoActiveFormatDescription(byte);
     method public final android.media.tv.TvTrackInfo.Builder setVideoFrameRate(float);
     method public final android.media.tv.TvTrackInfo.Builder setVideoHeight(int);
     method public final android.media.tv.TvTrackInfo.Builder setVideoPixelAspectRatio(float);
@@ -29062,6 +29065,7 @@
     method public static final int getThreadPriority(int) throws java.lang.IllegalArgumentException;
     method public static final int getUidForName(java.lang.String);
     method public static final boolean is64Bit();
+    method public static boolean isApplicationUid(int);
     method public static final void killProcess(int);
     method public static final int myPid();
     method public static final int myTid();
@@ -29242,6 +29246,7 @@
   public final class UserHandle implements android.os.Parcelable {
     ctor public UserHandle(android.os.Parcel);
     method public int describeContents();
+    method public static android.os.UserHandle getUserHandleForUid(int);
     method public static android.os.UserHandle readFromParcel(android.os.Parcel);
     method public void writeToParcel(android.os.Parcel, int);
     method public static void writeToParcel(android.os.UserHandle, android.os.Parcel);
@@ -51474,7 +51479,6 @@
     method public static int getLength(java.lang.Object);
     method public static long getLong(java.lang.Object, int) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
     method public static short getShort(java.lang.Object, int) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
-    method public static java.lang.Object newArray(java.lang.Class<?>, int) throws java.lang.NegativeArraySizeException;
     method public static java.lang.Object newInstance(java.lang.Class<?>, int) throws java.lang.NegativeArraySizeException;
     method public static java.lang.Object newInstance(java.lang.Class<?>, int...) throws java.lang.IllegalArgumentException, java.lang.NegativeArraySizeException;
     method public static void set(java.lang.Object, int, java.lang.Object) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
@@ -52150,7 +52154,6 @@
 
   public class InetAddress implements java.io.Serializable {
     method public byte[] getAddress();
-    method public byte[] getAddressInternal();
     method public static java.net.InetAddress[] getAllByName(java.lang.String) throws java.net.UnknownHostException;
     method public static java.net.InetAddress getByAddress(java.lang.String, byte[]) throws java.net.UnknownHostException;
     method public static java.net.InetAddress getByAddress(byte[]) throws java.net.UnknownHostException;
@@ -52424,7 +52427,7 @@
     method protected abstract void connect(java.net.InetAddress, int) throws java.io.IOException;
     method protected abstract void connect(java.net.SocketAddress, int) throws java.io.IOException;
     method protected abstract void create(boolean) throws java.io.IOException;
-    method public java.io.FileDescriptor getFileDescriptor();
+    method protected java.io.FileDescriptor getFileDescriptor();
     method protected java.net.InetAddress getInetAddress();
     method protected abstract java.io.InputStream getInputStream() throws java.io.IOException;
     method protected int getLocalPort();
@@ -52966,10 +52969,6 @@
 
 package java.nio.channels {
 
-  public class AcceptPendingException extends java.lang.IllegalStateException {
-    ctor public AcceptPendingException();
-  }
-
   public class AlreadyBoundException extends java.lang.IllegalStateException {
     ctor public AlreadyBoundException();
   }
@@ -52978,68 +52977,10 @@
     ctor public AlreadyConnectedException();
   }
 
-  public abstract interface AsynchronousByteChannel implements java.nio.channels.AsynchronousChannel {
-    method public abstract void read(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
-    method public abstract java.util.concurrent.Future<java.lang.Integer> read(java.nio.ByteBuffer);
-    method public abstract void write(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
-    method public abstract java.util.concurrent.Future<java.lang.Integer> write(java.nio.ByteBuffer);
-  }
-
-  public abstract interface AsynchronousChannel implements java.nio.channels.Channel {
-    method public abstract void close() throws java.io.IOException;
-  }
-
-  public abstract class AsynchronousChannelGroup {
-    ctor protected AsynchronousChannelGroup(java.nio.channels.spi.AsynchronousChannelProvider);
-    method public abstract boolean awaitTermination(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
-    method public abstract boolean isShutdown();
-    method public abstract boolean isTerminated();
-    method public final java.nio.channels.spi.AsynchronousChannelProvider provider();
-    method public abstract void shutdown();
-    method public abstract void shutdownNow() throws java.io.IOException;
-    method public static java.nio.channels.AsynchronousChannelGroup withCachedThreadPool(java.util.concurrent.ExecutorService, int) throws java.io.IOException;
-    method public static java.nio.channels.AsynchronousChannelGroup withFixedThreadPool(int, java.util.concurrent.ThreadFactory) throws java.io.IOException;
-    method public static java.nio.channels.AsynchronousChannelGroup withThreadPool(java.util.concurrent.ExecutorService) throws java.io.IOException;
-  }
-
   public class AsynchronousCloseException extends java.nio.channels.ClosedChannelException {
     ctor public AsynchronousCloseException();
   }
 
-  public abstract class AsynchronousServerSocketChannel implements java.nio.channels.AsynchronousChannel java.nio.channels.NetworkChannel {
-    ctor protected AsynchronousServerSocketChannel(java.nio.channels.spi.AsynchronousChannelProvider);
-    method public abstract void accept(A, java.nio.channels.CompletionHandler<java.nio.channels.AsynchronousSocketChannel, ? super A>);
-    method public abstract java.util.concurrent.Future<java.nio.channels.AsynchronousSocketChannel> accept();
-    method public final java.nio.channels.AsynchronousServerSocketChannel bind(java.net.SocketAddress) throws java.io.IOException;
-    method public abstract java.nio.channels.AsynchronousServerSocketChannel bind(java.net.SocketAddress, int) throws java.io.IOException;
-    method public static java.nio.channels.AsynchronousServerSocketChannel open(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
-    method public static java.nio.channels.AsynchronousServerSocketChannel open() throws java.io.IOException;
-    method public final java.nio.channels.spi.AsynchronousChannelProvider provider();
-    method public abstract java.nio.channels.AsynchronousServerSocketChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
-  }
-
-  public abstract class AsynchronousSocketChannel implements java.nio.channels.AsynchronousByteChannel java.nio.channels.NetworkChannel {
-    ctor protected AsynchronousSocketChannel(java.nio.channels.spi.AsynchronousChannelProvider);
-    method public abstract java.nio.channels.AsynchronousSocketChannel bind(java.net.SocketAddress) throws java.io.IOException;
-    method public abstract void connect(java.net.SocketAddress, A, java.nio.channels.CompletionHandler<java.lang.Void, ? super A>);
-    method public abstract java.util.concurrent.Future<java.lang.Void> connect(java.net.SocketAddress);
-    method public abstract java.net.SocketAddress getRemoteAddress() throws java.io.IOException;
-    method public static java.nio.channels.AsynchronousSocketChannel open(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
-    method public static java.nio.channels.AsynchronousSocketChannel open() throws java.io.IOException;
-    method public final java.nio.channels.spi.AsynchronousChannelProvider provider();
-    method public abstract void read(java.nio.ByteBuffer, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
-    method public final void read(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
-    method public abstract java.util.concurrent.Future<java.lang.Integer> read(java.nio.ByteBuffer);
-    method public abstract void read(java.nio.ByteBuffer[], int, int, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Long, ? super A>);
-    method public abstract java.nio.channels.AsynchronousSocketChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
-    method public abstract java.nio.channels.AsynchronousSocketChannel shutdownInput() throws java.io.IOException;
-    method public abstract java.nio.channels.AsynchronousSocketChannel shutdownOutput() throws java.io.IOException;
-    method public abstract void write(java.nio.ByteBuffer, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
-    method public final void write(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
-    method public abstract java.util.concurrent.Future<java.lang.Integer> write(java.nio.ByteBuffer);
-    method public abstract void write(java.nio.ByteBuffer[], int, int, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Long, ? super A>);
-  }
-
   public abstract interface ByteChannel implements java.nio.channels.ReadableByteChannel java.nio.channels.WritableByteChannel {
   }
 
@@ -53056,9 +52997,7 @@
     method public static java.nio.channels.ReadableByteChannel newChannel(java.io.InputStream);
     method public static java.nio.channels.WritableByteChannel newChannel(java.io.OutputStream);
     method public static java.io.InputStream newInputStream(java.nio.channels.ReadableByteChannel);
-    method public static java.io.InputStream newInputStream(java.nio.channels.AsynchronousByteChannel);
     method public static java.io.OutputStream newOutputStream(java.nio.channels.WritableByteChannel);
-    method public static java.io.OutputStream newOutputStream(java.nio.channels.AsynchronousByteChannel);
     method public static java.io.Reader newReader(java.nio.channels.ReadableByteChannel, java.nio.charset.CharsetDecoder, int);
     method public static java.io.Reader newReader(java.nio.channels.ReadableByteChannel, java.lang.String);
     method public static java.io.Writer newWriter(java.nio.channels.WritableByteChannel, java.nio.charset.CharsetEncoder, int);
@@ -53077,16 +53016,11 @@
     ctor public ClosedSelectorException();
   }
 
-  public abstract interface CompletionHandler {
-    method public abstract void completed(V, A);
-    method public abstract void failed(java.lang.Throwable, A);
-  }
-
   public class ConnectionPendingException extends java.lang.IllegalStateException {
     ctor public ConnectionPendingException();
   }
 
-  public abstract class DatagramChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.MulticastChannel java.nio.channels.ScatteringByteChannel {
+  public abstract class DatagramChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.ScatteringByteChannel {
     ctor protected DatagramChannel(java.nio.channels.spi.SelectorProvider);
     method public abstract java.nio.channels.DatagramChannel bind(java.net.SocketAddress) throws java.io.IOException;
     method public abstract java.nio.channels.DatagramChannel connect(java.net.SocketAddress) throws java.io.IOException;
@@ -53165,40 +53099,14 @@
     ctor public IllegalBlockingModeException();
   }
 
-  public class IllegalChannelGroupException extends java.lang.IllegalArgumentException {
-    ctor public IllegalChannelGroupException();
-  }
-
   public class IllegalSelectorException extends java.lang.IllegalArgumentException {
     ctor public IllegalSelectorException();
   }
 
-  public class InterruptedByTimeoutException extends java.io.IOException {
-    ctor public InterruptedByTimeoutException();
-  }
-
   public abstract interface InterruptibleChannel implements java.nio.channels.Channel {
     method public abstract void close() throws java.io.IOException;
   }
 
-  public abstract class MembershipKey {
-    ctor protected MembershipKey();
-    method public abstract java.nio.channels.MembershipKey block(java.net.InetAddress) throws java.io.IOException;
-    method public abstract java.nio.channels.MulticastChannel channel();
-    method public abstract void drop();
-    method public abstract java.net.InetAddress group();
-    method public abstract boolean isValid();
-    method public abstract java.net.NetworkInterface networkInterface();
-    method public abstract java.net.InetAddress sourceAddress();
-    method public abstract java.nio.channels.MembershipKey unblock(java.net.InetAddress);
-  }
-
-  public abstract interface MulticastChannel implements java.nio.channels.NetworkChannel {
-    method public abstract void close() throws java.io.IOException;
-    method public abstract java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface) throws java.io.IOException;
-    method public abstract java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface, java.net.InetAddress) throws java.io.IOException;
-  }
-
   public abstract interface NetworkChannel implements java.nio.channels.Channel {
     method public abstract java.nio.channels.NetworkChannel bind(java.net.SocketAddress) throws java.io.IOException;
     method public abstract java.net.SocketAddress getLocalAddress() throws java.io.IOException;
@@ -53248,10 +53156,6 @@
     method public final int validOps();
   }
 
-  public class ReadPendingException extends java.lang.IllegalStateException {
-    ctor public ReadPendingException();
-  }
-
   public abstract interface ReadableByteChannel implements java.nio.channels.Channel {
     method public abstract int read(java.nio.ByteBuffer) throws java.io.IOException;
   }
@@ -53329,10 +53233,6 @@
     method public final int validOps();
   }
 
-  public class ShutdownChannelGroupException extends java.lang.IllegalStateException {
-    ctor public ShutdownChannelGroupException();
-  }
-
   public abstract class SocketChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.NetworkChannel java.nio.channels.ScatteringByteChannel {
     ctor protected SocketChannel(java.nio.channels.spi.SelectorProvider);
     method public abstract java.nio.channels.SocketChannel bind(java.net.SocketAddress) throws java.io.IOException;
@@ -53368,10 +53268,6 @@
     method public abstract int write(java.nio.ByteBuffer) throws java.io.IOException;
   }
 
-  public class WritePendingException extends java.lang.IllegalStateException {
-    ctor public WritePendingException();
-  }
-
 }
 
 package java.nio.channels.spi {
@@ -53418,15 +53314,6 @@
     method protected abstract java.nio.channels.SelectionKey register(java.nio.channels.spi.AbstractSelectableChannel, int, java.lang.Object);
   }
 
-  public abstract class AsynchronousChannelProvider {
-    ctor protected AsynchronousChannelProvider();
-    method public abstract java.nio.channels.AsynchronousChannelGroup openAsynchronousChannelGroup(int, java.util.concurrent.ThreadFactory) throws java.io.IOException;
-    method public abstract java.nio.channels.AsynchronousChannelGroup openAsynchronousChannelGroup(java.util.concurrent.ExecutorService, int) throws java.io.IOException;
-    method public abstract java.nio.channels.AsynchronousServerSocketChannel openAsynchronousServerSocketChannel(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
-    method public abstract java.nio.channels.AsynchronousSocketChannel openAsynchronousSocketChannel(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
-    method public static java.nio.channels.spi.AsynchronousChannelProvider provider();
-  }
-
   public abstract class SelectorProvider {
     ctor protected SelectorProvider();
     method public java.nio.channels.Channel inheritedChannel() throws java.io.IOException;
@@ -56781,13 +56668,11 @@
     method public static final java.text.DecimalFormatSymbols getInstance(java.util.Locale);
     method public java.lang.String getInternationalCurrencySymbol();
     method public char getMinusSign();
-    method public java.lang.String getMinusSignString();
     method public char getMonetaryDecimalSeparator();
     method public java.lang.String getNaN();
     method public char getPatternSeparator();
     method public char getPerMill();
     method public char getPercent();
-    method public java.lang.String getPercentString();
     method public char getZeroDigit();
     method public void setCurrency(java.util.Currency);
     method public void setCurrencySymbol(java.lang.String);
diff --git a/api/system-current.txt b/api/system-current.txt
index 86419ce..ec422c0 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -8818,6 +8818,7 @@
     field public static final java.lang.String ACTION_MANAGED_PROFILE_ADDED = "android.intent.action.MANAGED_PROFILE_ADDED";
     field public static final java.lang.String ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED = "android.intent.action.MANAGED_PROFILE_AVAILABILITY_CHANGED";
     field public static final java.lang.String ACTION_MANAGED_PROFILE_REMOVED = "android.intent.action.MANAGED_PROFILE_REMOVED";
+    field public static final java.lang.String ACTION_MANAGED_PROFILE_UNLOCKED = "android.intent.action.MANAGED_PROFILE_UNLOCKED";
     field public static final java.lang.String ACTION_MANAGE_NETWORK_USAGE = "android.intent.action.MANAGE_NETWORK_USAGE";
     field public static final java.lang.String ACTION_MANAGE_PACKAGE_STORAGE = "android.intent.action.MANAGE_PACKAGE_STORAGE";
     field public static final java.lang.String ACTION_MEDIA_BAD_REMOVAL = "android.intent.action.MEDIA_BAD_REMOVAL";
@@ -20401,7 +20402,6 @@
     method public double getElevationUncertaintyInDeg();
     method public byte getLossOfLock();
     method public byte getMultipathIndicator();
-    method public byte getPrn();
     method public double getPseudorangeInMeters();
     method public double getPseudorangeRateCarrierInMetersPerSec();
     method public double getPseudorangeRateCarrierUncertaintyInMetersPerSec();
@@ -20412,6 +20412,7 @@
     method public long getReceivedGpsTowUncertaintyInNs();
     method public double getSnrInDb();
     method public short getState();
+    method public short getSvid();
     method public short getTimeFromLastBitInMs();
     method public double getTimeOffsetInNs();
     method public boolean hasAzimuthInDeg();
@@ -20471,7 +20472,6 @@
     method public void setElevationUncertaintyInDeg(double);
     method public void setLossOfLock(byte);
     method public void setMultipathIndicator(byte);
-    method public void setPrn(byte);
     method public void setPseudorangeInMeters(double);
     method public void setPseudorangeRateCarrierInMetersPerSec(double);
     method public void setPseudorangeRateCarrierUncertaintyInMetersPerSec(double);
@@ -20482,6 +20482,7 @@
     method public void setReceivedGpsTowUncertaintyInNs(long);
     method public void setSnrInDb(double);
     method public void setState(short);
+    method public void setSvid(short);
     method public void setTimeFromLastBitInMs(short);
     method public void setTimeOffsetInNs(double);
     method public void setUsedInFix(boolean);
@@ -20536,17 +20537,17 @@
     method public int describeContents();
     method public byte[] getData();
     method public short getMessageId();
-    method public byte getPrn();
     method public short getStatus();
     method public short getSubmessageId();
+    method public short getSvid();
     method public byte getType();
     method public void reset();
     method public void set(android.location.GnssNavigationMessage);
     method public void setData(byte[]);
     method public void setMessageId(short);
-    method public void setPrn(byte);
     method public void setStatus(short);
     method public void setSubmessageId(short);
+    method public void setSvid(short);
     method public void setType(byte);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.location.GnssNavigationMessage> CREATOR;
@@ -20592,8 +20593,8 @@
     method public int getConstellationType(int);
     method public float getElevation(int);
     method public int getNumSatellites();
-    method public int getPrn(int);
     method public float getSnr(int);
+    method public int getSvid(int);
     method public boolean hasAlmanac(int);
     method public boolean hasEphemeris(int);
     method public boolean usedInFix(int);
@@ -24649,10 +24650,8 @@
     field public static final int INPUT_STATE_CONNECTED_STANDBY = 1; // 0x1
     field public static final int INPUT_STATE_DISCONNECTED = 2; // 0x2
     field public static final java.lang.String META_DATA_CONTENT_RATING_SYSTEMS = "android.media.tv.metadata.CONTENT_RATING_SYSTEMS";
-    field public static final int RECORDING_ERROR_CONNECTION_FAILED = 1; // 0x1
-    field public static final int RECORDING_ERROR_DISCONNECTED = 2; // 0x2
-    field public static final int RECORDING_ERROR_INSUFFICIENT_SPACE = 3; // 0x3
-    field public static final int RECORDING_ERROR_RESOURCE_BUSY = 4; // 0x4
+    field public static final int RECORDING_ERROR_INSUFFICIENT_SPACE = 1; // 0x1
+    field public static final int RECORDING_ERROR_RESOURCE_BUSY = 2; // 0x2
     field public static final int RECORDING_ERROR_UNKNOWN = 0; // 0x0
     field public static final long TIME_SHIFT_INVALID_TIME = -9223372036854775808L; // 0x8000000000000000L
     field public static final int TIME_SHIFT_STATUS_AVAILABLE = 3; // 0x3
@@ -24811,6 +24810,8 @@
 
   public static abstract class TvRecordingClient.RecordingCallback {
     ctor public TvRecordingClient.RecordingCallback();
+    method public void onConnectionFailed(java.lang.String);
+    method public void onDisconnected(java.lang.String);
     method public void onError(int);
     method public void onEvent(java.lang.String, java.lang.String, android.os.Bundle);
     method public void onRecordingStopped(android.net.Uri);
@@ -24852,6 +24853,7 @@
     method public final java.lang.String getId();
     method public final java.lang.String getLanguage();
     method public final int getType();
+    method public final byte getVideoActiveFormatDescription();
     method public final float getVideoFrameRate();
     method public final int getVideoHeight();
     method public final float getVideoPixelAspectRatio();
@@ -24871,6 +24873,7 @@
     method public final android.media.tv.TvTrackInfo.Builder setDescription(java.lang.CharSequence);
     method public final android.media.tv.TvTrackInfo.Builder setExtra(android.os.Bundle);
     method public final android.media.tv.TvTrackInfo.Builder setLanguage(java.lang.String);
+    method public final android.media.tv.TvTrackInfo.Builder setVideoActiveFormatDescription(byte);
     method public final android.media.tv.TvTrackInfo.Builder setVideoFrameRate(float);
     method public final android.media.tv.TvTrackInfo.Builder setVideoHeight(int);
     method public final android.media.tv.TvTrackInfo.Builder setVideoPixelAspectRatio(float);
@@ -26680,6 +26683,7 @@
     method public boolean reconnect();
     method public boolean removeNetwork(int);
     method public boolean saveConfiguration();
+    method public boolean setMetered(int, boolean);
     method public void setTdlsEnabled(java.net.InetAddress, boolean);
     method public void setTdlsEnabledWithMacAddress(java.lang.String, boolean);
     method public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration);
@@ -31355,6 +31359,7 @@
     method public static final int getThreadPriority(int) throws java.lang.IllegalArgumentException;
     method public static final int getUidForName(java.lang.String);
     method public static final boolean is64Bit();
+    method public static boolean isApplicationUid(int);
     method public static final void killProcess(int);
     method public static final int myPid();
     method public static final int myTid();
@@ -31581,6 +31586,7 @@
     ctor public UserHandle(android.os.Parcel);
     method public int describeContents();
     method public int getIdentifier();
+    method public static android.os.UserHandle getUserHandleForUid(int);
     method public deprecated boolean isOwner();
     method public boolean isSystem();
     method public static int myUserId();
@@ -54562,7 +54568,6 @@
     method public static int getLength(java.lang.Object);
     method public static long getLong(java.lang.Object, int) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
     method public static short getShort(java.lang.Object, int) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
-    method public static java.lang.Object newArray(java.lang.Class<?>, int) throws java.lang.NegativeArraySizeException;
     method public static java.lang.Object newInstance(java.lang.Class<?>, int) throws java.lang.NegativeArraySizeException;
     method public static java.lang.Object newInstance(java.lang.Class<?>, int...) throws java.lang.IllegalArgumentException, java.lang.NegativeArraySizeException;
     method public static void set(java.lang.Object, int, java.lang.Object) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
@@ -55238,7 +55243,6 @@
 
   public class InetAddress implements java.io.Serializable {
     method public byte[] getAddress();
-    method public byte[] getAddressInternal();
     method public static java.net.InetAddress[] getAllByName(java.lang.String) throws java.net.UnknownHostException;
     method public static java.net.InetAddress getByAddress(java.lang.String, byte[]) throws java.net.UnknownHostException;
     method public static java.net.InetAddress getByAddress(byte[]) throws java.net.UnknownHostException;
@@ -55512,7 +55516,7 @@
     method protected abstract void connect(java.net.InetAddress, int) throws java.io.IOException;
     method protected abstract void connect(java.net.SocketAddress, int) throws java.io.IOException;
     method protected abstract void create(boolean) throws java.io.IOException;
-    method public java.io.FileDescriptor getFileDescriptor();
+    method protected java.io.FileDescriptor getFileDescriptor();
     method protected java.net.InetAddress getInetAddress();
     method protected abstract java.io.InputStream getInputStream() throws java.io.IOException;
     method protected int getLocalPort();
@@ -56054,10 +56058,6 @@
 
 package java.nio.channels {
 
-  public class AcceptPendingException extends java.lang.IllegalStateException {
-    ctor public AcceptPendingException();
-  }
-
   public class AlreadyBoundException extends java.lang.IllegalStateException {
     ctor public AlreadyBoundException();
   }
@@ -56066,68 +56066,10 @@
     ctor public AlreadyConnectedException();
   }
 
-  public abstract interface AsynchronousByteChannel implements java.nio.channels.AsynchronousChannel {
-    method public abstract void read(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
-    method public abstract java.util.concurrent.Future<java.lang.Integer> read(java.nio.ByteBuffer);
-    method public abstract void write(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
-    method public abstract java.util.concurrent.Future<java.lang.Integer> write(java.nio.ByteBuffer);
-  }
-
-  public abstract interface AsynchronousChannel implements java.nio.channels.Channel {
-    method public abstract void close() throws java.io.IOException;
-  }
-
-  public abstract class AsynchronousChannelGroup {
-    ctor protected AsynchronousChannelGroup(java.nio.channels.spi.AsynchronousChannelProvider);
-    method public abstract boolean awaitTermination(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
-    method public abstract boolean isShutdown();
-    method public abstract boolean isTerminated();
-    method public final java.nio.channels.spi.AsynchronousChannelProvider provider();
-    method public abstract void shutdown();
-    method public abstract void shutdownNow() throws java.io.IOException;
-    method public static java.nio.channels.AsynchronousChannelGroup withCachedThreadPool(java.util.concurrent.ExecutorService, int) throws java.io.IOException;
-    method public static java.nio.channels.AsynchronousChannelGroup withFixedThreadPool(int, java.util.concurrent.ThreadFactory) throws java.io.IOException;
-    method public static java.nio.channels.AsynchronousChannelGroup withThreadPool(java.util.concurrent.ExecutorService) throws java.io.IOException;
-  }
-
   public class AsynchronousCloseException extends java.nio.channels.ClosedChannelException {
     ctor public AsynchronousCloseException();
   }
 
-  public abstract class AsynchronousServerSocketChannel implements java.nio.channels.AsynchronousChannel java.nio.channels.NetworkChannel {
-    ctor protected AsynchronousServerSocketChannel(java.nio.channels.spi.AsynchronousChannelProvider);
-    method public abstract void accept(A, java.nio.channels.CompletionHandler<java.nio.channels.AsynchronousSocketChannel, ? super A>);
-    method public abstract java.util.concurrent.Future<java.nio.channels.AsynchronousSocketChannel> accept();
-    method public final java.nio.channels.AsynchronousServerSocketChannel bind(java.net.SocketAddress) throws java.io.IOException;
-    method public abstract java.nio.channels.AsynchronousServerSocketChannel bind(java.net.SocketAddress, int) throws java.io.IOException;
-    method public static java.nio.channels.AsynchronousServerSocketChannel open(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
-    method public static java.nio.channels.AsynchronousServerSocketChannel open() throws java.io.IOException;
-    method public final java.nio.channels.spi.AsynchronousChannelProvider provider();
-    method public abstract java.nio.channels.AsynchronousServerSocketChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
-  }
-
-  public abstract class AsynchronousSocketChannel implements java.nio.channels.AsynchronousByteChannel java.nio.channels.NetworkChannel {
-    ctor protected AsynchronousSocketChannel(java.nio.channels.spi.AsynchronousChannelProvider);
-    method public abstract java.nio.channels.AsynchronousSocketChannel bind(java.net.SocketAddress) throws java.io.IOException;
-    method public abstract void connect(java.net.SocketAddress, A, java.nio.channels.CompletionHandler<java.lang.Void, ? super A>);
-    method public abstract java.util.concurrent.Future<java.lang.Void> connect(java.net.SocketAddress);
-    method public abstract java.net.SocketAddress getRemoteAddress() throws java.io.IOException;
-    method public static java.nio.channels.AsynchronousSocketChannel open(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
-    method public static java.nio.channels.AsynchronousSocketChannel open() throws java.io.IOException;
-    method public final java.nio.channels.spi.AsynchronousChannelProvider provider();
-    method public abstract void read(java.nio.ByteBuffer, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
-    method public final void read(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
-    method public abstract java.util.concurrent.Future<java.lang.Integer> read(java.nio.ByteBuffer);
-    method public abstract void read(java.nio.ByteBuffer[], int, int, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Long, ? super A>);
-    method public abstract java.nio.channels.AsynchronousSocketChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
-    method public abstract java.nio.channels.AsynchronousSocketChannel shutdownInput() throws java.io.IOException;
-    method public abstract java.nio.channels.AsynchronousSocketChannel shutdownOutput() throws java.io.IOException;
-    method public abstract void write(java.nio.ByteBuffer, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
-    method public final void write(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
-    method public abstract java.util.concurrent.Future<java.lang.Integer> write(java.nio.ByteBuffer);
-    method public abstract void write(java.nio.ByteBuffer[], int, int, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Long, ? super A>);
-  }
-
   public abstract interface ByteChannel implements java.nio.channels.ReadableByteChannel java.nio.channels.WritableByteChannel {
   }
 
@@ -56144,9 +56086,7 @@
     method public static java.nio.channels.ReadableByteChannel newChannel(java.io.InputStream);
     method public static java.nio.channels.WritableByteChannel newChannel(java.io.OutputStream);
     method public static java.io.InputStream newInputStream(java.nio.channels.ReadableByteChannel);
-    method public static java.io.InputStream newInputStream(java.nio.channels.AsynchronousByteChannel);
     method public static java.io.OutputStream newOutputStream(java.nio.channels.WritableByteChannel);
-    method public static java.io.OutputStream newOutputStream(java.nio.channels.AsynchronousByteChannel);
     method public static java.io.Reader newReader(java.nio.channels.ReadableByteChannel, java.nio.charset.CharsetDecoder, int);
     method public static java.io.Reader newReader(java.nio.channels.ReadableByteChannel, java.lang.String);
     method public static java.io.Writer newWriter(java.nio.channels.WritableByteChannel, java.nio.charset.CharsetEncoder, int);
@@ -56165,16 +56105,11 @@
     ctor public ClosedSelectorException();
   }
 
-  public abstract interface CompletionHandler {
-    method public abstract void completed(V, A);
-    method public abstract void failed(java.lang.Throwable, A);
-  }
-
   public class ConnectionPendingException extends java.lang.IllegalStateException {
     ctor public ConnectionPendingException();
   }
 
-  public abstract class DatagramChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.MulticastChannel java.nio.channels.ScatteringByteChannel {
+  public abstract class DatagramChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.ScatteringByteChannel {
     ctor protected DatagramChannel(java.nio.channels.spi.SelectorProvider);
     method public abstract java.nio.channels.DatagramChannel bind(java.net.SocketAddress) throws java.io.IOException;
     method public abstract java.nio.channels.DatagramChannel connect(java.net.SocketAddress) throws java.io.IOException;
@@ -56253,40 +56188,14 @@
     ctor public IllegalBlockingModeException();
   }
 
-  public class IllegalChannelGroupException extends java.lang.IllegalArgumentException {
-    ctor public IllegalChannelGroupException();
-  }
-
   public class IllegalSelectorException extends java.lang.IllegalArgumentException {
     ctor public IllegalSelectorException();
   }
 
-  public class InterruptedByTimeoutException extends java.io.IOException {
-    ctor public InterruptedByTimeoutException();
-  }
-
   public abstract interface InterruptibleChannel implements java.nio.channels.Channel {
     method public abstract void close() throws java.io.IOException;
   }
 
-  public abstract class MembershipKey {
-    ctor protected MembershipKey();
-    method public abstract java.nio.channels.MembershipKey block(java.net.InetAddress) throws java.io.IOException;
-    method public abstract java.nio.channels.MulticastChannel channel();
-    method public abstract void drop();
-    method public abstract java.net.InetAddress group();
-    method public abstract boolean isValid();
-    method public abstract java.net.NetworkInterface networkInterface();
-    method public abstract java.net.InetAddress sourceAddress();
-    method public abstract java.nio.channels.MembershipKey unblock(java.net.InetAddress);
-  }
-
-  public abstract interface MulticastChannel implements java.nio.channels.NetworkChannel {
-    method public abstract void close() throws java.io.IOException;
-    method public abstract java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface) throws java.io.IOException;
-    method public abstract java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface, java.net.InetAddress) throws java.io.IOException;
-  }
-
   public abstract interface NetworkChannel implements java.nio.channels.Channel {
     method public abstract java.nio.channels.NetworkChannel bind(java.net.SocketAddress) throws java.io.IOException;
     method public abstract java.net.SocketAddress getLocalAddress() throws java.io.IOException;
@@ -56336,10 +56245,6 @@
     method public final int validOps();
   }
 
-  public class ReadPendingException extends java.lang.IllegalStateException {
-    ctor public ReadPendingException();
-  }
-
   public abstract interface ReadableByteChannel implements java.nio.channels.Channel {
     method public abstract int read(java.nio.ByteBuffer) throws java.io.IOException;
   }
@@ -56417,10 +56322,6 @@
     method public final int validOps();
   }
 
-  public class ShutdownChannelGroupException extends java.lang.IllegalStateException {
-    ctor public ShutdownChannelGroupException();
-  }
-
   public abstract class SocketChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.NetworkChannel java.nio.channels.ScatteringByteChannel {
     ctor protected SocketChannel(java.nio.channels.spi.SelectorProvider);
     method public abstract java.nio.channels.SocketChannel bind(java.net.SocketAddress) throws java.io.IOException;
@@ -56456,10 +56357,6 @@
     method public abstract int write(java.nio.ByteBuffer) throws java.io.IOException;
   }
 
-  public class WritePendingException extends java.lang.IllegalStateException {
-    ctor public WritePendingException();
-  }
-
 }
 
 package java.nio.channels.spi {
@@ -56506,15 +56403,6 @@
     method protected abstract java.nio.channels.SelectionKey register(java.nio.channels.spi.AbstractSelectableChannel, int, java.lang.Object);
   }
 
-  public abstract class AsynchronousChannelProvider {
-    ctor protected AsynchronousChannelProvider();
-    method public abstract java.nio.channels.AsynchronousChannelGroup openAsynchronousChannelGroup(int, java.util.concurrent.ThreadFactory) throws java.io.IOException;
-    method public abstract java.nio.channels.AsynchronousChannelGroup openAsynchronousChannelGroup(java.util.concurrent.ExecutorService, int) throws java.io.IOException;
-    method public abstract java.nio.channels.AsynchronousServerSocketChannel openAsynchronousServerSocketChannel(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
-    method public abstract java.nio.channels.AsynchronousSocketChannel openAsynchronousSocketChannel(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
-    method public static java.nio.channels.spi.AsynchronousChannelProvider provider();
-  }
-
   public abstract class SelectorProvider {
     ctor protected SelectorProvider();
     method public java.nio.channels.Channel inheritedChannel() throws java.io.IOException;
@@ -59869,13 +59757,11 @@
     method public static final java.text.DecimalFormatSymbols getInstance(java.util.Locale);
     method public java.lang.String getInternationalCurrencySymbol();
     method public char getMinusSign();
-    method public java.lang.String getMinusSignString();
     method public char getMonetaryDecimalSeparator();
     method public java.lang.String getNaN();
     method public char getPatternSeparator();
     method public char getPerMill();
     method public char getPercent();
-    method public java.lang.String getPercentString();
     method public char getZeroDigit();
     method public void setCurrency(java.util.Currency);
     method public void setCurrencySymbol(java.lang.String);
diff --git a/api/test-current.txt b/api/test-current.txt
index 675b993..d202108 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -8517,6 +8517,7 @@
     field public static final java.lang.String ACTION_MANAGED_PROFILE_ADDED = "android.intent.action.MANAGED_PROFILE_ADDED";
     field public static final java.lang.String ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED = "android.intent.action.MANAGED_PROFILE_AVAILABILITY_CHANGED";
     field public static final java.lang.String ACTION_MANAGED_PROFILE_REMOVED = "android.intent.action.MANAGED_PROFILE_REMOVED";
+    field public static final java.lang.String ACTION_MANAGED_PROFILE_UNLOCKED = "android.intent.action.MANAGED_PROFILE_UNLOCKED";
     field public static final java.lang.String ACTION_MANAGE_NETWORK_USAGE = "android.intent.action.MANAGE_NETWORK_USAGE";
     field public static final java.lang.String ACTION_MANAGE_PACKAGE_STORAGE = "android.intent.action.MANAGE_PACKAGE_STORAGE";
     field public static final java.lang.String ACTION_MEDIA_BAD_REMOVAL = "android.intent.action.MEDIA_BAD_REMOVAL";
@@ -19229,7 +19230,6 @@
     method public double getElevationUncertaintyInDeg();
     method public byte getLossOfLock();
     method public byte getMultipathIndicator();
-    method public byte getPrn();
     method public double getPseudorangeInMeters();
     method public double getPseudorangeRateCarrierInMetersPerSec();
     method public double getPseudorangeRateCarrierUncertaintyInMetersPerSec();
@@ -19240,6 +19240,7 @@
     method public long getReceivedGpsTowUncertaintyInNs();
     method public double getSnrInDb();
     method public short getState();
+    method public short getSvid();
     method public short getTimeFromLastBitInMs();
     method public double getTimeOffsetInNs();
     method public boolean hasAzimuthInDeg();
@@ -19299,7 +19300,6 @@
     method public void setElevationUncertaintyInDeg(double);
     method public void setLossOfLock(byte);
     method public void setMultipathIndicator(byte);
-    method public void setPrn(byte);
     method public void setPseudorangeInMeters(double);
     method public void setPseudorangeRateCarrierInMetersPerSec(double);
     method public void setPseudorangeRateCarrierUncertaintyInMetersPerSec(double);
@@ -19310,6 +19310,7 @@
     method public void setReceivedGpsTowUncertaintyInNs(long);
     method public void setSnrInDb(double);
     method public void setState(short);
+    method public void setSvid(short);
     method public void setTimeFromLastBitInMs(short);
     method public void setTimeOffsetInNs(double);
     method public void setUsedInFix(boolean);
@@ -19364,17 +19365,17 @@
     method public int describeContents();
     method public byte[] getData();
     method public short getMessageId();
-    method public byte getPrn();
     method public short getStatus();
     method public short getSubmessageId();
+    method public short getSvid();
     method public byte getType();
     method public void reset();
     method public void set(android.location.GnssNavigationMessage);
     method public void setData(byte[]);
     method public void setMessageId(short);
-    method public void setPrn(byte);
     method public void setStatus(short);
     method public void setSubmessageId(short);
+    method public void setSvid(short);
     method public void setType(byte);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.location.GnssNavigationMessage> CREATOR;
@@ -19420,8 +19421,8 @@
     method public int getConstellationType(int);
     method public float getElevation(int);
     method public int getNumSatellites();
-    method public int getPrn(int);
     method public float getSnr(int);
+    method public int getSvid(int);
     method public boolean hasAlmanac(int);
     method public boolean hasEphemeris(int);
     method public boolean usedInFix(int);
@@ -22959,10 +22960,8 @@
     field public static final int INPUT_STATE_CONNECTED_STANDBY = 1; // 0x1
     field public static final int INPUT_STATE_DISCONNECTED = 2; // 0x2
     field public static final java.lang.String META_DATA_CONTENT_RATING_SYSTEMS = "android.media.tv.metadata.CONTENT_RATING_SYSTEMS";
-    field public static final int RECORDING_ERROR_CONNECTION_FAILED = 1; // 0x1
-    field public static final int RECORDING_ERROR_DISCONNECTED = 2; // 0x2
-    field public static final int RECORDING_ERROR_INSUFFICIENT_SPACE = 3; // 0x3
-    field public static final int RECORDING_ERROR_RESOURCE_BUSY = 4; // 0x4
+    field public static final int RECORDING_ERROR_INSUFFICIENT_SPACE = 1; // 0x1
+    field public static final int RECORDING_ERROR_RESOURCE_BUSY = 2; // 0x2
     field public static final int RECORDING_ERROR_UNKNOWN = 0; // 0x0
     field public static final long TIME_SHIFT_INVALID_TIME = -9223372036854775808L; // 0x8000000000000000L
     field public static final int TIME_SHIFT_STATUS_AVAILABLE = 3; // 0x3
@@ -23062,6 +23061,8 @@
 
   public static abstract class TvRecordingClient.RecordingCallback {
     ctor public TvRecordingClient.RecordingCallback();
+    method public void onConnectionFailed(java.lang.String);
+    method public void onDisconnected(java.lang.String);
     method public void onError(int);
     method public void onRecordingStopped(android.net.Uri);
     method public void onTuned();
@@ -23076,6 +23077,7 @@
     method public final java.lang.String getId();
     method public final java.lang.String getLanguage();
     method public final int getType();
+    method public final byte getVideoActiveFormatDescription();
     method public final float getVideoFrameRate();
     method public final int getVideoHeight();
     method public final float getVideoPixelAspectRatio();
@@ -23095,6 +23097,7 @@
     method public final android.media.tv.TvTrackInfo.Builder setDescription(java.lang.CharSequence);
     method public final android.media.tv.TvTrackInfo.Builder setExtra(android.os.Bundle);
     method public final android.media.tv.TvTrackInfo.Builder setLanguage(java.lang.String);
+    method public final android.media.tv.TvTrackInfo.Builder setVideoActiveFormatDescription(byte);
     method public final android.media.tv.TvTrackInfo.Builder setVideoFrameRate(float);
     method public final android.media.tv.TvTrackInfo.Builder setVideoHeight(int);
     method public final android.media.tv.TvTrackInfo.Builder setVideoPixelAspectRatio(float);
@@ -29071,6 +29074,7 @@
     method public static final int getThreadPriority(int) throws java.lang.IllegalArgumentException;
     method public static final int getUidForName(java.lang.String);
     method public static final boolean is64Bit();
+    method public static boolean isApplicationUid(int);
     method public static final void killProcess(int);
     method public static final int myPid();
     method public static final int myTid();
@@ -29252,6 +29256,7 @@
     ctor public UserHandle(android.os.Parcel);
     method public int describeContents();
     method public static int getAppId(int);
+    method public static android.os.UserHandle getUserHandleForUid(int);
     method public static android.os.UserHandle readFromParcel(android.os.Parcel);
     method public void writeToParcel(android.os.Parcel, int);
     method public static void writeToParcel(android.os.UserHandle, android.os.Parcel);
@@ -51491,7 +51496,6 @@
     method public static int getLength(java.lang.Object);
     method public static long getLong(java.lang.Object, int) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
     method public static short getShort(java.lang.Object, int) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
-    method public static java.lang.Object newArray(java.lang.Class<?>, int) throws java.lang.NegativeArraySizeException;
     method public static java.lang.Object newInstance(java.lang.Class<?>, int) throws java.lang.NegativeArraySizeException;
     method public static java.lang.Object newInstance(java.lang.Class<?>, int...) throws java.lang.IllegalArgumentException, java.lang.NegativeArraySizeException;
     method public static void set(java.lang.Object, int, java.lang.Object) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
@@ -52167,7 +52171,6 @@
 
   public class InetAddress implements java.io.Serializable {
     method public byte[] getAddress();
-    method public byte[] getAddressInternal();
     method public static java.net.InetAddress[] getAllByName(java.lang.String) throws java.net.UnknownHostException;
     method public static java.net.InetAddress getByAddress(java.lang.String, byte[]) throws java.net.UnknownHostException;
     method public static java.net.InetAddress getByAddress(byte[]) throws java.net.UnknownHostException;
@@ -52441,7 +52444,7 @@
     method protected abstract void connect(java.net.InetAddress, int) throws java.io.IOException;
     method protected abstract void connect(java.net.SocketAddress, int) throws java.io.IOException;
     method protected abstract void create(boolean) throws java.io.IOException;
-    method public java.io.FileDescriptor getFileDescriptor();
+    method protected java.io.FileDescriptor getFileDescriptor();
     method protected java.net.InetAddress getInetAddress();
     method protected abstract java.io.InputStream getInputStream() throws java.io.IOException;
     method protected int getLocalPort();
@@ -52983,10 +52986,6 @@
 
 package java.nio.channels {
 
-  public class AcceptPendingException extends java.lang.IllegalStateException {
-    ctor public AcceptPendingException();
-  }
-
   public class AlreadyBoundException extends java.lang.IllegalStateException {
     ctor public AlreadyBoundException();
   }
@@ -52995,68 +52994,10 @@
     ctor public AlreadyConnectedException();
   }
 
-  public abstract interface AsynchronousByteChannel implements java.nio.channels.AsynchronousChannel {
-    method public abstract void read(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
-    method public abstract java.util.concurrent.Future<java.lang.Integer> read(java.nio.ByteBuffer);
-    method public abstract void write(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
-    method public abstract java.util.concurrent.Future<java.lang.Integer> write(java.nio.ByteBuffer);
-  }
-
-  public abstract interface AsynchronousChannel implements java.nio.channels.Channel {
-    method public abstract void close() throws java.io.IOException;
-  }
-
-  public abstract class AsynchronousChannelGroup {
-    ctor protected AsynchronousChannelGroup(java.nio.channels.spi.AsynchronousChannelProvider);
-    method public abstract boolean awaitTermination(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
-    method public abstract boolean isShutdown();
-    method public abstract boolean isTerminated();
-    method public final java.nio.channels.spi.AsynchronousChannelProvider provider();
-    method public abstract void shutdown();
-    method public abstract void shutdownNow() throws java.io.IOException;
-    method public static java.nio.channels.AsynchronousChannelGroup withCachedThreadPool(java.util.concurrent.ExecutorService, int) throws java.io.IOException;
-    method public static java.nio.channels.AsynchronousChannelGroup withFixedThreadPool(int, java.util.concurrent.ThreadFactory) throws java.io.IOException;
-    method public static java.nio.channels.AsynchronousChannelGroup withThreadPool(java.util.concurrent.ExecutorService) throws java.io.IOException;
-  }
-
   public class AsynchronousCloseException extends java.nio.channels.ClosedChannelException {
     ctor public AsynchronousCloseException();
   }
 
-  public abstract class AsynchronousServerSocketChannel implements java.nio.channels.AsynchronousChannel java.nio.channels.NetworkChannel {
-    ctor protected AsynchronousServerSocketChannel(java.nio.channels.spi.AsynchronousChannelProvider);
-    method public abstract void accept(A, java.nio.channels.CompletionHandler<java.nio.channels.AsynchronousSocketChannel, ? super A>);
-    method public abstract java.util.concurrent.Future<java.nio.channels.AsynchronousSocketChannel> accept();
-    method public final java.nio.channels.AsynchronousServerSocketChannel bind(java.net.SocketAddress) throws java.io.IOException;
-    method public abstract java.nio.channels.AsynchronousServerSocketChannel bind(java.net.SocketAddress, int) throws java.io.IOException;
-    method public static java.nio.channels.AsynchronousServerSocketChannel open(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
-    method public static java.nio.channels.AsynchronousServerSocketChannel open() throws java.io.IOException;
-    method public final java.nio.channels.spi.AsynchronousChannelProvider provider();
-    method public abstract java.nio.channels.AsynchronousServerSocketChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
-  }
-
-  public abstract class AsynchronousSocketChannel implements java.nio.channels.AsynchronousByteChannel java.nio.channels.NetworkChannel {
-    ctor protected AsynchronousSocketChannel(java.nio.channels.spi.AsynchronousChannelProvider);
-    method public abstract java.nio.channels.AsynchronousSocketChannel bind(java.net.SocketAddress) throws java.io.IOException;
-    method public abstract void connect(java.net.SocketAddress, A, java.nio.channels.CompletionHandler<java.lang.Void, ? super A>);
-    method public abstract java.util.concurrent.Future<java.lang.Void> connect(java.net.SocketAddress);
-    method public abstract java.net.SocketAddress getRemoteAddress() throws java.io.IOException;
-    method public static java.nio.channels.AsynchronousSocketChannel open(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
-    method public static java.nio.channels.AsynchronousSocketChannel open() throws java.io.IOException;
-    method public final java.nio.channels.spi.AsynchronousChannelProvider provider();
-    method public abstract void read(java.nio.ByteBuffer, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
-    method public final void read(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
-    method public abstract java.util.concurrent.Future<java.lang.Integer> read(java.nio.ByteBuffer);
-    method public abstract void read(java.nio.ByteBuffer[], int, int, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Long, ? super A>);
-    method public abstract java.nio.channels.AsynchronousSocketChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
-    method public abstract java.nio.channels.AsynchronousSocketChannel shutdownInput() throws java.io.IOException;
-    method public abstract java.nio.channels.AsynchronousSocketChannel shutdownOutput() throws java.io.IOException;
-    method public abstract void write(java.nio.ByteBuffer, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
-    method public final void write(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
-    method public abstract java.util.concurrent.Future<java.lang.Integer> write(java.nio.ByteBuffer);
-    method public abstract void write(java.nio.ByteBuffer[], int, int, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Long, ? super A>);
-  }
-
   public abstract interface ByteChannel implements java.nio.channels.ReadableByteChannel java.nio.channels.WritableByteChannel {
   }
 
@@ -53073,9 +53014,7 @@
     method public static java.nio.channels.ReadableByteChannel newChannel(java.io.InputStream);
     method public static java.nio.channels.WritableByteChannel newChannel(java.io.OutputStream);
     method public static java.io.InputStream newInputStream(java.nio.channels.ReadableByteChannel);
-    method public static java.io.InputStream newInputStream(java.nio.channels.AsynchronousByteChannel);
     method public static java.io.OutputStream newOutputStream(java.nio.channels.WritableByteChannel);
-    method public static java.io.OutputStream newOutputStream(java.nio.channels.AsynchronousByteChannel);
     method public static java.io.Reader newReader(java.nio.channels.ReadableByteChannel, java.nio.charset.CharsetDecoder, int);
     method public static java.io.Reader newReader(java.nio.channels.ReadableByteChannel, java.lang.String);
     method public static java.io.Writer newWriter(java.nio.channels.WritableByteChannel, java.nio.charset.CharsetEncoder, int);
@@ -53094,16 +53033,11 @@
     ctor public ClosedSelectorException();
   }
 
-  public abstract interface CompletionHandler {
-    method public abstract void completed(V, A);
-    method public abstract void failed(java.lang.Throwable, A);
-  }
-
   public class ConnectionPendingException extends java.lang.IllegalStateException {
     ctor public ConnectionPendingException();
   }
 
-  public abstract class DatagramChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.MulticastChannel java.nio.channels.ScatteringByteChannel {
+  public abstract class DatagramChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.ScatteringByteChannel {
     ctor protected DatagramChannel(java.nio.channels.spi.SelectorProvider);
     method public abstract java.nio.channels.DatagramChannel bind(java.net.SocketAddress) throws java.io.IOException;
     method public abstract java.nio.channels.DatagramChannel connect(java.net.SocketAddress) throws java.io.IOException;
@@ -53182,40 +53116,14 @@
     ctor public IllegalBlockingModeException();
   }
 
-  public class IllegalChannelGroupException extends java.lang.IllegalArgumentException {
-    ctor public IllegalChannelGroupException();
-  }
-
   public class IllegalSelectorException extends java.lang.IllegalArgumentException {
     ctor public IllegalSelectorException();
   }
 
-  public class InterruptedByTimeoutException extends java.io.IOException {
-    ctor public InterruptedByTimeoutException();
-  }
-
   public abstract interface InterruptibleChannel implements java.nio.channels.Channel {
     method public abstract void close() throws java.io.IOException;
   }
 
-  public abstract class MembershipKey {
-    ctor protected MembershipKey();
-    method public abstract java.nio.channels.MembershipKey block(java.net.InetAddress) throws java.io.IOException;
-    method public abstract java.nio.channels.MulticastChannel channel();
-    method public abstract void drop();
-    method public abstract java.net.InetAddress group();
-    method public abstract boolean isValid();
-    method public abstract java.net.NetworkInterface networkInterface();
-    method public abstract java.net.InetAddress sourceAddress();
-    method public abstract java.nio.channels.MembershipKey unblock(java.net.InetAddress);
-  }
-
-  public abstract interface MulticastChannel implements java.nio.channels.NetworkChannel {
-    method public abstract void close() throws java.io.IOException;
-    method public abstract java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface) throws java.io.IOException;
-    method public abstract java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface, java.net.InetAddress) throws java.io.IOException;
-  }
-
   public abstract interface NetworkChannel implements java.nio.channels.Channel {
     method public abstract java.nio.channels.NetworkChannel bind(java.net.SocketAddress) throws java.io.IOException;
     method public abstract java.net.SocketAddress getLocalAddress() throws java.io.IOException;
@@ -53265,10 +53173,6 @@
     method public final int validOps();
   }
 
-  public class ReadPendingException extends java.lang.IllegalStateException {
-    ctor public ReadPendingException();
-  }
-
   public abstract interface ReadableByteChannel implements java.nio.channels.Channel {
     method public abstract int read(java.nio.ByteBuffer) throws java.io.IOException;
   }
@@ -53346,10 +53250,6 @@
     method public final int validOps();
   }
 
-  public class ShutdownChannelGroupException extends java.lang.IllegalStateException {
-    ctor public ShutdownChannelGroupException();
-  }
-
   public abstract class SocketChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.NetworkChannel java.nio.channels.ScatteringByteChannel {
     ctor protected SocketChannel(java.nio.channels.spi.SelectorProvider);
     method public abstract java.nio.channels.SocketChannel bind(java.net.SocketAddress) throws java.io.IOException;
@@ -53385,10 +53285,6 @@
     method public abstract int write(java.nio.ByteBuffer) throws java.io.IOException;
   }
 
-  public class WritePendingException extends java.lang.IllegalStateException {
-    ctor public WritePendingException();
-  }
-
 }
 
 package java.nio.channels.spi {
@@ -53435,15 +53331,6 @@
     method protected abstract java.nio.channels.SelectionKey register(java.nio.channels.spi.AbstractSelectableChannel, int, java.lang.Object);
   }
 
-  public abstract class AsynchronousChannelProvider {
-    ctor protected AsynchronousChannelProvider();
-    method public abstract java.nio.channels.AsynchronousChannelGroup openAsynchronousChannelGroup(int, java.util.concurrent.ThreadFactory) throws java.io.IOException;
-    method public abstract java.nio.channels.AsynchronousChannelGroup openAsynchronousChannelGroup(java.util.concurrent.ExecutorService, int) throws java.io.IOException;
-    method public abstract java.nio.channels.AsynchronousServerSocketChannel openAsynchronousServerSocketChannel(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
-    method public abstract java.nio.channels.AsynchronousSocketChannel openAsynchronousSocketChannel(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
-    method public static java.nio.channels.spi.AsynchronousChannelProvider provider();
-  }
-
   public abstract class SelectorProvider {
     ctor protected SelectorProvider();
     method public java.nio.channels.Channel inheritedChannel() throws java.io.IOException;
@@ -56798,13 +56685,11 @@
     method public static final java.text.DecimalFormatSymbols getInstance(java.util.Locale);
     method public java.lang.String getInternationalCurrencySymbol();
     method public char getMinusSign();
-    method public java.lang.String getMinusSignString();
     method public char getMonetaryDecimalSeparator();
     method public java.lang.String getNaN();
     method public char getPatternSeparator();
     method public char getPerMill();
     method public char getPercent();
-    method public java.lang.String getPercentString();
     method public char getZeroDigit();
     method public void setCurrency(java.util.Currency);
     method public void setCurrencySymbol(java.lang.String);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 622012e..ea58e29 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -16,6 +16,8 @@
 
 package android.app;
 
+import static java.lang.Character.MIN_VALUE;
+
 import android.annotation.CallSuper;
 import android.annotation.DrawableRes;
 import android.annotation.IdRes;
@@ -26,20 +28,6 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.StyleRes;
-import android.os.PersistableBundle;
-import android.transition.Scene;
-import android.transition.TransitionManager;
-import android.util.ArrayMap;
-import android.util.SuperNotCalledException;
-import android.view.DragEvent;
-import android.view.DropPermissions;
-import android.view.Window.WindowControllerCallback;
-import android.widget.Toolbar;
-
-import com.android.internal.app.IVoiceInteractor;
-import com.android.internal.app.WindowDecorActionBar;
-import com.android.internal.app.ToolbarActionBar;
-
 import android.annotation.SystemApi;
 import android.app.admin.DevicePolicyManager;
 import android.app.assist.AssistContent;
@@ -61,7 +49,12 @@
 import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.InsetDrawable;
+import android.graphics.drawable.LayerDrawable;
+import android.graphics.drawable.ShapeDrawable;
 import android.media.AudioManager;
 import android.media.session.MediaController;
 import android.net.Uri;
@@ -71,6 +64,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Parcelable;
+import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.StrictMode;
 import android.os.UserHandle;
@@ -78,16 +72,22 @@
 import android.text.SpannableStringBuilder;
 import android.text.TextUtils;
 import android.text.method.TextKeyListener;
+import android.transition.Scene;
+import android.transition.TransitionManager;
+import android.util.ArrayMap;
 import android.util.AttributeSet;
 import android.util.EventLog;
 import android.util.Log;
 import android.util.PrintWriterPrinter;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SuperNotCalledException;
 import android.view.ActionMode;
 import android.view.ContextMenu;
 import android.view.ContextMenu.ContextMenuInfo;
 import android.view.ContextThemeWrapper;
+import android.view.DragEvent;
+import android.view.DropPermissions;
 import android.view.KeyEvent;
 import android.view.KeyboardShortcutGroup;
 import android.view.KeyboardShortcutInfo;
@@ -104,11 +104,17 @@
 import android.view.ViewManager;
 import android.view.ViewRootImpl;
 import android.view.Window;
+import android.view.Window.WindowControllerCallback;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.AdapterView;
+import android.widget.Toolbar;
 
+import com.android.internal.app.IVoiceInteractor;
+import com.android.internal.app.ToolbarActionBar;
+import com.android.internal.app.WindowDecorActionBar;
+import com.android.internal.policy.DecorView;
 import com.android.internal.policy.PhoneWindow;
 
 import java.io.FileDescriptor;
@@ -119,8 +125,6 @@
 import java.util.HashMap;
 import java.util.List;
 
-import static java.lang.Character.MIN_VALUE;
-
 /**
  * An activity is a single, focused thing that the user can do.  Almost all
  * activities interact with the user, so the Activity class takes care of
@@ -3974,17 +3978,52 @@
         // Get the primary color and update the TaskDescription for this activity
         if (theme != null) {
             TypedArray a = theme.obtainStyledAttributes(com.android.internal.R.styleable.Theme);
+            int windowBgResourceId = a.getResourceId(
+                    com.android.internal.R.styleable.Window_windowBackground, 0);
+            int windowBgFallbackResourceId = a.getResourceId(
+                    com.android.internal.R.styleable.Window_windowBackgroundFallback, 0);
             int colorPrimary = a.getColor(com.android.internal.R.styleable.Theme_colorPrimary, 0);
+            int colorBg = tryExtractColorFromDrawable(DecorView.getResizingBackgroundDrawable(this,
+                    windowBgResourceId, windowBgFallbackResourceId));
             a.recycle();
             if (colorPrimary != 0) {
-                ActivityManager.TaskDescription v = new ActivityManager.TaskDescription(null, null,
-                        colorPrimary);
-                setTaskDescription(v);
+                ActivityManager.TaskDescription td = new ActivityManager.TaskDescription();
+                td.setPrimaryColor(colorPrimary);
+                td.setBackgroundColor(colorBg);
+                setTaskDescription(td);
             }
         }
     }
 
     /**
+     * Attempts to extract the color from a given drawable.
+     *
+     * @return the extracted color or 0 if no color could be extracted.
+     */
+    private int tryExtractColorFromDrawable(Drawable drawable) {
+        if (drawable instanceof ColorDrawable) {
+            return ((ColorDrawable) drawable).getColor();
+        } else if (drawable instanceof InsetDrawable) {
+            return tryExtractColorFromDrawable(((InsetDrawable) drawable).getDrawable());
+        } else if (drawable instanceof ShapeDrawable) {
+            Paint p = ((ShapeDrawable) drawable).getPaint();
+            if (p != null) {
+                return p.getColor();
+            }
+        } else if (drawable instanceof LayerDrawable) {
+            LayerDrawable ld = (LayerDrawable) drawable;
+            int numLayers = ld.getNumberOfLayers();
+            for (int i = 0; i < numLayers; i++) {
+                int color = tryExtractColorFromDrawable(ld.getDrawable(i));
+                if (color != 0) {
+                    return color;
+                }
+            }
+        }
+        return 0;
+    }
+
+    /**
      * Requests permissions to be granted to this application. These permissions
      * must be requested in your manifest, they should not be granted to your app,
      * and they should have protection level {@link android.content.pm.PermissionInfo
@@ -5612,8 +5651,8 @@
         if (taskDescription.getIconFilename() == null && taskDescription.getIcon() != null) {
             final int size = ActivityManager.getLauncherLargeIconSizeInner(this);
             final Bitmap icon = Bitmap.createScaledBitmap(taskDescription.getIcon(), size, size, true);
-            td = new ActivityManager.TaskDescription(taskDescription.getLabel(), icon,
-                    taskDescription.getPrimaryColor());
+            td = new ActivityManager.TaskDescription(taskDescription);
+            td.setIcon(icon);
         } else {
             td = taskDescription;
         }
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 1eb2fe2..dd73261 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -632,6 +632,17 @@
         public static boolean useWindowFrameForBackdrop(int stackId) {
             return stackId == FREEFORM_WORKSPACE_STACK_ID || stackId == PINNED_STACK_ID;
         }
+
+        /**
+         * Returns true if a window from the specified stack with {@param stackId} are normally
+         * fullscreen, i. e. they can become the top opaque fullscreen window, meaning that it
+         * controls system bars, lockscreen occluded/dismissing state, screen rotation animation,
+         * etc.
+         */
+        public static boolean normallyFullscreenWindows(int stackId) {
+            return stackId != PINNED_STACK_ID && stackId != FREEFORM_WORKSPACE_STACK_ID
+                    && stackId != DOCKED_STACK_ID;
+        }
     }
 
     /**
@@ -863,8 +874,10 @@
         public static final String ATTR_TASKDESCRIPTION_PREFIX = "task_description_";
         private static final String ATTR_TASKDESCRIPTIONLABEL =
                 ATTR_TASKDESCRIPTION_PREFIX + "label";
-        private static final String ATTR_TASKDESCRIPTIONCOLOR =
+        private static final String ATTR_TASKDESCRIPTIONCOLOR_PRIMARY =
                 ATTR_TASKDESCRIPTION_PREFIX + "color";
+        private static final String ATTR_TASKDESCRIPTIONCOLOR_BACKGROUND =
+                ATTR_TASKDESCRIPTION_PREFIX + "colorBackground";
         private static final String ATTR_TASKDESCRIPTIONICONFILENAME =
                 ATTR_TASKDESCRIPTION_PREFIX + "icon_filename";
 
@@ -872,28 +885,21 @@
         private Bitmap mIcon;
         private String mIconFilename;
         private int mColorPrimary;
+        private int mColorBackground;
 
         /**
          * Creates the TaskDescription to the specified values.
          *
          * @param label A label and description of the current state of this task.
          * @param icon An icon that represents the current state of this task.
-         * @param colorPrimary A color to override the theme's primary color.  This color must be opaque.
+         * @param colorPrimary A color to override the theme's primary color.  This color must be
+         *                     opaque.
          */
         public TaskDescription(String label, Bitmap icon, int colorPrimary) {
+            this(label, icon, null, colorPrimary, 0);
             if ((colorPrimary != 0) && (Color.alpha(colorPrimary) != 255)) {
                 throw new RuntimeException("A TaskDescription's primary color should be opaque");
             }
-
-            mLabel = label;
-            mIcon = icon;
-            mColorPrimary = colorPrimary;
-        }
-
-        /** @hide */
-        public TaskDescription(String label, int colorPrimary, String iconFilename) {
-            this(label, null, colorPrimary);
-            mIconFilename = iconFilename;
         }
 
         /**
@@ -903,7 +909,7 @@
          * @param icon An icon that represents the current state of this activity.
          */
         public TaskDescription(String label, Bitmap icon) {
-            this(label, icon, 0);
+            this(label, icon, null, 0, 0);
         }
 
         /**
@@ -912,14 +918,24 @@
          * @param label A label and description of the current state of this activity.
          */
         public TaskDescription(String label) {
-            this(label, null, 0);
+            this(label, null, null, 0, 0);
         }
 
         /**
          * Creates an empty TaskDescription.
          */
         public TaskDescription() {
-            this(null, null, 0);
+            this(null, null, null, 0, 0);
+        }
+
+        /** @hide */
+        public TaskDescription(String label, Bitmap icon, String iconFilename, int colorPrimary,
+                int colorBackground) {
+            mLabel = label;
+            mIcon = icon;
+            mIconFilename = iconFilename;
+            mColorPrimary = colorPrimary;
+            mColorBackground = colorBackground;
         }
 
         /**
@@ -928,8 +944,9 @@
         public TaskDescription(TaskDescription td) {
             mLabel = td.mLabel;
             mIcon = td.mIcon;
-            mColorPrimary = td.mColorPrimary;
             mIconFilename = td.mIconFilename;
+            mColorPrimary = td.mColorPrimary;
+            mColorBackground = td.mColorBackground;
         }
 
         private TaskDescription(Parcel source) {
@@ -957,6 +974,18 @@
         }
 
         /**
+         * Sets the background color for this task description.
+         * @hide
+         */
+        public void setBackgroundColor(int backgroundColor) {
+            // Ensure that the given color is valid
+            if ((backgroundColor != 0) && (Color.alpha(backgroundColor) != 255)) {
+                throw new RuntimeException("A TaskDescription's background color should be opaque");
+            }
+            mColorBackground = backgroundColor;
+        }
+
+        /**
          * Sets the icon for this task description.
          * @hide
          */
@@ -1005,8 +1034,8 @@
         public static Bitmap loadTaskDescriptionIcon(String iconFilename, int userId) {
             if (iconFilename != null) {
                 try {
-                    return ActivityManagerNative.getDefault().
-                            getTaskDescriptionIcon(iconFilename, userId);
+                    return ActivityManagerNative.getDefault().getTaskDescriptionIcon(iconFilename,
+                            userId);
                 } catch (RemoteException e) {
                 }
             }
@@ -1020,13 +1049,26 @@
             return mColorPrimary;
         }
 
+        /**
+         * @return The background color.
+         * @hide
+         */
+        public int getBackgroundColor() {
+            return mColorBackground;
+        }
+
         /** @hide */
         public void saveToXml(XmlSerializer out) throws IOException {
             if (mLabel != null) {
                 out.attribute(null, ATTR_TASKDESCRIPTIONLABEL, mLabel);
             }
             if (mColorPrimary != 0) {
-                out.attribute(null, ATTR_TASKDESCRIPTIONCOLOR, Integer.toHexString(mColorPrimary));
+                out.attribute(null, ATTR_TASKDESCRIPTIONCOLOR_PRIMARY,
+                        Integer.toHexString(mColorPrimary));
+            }
+            if (mColorBackground != 0) {
+                out.attribute(null, ATTR_TASKDESCRIPTIONCOLOR_BACKGROUND,
+                        Integer.toHexString(mColorBackground));
             }
             if (mIconFilename != null) {
                 out.attribute(null, ATTR_TASKDESCRIPTIONICONFILENAME, mIconFilename);
@@ -1037,8 +1079,10 @@
         public void restoreFromXml(String attrName, String attrValue) {
             if (ATTR_TASKDESCRIPTIONLABEL.equals(attrName)) {
                 setLabel(attrValue);
-            } else if (ATTR_TASKDESCRIPTIONCOLOR.equals(attrName)) {
+            } else if (ATTR_TASKDESCRIPTIONCOLOR_PRIMARY.equals(attrName)) {
                 setPrimaryColor((int) Long.parseLong(attrValue, 16));
+            } else if (ATTR_TASKDESCRIPTIONCOLOR_BACKGROUND.equals(attrName)) {
+                setBackgroundColor((int) Long.parseLong(attrValue, 16));
             } else if (ATTR_TASKDESCRIPTIONICONFILENAME.equals(attrName)) {
                 setIconFilename(attrValue);
             }
@@ -1064,6 +1108,7 @@
                 mIcon.writeToParcel(dest, 0);
             }
             dest.writeInt(mColorPrimary);
+            dest.writeInt(mColorBackground);
             if (mIconFilename == null) {
                 dest.writeInt(0);
             } else {
@@ -1076,6 +1121,7 @@
             mLabel = source.readInt() > 0 ? source.readString() : null;
             mIcon = source.readInt() > 0 ? Bitmap.CREATOR.createFromParcel(source) : null;
             mColorPrimary = source.readInt();
+            mColorBackground = source.readInt();
             mIconFilename = source.readInt() > 0 ? source.readString() : null;
         }
 
@@ -1092,7 +1138,8 @@
         @Override
         public String toString() {
             return "TaskDescription Label: " + mLabel + " Icon: " + mIcon +
-                    " colorPrimary: " + mColorPrimary;
+                    " IconFilename: " + mIconFilename + " colorPrimary: " + mColorPrimary +
+                    " colorBackground: " + mColorBackground;
         }
     }
 
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 55c6353..35b7c39 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -3358,13 +3358,9 @@
                 return mN.bigContentView;
             } else if (mStyle != null) {
                 result = mStyle.makeBigContentView();
-            } else if (mActions.size() == 0) {
-                return null;
-            }
-            if (result == null) {
-                result = applyStandardTemplateWithActions(getBigBaseLayoutResource());
-            } else {
                 hideLine1Text(result);
+            } else if (mActions.size() != 0) {
+                result = applyStandardTemplateWithActions(getBigBaseLayoutResource());
             }
             adaptNotificationHeaderForBigContentView(result);
             return result;
@@ -3384,11 +3380,15 @@
         }
 
         private void hideLine1Text(RemoteViews result) {
-            result.setViewVisibility(R.id.text_line_1, View.GONE);
+            if (result != null) {
+                result.setViewVisibility(R.id.text_line_1, View.GONE);
+            }
         }
 
         private void adaptNotificationHeaderForBigContentView(RemoteViews result) {
-            result.setBoolean(R.id.notification_header, "setExpanded", true);
+            if (result != null) {
+                result.setBoolean(R.id.notification_header, "setExpanded", true);
+            }
         }
 
         /**
@@ -4326,6 +4326,15 @@
             return makeMediaBigContentView();
         }
 
+        /**
+         * @hide
+         */
+        @Override
+        public RemoteViews makeHeadsUpContentView() {
+            RemoteViews expanded = makeMediaBigContentView();
+            return expanded != null ? expanded : makeMediaContentView();
+        }
+
         /** @hide */
         @Override
         public void addExtras(Bundle extras) {
@@ -4407,6 +4416,13 @@
 
         private RemoteViews makeMediaBigContentView() {
             final int actionCount = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS);
+            // Dont add an expanded view if there is no more content to be revealed
+            int actionsInCompact = mActionsToShowInCompact == null
+                    ? 0
+                    : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
+            if (mBuilder.mN.mLargeIcon == null && actionCount <= actionsInCompact) {
+                return null;
+            }
             RemoteViews big = mBuilder.applyStandardTemplate(
                     R.layout.notification_template_material_big_media,
                     false);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 83f9357..f53170a 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1826,9 +1826,23 @@
      * this method; if it has not, a security exception will be thrown.
      */
     public int getCurrentFailedPasswordAttempts() {
+        return getCurrentFailedPasswordAttempts(myUserId());
+    }
+
+    /**
+     * Retrieve the number of times the given user has failed at entering a
+     * password since that last successful password entry.
+     *
+     * <p>The calling device admin must have requested
+     * {@link DeviceAdminInfo#USES_POLICY_WATCH_LOGIN} to be able to call this method; if it has
+     * not and it is not the system uid, a security exception will be thrown.
+     *
+     * @hide
+     */
+    public int getCurrentFailedPasswordAttempts(int userHandle) {
         if (mService != null) {
             try {
-                return mService.getCurrentFailedPasswordAttempts(myUserId(), mParentInstance);
+                return mService.getCurrentFailedPasswordAttempts(userHandle, mParentInstance);
             } catch (RemoteException e) {
                 Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
@@ -3023,13 +3037,39 @@
     }
 
     /**
+     * @hide
+     */
+    public void reportFailedFingerprintAttempt(int userHandle) {
+        if (mService != null) {
+            try {
+                mService.reportFailedFingerprintAttempt(userHandle);
+            } catch (RemoteException e) {
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
+            }
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public void reportSuccessfulFingerprintAttempt(int userHandle) {
+        if (mService != null) {
+            try {
+                mService.reportSuccessfulFingerprintAttempt(userHandle);
+            } catch (RemoteException e) {
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
+            }
+        }
+    }
+
+    /**
      * Should be called when keyguard has been dismissed.
      * @hide
      */
-    public void reportKeyguardDismissed() {
+    public void reportKeyguardDismissed(int userHandle) {
         if (mService != null) {
             try {
-                mService.reportKeyguardDismissed();
+                mService.reportKeyguardDismissed(userHandle);
             } catch (RemoteException e) {
                 Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
@@ -3040,10 +3080,10 @@
      * Should be called when keyguard view has been shown to the user.
      * @hide
      */
-    public void reportKeyguardSecured() {
+    public void reportKeyguardSecured(int userHandle) {
         if (mService != null) {
             try {
-                mService.reportKeyguardSecured();
+                mService.reportKeyguardSecured(userHandle);
             } catch (RemoteException e) {
                 Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
@@ -5730,4 +5770,32 @@
             return false;
         }
     }
+
+    /**
+     * @hide
+     * Returns whether the uninstall for {@code packageName} for the current user is in queue
+     * to be started
+     * @param packageName the package to check for
+     * @return whether the uninstall intent for {@code packageName} is pending
+     */
+    public boolean isUninstallInQueue(String packageName) {
+        try {
+            return mService.isUninstallInQueue(packageName);
+        } catch (RemoteException re) {
+            Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
+            return false;
+        }
+    }
+
+    /**
+     * @hide
+     * @param packageName the package containing active DAs to be uninstalled
+     */
+    public void uninstallPackageWithActiveAdmins(String packageName) {
+        try {
+            mService.uninstallPackageWithActiveAdmins(packageName);
+        } catch (RemoteException re) {
+            Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
+        }
+    }
 }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index c6a5344..bd68182 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -116,9 +116,10 @@
         int numbers, int symbols, int nonletter, int userHandle);
     void reportFailedPasswordAttempt(int userHandle);
     void reportSuccessfulPasswordAttempt(int userHandle);
-
-    void reportKeyguardDismissed();
-    void reportKeyguardSecured();
+    void reportFailedFingerprintAttempt(int userHandle);
+    void reportSuccessfulFingerprintAttempt(int userHandle);
+    void reportKeyguardDismissed(int userHandle);
+    void reportKeyguardSecured(int userHandle);
 
     boolean setDeviceOwner(in ComponentName who, String ownerName, int userId);
     ComponentName getDeviceOwnerComponent(boolean callingUserOnly);
@@ -293,4 +294,7 @@
     boolean getDeviceLoggingEnabled(in ComponentName admin);
     ParceledListSlice retrieveDeviceLogs(in ComponentName admin);
     ParceledListSlice retrievePreviousDeviceLogs(in ComponentName admin);
+
+    boolean isUninstallInQueue(String packageName);
+    void uninstallPackageWithActiveAdmins(String packageName);
 }
diff --git a/core/java/android/auditing/SecurityLog.java b/core/java/android/auditing/SecurityLog.java
index 8d8d2f5..f1703d6 100644
--- a/core/java/android/auditing/SecurityLog.java
+++ b/core/java/android/auditing/SecurityLog.java
@@ -77,8 +77,10 @@
             SecurityLogTags.SECURITY_KEYGUARD_DISMISSED;
     /**
      * Indicate that there has been an authentication attempt to dismiss the keyguard. The log entry
-     * contains the attempt result (integer, 1 for successful, 0 for unsuccessful), accessible via
-     * {@link SecurityEvent#getData()}}
+     * contains the following information about the attempt in order, accessible via
+     * {@link SecurityEvent#getData()}}: attempt result (integer, 1 for successful, 0 for
+     * unsuccessful), strength of auth method (integer, 1 if strong auth method was used,
+     * 0 otherwise)
      */
     public static final int TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT =
             SecurityLogTags.SECURITY_KEYGUARD_DISMISS_AUTH_ATTEMPT;
diff --git a/core/java/android/auditing/SecurityLogTags.logtags b/core/java/android/auditing/SecurityLogTags.logtags
index cf85894..ccc3799 100644
--- a/core/java/android/auditing/SecurityLogTags.logtags
+++ b/core/java/android/auditing/SecurityLogTags.logtags
@@ -8,5 +8,5 @@
 210004 security_adb_sync_send                   (path|3)
 210005 security_app_process_start               (process|3),(start_time|2|3),(uid|1),(pid|1),(seinfo|3),(sha256|3)
 210006 security_keyguard_dismissed
-210007 security_keyguard_dismiss_auth_attempt   (success|1)
+210007 security_keyguard_dismiss_auth_attempt   (success|1),(method_strength|1)
 210008 security_keyguard_secured
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 0f6f856..b476a25 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3028,6 +3028,17 @@
             "android.intent.action.MANAGED_PROFILE_REMOVED";
 
     /**
+     * Broadcast sent to the primary user when the credential-encrypted private storage for
+     * an associated managed profile is unlocked. Carries an extra {@link #EXTRA_USER} that
+     * specifies the UserHandle of the profile that was unlocked. Only applications (for example
+     * Launchers) that need to display merged content across both primary and managed profiles
+     * need to worry about this broadcast. This is only sent to registered receivers,
+     * not manifest receivers.
+     */
+    public static final String ACTION_MANAGED_PROFILE_UNLOCKED =
+            "android.intent.action.MANAGED_PROFILE_UNLOCKED";
+
+    /**
      * Broadcast sent to the primary user when an associated managed profile's availability has
      * changed. This includes when the user toggles the profile's quiet mode. Carries an extra
      * {@link #EXTRA_USER} that specifies the UserHandle of the profile. When quiet mode is changed,
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 43a0cc7..f58b16a 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -178,6 +178,11 @@
      */
     public static final int RESIZE_MODE_RESIZEABLE_AND_PIPABLE = 3;
     /**
+     * Activity is does not support resizing, but we are forcing it to be resizeable.
+     * @hide
+     */
+    public static final int RESIZE_MODE_FORCE_RESIZEABLE = 4;
+    /**
      * Value indicating if the resizing mode the activity supports.
      * See {@link android.R.attr#resizeableActivity}.
      * @hide
@@ -786,7 +791,9 @@
 
     /** @hide */
     public static boolean isResizeableMode(int mode) {
-        return mode == RESIZE_MODE_RESIZEABLE || mode == RESIZE_MODE_RESIZEABLE_AND_PIPABLE;
+        return mode == RESIZE_MODE_RESIZEABLE
+                || mode == RESIZE_MODE_RESIZEABLE_AND_PIPABLE
+                || mode == RESIZE_MODE_FORCE_RESIZEABLE;
     }
 
     /** @hide */
@@ -800,6 +807,8 @@
                 return "RESIZE_MODE_RESIZEABLE";
             case RESIZE_MODE_RESIZEABLE_AND_PIPABLE:
                 return "RESIZE_MODE_RESIZEABLE_AND_PIPABLE";
+            case RESIZE_MODE_FORCE_RESIZEABLE:
+                return "RESIZE_MODE_FORCE_RESIZEABLE";
             default:
                 return "unknown=" + mode;
         }
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index c79dae5..9082482 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -1038,10 +1038,10 @@
         }
 
         deviceEncryptedDataDir = Environment
-                .getDataUserDeviceEncryptedPackageDirectory(volumeUuid, userId, packageName)
+                .getDataUserDePackageDirectory(volumeUuid, userId, packageName)
                 .getAbsolutePath();
         credentialEncryptedDataDir = Environment
-                .getDataUserCredentialEncryptedPackageDirectory(volumeUuid, userId, packageName)
+                .getDataUserCePackageDirectory(volumeUuid, userId, packageName)
                 .getAbsolutePath();
 
         if ((privateFlags & PRIVATE_FLAG_FORCE_DEVICE_ENCRYPTED) != 0
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 5dddebd..5ae8d4c 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -80,6 +80,7 @@
 import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
 import static android.content.pm.ActivityInfo.FLAG_IMMERSIVE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_CROP_WINDOWS;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
@@ -3448,7 +3449,7 @@
                 a.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
             } else if (a.info.screenOrientation == SCREEN_ORIENTATION_UNSPECIFIED
                     && (a.info.flags & FLAG_IMMERSIVE) == 0) {
-                a.info.resizeMode = RESIZE_MODE_CROP_WINDOWS;
+                a.info.resizeMode = RESIZE_MODE_FORCE_RESIZEABLE;
             }
 
             if (sa.getBoolean(R.styleable.AndroidManifestActivity_alwaysFocusable, false)) {
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index 766868d..8724a96 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -847,6 +847,9 @@
          * to make forward progress from the partial results and avoid waiting for the completed
          * result.</p>
          *
+         * <p>For a particular request, {@link #onCaptureProgressed} may happen before or after
+         * {@link #onCaptureStarted}.</p>
+         *
          * <p>Each request will generate at least {@code 1} partial results, and at most
          * {@link CameraCharacteristics#REQUEST_PARTIAL_RESULT_COUNT} partial results.</p>
          *
diff --git a/core/java/android/net/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java
index 3f36d65..b6fe68a 100644
--- a/core/java/android/net/NetworkScoreManager.java
+++ b/core/java/android/net/NetworkScoreManager.java
@@ -105,7 +105,8 @@
     /**
      * Broadcast action: the active scorer has been changed. Scorer apps may listen to this to
      * perform initialization once selected as the active scorer, or clean up unneeded resources
-     * if another scorer has been selected. Note that it is unnecessary to clear existing scores as
+     * if another scorer has been selected. This is an explicit broadcast only sent to the
+     * previous scorer and new scorer. Note that it is unnecessary to clear existing scores as
      * this is handled by the system.
      *
      * <p>The new scorer will be specified in {@link #EXTRA_NEW_SCORER}.
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index e841dfe..59bf293 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -176,35 +176,37 @@
         return DIR_VENDOR_ROOT;
     }
 
-    /** {@hide} */
-    @Deprecated
-    public static File getSystemSecureDirectory() {
-        return getDataSystemDirectory();
-    }
-
-    /** {@hide} */
-    @Deprecated
-    public static File getSecureDataDirectory() {
-        return getDataDirectory();
-    }
-
     /**
-     * Return the system directory for a user. This is for use by system services to store
-     * files relating to the user. This directory will be automatically deleted when the user
-     * is removed.
+     * Return the system directory for a user. This is for use by system
+     * services to store files relating to the user. This directory will be
+     * automatically deleted when the user is removed.
      *
+     * @deprecated This directory is valid and still exists, but callers should
+     *             <em>strongly</em> consider switching to
+     *             {@link #getDataSystemCeDirectory(int)} which is protected
+     *             with user credentials or
+     *             {@link #getDataSystemDeDirectory(int)} which supports fast
+     *             user wipe.
      * @hide
      */
+    @Deprecated
     public static File getUserSystemDirectory(int userId) {
         return new File(new File(getDataSystemDirectory(), "users"), Integer.toString(userId));
     }
 
     /**
-     * Returns the config directory for a user. This is for use by system services to store files
-     * relating to the user which should be readable by any app running as that user.
+     * Returns the config directory for a user. This is for use by system
+     * services to store files relating to the user which should be readable by
+     * any app running as that user.
      *
+     * @deprecated This directory is valid and still exists, but callers should
+     *             <em>strongly</em> consider switching to
+     *             {@link #getDataMiscCeDirectory(int)} which is protected with
+     *             user credentials or {@link #getDataMiscDeDirectory(int)}
+     *             which supports fast user wipe.
      * @hide
      */
+    @Deprecated
     public static File getUserConfigDirectory(int userId) {
         return new File(new File(new File(
                 getDataDirectory(), "misc"), "user"), Integer.toString(userId));
@@ -232,13 +234,28 @@
     }
 
     /** {@hide} */
-    public static File getDataSystemCredentialEncryptedDirectory() {
-        return new File(getDataDirectory(), "system_ce");
+    public static File getDataSystemCeDirectory(int userId) {
+        return buildPath(getDataDirectory(), "system_ce", String.valueOf(userId));
     }
 
     /** {@hide} */
-    public static File getDataSystemCredentialEncryptedDirectory(int userId) {
-        return new File(getDataSystemCredentialEncryptedDirectory(), String.valueOf(userId));
+    public static File getDataSystemDeDirectory(int userId) {
+        return buildPath(getDataDirectory(), "system_de", String.valueOf(userId));
+    }
+
+    /** {@hide} */
+    public static File getDataMiscDirectory() {
+        return new File(getDataDirectory(), "misc");
+    }
+
+    /** {@hide} */
+    public static File getDataMiscCeDirectory(int userId) {
+        return buildPath(getDataDirectory(), "misc_ce", String.valueOf(userId));
+    }
+
+    /** {@hide} */
+    public static File getDataMiscDeDirectory(int userId) {
+        return buildPath(getDataDirectory(), "misc_de", String.valueOf(userId));
     }
 
     /** {@hide} */
@@ -252,57 +269,37 @@
     }
 
     /** {@hide} */
-    @Deprecated
-    public static File getDataUserDirectory(String volumeUuid) {
-        return getDataUserCredentialEncryptedDirectory(volumeUuid);
-    }
-
-    /** {@hide} */
-    @Deprecated
-    public static File getDataUserDirectory(String volumeUuid, int userId) {
-        return getDataUserCredentialEncryptedDirectory(volumeUuid, userId);
-    }
-
-    /** {@hide} */
-    @Deprecated
-    public static File getDataUserPackageDirectory(String volumeUuid, int userId,
-            String packageName) {
-        return getDataUserCredentialEncryptedPackageDirectory(volumeUuid, userId, packageName);
-    }
-
-    /** {@hide} */
-    public static File getDataUserCredentialEncryptedDirectory(String volumeUuid) {
+    public static File getDataUserCeDirectory(String volumeUuid) {
         return new File(getDataDirectory(volumeUuid), "user");
     }
 
     /** {@hide} */
-    public static File getDataUserCredentialEncryptedDirectory(String volumeUuid, int userId) {
-        return new File(getDataUserCredentialEncryptedDirectory(volumeUuid),
-                String.valueOf(userId));
+    public static File getDataUserCeDirectory(String volumeUuid, int userId) {
+        return new File(getDataUserCeDirectory(volumeUuid), String.valueOf(userId));
     }
 
     /** {@hide} */
-    public static File getDataUserCredentialEncryptedPackageDirectory(String volumeUuid, int userId,
+    public static File getDataUserCePackageDirectory(String volumeUuid, int userId,
             String packageName) {
         // TODO: keep consistent with installd
-        return new File(getDataUserCredentialEncryptedDirectory(volumeUuid, userId), packageName);
+        return new File(getDataUserCeDirectory(volumeUuid, userId), packageName);
     }
 
     /** {@hide} */
-    public static File getDataUserDeviceEncryptedDirectory(String volumeUuid) {
+    public static File getDataUserDeDirectory(String volumeUuid) {
         return new File(getDataDirectory(volumeUuid), "user_de");
     }
 
     /** {@hide} */
-    public static File getDataUserDeviceEncryptedDirectory(String volumeUuid, int userId) {
-        return new File(getDataUserDeviceEncryptedDirectory(volumeUuid), String.valueOf(userId));
+    public static File getDataUserDeDirectory(String volumeUuid, int userId) {
+        return new File(getDataUserDeDirectory(volumeUuid), String.valueOf(userId));
     }
 
     /** {@hide} */
-    public static File getDataUserDeviceEncryptedPackageDirectory(String volumeUuid, int userId,
+    public static File getDataUserDePackageDirectory(String volumeUuid, int userId,
             String packageName) {
         // TODO: keep consistent with installd
-        return new File(getDataUserDeviceEncryptedDirectory(volumeUuid, userId), packageName);
+        return new File(getDataUserDeDirectory(volumeUuid, userId), packageName);
     }
 
     /**
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index b51d2dfb..9984755 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -823,6 +823,16 @@
     }
 
     /**
+     * Returns whether the given uid belongs to an application.
+     * @param uid A kernel uid.
+     * @return Whether the uid corresponds to an application sandbox running in
+     *     a specific user.
+     */
+    public static boolean isApplicationUid(int uid) {
+        return UserHandle.isApp(uid);
+    }
+
+    /**
      * Returns whether the current process is in an isolated sandbox.
      * @hide
      */
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index 24666fe..b3f4453 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -130,6 +130,15 @@
     }
 
     /**
+     * Returns the user for a given uid.
+     * @param uid A uid for an application running in a particular user.
+     * @return A {@link UserHandle} for that user.
+     */
+    public static UserHandle getUserHandleForUid(int uid) {
+        return of(getUserId(uid));
+    }
+
+    /**
      * Returns the user id for a given uid.
      * @hide
      */
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index dc0e249..69d564f 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1141,6 +1141,8 @@
         UserInfo user = null;
         try {
             user = mService.createUser(name, flags);
+            // TODO: Keep this in sync with
+            // UserManagerService.LocalService.createUserEvenWhenDisallowed
             if (user != null && !user.isAdmin()) {
                 mService.setUserRestriction(DISALLOW_SMS, true, user.id);
                 mService.setUserRestriction(DISALLOW_OUTGOING_CALLS, true, user.id);
diff --git a/core/java/android/os/UserManagerInternal.java b/core/java/android/os/UserManagerInternal.java
index 58a0269..d2ece8b 100644
--- a/core/java/android/os/UserManagerInternal.java
+++ b/core/java/android/os/UserManagerInternal.java
@@ -17,6 +17,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.pm.UserInfo;
 import android.graphics.Bitmap;
 
 /**
@@ -106,4 +107,12 @@
      * non-ephemeral users left.
      */
     public abstract void removeAllUsers();
+
+    /**
+     * Same as UserManager.createUser(), but bypasses the check for DISALLOW_ADD_USER.
+     *
+     * <p>Called by the {@link com.android.server.devicepolicy.DevicePolicyManagerService} when
+     * createAndManageUser is called by the device owner.
+     */
+    public abstract UserInfo createUserEvenWhenDisallowed(String name, int flags);
 }
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 8468040..2ca7589 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -1201,7 +1201,7 @@
         final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
                 sourceDocumentUri.getAuthority());
         try {
-            return moveDocument(client, sourceParentDocumentUri, sourceDocumentUri,
+            return moveDocument(client, sourceDocumentUri, sourceParentDocumentUri,
                     targetParentDocumentUri);
         } catch (Exception e) {
             Log.w(TAG, "Failed to move document", e);
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 7830142..0658bd4 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5139,14 +5139,6 @@
         public static final String TTS_DEFAULT_SYNTH = "tts_default_synth";
 
         /**
-         * Whether text-to-speech higher speech rate is enabled.
-         * 0 = disabled.
-         * 1 = enabled.
-         * @hide
-         */
-        public static final String TTS_DEFAULT_HIGHER_SPEECH_RATE_ENABLED =
-            "tts_default_higher_speech_rate_enabled";
-        /**
          * Default text-to-speech language.
          *
          * @deprecated this setting is no longer in use, as of the Ice Cream
@@ -5907,13 +5899,6 @@
                 "camera_double_tap_power_gesture_disabled";
 
         /**
-         * Name of the package used as WebView provider (if unset the provider is instead determined
-         * by the system).
-         * @hide
-         */
-        public static final String WEBVIEW_PROVIDER = "webview_provider";
-
-        /**
          * This are the settings to be backed up.
          *
          * NOTE: Settings are backed up and restored in the order they appear
@@ -5958,7 +5943,6 @@
             ACCESSIBILITY_CAPTIONING_WINDOW_COLOR,
             TTS_USE_DEFAULTS,
             TTS_DEFAULT_RATE,
-            TTS_DEFAULT_HIGHER_SPEECH_RATE_ENABLED,
             TTS_DEFAULT_PITCH,
             TTS_DEFAULT_SYNTH,
             TTS_DEFAULT_LANG,
@@ -6940,6 +6924,13 @@
         public static final String WEBVIEW_DATA_REDUCTION_PROXY_KEY =
                 "webview_data_reduction_proxy_key";
 
+        /**
+         * Name of the package used as WebView provider (if unset the provider is instead determined
+         * by the system).
+         * @hide
+         */
+        public static final String WEBVIEW_PROVIDER = "webview_provider";
+
        /**
         * Whether Wifi display is enabled/disabled
         * 0=disabled. 1=enabled.
diff --git a/core/java/android/text/BidiFormatter.java b/core/java/android/text/BidiFormatter.java
index 675803c..707c0fc 100644
--- a/core/java/android/text/BidiFormatter.java
+++ b/core/java/android/text/BidiFormatter.java
@@ -16,6 +16,7 @@
 
 package android.text;
 
+import android.annotation.Nullable;
 import android.view.View;
 
 import static android.text.TextDirectionHeuristics.FIRSTSTRONG_LTR;
@@ -390,14 +391,17 @@
      * @return Input string after applying the above processing. {@code null} if {@code str} is
      *     {@code null}.
      */
-    public String unicodeWrap(String str, TextDirectionHeuristic heuristic, boolean isolate) {
+    public @Nullable String unicodeWrap(@Nullable String str, TextDirectionHeuristic heuristic,
+            boolean isolate) {
+        if (str == null) return null;
         return unicodeWrap((CharSequence) str, heuristic, isolate).toString();
     }
 
     /**
      * @hide
      */
-    public CharSequence unicodeWrap(CharSequence str, TextDirectionHeuristic heuristic, boolean isolate) {
+    public @Nullable CharSequence unicodeWrap(@Nullable CharSequence str,
+            TextDirectionHeuristic heuristic, boolean isolate) {
         if (str == null) return null;
         final boolean isRtl = heuristic.isRtl(str, 0, str.length());
         SpannableStringBuilder result = new SpannableStringBuilder();
diff --git a/core/java/android/text/Html.java b/core/java/android/text/Html.java
index 82f69ef..409994d 100644
--- a/core/java/android/text/Html.java
+++ b/core/java/android/text/Html.java
@@ -54,6 +54,9 @@
 
 import java.io.IOException;
 import java.io.StringReader;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -356,24 +359,48 @@
         }
     }
 
-    private static String getTextStyles(Spanned text, int start, int end) {
-        final StringBuilder style = new StringBuilder(" style=\"margin-top:0; margin-bottom:0;");
+    private static String getTextStyles(Spanned text, int start, int end,
+            boolean forceNoVerticalMargin, boolean includeTextAlign) {
+        String margin = null;
+        String textAlign = null;
 
-        final AlignmentSpan[] alignmentSpans = text.getSpans(start, end, AlignmentSpan.class);
-        final int len = alignmentSpans.length;
-        if (len > 0) {
-            final Layout.Alignment alignment = alignmentSpans[len - 1].getAlignment();
-            if (alignment == Layout.Alignment.ALIGN_NORMAL) {
-                style.append(" text-align:start;");
-            } else if (alignment == Layout.Alignment.ALIGN_CENTER) {
-                style.append(" text-align:center;");
-            } else if (alignment == Layout.Alignment.ALIGN_OPPOSITE) {
-                style.append(" text-align:end;");
+        if (forceNoVerticalMargin) {
+            margin = "margin-top:0; margin-bottom:0;";
+        }
+        if (includeTextAlign) {
+            final AlignmentSpan[] alignmentSpans = text.getSpans(start, end, AlignmentSpan.class);
+
+            // Only use the last AlignmentSpan with flag SPAN_PARAGRAPH
+            for (int i = alignmentSpans.length - 1; i >= 0; i--) {
+                AlignmentSpan s = alignmentSpans[i];
+                if ((text.getSpanFlags(s) & Spanned.SPAN_PARAGRAPH) == Spanned.SPAN_PARAGRAPH) {
+                    final Layout.Alignment alignment = s.getAlignment();
+                    if (alignment == Layout.Alignment.ALIGN_NORMAL) {
+                        textAlign = "text-align:start;";
+                    } else if (alignment == Layout.Alignment.ALIGN_CENTER) {
+                        textAlign = "text-align:center;";
+                    } else if (alignment == Layout.Alignment.ALIGN_OPPOSITE) {
+                        textAlign = "text-align:end;";
+                    }
+                    break;
+                }
             }
         }
 
-        style.append("\"");
-        return style.toString();
+        if (margin == null && textAlign == null) {
+            return "";
+        }
+
+        final StringBuilder style = new StringBuilder(" style=\"");
+        if (margin != null && textAlign != null) {
+            style.append(margin).append(" ").append(textAlign);
+        } else if (margin != null) {
+            style.append(margin);
+        } else if (textAlign != null) {
+            style.append(textAlign);
+        }
+
+        return style.append("\"").toString();
     }
 
     private static void withinBlockquote(StringBuilder out, Spanned text, int start, int end,
@@ -395,46 +422,55 @@
                 next = end;
             }
 
-            boolean isListItem = false;
-            ParagraphStyle[] paragraphStyles = text.getSpans(i, next, ParagraphStyle.class);
-            for (ParagraphStyle paragraphStyle : paragraphStyles) {
-                final int spanFlags = text.getSpanFlags(paragraphStyle);
-                if ((spanFlags & Spanned.SPAN_PARAGRAPH) == Spanned.SPAN_PARAGRAPH
-                        && paragraphStyle instanceof BulletSpan) {
-                    isListItem = true;
-                    break;
+            if (next == i) {
+                if (isInList) {
+                    // Current paragraph is no longer a list item; close the previously opened list
+                    isInList = false;
+                    out.append("</ul>\n");
                 }
-            }
-
-            if (isListItem && !isInList) {
-                // Current paragraph is the first item in a list
-                isInList = true;
-                out.append("<ul>\n");
-            }
-
-            if (isInList && !isListItem) {
-                // Current paragraph is no longer a list item; close the previously opened list
-                isInList = false;
-                out.append("</ul>\n");
-            }
-
-            String tagType = isListItem ? "li" : "p";
-            out.append("<").append(tagType).append(getTextDirection(text, start, next))
-                    .append(getTextStyles(text, start, next)).append(">");
-
-            if (next - i == 0) {
-                out.append("<br>");
+                out.append("<br>\n");
             } else {
+                boolean isListItem = false;
+                ParagraphStyle[] paragraphStyles = text.getSpans(i, next, ParagraphStyle.class);
+                for (ParagraphStyle paragraphStyle : paragraphStyles) {
+                    final int spanFlags = text.getSpanFlags(paragraphStyle);
+                    if ((spanFlags & Spanned.SPAN_PARAGRAPH) == Spanned.SPAN_PARAGRAPH
+                            && paragraphStyle instanceof BulletSpan) {
+                        isListItem = true;
+                        break;
+                    }
+                }
+
+                if (isListItem && !isInList) {
+                    // Current paragraph is the first item in a list
+                    isInList = true;
+                    out.append("<ul")
+                            .append(getTextStyles(text, i, next, true, false))
+                            .append(">\n");
+                }
+
+                if (isInList && !isListItem) {
+                    // Current paragraph is no longer a list item; close the previously opened list
+                    isInList = false;
+                    out.append("</ul>\n");
+                }
+
+                String tagType = isListItem ? "li" : "p";
+                out.append("<").append(tagType)
+                        .append(getTextDirection(text, i, next))
+                        .append(getTextStyles(text, i, next, !isListItem, true))
+                        .append(">");
+
                 withinParagraph(out, text, i, next);
-            }
 
-            out.append("</");
-            out.append(tagType);
-            out.append(">\n");
+                out.append("</");
+                out.append(tagType);
+                out.append(">\n");
 
-            if (next == end && isInList) {
-                isInList = false;
-                out.append("</ul>\n");
+                if (next == end && isInList) {
+                    isInList = false;
+                    out.append("</ul>\n");
+                }
             }
 
             next++;
@@ -654,6 +690,25 @@
     private int mFlags;
 
     private static Pattern sTextAlignPattern;
+    private static Pattern sForegroundColorPattern;
+    private static Pattern sBackgroundColorPattern;
+    private static Pattern sTextDecorationPattern;
+
+    /**
+     * Name-value mapping of HTML/CSS colors which have different values in {@link Color}.
+     */
+    private static final Map<String, Integer> sColorMap;
+
+    static {
+        sColorMap = new HashMap<>();
+        sColorMap.put("darkgray", 0xFFA9A9A9);
+        sColorMap.put("gray", 0xFF808080);
+        sColorMap.put("lightgray", 0xFFD3D3D3);
+        sColorMap.put("darkgrey", 0xFFA9A9A9);
+        sColorMap.put("grey", 0xFF808080);
+        sColorMap.put("lightgrey", 0xFFD3D3D3);
+        sColorMap.put("green", 0xFF008000);
+    }
 
     private static Pattern getTextAlignPattern() {
         if (sTextAlignPattern == null) {
@@ -662,6 +717,30 @@
         return sTextAlignPattern;
     }
 
+    private static Pattern getForegroundColorPattern() {
+        if (sForegroundColorPattern == null) {
+            sForegroundColorPattern = Pattern.compile(
+                    "(?:\\s+|\\A)color\\s*:\\s*(\\S*)\\b");
+        }
+        return sForegroundColorPattern;
+    }
+
+    private static Pattern getBackgroundColorPattern() {
+        if (sBackgroundColorPattern == null) {
+            sBackgroundColorPattern = Pattern.compile(
+                    "(?:\\s+|\\A)background(?:-color)?\\s*:\\s*(\\S*)\\b");
+        }
+        return sBackgroundColorPattern;
+    }
+
+    private static Pattern getTextDecorationPattern() {
+        if (sTextDecorationPattern == null) {
+            sTextDecorationPattern = Pattern.compile(
+                    "(?:\\s+|\\A)text-decoration\\s*:\\s*(\\S*)\\b");
+        }
+        return sTextDecorationPattern;
+    }
+
     public HtmlToSpannedConverter( String source, Html.ImageGetter imageGetter,
             Html.TagHandler tagHandler, Parser parser, int flags) {
         mSource = source;
@@ -715,8 +794,15 @@
             // so we can safely emit the linebreaks when we handle the close tag.
         } else if (tag.equalsIgnoreCase("p")) {
             startBlockElement(mSpannableStringBuilder, attributes, getMarginParagraph());
+            startCssStyle(mSpannableStringBuilder, attributes);
+        } else if (tag.equalsIgnoreCase("ul")) {
+            startBlockElement(mSpannableStringBuilder, attributes, getMarginList());
+        } else if (tag.equalsIgnoreCase("li")) {
+            startLi(mSpannableStringBuilder, attributes);
         } else if (tag.equalsIgnoreCase("div")) {
             startBlockElement(mSpannableStringBuilder, attributes, getMarginDiv());
+        } else if (tag.equalsIgnoreCase("span")) {
+            startCssStyle(mSpannableStringBuilder, attributes);
         } else if (tag.equalsIgnoreCase("strong")) {
             start(mSpannableStringBuilder, new Bold());
         } else if (tag.equalsIgnoreCase("b")) {
@@ -768,9 +854,16 @@
         if (tag.equalsIgnoreCase("br")) {
             handleBr(mSpannableStringBuilder);
         } else if (tag.equalsIgnoreCase("p")) {
+            endCssStyle(mSpannableStringBuilder);
             endBlockElement(mSpannableStringBuilder);
+        } else if (tag.equalsIgnoreCase("ul")) {
+            endBlockElement(mSpannableStringBuilder);
+        } else if (tag.equalsIgnoreCase("li")) {
+            endLi(mSpannableStringBuilder);
         } else if (tag.equalsIgnoreCase("div")) {
             endBlockElement(mSpannableStringBuilder);
+        } else if (tag.equalsIgnoreCase("span")) {
+            endCssStyle(mSpannableStringBuilder);
         } else if (tag.equalsIgnoreCase("strong")) {
             end(mSpannableStringBuilder, Bold.class, new StyleSpan(Typeface.BOLD));
         } else if (tag.equalsIgnoreCase("b")) {
@@ -824,6 +917,14 @@
         return getMargin(Html.FROM_HTML_SEPARATOR_LINE_BREAK_HEADING);
     }
 
+    private int getMarginListItem() {
+        return getMargin(Html.FROM_HTML_SEPARATOR_LINE_BREAK_LIST_ITEM);
+    }
+
+    private int getMarginList() {
+        return getMargin(Html.FROM_HTML_SEPARATOR_LINE_BREAK_LIST);
+    }
+
     private int getMarginDiv() {
         return getMargin(Html.FROM_HTML_SEPARATOR_LINE_BREAK_DIV);
     }
@@ -866,7 +967,7 @@
         final int len = text.length();
         if (margin > 0) {
             appendNewlines(text, margin);
-            text.setSpan(new Newline(margin), len, len, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
+            start(text, new Newline(margin));
         }
 
         String style = attributes.getValue("", "style");
@@ -875,14 +976,11 @@
             if (m.find()) {
                 String alignment = m.group(1);
                 if (alignment.equalsIgnoreCase("start")) {
-                    text.setSpan(new Alignment(Layout.Alignment.ALIGN_NORMAL),
-                            len, len, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
+                    start(text, new Alignment(Layout.Alignment.ALIGN_NORMAL));
                 } else if (alignment.equalsIgnoreCase("center")) {
-                    text.setSpan(new Alignment(Layout.Alignment.ALIGN_CENTER),
-                            len, len, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
+                    start(text, new Alignment(Layout.Alignment.ALIGN_CENTER));
                 } else if (alignment.equalsIgnoreCase("end")) {
-                    text.setSpan(new Alignment(Layout.Alignment.ALIGN_OPPOSITE),
-                            len, len, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
+                    start(text, new Alignment(Layout.Alignment.ALIGN_OPPOSITE));
                 }
             }
         }
@@ -905,6 +1003,18 @@
         text.append('\n');
     }
 
+    private void startLi(Editable text, Attributes attributes) {
+        startBlockElement(text, attributes, getMarginListItem());
+        start(text, new Bullet());
+        startCssStyle(text, attributes);
+    }
+
+    private static void endLi(Editable text) {
+        endCssStyle(text);
+        endBlockElement(text);
+        end(text, Bullet.class, new BulletSpan());
+    }
+
     private void startBlockquote(Editable text, Attributes attributes) {
         startBlockElement(text, attributes, getMarginBlockquote());
         start(text, new Blockquote());
@@ -959,7 +1069,7 @@
 
     private static void start(Editable text, Object mark) {
         int len = text.length();
-        text.setSpan(mark, len, len, Spannable.SPAN_MARK_MARK);
+        text.setSpan(mark, len, len, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
     }
 
     private static void end(Editable text, Class kind, Object repl) {
@@ -970,6 +1080,52 @@
         }
     }
 
+    private void startCssStyle(Editable text, Attributes attributes) {
+        String style = attributes.getValue("", "style");
+        if (style != null) {
+            Matcher m = getForegroundColorPattern().matcher(style);
+            if (m.find()) {
+                int c = getHtmlColor(m.group(1));
+                if (c != -1) {
+                    start(text, new Foreground(c | 0xFF000000));
+                }
+            }
+
+            m = getBackgroundColorPattern().matcher(style);
+            if (m.find()) {
+                int c = getHtmlColor(m.group(1));
+                if (c != -1) {
+                    start(text, new Background(c | 0xFF000000));
+                }
+            }
+
+            m = getTextDecorationPattern().matcher(style);
+            if (m.find()) {
+                String textDecoration = m.group(1);
+                if (textDecoration.equalsIgnoreCase("line-through")) {
+                    start(text, new Strikethrough());
+                }
+            }
+        }
+    }
+
+    private static void endCssStyle(Editable text) {
+        Strikethrough s = getLast(text, Strikethrough.class);
+        if (s != null) {
+            setSpanFromMark(text, s, new StrikethroughSpan());
+        }
+
+        Background b = getLast(text, Background.class);
+        if (b != null) {
+            setSpanFromMark(text, b, new BackgroundColorSpan(b.mBackgroundColor));
+        }
+
+        Foreground f = getLast(text, Foreground.class);
+        if (f != null) {
+            setSpanFromMark(text, f, new ForegroundColorSpan(f.mForegroundColor));
+        }
+    }
+
     private static void startImg(Editable text, Attributes attributes, Html.ImageGetter img) {
         String src = attributes.getValue("", "src");
         Drawable d = null;
@@ -991,58 +1147,41 @@
                      Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
     }
 
-    private static void startFont(Editable text, Attributes attributes) {
+    private void startFont(Editable text, Attributes attributes) {
         String color = attributes.getValue("", "color");
         String face = attributes.getValue("", "face");
 
-        int len = text.length();
-        text.setSpan(new Font(color, face), len, len, Spannable.SPAN_MARK_MARK);
+        if (!TextUtils.isEmpty(color)) {
+            int c = getHtmlColor(color);
+            if (c != -1) {
+                start(text, new Foreground(c | 0xFF000000));
+            }
+        }
+
+        if (!TextUtils.isEmpty(face)) {
+            start(text, new Font(face));
+        }
     }
 
     private static void endFont(Editable text) {
-        int len = text.length();
-        Font f = getLast(text, Font.class);
-        int where = text.getSpanStart(f);
-        text.removeSpan(f);
+        Font font = getLast(text, Font.class);
+        if (font != null) {
+            setSpanFromMark(text, font, new TypefaceSpan(font.mFace));
+        }
 
-        if (where != len) {
-            if (!TextUtils.isEmpty(f.mColor)) {
-                if (f.mColor.startsWith("@")) {
-                    Resources res = Resources.getSystem();
-                    String name = f.mColor.substring(1);
-                    int colorRes = res.getIdentifier(name, "color", "android");
-                    if (colorRes != 0) {
-                        ColorStateList colors = res.getColorStateList(colorRes, null);
-                        text.setSpan(new TextAppearanceSpan(null, 0, 0, colors, null),
-                                where, len,
-                                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
-                    }
-                } else {
-                    int c = Color.getHtmlColor(f.mColor);
-                    if (c != -1) {
-                        text.setSpan(new ForegroundColorSpan(c | 0xFF000000),
-                                where, len,
-                                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
-                    }
-                }
-            }
-
-            if (f.mFace != null) {
-                text.setSpan(new TypefaceSpan(f.mFace), where, len,
-                             Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
-            }
+        Foreground foreground = getLast(text, Foreground.class);
+        if (foreground != null) {
+            setSpanFromMark(text, foreground,
+                    new ForegroundColorSpan(foreground.mForegroundColor));
         }
     }
 
     private static void startA(Editable text, Attributes attributes) {
         String href = attributes.getValue("", "href");
-
-        int len = text.length();
-        text.setSpan(new Href(href), len, len, Spannable.SPAN_MARK_MARK);
+        start(text, new Href(href));
     }
 
     private static void endA(Editable text) {
-        int len = text.length();
         Href h = getLast(text, Href.class);
         if (h != null) {
             if (h.mHref != null) {
@@ -1051,6 +1190,17 @@
         }
     }
 
+    private int getHtmlColor(String color) {
+        if ((mFlags & Html.FROM_HTML_OPTION_USE_CSS_COLORS)
+                == Html.FROM_HTML_OPTION_USE_CSS_COLORS) {
+            Integer i = sColorMap.get(color.toLowerCase(Locale.US));
+            if (i != null) {
+                return i;
+            }
+        }
+        return Color.getHtmlColor(color);
+    }
+
     public void setDocumentLocator(Locator locator) {
     }
 
@@ -1132,13 +1282,12 @@
     private static class Blockquote { }
     private static class Super { }
     private static class Sub { }
+    private static class Bullet { }
 
     private static class Font {
-        public String mColor;
         public String mFace;
 
-        public Font(String color, String face) {
-            mColor = color;
+        public Font(String face) {
             mFace = face;
         }
     }
@@ -1151,6 +1300,22 @@
         }
     }
 
+    private static class Foreground {
+        private int mForegroundColor;
+
+        public Foreground(int foregroundColor) {
+            mForegroundColor = foregroundColor;
+        }
+    }
+
+    private static class Background {
+        private int mBackgroundColor;
+
+        public Background(int backgroundColor) {
+            mBackgroundColor = backgroundColor;
+        }
+    }
+
     private static class Heading {
         private int mLevel;
 
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 2612ab2..bba5a17 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -6874,6 +6874,15 @@
      * @param info The info whose drawing order should be populated
      */
     private void populateAccessibilityNodeInfoDrawingOrderInParent(AccessibilityNodeInfo info) {
+        /*
+         * If the view's bounds haven't been set yet, layout has not completed. In that situation,
+         * drawing order may not be well-defined, and some Views with custom drawing order may
+         * not be initialized sufficiently to respond properly getChildDrawingOrder.
+         */
+        if ((mPrivateFlags & PFLAG_HAS_BOUNDS) == 0) {
+            info.setDrawingOrder(0);
+            return;
+        }
         int drawingOrderInParent = 1;
         // Iterate up the hierarchy if parents are not important for a11y
         View viewAtDrawingLevel = this;
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 947906b..609c471 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.SystemApi;
+import android.app.ActivityManager.StackId;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.content.res.CompatibilityInfo;
@@ -388,6 +389,12 @@
          * Check whether the window is currently dimming.
          */
         public boolean isDimming();
+
+        /**
+         * @return the stack id this windows belongs to, or {@link StackId#INVALID_STACK_ID} if
+         *         not attached to any stack.
+         */
+        int getStackId();
     }
 
     /**
@@ -465,6 +472,11 @@
          * @return The content insets of the docked divider window.
          */
         int getDockedDividerInsetsLw();
+
+        /**
+         * Retrieves the {@param outBounds} from the stack with id {@param stackId}.
+         */
+        void getStackBounds(int stackId, Rect outBounds);
     }
 
     public interface PointerEventListener {
diff --git a/core/java/android/webkit/WebViewProviderInfo.java b/core/java/android/webkit/WebViewProviderInfo.java
index 3f50fe2..94e8b70 100644
--- a/core/java/android/webkit/WebViewProviderInfo.java
+++ b/core/java/android/webkit/WebViewProviderInfo.java
@@ -97,22 +97,12 @@
      */
     public boolean isEnabled() {
         try {
-            PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
-            int enabled_state = pm.getApplicationEnabledSetting(packageName);
-            switch (enabled_state) {
-                case PackageManager.COMPONENT_ENABLED_STATE_ENABLED:
-                    return true;
-                case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT:
-                    ApplicationInfo applicationInfo = getPackageInfo().applicationInfo;
-                    return applicationInfo.enabled;
-                default:
-                    return false;
-            }
+            // Explicitly fetch up-to-date package info here since the enabled-state of the package
+            // might have changed since we last fetched its package info.
+            updatePackageInfo();
+            return getPackageInfo().applicationInfo.enabled;
         } catch (WebViewPackageNotFoundException e) {
             return false;
-        } catch (IllegalArgumentException e) {
-            // Thrown by PackageManager.getApplicationEnabledSetting if the package does not exist
-            return false;
         }
     }
 
@@ -124,14 +114,18 @@
         return availableByDefault;
     }
 
+    private void updatePackageInfo() {
+        try {
+            PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
+            packageInfo = pm.getPackageInfo(packageName, PACKAGE_FLAGS);
+        } catch (PackageManager.NameNotFoundException e) {
+            throw new WebViewPackageNotFoundException(e);
+        }
+    }
+
     public PackageInfo getPackageInfo() {
         if (packageInfo == null) {
-            try {
-                PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
-                packageInfo = pm.getPackageInfo(packageName, PACKAGE_FLAGS);
-            } catch (PackageManager.NameNotFoundException e) {
-                throw new WebViewPackageNotFoundException(e);
-            }
+            updatePackageInfo();
         }
         return packageInfo;
     }
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index b0fb93b..3402989 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -45,6 +45,7 @@
 import android.os.ResultReceiver;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.storage.StorageManager;
 import android.provider.DocumentsContract;
 import android.service.chooser.ChooserTarget;
 import android.service.chooser.ChooserTargetService;
@@ -232,7 +233,7 @@
         // the case where we don't have access to credential encrypted storage we just won't
         // have our pinned target info.
         final File prefsFile = new File(new File(
-                Environment.getDataUserCredentialEncryptedPackageDirectory(null,
+                Environment.getDataUserCePackageDirectory(StorageManager.UUID_PRIVATE_INTERNAL,
                         context.getUserId(), context.getPackageName()),
                 "shared_prefs"),
                 PINNED_SHARED_PREFS_NAME + ".xml");
diff --git a/core/java/com/android/internal/app/LocaleStore.java b/core/java/com/android/internal/app/LocaleStore.java
index 210adce..465c4d8 100644
--- a/core/java/com/android/internal/app/LocaleStore.java
+++ b/core/java/com/android/internal/app/LocaleStore.java
@@ -104,6 +104,9 @@
         }
 
         private boolean isSuggestionOfType(int suggestionMask) {
+            if (!mIsTranslated) { // Never suggest an untranslated locale
+                return false;
+            }
             return (mSuggestionFlags & suggestionMask) == suggestionMask;
         }
 
@@ -207,6 +210,27 @@
         }
     }
 
+    /*
+     * Show all the languages supported for a country in the suggested list.
+     * This is also handy for devices without SIM (tablets).
+     */
+    private static void addSuggestedLocalesForRegion(Locale locale) {
+        if (locale == null) {
+            return;
+        }
+        final String country = locale.getCountry();
+        if (country.isEmpty()) {
+            return;
+        }
+
+        for (LocaleInfo li : sLocaleCache.values()) {
+            if (country.equals(li.getLocale().getCountry())) {
+                // We don't need to differentiate between manual and SIM suggestions
+                li.mSuggestionFlags |= LocaleInfo.SUGGESTION_TYPE_SIM;
+            }
+        }
+    }
+
     public static void fillCache(Context context) {
         if (sFullyInitialized) {
             return;
@@ -256,6 +280,8 @@
             li.setTranslated(localizedLocales.contains(li.getLangScriptKey()));
         }
 
+        addSuggestedLocalesForRegion(Locale.getDefault());
+
         sFullyInitialized = true;
     }
 
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 88af920..f1c79fa 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -1718,8 +1718,13 @@
 
     private void loadBackgroundDrawablesIfNeeded() {
         if (mResizingBackgroundDrawable == null) {
-            mResizingBackgroundDrawable = getResizingBackgroundDrawable(
+            mResizingBackgroundDrawable = getResizingBackgroundDrawable(getContext(),
                     mWindow.mBackgroundResource, mWindow.mBackgroundFallbackResource);
+            if (mResizingBackgroundDrawable == null) {
+                // We shouldn't really get here as the background fallback should be always
+                // available since it is defaulted by the system.
+                Log.w(mLogTag, "Failed to find background drawable for PhoneWindow=" + mWindow);
+            }
         }
         if (mCaptionBackgroundDrawable == null) {
             mCaptionBackgroundDrawable = getContext().getDrawable(
@@ -1817,9 +1822,8 @@
      * Returns the color used to fill areas the app has not rendered content to yet when the
      * user is resizing the window of an activity in multi-window mode.
      */
-    private Drawable getResizingBackgroundDrawable(int backgroundRes, int backgroundFallbackRes) {
-        final Context context = getContext();
-
+    public static Drawable getResizingBackgroundDrawable(Context context, int backgroundRes,
+            int backgroundFallbackRes) {
         if (backgroundRes != 0) {
             final Drawable drawable = context.getDrawable(backgroundRes);
             if (drawable != null) {
@@ -1833,10 +1837,6 @@
                 return fallbackDrawable;
             }
         }
-
-        // We shouldn't really get here as the background fallback should be always available since
-        // it is defaulted by the system.
-        Log.w(mLogTag, "Failed to find background drawable for PhoneWindow=" + mWindow);
         return null;
     }
 
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 6e374e2..08d4fba 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -17,6 +17,7 @@
 package com.android.internal.statusbar;
 
 import android.content.ComponentName;
+import android.graphics.Rect;
 import android.os.Bundle;
 import android.service.notification.StatusBarNotification;
 
@@ -31,7 +32,23 @@
     void animateExpandNotificationsPanel();
     void animateExpandSettingsPanel(String subPanel);
     void animateCollapsePanels();
-    void setSystemUiVisibility(int vis, int mask);
+
+    /**
+     * Notifies the status bar of a System UI visibility flag change.
+     *
+     * @param vis the visibility flags except SYSTEM_UI_FLAG_LIGHT_STATUS_BAR which will be reported
+     *            separately in fullscreenStackVis and dockedStackVis
+     * @param fullscreenStackVis the flags which only apply in the region of the fullscreen stack,
+     *                           which is currently only SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
+     * @param dockedStackVis the flags that only apply in the region of the docked stack, which is
+     *                       currently only SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
+     * @param mask which flags to change
+     * @param fullscreenBounds the current bounds of the fullscreen stack, in screen coordinates
+     * @param dockedBounds the current bounds of the docked stack, in screen coordinates
+     */
+    void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis, int mask,
+            in Rect fullscreenBounds, in Rect dockedBounds);
+
     void topAppWindowChanged(boolean menuVisible);
     void setImeWindowStatus(in IBinder token, int vis, int backDisposition,
             boolean showImeSwitcher);
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index bec18ec..8acf5d3 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -17,6 +17,7 @@
 package com.android.internal.statusbar;
 
 import android.content.ComponentName;
+import android.graphics.Rect;
 import android.os.Bundle;
 import android.service.notification.StatusBarNotification;
 
@@ -37,7 +38,6 @@
     void setIcon(String slot, String iconPackage, int iconId, int iconLevel, String contentDescription);
     void setIconVisibility(String slot, boolean visible);
     void removeIcon(String slot);
-    void topAppWindowChanged(boolean menuVisible);
     void setImeWindowStatus(in IBinder token, int vis, int backDisposition,
             boolean showImeSwitcher);
     void expandSettingsPanel(String subPanel);
@@ -47,7 +47,8 @@
     // You need the STATUS_BAR_SERVICE permission
     void registerStatusBar(IStatusBar callbacks, out List<String> iconSlots,
             out List<StatusBarIcon> iconList,
-            out int[] switches, out List<IBinder> binders);
+            out int[] switches, out List<IBinder> binders, out Rect fullscreenStackBounds,
+            out Rect dockedStackBounds);
     void onPanelRevealed(boolean clearNotificationEffects, int numItems);
     void onPanelHidden();
     // Mark current notifications as "seen" and stop ringing, vibrating, blinking.
diff --git a/core/java/com/android/internal/util/LineBreakBufferedWriter.java b/core/java/com/android/internal/util/LineBreakBufferedWriter.java
index f831e7a..552a93f 100644
--- a/core/java/com/android/internal/util/LineBreakBufferedWriter.java
+++ b/core/java/com/android/internal/util/LineBreakBufferedWriter.java
@@ -96,7 +96,7 @@
 
     @Override
     public void write(int c) {
-        if (bufferIndex < bufferSize) {
+        if (bufferIndex < buffer.length) {
             buffer[bufferIndex] = (char)c;
             bufferIndex++;
             if ((char)c == '\n') {
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index e239852..cbc735f 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -283,6 +283,15 @@
         getTrustManager().reportUnlockAttempt(true /* authenticated */, userId);
     }
 
+    public int getCurrentFailedPasswordAttempts(int userId) {
+        return getDevicePolicyManager().getCurrentFailedPasswordAttempts(userId);
+    }
+
+    public int getMaximumFailedPasswordsForWipe(int userId) {
+        return getDevicePolicyManager().getMaximumFailedPasswordsForWipe(
+                null /* componentName */, userId);
+    }
+
     /**
      * Check to see if a pattern matches the saved pattern.
      * If pattern matches, return an opaque attestation that the challenge
diff --git a/core/java/com/android/server/backup/AccountSyncSettingsBackupHelper.java b/core/java/com/android/server/backup/AccountSyncSettingsBackupHelper.java
index 0449340..1b40492 100644
--- a/core/java/com/android/server/backup/AccountSyncSettingsBackupHelper.java
+++ b/core/java/com/android/server/backup/AccountSyncSettingsBackupHelper.java
@@ -16,10 +16,6 @@
 
 package com.android.server.backup;
 
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
 import android.accounts.Account;
 import android.accounts.AccountManager;
 import android.app.backup.BackupDataInputStream;
@@ -28,14 +24,21 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.SyncAdapterType;
+import android.os.Environment;
 import android.os.ParcelFileDescriptor;
 import android.util.Log;
 
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
 import java.io.BufferedOutputStream;
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.EOFException;
+import java.io.File;
 import java.io.FileInputStream;
+import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.security.MessageDigest;
@@ -73,6 +76,8 @@
     private static final String KEY_AUTHORITY_NAME = "name";
     private static final String KEY_AUTHORITY_SYNC_STATE = "syncState";
     private static final String KEY_AUTHORITY_SYNC_ENABLED = "syncEnabled";
+    private static final String STASH_FILE = Environment.getDataDirectory()
+            + "/backup/unadded_account_syncsettings.json";
 
     private Context mContext;
     private AccountManager mAccountManager;
@@ -256,41 +261,99 @@
             }
 
             try {
-                HashSet<Account> currentAccounts = getAccountsHashSet();
-                for (int i = 0; i < accountJSONArray.length(); i++) {
-                    JSONObject accountJSON = (JSONObject) accountJSONArray.get(i);
-                    String accountName = accountJSON.getString(KEY_ACCOUNT_NAME);
-                    String accountType = accountJSON.getString(KEY_ACCOUNT_TYPE);
-
-                    Account account = new Account(accountName, accountType);
-
-                    // Check if the account already exists. Accounts that don't exist on the device
-                    // yet won't be restored.
-                    if (currentAccounts.contains(account)) {
-                        restoreExistingAccountSyncSettingsFromJSON(accountJSON);
-                    } else {
-                        // TODO:
-                        // Stash the data to a file that the SyncManager can read from to restore
-                        // settings at a later date.
-                    }
-                }
+                restoreFromJsonArray(accountJSONArray);
             } finally {
                 // Set the master sync preference to the value from the backup set.
                 ContentResolver.setMasterSyncAutomatically(masterSyncEnabled);
             }
-
             Log.i(TAG, "Restore successful.");
         } catch (IOException | JSONException e) {
             Log.e(TAG, "Couldn't restore account sync settings\n" + e);
         }
     }
 
+    private void restoreFromJsonArray(JSONArray accountJSONArray)
+            throws JSONException {
+        HashSet<Account> currentAccounts = getAccounts();
+        JSONArray unaddedAccountsJSONArray = new JSONArray();
+        for (int i = 0; i < accountJSONArray.length(); i++) {
+            JSONObject accountJSON = (JSONObject) accountJSONArray.get(i);
+            String accountName = accountJSON.getString(KEY_ACCOUNT_NAME);
+            String accountType = accountJSON.getString(KEY_ACCOUNT_TYPE);
+
+            Account account = null;
+            try {
+                account = new Account(accountName, accountType);
+            } catch (IllegalArgumentException iae) {
+                continue;
+            }
+
+            // Check if the account already exists. Accounts that don't exist on the device
+            // yet won't be restored.
+            if (currentAccounts.contains(account)) {
+                if (DEBUG) Log.i(TAG, "Restoring Sync Settings for" + accountName);
+                restoreExistingAccountSyncSettingsFromJSON(accountJSON);
+            } else {
+                unaddedAccountsJSONArray.put(accountJSON);
+            }
+        }
+
+        if (unaddedAccountsJSONArray.length() > 0) {
+            try (FileOutputStream fOutput = new FileOutputStream(STASH_FILE)) {
+                String jsonString = unaddedAccountsJSONArray.toString();
+                DataOutputStream out = new DataOutputStream(fOutput);
+                out.writeUTF(jsonString);
+            } catch (IOException ioe) {
+                // Error in writing to stash file
+                Log.e(TAG, "unable to write the sync settings to the stash file", ioe);
+            }
+        } else {
+            File stashFile = new File(STASH_FILE);
+            if (stashFile.exists()) stashFile.delete();
+        }
+    }
+
+    /**
+     * Restore SyncSettings for all existing accounts from a stashed backup-set
+     */
+    private void accountAddedInternal() {
+        String jsonString;
+
+        try (FileInputStream fIn = new FileInputStream(new File(STASH_FILE))) {
+            DataInputStream in = new DataInputStream(fIn);
+            jsonString = in.readUTF();
+        } catch (FileNotFoundException fnfe) {
+            // This is expected to happen when there is no accounts info stashed
+            if (DEBUG) Log.d(TAG, "unable to find the stash file", fnfe);
+            return;
+        } catch (IOException ioe) {
+            if (DEBUG) Log.d(TAG, "could not read sync settings from stash file", ioe);
+            return;
+        }
+
+        try {
+            JSONArray unaddedAccountsJSONArray = new JSONArray(jsonString);
+            restoreFromJsonArray(unaddedAccountsJSONArray);
+        } catch (JSONException jse) {
+            // Malformed jsonString
+            Log.e(TAG, "there was an error with the stashed sync settings", jse);
+        }
+    }
+
+    /**
+     * Restore SyncSettings for all existing accounts from a stashed backup-set
+     */
+    public static void accountAdded(Context context) {
+        AccountSyncSettingsBackupHelper helper = new AccountSyncSettingsBackupHelper(context);
+        helper.accountAddedInternal();
+    }
+
     /**
      * Helper method - fetch accounts and return them as a HashSet.
      *
      * @return Accounts in a HashSet.
      */
-    private HashSet<Account> getAccountsHashSet() {
+    private HashSet<Account> getAccounts() {
         Account[] accounts = mAccountManager.getAccounts();
         HashSet<Account> accountHashSet = new HashSet<Account>();
         for (Account account : accounts) {
@@ -359,4 +422,4 @@
     public void writeNewStateDescription(ParcelFileDescriptor newState) {
 
     }
-}
+}
\ No newline at end of file
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index dd0e456..ac77007 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -322,7 +322,11 @@
 void NotifyHandler::handleMessage(const Message& message) {
     JNIEnv* env = getenv(mVm);
 
-    jobject target = env->NewLocalRef(mObserver->getObserverReference());
+    ObserverProxy* observer = mObserver.get();
+    LOG_ALWAYS_FATAL_IF(observer == nullptr, "received message with no observer configured");
+    LOG_ALWAYS_FATAL_IF(mBuffer == nullptr, "received message with no data to report");
+
+    jobject target = env->NewLocalRef(observer->getObserverReference());
 
     if (target != nullptr) {
         jlongArray javaBuffer = get_metrics_buffer(env, target);
diff --git a/core/res/res/layout/floating_popup_container.xml b/core/res/res/layout/floating_popup_container.xml
index dd161e3..ca03737 100644
--- a/core/res/res/layout/floating_popup_container.xml
+++ b/core/res/res/layout/floating_popup_container.xml
@@ -19,8 +19,8 @@
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:padding="0dp"
-    android:layout_margin="20dp"
-    android:elevation="2dp"
+    android:layout_margin="@android:dimen/text_edit_floating_toolbar_margin"
+    android:elevation="@android:dimen/text_edit_floating_toolbar_elevation"
     android:focusable="true"
     android:focusableInTouchMode="true"
     android:background="?attr/floatingToolbarPopupBackgroundDrawable"/>
diff --git a/core/res/res/layout/text_edit_suggestion_container.xml b/core/res/res/layout/text_edit_suggestion_container.xml
index 17e93d0..b2589da 100644
--- a/core/res/res/layout/text_edit_suggestion_container.xml
+++ b/core/res/res/layout/text_edit_suggestion_container.xml
@@ -22,8 +22,8 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:orientation="vertical"
-        android:elevation="2dp"
-        android:layout_margin="20dp"
+        android:elevation="@android:dimen/text_edit_floating_toolbar_elevation"
+        android:layout_margin="@android:dimen/text_edit_floating_toolbar_margin"
         android:background="@drawable/text_edit_suggestions_window"
         android:dropDownSelector="@drawable/list_selector_background"
         android:divider="@null">
diff --git a/core/res/res/layout/text_edit_suggestion_container_material.xml b/core/res/res/layout/text_edit_suggestion_container_material.xml
index 7826803..20a80489 100644
--- a/core/res/res/layout/text_edit_suggestion_container_material.xml
+++ b/core/res/res/layout/text_edit_suggestion_container_material.xml
@@ -24,8 +24,8 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:background="?android:attr/floatingToolbarPopupBackgroundDrawable"
-        android:elevation="2dp"
-        android:layout_margin="20dp"
+        android:elevation="@android:dimen/text_edit_floating_toolbar_elevation"
+        android:layout_margin="@android:dimen/text_edit_floating_toolbar_margin"
         android:orientation="vertical"
         android:divider="?android:attr/listDivider"
         android:showDividers="middle">
diff --git a/core/res/res/values-watch/themes_device_defaults.xml b/core/res/res/values-watch/themes_device_defaults.xml
index 63df5be..61753b1 100644
--- a/core/res/res/values-watch/themes_device_defaults.xml
+++ b/core/res/res/values-watch/themes_device_defaults.xml
@@ -25,6 +25,7 @@
     <style name="Theme.DeviceDefault.Light.Dialog" parent="Theme.Micro.Dialog" />
     <style name="Theme.DeviceDefault.Light.DialogWhenLarge" parent="Theme.Micro.Dialog" />
     <style name="Theme.DeviceDefault.Light.Dialog.Alert" parent="Theme.Micro.Dialog.Alert" />
+    <style name="Theme.DeviceDefault.Settings" parent="Theme.Micro" />
     <style name="Theme.DeviceDefault.Wallpaper" parent="Theme.Micro" />
 
 </resources>
diff --git a/core/res/res/values/dimens_material.xml b/core/res/res/values/dimens_material.xml
index 96a81d1..2fe4f66 100644
--- a/core/res/res/values/dimens_material.xml
+++ b/core/res/res/values/dimens_material.xml
@@ -82,6 +82,9 @@
     <dimen name="text_size_medium_material">18sp</dimen>
     <dimen name="text_size_small_material">14sp</dimen>
 
+    <dimen name="text_edit_floating_toolbar_elevation">2dp</dimen>
+    <dimen name="text_edit_floating_toolbar_margin">20dp</dimen>
+
     <dimen name="floating_window_z">16dp</dimen>
     <dimen name="floating_window_margin_left">16dp</dimen>
     <dimen name="floating_window_margin_top">8dp</dimen>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 4e10d39..daa8202 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4204,11 +4204,16 @@
     <string name="new_sms_notification_content">Open SMS app to view</string>
 
     <!-- Notification title shown when user profile is credential encrypted and requires the user to unlock before some features are usable [CHAR LIMIT=30] -->
-    <string name="user_encrypted_title">Some functions might not be available</string>
+    <string name="user_encrypted_title">Some functionality may be limited</string>
     <!-- Notification message shown when user profile is credential encrypted and requires the user to unlock before some features are usable [CHAR LIMIT=30] -->
-    <string name="user_encrypted_message">Touch to continue</string>
+    <string name="user_encrypted_message">Tap to unlock</string>
     <!-- Notification detail shown when user profile is credential encrypted and requires the user to unlock before some features are usable [CHAR LIMIT=30] -->
-    <string name="user_encrypted_detail">User profile locked</string>
+    <string name="user_encrypted_detail">User data locked</string>
+
+    <!-- Notification detail shown when work profile is credential encrypted and requires the user to unlock before some features are usable [CHAR LIMIT=30] -->
+    <string name="profile_encrypted_detail">Work profile locked</string>
+    <!-- Notification message shown when work profile is credential encrypted and requires the user to unlock before some features are usable [CHAR LIMIT=30] -->
+    <string name="profile_encrypted_message">Tap to unlock work profile</string>
 
     <!-- Title of notification shown after a MTP device is connected to Android. -->
     <string name="usb_mtp_launch_notification_title">Connected to <xliff:g id="product_name">%1$s</xliff:g></string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index f75f023..8df6c2e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2522,6 +2522,8 @@
   <java-symbol type="string" name="user_encrypted_title" />
   <java-symbol type="string" name="user_encrypted_message" />
   <java-symbol type="string" name="user_encrypted_detail" />
+  <java-symbol type="string" name="profile_encrypted_detail" />
+  <java-symbol type="string" name="profile_encrypted_message" />
   <java-symbol type="drawable" name="ic_user_secure" />
 
   <java-symbol type="string" name="usb_mtp_launch_notification_title" />
diff --git a/core/tests/coretests/src/com/android/internal/util/LineBreakBufferedWriterTest.java b/core/tests/coretests/src/com/android/internal/util/LineBreakBufferedWriterTest.java
index 49ae104..4845c4e 100644
--- a/core/tests/coretests/src/com/android/internal/util/LineBreakBufferedWriterTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/LineBreakBufferedWriterTest.java
@@ -180,6 +180,22 @@
         assertOutput("aaaaaaaaaabbbbbc\nd", "ddddddddd");
     }
 
+    public void testMoreThenInitialCapacitySimpleWrites() {
+        // This check is different from testMoreThanBufferSizeChar. The initial capacity is lower
+        // than the maximum buffer size here.
+        final LineBreakBufferedWriter lw = new LineBreakBufferedWriter(mWriter, 1024, 3);
+
+        for(int i = 0; i < 10; i++) {
+            lw.print('$');
+        }
+        for(int i = 0; i < 10; i++) {
+            lw.print('%');
+        }
+        lw.flush();
+
+        assertOutput("$$$$$$$$$$%%%%%%%%%%");
+    }
+
     private void assertOutput(String... golden) {
         List<String> goldList = createTestGolden(golden);
         assertEquals(goldList, mWriter.getStrings());
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 1d9fe35..3277c36 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -2236,7 +2236,7 @@
     // See if any of the regions is better than the other
     const int region_comparison = localeDataCompareRegions(
             country, o.country,
-            language, localeScript, requested->country);
+            language, requested->localeScript, requested->country);
     if (region_comparison != 0) {
         return (region_comparison > 0);
     }
@@ -2526,17 +2526,34 @@
 
         // For backward compatibility and supporting private-use locales, we
         // fall back to old behavior if we couldn't determine the script for
-        // either of the desired locale or the provided locale.
-        if (localeScript[0] == '\0' || localeScript[1] == '\0') {
+        // either of the desired locale or the provided locale. But if we could determine
+        // the scripts, they should be the same for the locales to match.
+        bool countriesMustMatch = false;
+        char computed_script[4];
+        const char* script;
+        if (settings.localeScript[0] == '\0') { // could not determine the request's script
+            countriesMustMatch = true;
+        } else {
+            if (localeScript[0] == '\0') { // script was not provided, so we try to compute it
+                localeDataComputeScript(computed_script, language, country);
+                if (computed_script[0] == '\0') { // we could not compute the script
+                    countriesMustMatch = true;
+                } else {
+                    script = computed_script;
+                }
+            } else { // script was provided, so just use it
+                script = localeScript;
+            }
+        }
+
+        if (countriesMustMatch) {
             if (country[0] != '\0'
                 && (country[0] != settings.country[0]
                     || country[1] != settings.country[1])) {
                 return false;
             }
         } else {
-            // But if we could determine the scripts, they should be the same
-            // for the locales to match.
-            if (memcmp(localeScript, settings.localeScript, sizeof(localeScript)) != 0) {
+            if (memcmp(script, settings.localeScript, sizeof(settings.localeScript)) != 0) {
                 return false;
             }
         }
diff --git a/libs/androidfw/tests/ConfigLocale_test.cpp b/libs/androidfw/tests/ConfigLocale_test.cpp
index 7b38640..4b8d65c 100644
--- a/libs/androidfw/tests/ConfigLocale_test.cpp
+++ b/libs/androidfw/tests/ConfigLocale_test.cpp
@@ -371,6 +371,19 @@
     EXPECT_TRUE(supported.match(requested));
 }
 
+TEST(ConfigLocaleTest, match_emptyScript) {
+    ResTable_config supported, requested;
+
+    fillIn("fr", "FR", NULL, NULL, &supported);
+    fillIn("fr", "CA", NULL, NULL, &requested);
+
+    // emulate packages built with older AAPT
+    memset(supported.localeScript, '\0', 4);
+    supported.localeScriptWasProvided = false;
+
+    EXPECT_TRUE(supported.match(requested));
+}
+
 TEST(ConfigLocaleTest, isLocaleBetterThan_basics) {
     ResTable_config config1, config2, request;
 
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 1f242a3..6a565033 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -144,20 +144,8 @@
     external/skia/include/private \
     external/skia/src/core
 
-hwui_shared_libraries := \
-    liblog \
-    libcutils \
-    libutils \
-    libEGL \
-    libGLESv2 \
-    libskia \
-    libui \
-    libgui \
-    libprotobuf-cpp-lite \
-
 ifneq (false,$(ANDROID_ENABLE_RENDERSCRIPT))
     hwui_cflags += -DANDROID_ENABLE_RENDERSCRIPT
-    hwui_shared_libraries += libRS libRScpp
     hwui_c_includes += \
         $(call intermediates-dir-for,STATIC_LIBRARIES,libRS,TARGET,) \
         frameworks/rs/cpp \
@@ -180,12 +168,15 @@
 
 LOCAL_MODULE_CLASS := STATIC_LIBRARIES
 LOCAL_MODULE := libhwui_static
-LOCAL_SHARED_LIBRARIES := $(hwui_shared_libraries)
 LOCAL_CFLAGS := $(hwui_cflags)
 LOCAL_SRC_FILES := $(hwui_src_files)
 LOCAL_C_INCLUDES := $(hwui_c_includes) $(call hwui_proto_include)
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(hwui_c_includes) $(call hwui_proto_include)
+LOCAL_EXPORT_C_INCLUDE_DIRS := \
+        $(LOCAL_PATH) \
+        $(hwui_c_includes) \
+        $(call hwui_proto_include)
 
+include $(LOCAL_PATH)/hwui_static_deps.mk
 include $(BUILD_STATIC_LIBRARY)
 
 # ------------------------
@@ -196,7 +187,6 @@
 
 LOCAL_MODULE_CLASS := STATIC_LIBRARIES
 LOCAL_MODULE := libhwui_static_null_gpu
-LOCAL_SHARED_LIBRARIES := $(hwui_shared_libraries)
 LOCAL_CFLAGS := \
         $(hwui_cflags) \
         -DHWUI_NULL_GPU
@@ -205,8 +195,12 @@
         debug/nullegl.cpp \
         debug/nullgles.cpp
 LOCAL_C_INCLUDES := $(hwui_c_includes) $(call hwui_proto_include)
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(hwui_c_includes) $(call hwui_proto_include)
+LOCAL_EXPORT_C_INCLUDE_DIRS := \
+        $(LOCAL_PATH) \
+        $(hwui_c_includes) \
+        $(call hwui_proto_include)
 
+include $(LOCAL_PATH)/hwui_static_deps.mk
 include $(BUILD_STATIC_LIBRARY)
 
 # ------------------------
@@ -218,8 +212,9 @@
 LOCAL_MODULE_CLASS := SHARED_LIBRARIES
 LOCAL_MODULE := libhwui
 LOCAL_WHOLE_STATIC_LIBRARIES := libhwui_static
-LOCAL_SHARED_LIBRARIES := $(hwui_shared_libraries)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
 
+include $(LOCAL_PATH)/hwui_static_deps.mk
 include $(BUILD_SHARED_LIBRARY)
 
 # ------------------------
@@ -230,7 +225,6 @@
 
 LOCAL_MODULE := hwui_unit_tests
 LOCAL_MODULE_TAGS := tests
-LOCAL_SHARED_LIBRARIES := $(hwui_shared_libraries)
 LOCAL_STATIC_LIBRARIES := libhwui_static_null_gpu
 LOCAL_CFLAGS := \
         $(hwui_cflags) \
@@ -263,6 +257,7 @@
         tests/unit/RecordingCanvasTests.cpp
 endif
 
+include $(LOCAL_PATH)/hwui_static_deps.mk
 include $(BUILD_NATIVE_TEST)
 
 # ------------------------
@@ -278,7 +273,6 @@
 LOCAL_MULTILIB := both
 LOCAL_MODULE_STEM_32 := hwuitest
 LOCAL_MODULE_STEM_64 := hwuitest64
-LOCAL_SHARED_LIBRARIES := $(hwui_shared_libraries)
 LOCAL_CFLAGS := $(hwui_cflags)
 
 # set to libhwui_static_null_gpu to skip actual GL commands
@@ -289,6 +283,7 @@
     tests/macrobench/TestSceneRunner.cpp \
     tests/macrobench/main.cpp
 
+include $(LOCAL_PATH)/hwui_static_deps.mk
 include $(BUILD_EXECUTABLE)
 
 # ------------------------
@@ -303,7 +298,6 @@
 LOCAL_MULTILIB := both
 LOCAL_MODULE_STEM_32 := hwuimicro
 LOCAL_MODULE_STEM_64 := hwuimicro64
-LOCAL_SHARED_LIBRARIES := $(hwui_shared_libraries)
 LOCAL_CFLAGS := \
         $(hwui_cflags) \
         -DHWUI_NULL_GPU
@@ -325,6 +319,5 @@
         tests/microbench/FrameBuilderBench.cpp
 endif
 
-LOCAL_CLANG := true # workaround gcc bug
-
+include $(LOCAL_PATH)/hwui_static_deps.mk
 include $(BUILD_EXECUTABLE)
diff --git a/libs/hwui/BakedOpState.h b/libs/hwui/BakedOpState.h
index 3db28c9..5a5845a 100644
--- a/libs/hwui/BakedOpState.h
+++ b/libs/hwui/BakedOpState.h
@@ -100,7 +100,7 @@
     static BakedOpState* tryConstruct(LinearAllocator& allocator,
             Snapshot& snapshot, const RecordedOp& recordedOp) {
         if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr;
-        BakedOpState* bakedState = new (allocator) BakedOpState(
+        BakedOpState* bakedState = allocator.create_trivial<BakedOpState>(
                 allocator, snapshot, recordedOp, false);
         if (bakedState->computedState.clippedBounds.isEmpty()) {
             // bounds are empty, so op is rejected
@@ -124,7 +124,7 @@
                 ? (recordedOp.paint && recordedOp.paint->getStyle() != SkPaint::kFill_Style)
                 : true;
 
-        BakedOpState* bakedState = new (allocator) BakedOpState(
+        BakedOpState* bakedState = allocator.create_trivial<BakedOpState>(
                 allocator, snapshot, recordedOp, expandForStroke);
         if (bakedState->computedState.clippedBounds.isEmpty()) {
             // bounds are empty, so op is rejected
@@ -140,16 +140,12 @@
         if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr;
 
         // clip isn't empty, so construct the op
-        return new (allocator) BakedOpState(allocator, snapshot, shadowOpPtr);
+        return allocator.create_trivial<BakedOpState>(allocator, snapshot, shadowOpPtr);
     }
 
     static BakedOpState* directConstruct(LinearAllocator& allocator,
             const ClipRect* clip, const Rect& dstRect, const RecordedOp& recordedOp) {
-        return new (allocator) BakedOpState(clip, dstRect, recordedOp);
-    }
-
-    static void* operator new(size_t size, LinearAllocator& allocator) {
-        return allocator.alloc(size);
+        return allocator.create_trivial<BakedOpState>(clip, dstRect, recordedOp);
     }
 
     // computed state:
@@ -162,6 +158,8 @@
     const RecordedOp* op;
 
 private:
+    friend class LinearAllocator;
+
     BakedOpState(LinearAllocator& allocator, Snapshot& snapshot,
             const RecordedOp& recordedOp, bool expandForStroke)
             : computedState(allocator, snapshot, recordedOp, expandForStroke)
diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp
index c2e14a2..6d5833b 100644
--- a/libs/hwui/DamageAccumulator.cpp
+++ b/libs/hwui/DamageAccumulator.cpp
@@ -45,7 +45,7 @@
 };
 
 DamageAccumulator::DamageAccumulator() {
-    mHead = (DirtyStack*) mAllocator.alloc(sizeof(DirtyStack));
+    mHead = mAllocator.create_trivial<DirtyStack>();
     memset(mHead, 0, sizeof(DirtyStack));
     // Create a root that we will not pop off
     mHead->prev = mHead;
@@ -78,7 +78,7 @@
 
 void DamageAccumulator::pushCommon() {
     if (!mHead->next) {
-        DirtyStack* nextFrame = (DirtyStack*) mAllocator.alloc(sizeof(DirtyStack));
+        DirtyStack* nextFrame = mAllocator.create_trivial<DirtyStack>();
         nextFrame->next = nullptr;
         nextFrame->prev = mHead;
         mHead->next = nextFrame;
diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h
index 2d5979f..98ccf11 100644
--- a/libs/hwui/DeferredDisplayList.h
+++ b/libs/hwui/DeferredDisplayList.h
@@ -49,11 +49,6 @@
 
 class DeferredDisplayState {
 public:
-    static void* operator new(size_t size) = delete;
-    static void* operator new(size_t size, LinearAllocator& allocator) {
-        return allocator.alloc(size);
-    }
-
     // global op bounds, mapped by mMatrix to be in screen space coordinates, clipped
     Rect mBounds;
 
@@ -124,7 +119,7 @@
     DeferredDisplayList(const DeferredDisplayList& other); // disallow copy
 
     DeferredDisplayState* createState() {
-        return new (mAllocator) DeferredDisplayState();
+        return mAllocator.create_trivial<DeferredDisplayState>();
     }
 
     void tryRecycleState(DeferredDisplayState* state) {
diff --git a/libs/hwui/DisplayListCanvas.h b/libs/hwui/DisplayListCanvas.h
index e5711e3..a703e22 100644
--- a/libs/hwui/DisplayListCanvas.h
+++ b/libs/hwui/DisplayListCanvas.h
@@ -251,7 +251,7 @@
     inline const T* refBuffer(const T* srcBuffer, int32_t count) {
         if (!srcBuffer) return nullptr;
 
-        T* dstBuffer = (T*) mDisplayList->allocator.alloc(count * sizeof(T));
+        T* dstBuffer = (T*) mDisplayList->allocator.alloc<T>(count * sizeof(T));
         memcpy(dstBuffer, srcBuffer, count * sizeof(T));
         return dstBuffer;
     }
@@ -320,8 +320,7 @@
         // correctly, such as creating the bitmap from scratch, drawing with it, changing its
         // contents, and drawing again. The only fix would be to always copy it the first time,
         // which doesn't seem worth the extra cycles for this unlikely case.
-        SkBitmap* localBitmap = new (alloc()) SkBitmap(bitmap);
-        alloc().autoDestroy(localBitmap);
+        SkBitmap* localBitmap = alloc().create<SkBitmap>(bitmap);
         mDisplayList->bitmapResources.push_back(localBitmap);
         return localBitmap;
     }
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 20501ba..98315d0 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -64,7 +64,9 @@
     static void operator delete(void* ptr) { LOG_ALWAYS_FATAL("delete not supported"); }
     static void* operator new(size_t size) = delete; /** PURPOSELY OMITTED **/
     static void* operator new(size_t size, LinearAllocator& allocator) {
-        return allocator.alloc(size);
+        // FIXME: Quick hack to keep old pipeline working, delete this when
+        // we no longer need to support HWUI_NEWOPS := false
+        return allocator.alloc<char>(size);
     }
 
     enum OpLogFlag {
diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp
index 185acce..4f51036 100644
--- a/libs/hwui/FrameBuilder.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -209,7 +209,7 @@
         // not rejected, so defer render as either Layer, or direct (possibly wrapped in saveLayer)
         if (node.getLayer()) {
             // HW layer
-            LayerOp* drawLayerOp = new (mAllocator) LayerOp(node);
+            LayerOp* drawLayerOp = mAllocator.create_trivial<LayerOp>(node);
             BakedOpState* bakedOpState = tryBakeOpState(*drawLayerOp);
             if (bakedOpState) {
                 // Node's layer already deferred, schedule it to render into parent layer
@@ -220,13 +220,13 @@
             // (temp layers are clipped to viewport, since they don't persist offscreen content)
             SkPaint saveLayerPaint;
             saveLayerPaint.setAlpha(properties.getAlpha());
-            deferBeginLayerOp(*new (mAllocator) BeginLayerOp(
+            deferBeginLayerOp(*mAllocator.create_trivial<BeginLayerOp>(
                     saveLayerBounds,
                     Matrix4::identity(),
                     nullptr, // no record-time clip - need only respect defer-time one
                     &saveLayerPaint));
             deferNodeOps(node);
-            deferEndLayerOp(*new (mAllocator) EndLayerOp());
+            deferEndLayerOp(*mAllocator.create_trivial<EndLayerOp>());
         } else {
             deferNodeOps(node);
         }
@@ -549,7 +549,7 @@
 void FrameBuilder::deferVectorDrawableOp(const VectorDrawableOp& op) {
     const SkBitmap& bitmap = op.vectorDrawable->getBitmapUpdateIfDirty();
     SkPaint* paint = op.vectorDrawable->getPaint();
-    const BitmapRectOp* resolvedOp = new (mAllocator) BitmapRectOp(op.unmappedBounds,
+    const BitmapRectOp* resolvedOp = mAllocator.create_trivial<BitmapRectOp>(op.unmappedBounds,
             op.localMatrix,
             op.localClip,
             paint,
@@ -565,7 +565,7 @@
     float y = *(op.y);
     float radius = *(op.radius);
     Rect unmappedBounds(x - radius, y - radius, x + radius, y + radius);
-    const OvalOp* resolvedOp = new (mAllocator) OvalOp(
+    const OvalOp* resolvedOp = mAllocator.create_trivial<OvalOp>(
             unmappedBounds,
             op.localMatrix,
             op.localClip,
@@ -626,7 +626,7 @@
 void FrameBuilder::deferRoundRectPropsOp(const RoundRectPropsOp& op) {
     // allocate a temporary round rect op (with mAllocator, so it persists until render), so the
     // renderer doesn't have to handle the RoundRectPropsOp type, and so state baking is simple.
-    const RoundRectOp* resolvedOp = new (mAllocator) RoundRectOp(
+    const RoundRectOp* resolvedOp = mAllocator.create_trivial<RoundRectOp>(
             Rect(*(op.left), *(op.top), *(op.right), *(op.bottom)),
             op.localMatrix,
             op.localClip,
@@ -754,7 +754,7 @@
 
     // record the draw operation into the previous layer's list of draw commands
     // uses state from the associated beginLayerOp, since it has all the state needed for drawing
-    LayerOp* drawLayerOp = new (mAllocator) LayerOp(
+    LayerOp* drawLayerOp = mAllocator.create_trivial<LayerOp>(
             beginLayerOp.unmappedBounds,
             beginLayerOp.localMatrix,
             beginLayerOp.localClip,
@@ -788,7 +788,7 @@
     /**
      * First, defer an operation to copy out the content from the rendertarget into a layer.
      */
-    auto copyToOp = new (mAllocator) CopyToLayerOp(op, layerHandle);
+    auto copyToOp = mAllocator.create_trivial<CopyToLayerOp>(op, layerHandle);
     BakedOpState* bakedState = BakedOpState::directConstruct(mAllocator,
             &(currentLayer().viewportClip), dstRect, *copyToOp);
     currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::CopyToLayer);
@@ -803,7 +803,7 @@
      * And stash an operation to copy that layer back under the rendertarget until
      * a balanced EndUnclippedLayerOp is seen
      */
-    auto copyFromOp = new (mAllocator) CopyFromLayerOp(op, layerHandle);
+    auto copyFromOp = mAllocator.create_trivial<CopyFromLayerOp>(op, layerHandle);
     bakedState = BakedOpState::directConstruct(mAllocator,
             &(currentLayer().viewportClip), dstRect, *copyFromOp);
     currentLayer().activeUnclippedSaveLayers.push_back(bakedState);
diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp
index 11293d6..c8f5e94 100644
--- a/libs/hwui/GradientCache.cpp
+++ b/libs/hwui/GradientCache.cpp
@@ -165,6 +165,10 @@
     generateTexture(colors, positions, info.width, 2, texture);
 
     mSize += size;
+    LOG_ALWAYS_FATAL_IF((int)size != texture->objectSize(),
+            "size != texture->objectSize(), size %" PRIu32 ", objectSize %d"
+            " width = %" PRIu32 " bytesPerPixel() = %zu",
+            size, texture->objectSize(), info.width, bytesPerPixel());
     mCache.put(gradient, texture);
 
     return texture;
diff --git a/libs/hwui/LayerBuilder.cpp b/libs/hwui/LayerBuilder.cpp
index 7170d4f..1ba3bf2 100644
--- a/libs/hwui/LayerBuilder.cpp
+++ b/libs/hwui/LayerBuilder.cpp
@@ -64,10 +64,6 @@
 
 class OpBatch : public BatchBase {
 public:
-    static void* operator new(size_t size, LinearAllocator& allocator) {
-        return allocator.alloc(size);
-    }
-
     OpBatch(batchid_t batchId, BakedOpState* op)
             : BatchBase(batchId, op, false) {
     }
@@ -80,10 +76,6 @@
 
 class MergingOpBatch : public BatchBase {
 public:
-    static void* operator new(size_t size, LinearAllocator& allocator) {
-        return allocator.alloc(size);
-    }
-
     MergingOpBatch(batchid_t batchId, BakedOpState* op)
             : BatchBase(batchId, op, true)
             , mClipSideFlags(op->computedState.clipSideFlags) {
@@ -247,7 +239,7 @@
         // put the verts in the frame allocator, since
         //     1) SimpleRectsOps needs verts, not rects
         //     2) even if mClearRects stored verts, std::vectors will move their contents
-        Vertex* const verts = (Vertex*) allocator.alloc(vertCount * sizeof(Vertex));
+        Vertex* const verts = (Vertex*) allocator.alloc<Vertex>(vertCount * sizeof(Vertex));
 
         Vertex* currentVert = verts;
         Rect bounds = mClearRects[0];
@@ -264,7 +256,7 @@
         // Flush all of these clears with a single draw
         SkPaint* paint = allocator.create<SkPaint>();
         paint->setXfermodeMode(SkXfermode::kClear_Mode);
-        SimpleRectsOp* op = new (allocator) SimpleRectsOp(bounds,
+        SimpleRectsOp* op = allocator.create_trivial<SimpleRectsOp>(bounds,
                 Matrix4::identity(), nullptr, paint,
                 verts, vertCount);
         BakedOpState* bakedState = BakedOpState::directConstruct(allocator,
@@ -292,7 +284,7 @@
         targetBatch->batchOp(op);
     } else  {
         // new non-merging batch
-        targetBatch = new (allocator) OpBatch(batchId, op);
+        targetBatch = allocator.create<OpBatch>(batchId, op);
         mBatchLookup[batchId] = targetBatch;
         mBatches.insert(mBatches.begin() + insertBatchIndex, targetBatch);
     }
@@ -323,7 +315,7 @@
         targetBatch->mergeOp(op);
     } else  {
         // new merging batch
-        targetBatch = new (allocator) MergingOpBatch(batchId, op);
+        targetBatch = allocator.create<MergingOpBatch>(batchId, op);
         mMergingBatchLookup[batchId].insert(std::make_pair(mergeId, targetBatch));
 
         mBatches.insert(mBatches.begin() + insertBatchIndex, targetBatch);
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 16929b8..269e590 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -83,9 +83,9 @@
 
 void RecordingCanvas::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {
     if (removed.flags & Snapshot::kFlagIsFboLayer) {
-        addOp(new (alloc()) EndLayerOp());
+        addOp(alloc().create_trivial<EndLayerOp>());
     } else if (removed.flags & Snapshot::kFlagIsLayer) {
-        addOp(new (alloc()) EndUnclippedLayerOp());
+        addOp(alloc().create_trivial<EndUnclippedLayerOp>());
     }
 }
 
@@ -167,7 +167,7 @@
         snapshot.resetClip(clip.left, clip.top, clip.right, clip.bottom);
         snapshot.roundRectClipState = nullptr;
 
-        addOp(new (alloc()) BeginLayerOp(
+        addOp(alloc().create_trivial<BeginLayerOp>(
                 unmappedBounds,
                 *previous.transform, // transform to *draw* with
                 previousClip, // clip to *draw* with
@@ -175,7 +175,7 @@
     } else {
         snapshot.flags |= Snapshot::kFlagIsLayer;
 
-        addOp(new (alloc()) BeginUnclippedLayerOp(
+        addOp(alloc().create_trivial<BeginUnclippedLayerOp>(
                 unmappedBounds,
                 *mState.currentSnapshot()->transform,
                 getRecordedClip(),
@@ -241,7 +241,7 @@
 }
 
 void RecordingCanvas::drawPaint(const SkPaint& paint) {
-    addOp(new (alloc()) RectOp(
+    addOp(alloc().create_trivial<RectOp>(
             mState.getRenderTargetClipBounds(), // OK, since we've not passed transform
             Matrix4::identity(),
             getRecordedClip(),
@@ -261,7 +261,7 @@
     if (floatCount < 2) return;
     floatCount &= ~0x1; // round down to nearest two
 
-    addOp(new (alloc()) PointsOp(
+    addOp(alloc().create_trivial<PointsOp>(
             calcBoundsOfPoints(points, floatCount),
             *mState.currentSnapshot()->transform,
             getRecordedClip(),
@@ -272,7 +272,7 @@
     if (floatCount < 4) return;
     floatCount &= ~0x3; // round down to nearest four
 
-    addOp(new (alloc()) LinesOp(
+    addOp(alloc().create_trivial<LinesOp>(
             calcBoundsOfPoints(points, floatCount),
             *mState.currentSnapshot()->transform,
             getRecordedClip(),
@@ -280,7 +280,7 @@
 }
 
 void RecordingCanvas::drawRect(float left, float top, float right, float bottom, const SkPaint& paint) {
-    addOp(new (alloc()) RectOp(
+    addOp(alloc().create_trivial<RectOp>(
             Rect(left, top, right, bottom),
             *(mState.currentSnapshot()->transform),
             getRecordedClip(),
@@ -290,7 +290,7 @@
 void RecordingCanvas::drawSimpleRects(const float* rects, int vertexCount, const SkPaint* paint) {
     if (rects == nullptr) return;
 
-    Vertex* rectData = (Vertex*) mDisplayList->allocator.alloc(vertexCount * sizeof(Vertex));
+    Vertex* rectData = (Vertex*) mDisplayList->allocator.alloc<Vertex>(vertexCount * sizeof(Vertex));
     Vertex* vertex = rectData;
 
     float left = FLT_MAX;
@@ -313,7 +313,7 @@
         right = std::max(right, r);
         bottom = std::max(bottom, b);
     }
-    addOp(new (alloc()) SimpleRectsOp(
+    addOp(alloc().create_trivial<SimpleRectsOp>(
             Rect(left, top, right, bottom),
             *(mState.currentSnapshot()->transform),
             getRecordedClip(),
@@ -347,7 +347,7 @@
 }
 void RecordingCanvas::drawRoundRect(float left, float top, float right, float bottom,
             float rx, float ry, const SkPaint& paint) {
-    addOp(new (alloc()) RoundRectOp(
+    addOp(alloc().create_trivial<RoundRectOp>(
             Rect(left, top, right, bottom),
             *(mState.currentSnapshot()->transform),
             getRecordedClip(),
@@ -367,7 +367,7 @@
     mDisplayList->ref(ry);
     mDisplayList->ref(paint);
     refBitmapsInShader(paint->value.getShader());
-    addOp(new (alloc()) RoundRectPropsOp(
+    addOp(alloc().create_trivial<RoundRectPropsOp>(
             *(mState.currentSnapshot()->transform),
             getRecordedClip(),
             &paint->value,
@@ -389,7 +389,7 @@
     mDisplayList->ref(radius);
     mDisplayList->ref(paint);
     refBitmapsInShader(paint->value.getShader());
-    addOp(new (alloc()) CirclePropsOp(
+    addOp(alloc().create_trivial<CirclePropsOp>(
             *(mState.currentSnapshot()->transform),
             getRecordedClip(),
             &paint->value,
@@ -397,7 +397,7 @@
 }
 
 void RecordingCanvas::drawOval(float left, float top, float right, float bottom, const SkPaint& paint) {
-    addOp(new (alloc()) OvalOp(
+    addOp(alloc().create_trivial<OvalOp>(
             Rect(left, top, right, bottom),
             *(mState.currentSnapshot()->transform),
             getRecordedClip(),
@@ -406,7 +406,7 @@
 
 void RecordingCanvas::drawArc(float left, float top, float right, float bottom,
         float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) {
-    addOp(new (alloc()) ArcOp(
+    addOp(alloc().create_trivial<ArcOp>(
             Rect(left, top, right, bottom),
             *(mState.currentSnapshot()->transform),
             getRecordedClip(),
@@ -415,7 +415,7 @@
 }
 
 void RecordingCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
-    addOp(new (alloc()) PathOp(
+    addOp(alloc().create_trivial<PathOp>(
             Rect(path.getBounds()),
             *(mState.currentSnapshot()->transform),
             getRecordedClip(),
@@ -424,7 +424,7 @@
 
 void RecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) {
     mDisplayList->ref(tree);
-    addOp(new (alloc()) VectorDrawableOp(
+    addOp(alloc().create_trivial<VectorDrawableOp>(
             tree,
             Rect(tree->getBounds()),
             *(mState.currentSnapshot()->transform),
@@ -475,7 +475,7 @@
         drawBitmap(&bitmap, paint);
         restore();
     } else {
-        addOp(new (alloc()) BitmapRectOp(
+        addOp(alloc().create_trivial<BitmapRectOp>(
                 Rect(dstLeft, dstTop, dstRight, dstBottom),
                 *(mState.currentSnapshot()->transform),
                 getRecordedClip(),
@@ -487,7 +487,7 @@
 void RecordingCanvas::drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
             const float* vertices, const int* colors, const SkPaint* paint) {
     int vertexCount = (meshWidth + 1) * (meshHeight + 1);
-    addOp(new (alloc()) BitmapMeshOp(
+    addOp(alloc().create_trivial<BitmapMeshOp>(
             calcBoundsOfPoints(vertices, vertexCount * 2),
             *(mState.currentSnapshot()->transform),
             getRecordedClip(),
@@ -499,7 +499,7 @@
 void RecordingCanvas::drawNinePatch(const SkBitmap& bitmap, const android::Res_png_9patch& patch,
             float dstLeft, float dstTop, float dstRight, float dstBottom,
             const SkPaint* paint) {
-    addOp(new (alloc()) PatchOp(
+    addOp(alloc().create_trivial<PatchOp>(
             Rect(dstLeft, dstTop, dstRight, dstBottom),
             *(mState.currentSnapshot()->transform),
             getRecordedClip(),
@@ -515,7 +515,7 @@
     positions = refBuffer<float>(positions, glyphCount * 2);
 
     // TODO: either must account for text shadow in bounds, or record separate ops for text shadows
-    addOp(new (alloc()) TextOp(
+    addOp(alloc().create_trivial<TextOp>(
             Rect(boundsLeft, boundsTop, boundsRight, boundsBottom),
             *(mState.currentSnapshot()->transform),
             getRecordedClip(),
@@ -527,7 +527,7 @@
             float hOffset, float vOffset, const SkPaint& paint) {
     if (!glyphs || glyphCount <= 0 || PaintUtils::paintWillNotDrawText(paint)) return;
     glyphs = refBuffer<glyph_t>(glyphs, glyphCount);
-    addOp(new (alloc()) TextOnPathOp(
+    addOp(alloc().create_trivial<TextOnPathOp>(
             mState.getLocalClipBounds(), // TODO: explicitly define bounds
             *(mState.currentSnapshot()->transform),
             getRecordedClip(),
@@ -535,7 +535,7 @@
 }
 
 void RecordingCanvas::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) {
-    addOp(new (alloc()) BitmapOp(
+    addOp(alloc().create_trivial<BitmapOp>(
             Rect(bitmap->width(), bitmap->height()),
             *(mState.currentSnapshot()->transform),
             getRecordedClip(),
@@ -544,7 +544,7 @@
 
 void RecordingCanvas::drawRenderNode(RenderNode* renderNode) {
     auto&& stagingProps = renderNode->stagingProperties();
-    RenderNodeOp* op = new (alloc()) RenderNodeOp(
+    RenderNodeOp* op = alloc().create_trivial<RenderNodeOp>(
             Rect(stagingProps.getWidth(), stagingProps.getHeight()),
             *(mState.currentSnapshot()->transform),
             getRecordedClip(),
@@ -570,7 +570,7 @@
     Matrix4 totalTransform(*(mState.currentSnapshot()->transform));
     totalTransform.multiply(layer->getTransform());
 
-    addOp(new (alloc()) TextureLayerOp(
+    addOp(alloc().create_trivial<TextureLayerOp>(
             Rect(layer->getWidth(), layer->getHeight()),
             totalTransform,
             getRecordedClip(),
@@ -579,7 +579,7 @@
 
 void RecordingCanvas::callDrawGLFunction(Functor* functor) {
     mDisplayList->functors.push_back(functor);
-    addOp(new (alloc()) FunctorOp(
+    addOp(alloc().create_trivial<FunctorOp>(
             mState.getLocalClipBounds(), // TODO: explicitly define bounds
             *(mState.currentSnapshot()->transform),
             getRecordedClip(),
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index cc14e61..719872d 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -219,7 +219,7 @@
     inline const T* refBuffer(const T* srcBuffer, int32_t count) {
         if (!srcBuffer) return nullptr;
 
-        T* dstBuffer = (T*) mDisplayList->allocator.alloc(count * sizeof(T));
+        T* dstBuffer = (T*) mDisplayList->allocator.alloc<T>(count * sizeof(T));
         memcpy(dstBuffer, srcBuffer, count * sizeof(T));
         return dstBuffer;
     }
@@ -290,8 +290,7 @@
         // correctly, such as creating the bitmap from scratch, drawing with it, changing its
         // contents, and drawing again. The only fix would be to always copy it the first time,
         // which doesn't seem worth the extra cycles for this unlikely case.
-        SkBitmap* localBitmap = new (alloc()) SkBitmap(bitmap);
-        alloc().autoDestroy(localBitmap);
+        SkBitmap* localBitmap = alloc().create<SkBitmap>(bitmap);
         mDisplayList->bitmapResources.push_back(localBitmap);
         return localBitmap;
     }
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index dbaa905..0ac2f14 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -46,7 +46,7 @@
 public:
     /** static void* operator new(size_t size); PURPOSELY OMITTED, allocator only **/
     static void* operator new(size_t size, LinearAllocator& allocator) {
-        return allocator.alloc(size);
+        return allocator.alloc<RoundRectClipState>(size);
     }
 
     bool areaRequiresRoundRectClip(const Rect& rect) const {
@@ -67,7 +67,7 @@
 public:
     /** static void* operator new(size_t size); PURPOSELY OMITTED, allocator only **/
     static void* operator new(size_t size, LinearAllocator& allocator) {
-        return allocator.alloc(size);
+        return allocator.alloc<ProjectionPathMask>(size);
     }
 
     const SkPath* projectionMask;
diff --git a/libs/hwui/Texture.cpp b/libs/hwui/Texture.cpp
index c09b6dd..4f49a35 100644
--- a/libs/hwui/Texture.cpp
+++ b/libs/hwui/Texture.cpp
@@ -28,13 +28,19 @@
 
 static int bytesPerPixel(GLint glFormat) {
     switch (glFormat) {
+    // The wrapped-texture case, usually means a SurfaceTexture
+    case 0:
+        return 0;
     case GL_ALPHA:
         return 1;
     case GL_RGB:
         return 3;
     case GL_RGBA:
-    default:
         return 4;
+    case GL_RGBA16F:
+        return 16;
+    default:
+        LOG_ALWAYS_FATAL("UNKNOWN FORMAT %d", glFormat);
     }
 }
 
diff --git a/libs/hwui/hwui_static_deps.mk b/libs/hwui/hwui_static_deps.mk
new file mode 100644
index 0000000..7d4ef0f
--- /dev/null
+++ b/libs/hwui/hwui_static_deps.mk
@@ -0,0 +1,28 @@
+###############################################################################
+#
+#
+# This file contains the shared and static dependencies needed by any target
+# that attempts to statically link HWUI (i.e. libhwui_static build target). This
+# file should be included by any target that lists libhwui_static as a
+# dependency.
+#
+# This is a workaround for the fact that the build system does not add these
+# transitive dependencies when it attempts to link libhwui_static into another
+# library.
+#
+###############################################################################
+
+LOCAL_SHARED_LIBRARIES += \
+    liblog \
+    libcutils \
+    libutils \
+    libEGL \
+    libGLESv2 \
+    libskia \
+    libui \
+    libgui \
+    libprotobuf-cpp-lite
+
+ifneq (false,$(ANDROID_ENABLE_RENDERSCRIPT))
+    LOCAL_SHARED_LIBRARIES += libRS libRScpp
+endif
\ No newline at end of file
diff --git a/libs/hwui/tests/unit/LinearAllocatorTests.cpp b/libs/hwui/tests/unit/LinearAllocatorTests.cpp
index 5c44290..402a09c 100644
--- a/libs/hwui/tests/unit/LinearAllocatorTests.cpp
+++ b/libs/hwui/tests/unit/LinearAllocatorTests.cpp
@@ -30,7 +30,7 @@
 TEST(LinearAllocator, create) {
     LinearAllocator la;
     EXPECT_EQ(0u, la.usedSize());
-    la.alloc(64);
+    la.alloc<char>(64);
     // There's some internal tracking as well as padding
     // so the usedSize isn't strictly defined
     EXPECT_LE(64u, la.usedSize());
@@ -50,13 +50,12 @@
             la.create<TestUtils::SignalingDtor>()->setSignal(destroyed + i);
             la.create<SimplePair>();
         }
-        la.alloc(100);
+        la.alloc<char>(100);
         for (int i = 0; i < 5; i++) {
-            auto sd = new (la) TestUtils::SignalingDtor(destroyed + 5 + i);
-            la.autoDestroy(sd);
-            new (la) SimplePair();
+            la.create<TestUtils::SignalingDtor>(destroyed + 5 + i);
+            la.create_trivial<SimplePair>();
         }
-        la.alloc(100);
+        la.alloc<char>(100);
         for (int i = 0; i < 10; i++) {
             EXPECT_EQ(0, destroyed[i]);
         }
@@ -70,7 +69,7 @@
     int destroyed = 0;
     {
         LinearAllocator la;
-        auto addr = la.alloc(100);
+        auto addr = la.alloc<char>(100);
         EXPECT_LE(100u, la.usedSize());
         la.rewindIfLastAlloc(addr, 100);
         EXPECT_GT(16u, la.usedSize());
diff --git a/libs/hwui/utils/LinearAllocator.cpp b/libs/hwui/utils/LinearAllocator.cpp
index e6a4c03..5bba420 100644
--- a/libs/hwui/utils/LinearAllocator.cpp
+++ b/libs/hwui/utils/LinearAllocator.cpp
@@ -81,10 +81,6 @@
 
 #define min(x,y) (((x) < (y)) ? (x) : (y))
 
-void* operator new(std::size_t size, android::uirenderer::LinearAllocator& la) {
-    return la.alloc(size);
-}
-
 namespace android {
 namespace uirenderer {
 
@@ -171,7 +167,7 @@
     mNext = start(mCurrentPage);
 }
 
-void* LinearAllocator::alloc(size_t size) {
+void* LinearAllocator::allocImpl(size_t size) {
     size = ALIGN(size);
     if (size > mMaxAllocSize && !fitsInCurrentPage(size)) {
         ALOGV("Exceeded max size %zu > %zu", size, mMaxAllocSize);
@@ -196,7 +192,7 @@
                   "DestructorNode must have standard layout");
     static_assert(std::is_trivially_destructible<DestructorNode>::value,
                   "DestructorNode must be trivially destructable");
-    auto node = new (*this) DestructorNode();
+    auto node = new (allocImpl(sizeof(DestructorNode))) DestructorNode();
     node->dtor = dtor;
     node->addr = addr;
     node->next = mDtorList;
diff --git a/libs/hwui/utils/LinearAllocator.h b/libs/hwui/utils/LinearAllocator.h
index dcbc0dd..0a0e185 100644
--- a/libs/hwui/utils/LinearAllocator.h
+++ b/libs/hwui/utils/LinearAllocator.h
@@ -52,30 +52,36 @@
      * The lifetime of the returned buffers is tied to that of the LinearAllocator. If calling
      * delete() on an object stored in a buffer is needed, it should be overridden to use
      * rewindIfLastAlloc()
+     *
+     * Note that unlike create, for alloc the type is purely for compile-time error
+     * checking and does not affect size.
      */
-    void* alloc(size_t size);
+    template<class T>
+    void* alloc(size_t size) {
+        static_assert(std::is_trivially_destructible<T>::value,
+                "Error, type is non-trivial! did you mean to use create()?");
+        return allocImpl(size);
+    }
 
     /**
      * Allocates an instance of the template type with the given construction parameters
      * and adds it to the automatic destruction list.
      */
     template<class T, typename... Params>
-    T* create(Params... params) {
-        T* ret = new (*this) T(params...);
-        autoDestroy(ret);
+    T* create(Params&&... params) {
+        T* ret = new (allocImpl(sizeof(T))) T(std::forward<Params>(params)...);
+        if (!std::is_trivially_destructible<T>::value) {
+            auto dtor = [](void* ret) { ((T*)ret)->~T(); };
+            addToDestructionList(dtor, ret);
+        }
         return ret;
     }
 
-    /**
-     * Adds the pointer to the tracking list to have its destructor called
-     * when the LinearAllocator is destroyed.
-     */
-    template<class T>
-    void autoDestroy(T* addr) {
-        if (!std::is_trivially_destructible<T>::value) {
-            auto dtor = [](void* addr) { ((T*)addr)->~T(); };
-            addToDestructionList(dtor, addr);
-        }
+    template<class T, typename... Params>
+    T* create_trivial(Params&&... params) {
+        static_assert(std::is_trivially_destructible<T>::value,
+                "Error, called create_trivial on a non-trivial type");
+        return new (allocImpl(sizeof(T))) T(std::forward<Params>(params)...);
     }
 
     /**
@@ -114,6 +120,8 @@
         DestructorNode* next = nullptr;
     };
 
+    void* allocImpl(size_t size);
+
     void addToDestructionList(Destructor, void* addr);
     void runDestructorFor(void* addr);
     Page* newPage(size_t pageSize);
@@ -159,7 +167,7 @@
             : linearAllocator(other.linearAllocator) {}
 
     T* allocate(size_t num, const void* = 0) {
-        return (T*)(linearAllocator.alloc(num * sizeof(T)));
+        return (T*)(linearAllocator.alloc<void*>(num * sizeof(T)));
     }
 
     void deallocate(pointer p, size_t num) {
@@ -187,6 +195,4 @@
 }; // namespace uirenderer
 }; // namespace android
 
-void* operator new(std::size_t size, android::uirenderer::LinearAllocator& la);
-
 #endif // ANDROID_LINEARALLOCATOR_H
diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java
index d8f507c..a490685 100644
--- a/location/java/android/location/GnssMeasurement.java
+++ b/location/java/android/location/GnssMeasurement.java
@@ -28,7 +28,7 @@
  */
 public final class GnssMeasurement implements Parcelable {
     private int mFlags;
-    private byte mPrn;
+    private short mSvid;
     private double mTimeOffsetInNs;
     private short mState;
     private long mReceivedGpsTowInNs;
@@ -198,7 +198,7 @@
      */
     public void set(GnssMeasurement measurement) {
         mFlags = measurement.mFlags;
-        mPrn = measurement.mPrn;
+        mSvid = measurement.mSvid;
         mTimeOffsetInNs = measurement.mTimeOffsetInNs;
         mState = measurement.mState;
         mReceivedGpsTowInNs = measurement.mReceivedGpsTowInNs;
@@ -248,15 +248,15 @@
      * Gets the Pseudo-random number (PRN).
      * Range: [1, 32]
      */
-    public byte getPrn() {
-        return mPrn;
+    public short getSvid() {
+        return mSvid;
     }
 
     /**
      * Sets the Pseud-random number (PRN).
      */
-    public void setPrn(byte value) {
-        mPrn = value;
+    public void setSvid(short value) {
+        mSvid = value;
     }
 
     /**
@@ -1210,7 +1210,7 @@
             GnssMeasurement gnssMeasurement = new GnssMeasurement();
 
             gnssMeasurement.mFlags = parcel.readInt();
-            gnssMeasurement.mPrn = parcel.readByte();
+            gnssMeasurement.mSvid = (short) parcel.readInt();
             gnssMeasurement.mTimeOffsetInNs = parcel.readDouble();
             gnssMeasurement.mState = (short) parcel.readInt();
             gnssMeasurement.mReceivedGpsTowInNs = parcel.readLong();
@@ -1253,9 +1253,10 @@
         }
     };
 
+    @Override
     public void writeToParcel(Parcel parcel, int flags) {
         parcel.writeInt(mFlags);
-        parcel.writeByte(mPrn);
+        parcel.writeInt(mSvid);
         parcel.writeDouble(mTimeOffsetInNs);
         parcel.writeInt(mState);
         parcel.writeLong(mReceivedGpsTowInNs);
@@ -1301,7 +1302,7 @@
         final String formatWithUncertainty = "   %-29s = %-25s   %-40s = %s\n";
         StringBuilder builder = new StringBuilder("GnssMeasurement:\n");
 
-        builder.append(String.format(format, "Prn", mPrn));
+        builder.append(String.format(format, "Svid", mSvid));
 
         builder.append(String.format(format, "TimeOffsetInNs", mTimeOffsetInNs));
 
@@ -1422,7 +1423,7 @@
 
     private void initialize() {
         mFlags = HAS_NO_FLAGS;
-        setPrn(Byte.MIN_VALUE);
+        setSvid((short) 0);
         setTimeOffsetInNs(Long.MIN_VALUE);
         setState(STATE_UNKNOWN);
         setReceivedGpsTowInNs(Long.MIN_VALUE);
diff --git a/location/java/android/location/GnssNavigationMessage.java b/location/java/android/location/GnssNavigationMessage.java
index 0e011d5..86328eb 100644
--- a/location/java/android/location/GnssNavigationMessage.java
+++ b/location/java/android/location/GnssNavigationMessage.java
@@ -26,7 +26,7 @@
 import java.security.InvalidParameterException;
 
 /**
- * A class containing a GPS satellite Navigation Message.
+ * A class containing a GNSS satellite Navigation Message.
  */
 public final class GnssNavigationMessage implements Parcelable {
 
@@ -84,7 +84,7 @@
     // End enumerations in sync with gps.h
 
     private byte mType;
-    private byte mPrn;
+    private short mSvid;
     private short mMessageId;
     private short mSubmessageId;
     private byte[] mData;
@@ -99,7 +99,7 @@
      */
     public void set(GnssNavigationMessage navigationMessage) {
         mType = navigationMessage.mType;
-        mPrn = navigationMessage.mPrn;
+        mSvid = navigationMessage.mSvid;
         mMessageId = navigationMessage.mMessageId;
         mSubmessageId = navigationMessage.mSubmessageId;
         mData = navigationMessage.mData;
@@ -153,15 +153,15 @@
      * Gets the Pseudo-random number.
      * Range: [1, 32].
      */
-    public byte getPrn() {
-        return mPrn;
+    public short getSvid() {
+        return mSvid;
     }
 
     /**
      * Sets the Pseud-random number.
      */
-    public void setPrn(byte value) {
-        mPrn = value;
+    public void setSvid(short value) {
+        mSvid = value;
     }
 
     /**
@@ -256,7 +256,7 @@
             GnssNavigationMessage navigationMessage = new GnssNavigationMessage();
 
             navigationMessage.setType(parcel.readByte());
-            navigationMessage.setPrn(parcel.readByte());
+            navigationMessage.setSvid((short) parcel.readInt());
             navigationMessage.setMessageId((short) parcel.readInt());
             navigationMessage.setSubmessageId((short) parcel.readInt());
 
@@ -281,9 +281,10 @@
         }
     };
 
+    @Override
     public void writeToParcel(Parcel parcel, int flags) {
         parcel.writeByte(mType);
-        parcel.writeByte(mPrn);
+        parcel.writeInt(mSvid);
         parcel.writeInt(mMessageId);
         parcel.writeInt(mSubmessageId);
         parcel.writeInt(mData.length);
@@ -302,7 +303,7 @@
         StringBuilder builder = new StringBuilder("GnssNavigationMessage:\n");
 
         builder.append(String.format(format, "Type", getTypeString()));
-        builder.append(String.format(format, "Prn", mPrn));
+        builder.append(String.format(format, "Svid", mSvid));
         builder.append(String.format(format, "Status", getStatusString()));
         builder.append(String.format(format, "MessageId", mMessageId));
         builder.append(String.format(format, "SubmessageId", mSubmessageId));
@@ -321,7 +322,7 @@
 
     private void initialize() {
         mType = MESSAGE_TYPE_UNKNOWN;
-        mPrn = 0;
+        mSvid = 0;
         mMessageId = -1;
         mSubmessageId = -1;
         mData = EMPTY_ARRAY;
diff --git a/location/java/android/location/GnssStatus.java b/location/java/android/location/GnssStatus.java
index 77e8a5b..906e944 100644
--- a/location/java/android/location/GnssStatus.java
+++ b/location/java/android/location/GnssStatus.java
@@ -47,24 +47,26 @@
     public static final int GNSS_SV_FLAGS_USED_IN_FIX = (1 << 2);
 
     /** @hide */
-    public static final int PRN_SHIFT_WIDTH = 3;
+    public static final int SVID_SHIFT_WIDTH = 7;
+    /** @hide */
+    public static final int CONSTELLATION_TYPE_SHIFT_WIDTH = 3;
+    /** @hide */
+    public static final int CONSTELLATION_TYPE_MASK = 0xf;
 
     /* These package private values are modified by the LocationManager class */
-    /* package */ int[] mPrnWithFlags;
+    /* package */ int[] mSvidWithFlags;
     /* package */ float[] mSnrs;
     /* package */ float[] mElevations;
     /* package */ float[] mAzimuths;
-    /* package */ int[] mConstellationTypes;
     /* package */ int mSvCount;
 
-    GnssStatus(int svCount, int[] prnWithFlags, float[] snrs, float[] elevations, float[] azimuths,
-            int[] constellationTypes) {
+    GnssStatus(int svCount, int[] svidWithFlags, float[] snrs, float[] elevations,
+            float[] azimuths) {
         mSvCount = svCount;
-        mPrnWithFlags = prnWithFlags;
+        mSvidWithFlags = svidWithFlags;
         mSnrs = snrs;
         mElevations = elevations;
         mAzimuths = azimuths;
-        mConstellationTypes = constellationTypes;
     }
 
     /**
@@ -79,15 +81,16 @@
      * @param satIndex the index of the satellite in the list.
      */
     public int getConstellationType(int satIndex) {
-        return mConstellationTypes[satIndex];
+        return (mSvidWithFlags[satIndex] >> CONSTELLATION_TYPE_SHIFT_WIDTH)
+                & CONSTELLATION_TYPE_MASK;
     }
 
     /**
      * Retrieves the pseudo-random number of the satellite at the specified position.
      * @param satIndex the index of the satellite in the list.
      */
-    public int getPrn(int satIndex) {
-        return mPrnWithFlags[satIndex] >> PRN_SHIFT_WIDTH;
+    public int getSvid(int satIndex) {
+        return mSvidWithFlags[satIndex] >> SVID_SHIFT_WIDTH;
     }
 
     /**
@@ -119,7 +122,7 @@
      * @param satIndex the index of the satellite in the list.
      */
     public boolean hasEphemeris(int satIndex) {
-        return (mPrnWithFlags[satIndex] & GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA) != 0;
+        return (mSvidWithFlags[satIndex] & GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA) != 0;
     }
 
     /**
@@ -127,7 +130,7 @@
      * @param satIndex the index of the satellite in the list.
      */
     public boolean hasAlmanac(int satIndex) {
-        return (mPrnWithFlags[satIndex] & GNSS_SV_FLAGS_HAS_ALMANAC_DATA) != 0;
+        return (mSvidWithFlags[satIndex] & GNSS_SV_FLAGS_HAS_ALMANAC_DATA) != 0;
     }
 
     /**
@@ -135,6 +138,6 @@
      * @param satIndex the index of the satellite in the list.
      */
     public boolean usedInFix(int satIndex) {
-        return (mPrnWithFlags[satIndex] & GNSS_SV_FLAGS_USED_IN_FIX) != 0;
+        return (mSvidWithFlags[satIndex] & GNSS_SV_FLAGS_USED_IN_FIX) != 0;
     }
 }
diff --git a/location/java/android/location/GpsStatus.java b/location/java/android/location/GpsStatus.java
index 8d2f781..e41e20c 100644
--- a/location/java/android/location/GpsStatus.java
+++ b/location/java/android/location/GpsStatus.java
@@ -138,15 +138,19 @@
     // For API-compat a public ctor() is not available
     GpsStatus() {}
 
-    private void setStatus(int svCount, int[] prnWithFlags, float[] snrs, float[] elevations,
-            float[] azimuths, int[] constellationTypes) { 
+    private void setStatus(int svCount, int[] svidWithFlags, float[] snrs, float[] elevations,
+            float[] azimuths) {
         clearSatellites();
         for (int i = 0; i < svCount; i++) {
+            final int constellationType =
+                    (svidWithFlags[i] >> GnssStatus.CONSTELLATION_TYPE_SHIFT_WIDTH)
+                    & GnssStatus.CONSTELLATION_TYPE_MASK;
             // Skip all non-GPS satellites.
-            if (constellationTypes[i] != GnssStatus.CONSTELLATION_GPS) {
+            if (constellationType != GnssStatus.CONSTELLATION_GPS) {
+                // TODO: translate the defacto pre-N use of  prn's >32 to new struct
                 continue;
             }
-            int prn = prnWithFlags[i] >> GnssStatus.PRN_SHIFT_WIDTH;
+            int prn = svidWithFlags[i] >> GnssStatus.SVID_SHIFT_WIDTH;
             if (prn > 0 && prn <= NUM_SATELLITES) {
                 GpsSatellite satellite = mSatellites.get(prn);
                 if (satellite == null) {
@@ -159,11 +163,11 @@
                 satellite.mElevation = elevations[i];
                 satellite.mAzimuth = azimuths[i];
                 satellite.mHasEphemeris =
-                        (prnWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA) != 0;
+                        (svidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA) != 0;
                 satellite.mHasAlmanac =
-                        (prnWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_ALMANAC_DATA) != 0;
+                        (svidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_ALMANAC_DATA) != 0;
                 satellite.mUsedInFix =
-                        (prnWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_USED_IN_FIX) != 0;
+                        (svidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_USED_IN_FIX) != 0;
             }
         }
     }
@@ -176,8 +180,8 @@
      */
     void setStatus(GnssStatus status, int timeToFirstFix) {
         mTimeToFirstFix = timeToFirstFix;
-        setStatus(status.mSvCount, status.mPrnWithFlags, status.mSnrs, status.mElevations,
-                status.mAzimuths, status.mConstellationTypes);
+        setStatus(status.mSvCount, status.mSvidWithFlags, status.mSnrs, status.mElevations,
+                status.mAzimuths);
     }
 
     void setTimeToFirstFix(int ttff) {
diff --git a/location/java/android/location/IGnssStatusListener.aidl b/location/java/android/location/IGnssStatusListener.aidl
index d1c6a85..8c7d06e 100644
--- a/location/java/android/location/IGnssStatusListener.aidl
+++ b/location/java/android/location/IGnssStatusListener.aidl
@@ -26,7 +26,7 @@
     void onGnssStarted();
     void onGnssStopped();
     void onFirstFix(int ttff);
-    void onSvStatusChanged(int svCount, in int[] prnWithFlags, in float[] snrs,
-            in float[] elevations, in float[] azimuths, in int[] constellationTypes);
+    void onSvStatusChanged(int svCount, in int[] svidWithFlags, in float[] snrs,
+            in float[] elevations, in float[] azimuths);
     void onNmeaReceived(long timestamp, String nmea);
 }
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 30cf101..23f0710 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -1562,10 +1562,9 @@
 
         @Override
         public void onSvStatusChanged(int svCount, int[] prnWithFlags,
-                float[] snrs, float[] elevations, float[] azimuths, int[] constellationTypes) {
+                float[] snrs, float[] elevations, float[] azimuths) {
             if (mGnssCallback != null) {
-                mGnssStatus = new GnssStatus(svCount, prnWithFlags, snrs, elevations, azimuths,
-                        constellationTypes);
+                mGnssStatus = new GnssStatus(svCount, prnWithFlags, snrs, elevations, azimuths);
 
                 Message msg = Message.obtain();
                 msg.what = GpsStatus.GPS_EVENT_SATELLITE_STATUS;
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index bdf6d9f..4d6c451 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -1052,32 +1052,27 @@
         }
     }
 
-// TODO Change getBufferCapacityInFrames() reference below to
-// {@link #getBufferCapacityInFrames()} after @hide is removed.
-// TODO Change setBufferSizeInFrames(int) reference below to
-// {@link #setBufferSizeInFrames(int)} after @hide is removed.
+
     /**
-     *  Returns the effective size of the <code>AudioTrack</code> buffer
+     * Returns the effective size of the <code>AudioTrack</code> buffer
      * that the application writes to.
-     *  <p> This will be less than or equal to the result of
-     * getBufferCapacityInFrames().
-     * It will be equal if setBufferSizeInFrames(int) has never been called.
-     *  <p> If the track is subsequently routed to a different output sink, the buffer
-     *  size and capacity may enlarge to accommodate.
-     *  <p> If the <code>AudioTrack</code> encoding indicates compressed data,
-     *  e.g. {@link AudioFormat#ENCODING_AC3}, then the frame count returned is
-     *  the size of the native <code>AudioTrack</code> buffer in bytes.
-     *  <p> See also {@link AudioManager#getProperty(String)} for key
-     *  {@link AudioManager#PROPERTY_OUTPUT_FRAMES_PER_BUFFER}.
-     *  @return current size in frames of the <code>AudioTrack</code> buffer.
-     *  @throws IllegalStateException
+     * <p> This will be less than or equal to the result of
+     * {@link #getBufferCapacityInFrames()}.
+     * It will be equal if {@link #setBufferSizeInFrames(int)} has never been called.
+     * <p> If the track is subsequently routed to a different output sink, the buffer
+     * size and capacity may enlarge to accommodate.
+     * <p> If the <code>AudioTrack</code> encoding indicates compressed data,
+     * e.g. {@link AudioFormat#ENCODING_AC3}, then the frame count returned is
+     * the size of the native <code>AudioTrack</code> buffer in bytes.
+     * <p> See also {@link AudioManager#getProperty(String)} for key
+     * {@link AudioManager#PROPERTY_OUTPUT_FRAMES_PER_BUFFER}.
+     * @return current size in frames of the <code>AudioTrack</code> buffer.
+     * @throws IllegalStateException
      */
     public int getBufferSizeInFrames() {
         return native_get_buffer_size_frames();
     }
 
-// TODO Change getBufferCapacityInFrames() reference below to
-// {@link #getBufferCapacityInFrames()} after @hide is removed.
     /**
      * Limits the effective size of the <code>AudioTrack</code> buffer
      * that the application writes to.
@@ -1087,9 +1082,9 @@
      * <p>Changing this limit modifies the latency associated with
      * the buffer for this track. A smaller size will give lower latency
      * but there may be more glitches due to buffer underruns.
-     *  <p>The actual size used may not be equal to this requested size.
+     * <p>The actual size used may not be equal to this requested size.
      * It will be limited to a valid range with a maximum of
-     * getBufferCapacityInFrames().
+     * {@link #getBufferCapacityInFrames()}.
      * It may also be adjusted slightly for internal reasons.
      * If bufferSizeInFrames is less than zero then {@link #ERROR_BAD_VALUE}
      * will be returned.
@@ -1097,9 +1092,9 @@
      * It is not supported for compressed audio tracks.
      *
      * @param bufferSizeInFrames requested buffer size
-     * @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE},
-     *    {@link #ERROR_INVALID_OPERATION}
-     *  @throws IllegalStateException
+     * @return the actual buffer size in frames or an error code,
+     *    {@link #ERROR_BAD_VALUE}, {@link #ERROR_INVALID_OPERATION}
+     * @throws IllegalStateException
      */
     public int setBufferSizeInFrames(int bufferSizeInFrames) {
         if (mDataLoadMode == MODE_STATIC || mState == STATE_UNINITIALIZED) {
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 1320e38..00083e6 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -157,35 +157,22 @@
     public static final int RECORDING_ERROR_UNKNOWN = 0;
 
     /**
-     * Error for {@link TvRecordingClient.RecordingCallback#onError(int)}: The recording client has
-     * failed to establish a connection to a recording session.
-     */
-    public static final int RECORDING_ERROR_CONNECTION_FAILED = 1;
-
-    /**
-     * Error for {@link TvRecordingClient.RecordingCallback#onError(int)}: The recording client has
-     * been disconnected from the current recording session.
-     */
-    public static final int RECORDING_ERROR_DISCONNECTED = 2;
-
-    /**
      * Error for {@link TvInputService.RecordingSession#notifyError(int)} and
      * {@link TvRecordingClient.RecordingCallback#onError(int)}: Recording cannot proceed due to
      * insufficient storage space.
      */
-    public static final int RECORDING_ERROR_INSUFFICIENT_SPACE = 3;
+    public static final int RECORDING_ERROR_INSUFFICIENT_SPACE = 1;
 
     /**
      * Error for {@link TvInputService.RecordingSession#notifyError(int)} and
      * {@link TvRecordingClient.RecordingCallback#onError(int)}: Recording cannot proceed because
      * a required recording resource was not able to be allocated.
      */
-    public static final int RECORDING_ERROR_RESOURCE_BUSY = 4;
+    public static final int RECORDING_ERROR_RESOURCE_BUSY = 2;
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({RECORDING_ERROR_UNKNOWN, RECORDING_ERROR_CONNECTION_FAILED,
-            RECORDING_ERROR_DISCONNECTED, RECORDING_ERROR_INSUFFICIENT_SPACE,
+    @IntDef({RECORDING_ERROR_UNKNOWN, RECORDING_ERROR_INSUFFICIENT_SPACE,
             RECORDING_ERROR_RESOURCE_BUSY})
     public @interface RecordingError {}
 
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 334c84b..4ebd0fc 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -1584,6 +1584,10 @@
          * new data entry in the {@link TvContract.RecordedPrograms} table that describes the newly
          * recorded program.
          *
+         * <p>The recording session must call this method in response to {@link #onStopRecording()}.
+         * The session may call it even before receiving a call to {@link #onStopRecording()} if a
+         * partially recorded program is available when there is an error.
+         *
          * @param recordedProgramUri The URI of the newly recorded program.
          */
         public void notifyRecordingStopped(final Uri recordedProgramUri) {
@@ -1604,8 +1608,14 @@
         }
 
         /**
-         * Informs the application that there is an error. It may be called at any time after this
-         * recording session is created until {@link #onRelease()} is called.
+         * Informs the application that there is an error and this recording session is no longer
+         * able to start or continue recording. It may be called at any time after the recording
+         * session is created until {@link #onRelease()} is called.
+         *
+         * <p>The application may release the current session upon receiving the error code through
+         * {@link TvRecordingClient.RecordingCallback#onError(int)}. The session may call
+         * {@link #notifyRecordingStopped(Uri)} if a partially recorded but still playable program
+         * is available, before calling this method.
          *
          * @param error The error code. Should be one of the followings.
          * <ul>
diff --git a/media/java/android/media/tv/TvRecordingClient.java b/media/java/android/media/tv/TvRecordingClient.java
index 72606f5..1c920f5 100644
--- a/media/java/android/media/tv/TvRecordingClient.java
+++ b/media/java/android/media/tv/TvRecordingClient.java
@@ -68,7 +68,7 @@
 
     /**
      * Tunes to a given channel for TV program recording. The first tune request will create a new
-     * recording session for the corresponding TV input and establish the connection between the
+     * recording session for the corresponding TV input and establish a connection between the
      * application and the session. If recording has already started in the current recording
      * session, this method throws an exception.
      *
@@ -88,7 +88,7 @@
 
     /**
      * Tunes to a given channel for TV program recording. The first tune request will create a new
-     * recording session for the corresponding TV input and establish the connection between the
+     * recording session for the corresponding TV input and establish a connection between the
      * application and the session. If recording has already started in the current recording
      * session, this method throws an exception.
      *
@@ -226,6 +226,23 @@
      */
     public abstract static class RecordingCallback {
         /**
+         * This is called when an error occurred while establishing a connection to the recording
+         * session for the corresponding TV input.
+         *
+         * @param inputId The ID of the TV input bound to the current TvRecordingClient.
+         */
+        public void onConnectionFailed(String inputId) {
+        }
+
+        /**
+         * This is called when the connection to the current recording session is lost.
+         *
+         * @param inputId The ID of the TV input bound to the current TvRecordingClient.
+         */
+        public void onDisconnected(String inputId) {
+        }
+
+        /**
          * This is called when the recording session has been tuned to the given channel and is
          * ready to start recording.
          */
@@ -249,8 +266,6 @@
          * @param error The error code. Should be one of the followings.
          * <ul>
          * <li>{@link TvInputManager#RECORDING_ERROR_UNKNOWN}
-         * <li>{@link TvInputManager#RECORDING_ERROR_CONNECTION_FAILED}
-         * <li>{@link TvInputManager#RECORDING_ERROR_DISCONNECTED}
          * <li>{@link TvInputManager#RECORDING_ERROR_INSUFFICIENT_SPACE}
          * <li>{@link TvInputManager#RECORDING_ERROR_RESOURCE_BUSY}
          * </ul>
@@ -305,7 +320,9 @@
                 mSession.tune(mChannelUri, mConnectionParams);
             } else {
                 mSessionCallback = null;
-                mCallback.onError(TvInputManager.RECORDING_ERROR_CONNECTION_FAILED);
+                if (mCallback != null) {
+                    mCallback.onConnectionFailed(mInputId);
+                }
             }
         }
 
@@ -331,11 +348,13 @@
                 Log.w(TAG, "onSessionReleased - session not created");
                 return;
             }
-            mSessionCallback = null;
-            mSession = null;
             mIsTuned = false;
             mIsRecordingStarted = false;
-            mCallback.onError(TvInputManager.RECORDING_ERROR_DISCONNECTED);
+            mSessionCallback = null;
+            mSession = null;
+            if (mCallback != null) {
+                mCallback.onDisconnected(mInputId);
+            }
         }
 
         @Override
diff --git a/media/java/android/media/tv/TvTrackInfo.java b/media/java/android/media/tv/TvTrackInfo.java
index ed432c46..6a44b1e 100644
--- a/media/java/android/media/tv/TvTrackInfo.java
+++ b/media/java/android/media/tv/TvTrackInfo.java
@@ -52,11 +52,14 @@
     private final int mVideoHeight;
     private final float mVideoFrameRate;
     private final float mVideoPixelAspectRatio;
+    private final byte mVideoActiveFormatDescription;
+
     private final Bundle mExtra;
 
     private TvTrackInfo(int type, String id, String language, CharSequence description,
             int audioChannelCount, int audioSampleRate, int videoWidth, int videoHeight,
-            float videoFrameRate, float videoPixelAspectRatio, Bundle extra) {
+            float videoFrameRate, float videoPixelAspectRatio, byte videoActiveFormatDescription,
+            Bundle extra) {
         mType = type;
         mId = id;
         mLanguage = language;
@@ -67,6 +70,7 @@
         mVideoHeight = videoHeight;
         mVideoFrameRate = videoFrameRate;
         mVideoPixelAspectRatio = videoPixelAspectRatio;
+        mVideoActiveFormatDescription = videoActiveFormatDescription;
         mExtra = extra;
     }
 
@@ -81,6 +85,7 @@
         mVideoHeight = in.readInt();
         mVideoFrameRate = in.readFloat();
         mVideoPixelAspectRatio = in.readFloat();
+        mVideoActiveFormatDescription = in.readByte();
         mExtra = in.readBundle();
     }
 
@@ -179,6 +184,20 @@
     }
 
     /**
+     * Returns the Active Format Description (AFD) code of the video.
+     * Valid only for {@link #TYPE_VIDEO} tracks.
+     *
+     * <p>The complete list of values are defined in ETSI TS 101 154 V1.7.1 Annex B, ATSC A/53 Part
+     * 4 and SMPTE 2016-1-2007.
+     */
+    public final byte getVideoActiveFormatDescription() {
+        if (mType != TYPE_VIDEO) {
+            throw new IllegalStateException("Not a video track");
+        }
+        return mVideoActiveFormatDescription;
+    }
+
+    /**
      * Returns the extra information about the current track.
      */
     public final Bundle getExtra() {
@@ -208,6 +227,7 @@
         dest.writeInt(mVideoHeight);
         dest.writeFloat(mVideoFrameRate);
         dest.writeFloat(mVideoPixelAspectRatio);
+        dest.writeByte(mVideoActiveFormatDescription);
         dest.writeBundle(mExtra);
     }
 
@@ -238,6 +258,7 @@
         private int mVideoHeight;
         private float mVideoFrameRate;
         private float mVideoPixelAspectRatio = 1.0f;
+        private byte mVideoActiveFormatDescription;
         private Bundle mExtra;
 
         /**
@@ -368,6 +389,25 @@
         }
 
         /**
+         * Sets the Active Format Description (AFD) code of the video.
+         * Valid only for {@link #TYPE_VIDEO} tracks.
+         *
+         * <p>This is needed for applications to be able to scale the video properly based on the
+         * information about where in the coded picture the active video is.
+         * The complete list of values are defined in ETSI TS 101 154 V1.7.1 Annex B, ATSC A/53 Part
+         * 4 and SMPTE 2016-1-2007.
+         *
+         * @param videoActiveFormatDescription The AFD code of the video.
+         */
+        public final Builder setVideoActiveFormatDescription(byte videoActiveFormatDescription) {
+            if (mType != TYPE_VIDEO) {
+                throw new IllegalStateException("Not a video track");
+            }
+            mVideoActiveFormatDescription = videoActiveFormatDescription;
+            return this;
+        }
+
+        /**
          * Sets the extra information about the current track.
          *
          * @param extra The extra information.
@@ -385,7 +425,7 @@
         public TvTrackInfo build() {
             return new TvTrackInfo(mType, mId, mLanguage, mDescription, mAudioChannelCount,
                     mAudioSampleRate, mVideoWidth, mVideoHeight, mVideoFrameRate,
-                    mVideoPixelAspectRatio, mExtra);
+                    mVideoPixelAspectRatio, mVideoActiveFormatDescription, mExtra);
         }
     }
 }
diff --git a/packages/DocumentsUI/res/layout/fixed_layout.xml b/packages/DocumentsUI/res/layout/fixed_layout.xml
index 8414feb..84a928d 100644
--- a/packages/DocumentsUI/res/layout/fixed_layout.xml
+++ b/packages/DocumentsUI/res/layout/fixed_layout.xml
@@ -16,11 +16,14 @@
 
 <!-- CoordinatorLayout is necessary for various components (e.g. Snackbars, and
      floating action buttons) to operate correctly. -->
+<!-- focusableInTouchMode is set in order to force key events to go to the activity's global key
+     callback, which is necessary for proper event routing. See BaseActivity.onKeyDown. -->
 <android.support.design.widget.CoordinatorLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:id="@+id/coordinator_layout">
+    android:id="@+id/coordinator_layout"
+    android:focusableInTouchMode="true">
 
     <LinearLayout
         android:layout_width="match_parent"
diff --git a/packages/DocumentsUI/res/layout/fragment_directory.xml b/packages/DocumentsUI/res/layout/fragment_directory.xml
index d0364ff..0fb74e5 100644
--- a/packages/DocumentsUI/res/layout/fragment_directory.xml
+++ b/packages/DocumentsUI/res/layout/fragment_directory.xml
@@ -83,7 +83,7 @@
         android:layout_height="match_parent">
 
         <android.support.v7.widget.RecyclerView
-            android:id="@+id/list"
+            android:id="@+id/dir_list"
             android:scrollbars="vertical"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
diff --git a/packages/DocumentsUI/res/layout/fragment_roots.xml b/packages/DocumentsUI/res/layout/fragment_roots.xml
index f3de3b4..b33b8d0 100644
--- a/packages/DocumentsUI/res/layout/fragment_roots.xml
+++ b/packages/DocumentsUI/res/layout/fragment_roots.xml
@@ -14,8 +14,8 @@
      limitations under the License.
 -->
 
-<ListView xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@android:id/list"
+<com.android.documentsui.RootsList xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/roots_list"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:paddingTop="8dp"
diff --git a/packages/DocumentsUI/res/layout/single_pane_layout.xml b/packages/DocumentsUI/res/layout/single_pane_layout.xml
index f53d698..235d22d 100644
--- a/packages/DocumentsUI/res/layout/single_pane_layout.xml
+++ b/packages/DocumentsUI/res/layout/single_pane_layout.xml
@@ -16,11 +16,14 @@
 
 <!-- CoordinatorLayout is necessary for various components (e.g. Snackbars, and
      floating action buttons) to operate correctly. -->
+<!-- focusableInTouchMode is set in order to force key events to go to the activity's global key 
+     callback, which is necessary for proper event routing. See BaseActivity.onKeyDown. -->
 <android.support.design.widget.CoordinatorLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:id="@+id/coordinator_layout">
+    android:id="@+id/coordinator_layout"
+    android:focusableInTouchMode="true">
 
     <LinearLayout
         android:layout_width="match_parent"
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
index 475387b..4a55906 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
@@ -42,6 +42,7 @@
 import android.support.annotation.LayoutRes;
 import android.support.annotation.Nullable;
 import android.util.Log;
+import android.view.KeyEvent;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.widget.Spinner;
@@ -83,6 +84,8 @@
     // We use the time gap to figure out whether to close app or reopen the drawer.
     private long mDrawerLastFiddled;
 
+    private boolean mNavDrawerHasFocus;
+
     public abstract void onDocumentPicked(DocumentInfo doc, @Nullable SiblingProvider siblings);
     public abstract void onDocumentsPicked(List<DocumentInfo> docs);
 
@@ -580,6 +583,54 @@
         }
     }
 
+    /**
+     * Declare a global key handler to route key events when there isn't a specific focus view. This
+     * covers the scenario where a user opens DocumentsUI and just starts typing.
+     *
+     * @param keyCode
+     * @param event
+     * @return
+     */
+    @CallSuper
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        if (Events.isNavigationKeyCode(keyCode)) {
+            // Forward all unclaimed navigation keystrokes to the DirectoryFragment. This causes any
+            // stray navigation keystrokes focus the content pane, which is probably what the user
+            // is trying to do.
+            DirectoryFragment df = DirectoryFragment.get(getFragmentManager());
+            if (df != null) {
+                df.requestFocus();
+                return true;
+            }
+        } else if (keyCode == KeyEvent.KEYCODE_TAB) {
+            toggleNavDrawerFocus();
+            return true;
+        }
+        return super.onKeyDown(keyCode, event);
+    }
+
+    /**
+     * Toggles focus between the navigation drawer and the directory listing. If the drawer isn't
+     * locked, open/close it as appropriate.
+     */
+    void toggleNavDrawerFocus() {
+        if (mNavDrawerHasFocus) {
+            mDrawer.setOpen(false);
+            DirectoryFragment df = DirectoryFragment.get(getFragmentManager());
+            if (df != null) {
+                df.requestFocus();
+            }
+        } else {
+            mDrawer.setOpen(true);
+            RootsFragment rf = RootsFragment.get(getFragmentManager());
+            if (rf != null) {
+                rf.requestFocus();
+            }
+        }
+        mNavDrawerHasFocus = !mNavDrawerHasFocus;
+    }
+
     DocumentInfo getRootDocumentBlocking(RootInfo root) {
         try {
             final Uri uri = DocumentsContract.buildDocumentUri(
diff --git a/packages/DocumentsUI/src/com/android/documentsui/NavigationView.java b/packages/DocumentsUI/src/com/android/documentsui/NavigationView.java
index ff1940a..4cba135 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/NavigationView.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/NavigationView.java
@@ -132,7 +132,7 @@
             showBreadcrumb(true);
             mToolbar.setTitle(null);
             mIgnoreNextNavigation = true;
-            mBreadcrumb.setSelection(mBreadcrumbAdapter.getCount() - 1);
+            mBreadcrumb.setSelection(mBreadcrumbAdapter.getCount() - 1, false);
         }
 
         if (DEBUG) Log.d(TAG, "Final toolbar title is: " + mToolbar.getTitle());
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
index 7dac0c1..0e27622 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
@@ -83,7 +83,7 @@
 
         final View view = inflater.inflate(R.layout.fragment_directory, container, false);
 
-        mRecView = (RecyclerView) view.findViewById(R.id.list);
+        mRecView = (RecyclerView) view.findViewById(R.id.dir_list);
         mRecView.setLayoutManager(new LinearLayoutManager(getContext()));
         mRecView.addOnItemTouchListener(mItemListener);
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
index 26bda31..53f8297 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
@@ -89,7 +89,7 @@
         final Context context = inflater.getContext();
 
         final View view = inflater.inflate(R.layout.fragment_roots, container, false);
-        mList = (ListView) view.findViewById(android.R.id.list);
+        mList = (ListView) view.findViewById(R.id.roots_list);
         mList.setOnItemClickListener(mItemListener);
         mList.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
         return view;
@@ -167,6 +167,13 @@
         }
     }
 
+    /**
+     * Attempts to shift focus back to the navigation drawer.
+     */
+    public void requestFocus() {
+        mList.requestFocus();
+    }
+
     private void showAppDetails(ResolveInfo ri) {
         final Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
         intent.setData(Uri.fromParts("package", ri.activityInfo.packageName, null));
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsList.java b/packages/DocumentsUI/src/com/android/documentsui/RootsList.java
new file mode 100644
index 0000000..bf03ffd
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsList.java
@@ -0,0 +1,63 @@
+/*
+ * 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.documentsui;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.widget.ListView;
+
+/**
+ * The list in the navigation drawer. This class exists for the purpose of overriding the key
+ * handler on ListView. Ignoring keystrokes (e.g. the tab key) cannot be properly done using
+ * View.OnKeyListener.
+ */
+public class RootsList extends ListView {
+
+    // Multiple constructors are needed to handle all the different ways this View could be
+    // constructed by the framework. Don't remove them!
+    public RootsList(Context context) {
+        super(context);
+    }
+
+    public RootsList(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    public RootsList(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public RootsList(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        switch (keyCode) {
+            // Ignore tab key events - this causes them to bubble up to the global key handler where
+            // they are appropriately handled. See BaseActivity.onKeyDown.
+            case KeyEvent.KEYCODE_TAB:
+                return false;
+            // Prevent left/right arrow keystrokes from shifting focus away from the roots list.
+            case KeyEvent.KEYCODE_DPAD_LEFT:
+            case KeyEvent.KEYCODE_DPAD_RIGHT:
+                return true;
+            default:
+                return super.onKeyDown(keyCode, event);
+        }
+    }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 174984c..f8735b2 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -101,7 +101,6 @@
 import com.android.documentsui.services.FileOperationService;
 import com.android.documentsui.services.FileOperationService.OpType;
 import com.android.documentsui.services.FileOperations;
-
 import com.google.common.collect.Lists;
 
 import java.lang.annotation.Retention;
@@ -183,7 +182,7 @@
 
         mEmptyView = view.findViewById(android.R.id.empty);
 
-        mRecView = (RecyclerView) view.findViewById(R.id.list);
+        mRecView = (RecyclerView) view.findViewById(R.id.dir_list);
         mRecView.setRecyclerListener(
                 new RecyclerListener() {
                     @Override
@@ -194,6 +193,11 @@
 
         mRecView.setItemAnimator(new DirectoryItemAnimator(getActivity()));
 
+        // Make the RecyclerView unfocusable. This is needed in order for the focus search code in
+        // FocusManager to work correctly. Setting android:focusable=false in the layout xml doesn't
+        // work, for some reason.
+        mRecView.setFocusable(false);
+
         // TODO: Add a divider between views (which might use RecyclerView.ItemDecoration).
         if (DEBUG_ENABLE_DND) {
             setupDragAndDropOnDirectoryView(mRecView);
@@ -263,7 +267,8 @@
 
         mSelectionManager.addCallback(selectionListener);
 
-        mFocusManager = new FocusManager(mRecView, mSelectionManager);
+        // Make sure this is done after the RecyclerView is set up.
+        mFocusManager = new FocusManager(mRecView);
 
         mModel = new Model();
         mModel.addUpdateListener(mAdapter);
@@ -834,6 +839,7 @@
     @Override
     public void initDocumentHolder(DocumentHolder holder) {
         holder.addEventListener(mItemEventListener);
+        holder.itemView.setOnFocusChangeListener(mFocusManager);
     }
 
     @Override
@@ -1054,6 +1060,13 @@
         }
     }
 
+    /**
+     * Attempts to restore focus on the directory listing.
+     */
+    public void requestFocus() {
+        mFocusManager.restoreLastFocus();
+    }
+
     private void setupDragAndDropOnDirectoryView(View view) {
         // Listen for drops on non-directory items and empty space.
         view.setOnDragListener(mOnDragListener);
@@ -1253,16 +1266,37 @@
             }
 
             if (mFocusManager.handleKey(doc, keyCode, event)) {
+                // Handle range selection adjustments. Extending the selection will adjust the
+                // bounds of the in-progress range selection. Each time an unshifted navigation
+                // event is received, the range selection is restarted.
+                if (shouldExtendSelection(event)) {
+                    if (!mSelectionManager.isRangeSelectionActive()) {
+                        // Start a range selection if one isn't active
+                        mSelectionManager.startRangeSelection(doc.getAdapterPosition());
+                    }
+                    mSelectionManager.snapRangeSelection(mFocusManager.getFocusPosition());
+                } else {
+                    mSelectionManager.endRangeSelection();
+                }
                 return true;
             }
 
             // Handle enter key events
             if (keyCode == KeyEvent.KEYCODE_ENTER) {
-                return onActivate(doc);
+                if (event.isShiftPressed()) {
+                    return onSelect(doc);
+                } else {
+                    return onActivate(doc);
+                }
             }
 
             return false;
         }
+
+        private boolean shouldExtendSelection(KeyEvent event) {
+            return Events.isNavigationKeyCode(event.getKeyCode()) &&
+                    event.isShiftPressed();
+        }
     }
 
     private final class ModelUpdateListener implements Model.UpdateListener {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/FocusManager.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FocusManager.java
index 86b9146..93ec842 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/FocusManager.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FocusManager.java
@@ -16,7 +16,7 @@
 
 package com.android.documentsui.dirlist;
 
-import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.GridLayoutManager;
 import android.support.v7.widget.RecyclerView;
 import android.util.Log;
 import android.view.KeyEvent;
@@ -27,19 +27,19 @@
 /**
  * A class that handles navigation and focus within the DirectoryFragment.
  */
-class FocusManager {
+class FocusManager implements View.OnFocusChangeListener {
     private static final String TAG = "FocusManager";
 
     private RecyclerView mView;
     private RecyclerView.Adapter<?> mAdapter;
-    private LinearLayoutManager mLayout;
-    private MultiSelectManager mSelectionManager;
+    private GridLayoutManager mLayout;
 
-    public FocusManager(RecyclerView view, MultiSelectManager selectionManager) {
+    private int mLastFocusPosition = RecyclerView.NO_POSITION;
+
+    public FocusManager(RecyclerView view) {
         mView = view;
         mAdapter = view.getAdapter();
-        mLayout = (LinearLayoutManager) view.getLayoutManager();
-        mSelectionManager = selectionManager;
+        mLayout = (GridLayoutManager) view.getLayoutManager();
     }
 
     /**
@@ -52,24 +52,58 @@
      * @return Whether the event was handled.
      */
     public boolean handleKey(DocumentHolder doc, int keyCode, KeyEvent event) {
-        boolean handled = false;
+        boolean extendSelection = false;
+        // Translate space/shift-space into PgDn/PgUp
+        if (keyCode == KeyEvent.KEYCODE_SPACE) {
+            if (event.isShiftPressed()) {
+                keyCode = KeyEvent.KEYCODE_PAGE_UP;
+            } else {
+                keyCode = KeyEvent.KEYCODE_PAGE_DOWN;
+            }
+        } else {
+            extendSelection = event.isShiftPressed();
+        }
+
         if (Events.isNavigationKeyCode(keyCode)) {
             // Find the target item and focus it.
             int endPos = findTargetPosition(doc.itemView, keyCode, event);
 
             if (endPos != RecyclerView.NO_POSITION) {
                 focusItem(endPos);
-
-                // Handle any necessary adjustments to selection.
-                boolean extendSelection = event.isShiftPressed();
-                if (extendSelection) {
-                    int startPos = doc.getAdapterPosition();
-                    mSelectionManager.selectRange(startPos, endPos);
-                }
-                handled = true;
             }
+            // Swallow all navigation keystrokes. Otherwise they go to the app's global
+            // key-handler, which will route them back to the DF and cause focus to be reset.
+            return true;
         }
-        return handled;
+        return false;
+    }
+
+    @Override
+    public void onFocusChange(View v, boolean hasFocus) {
+        // Remember focus events on items.
+        if (hasFocus && v.getParent() == mView) {
+            mLastFocusPosition = mView.getChildAdapterPosition(v);
+        }
+    }
+
+    /**
+     * Requests focus on the item that last had focus. Scrolls to that item if necessary.
+     */
+    public void restoreLastFocus() {
+        if (mLastFocusPosition != RecyclerView.NO_POSITION) {
+            // The system takes care of situations when a view is no longer on screen, etc,
+            focusItem(mLastFocusPosition);
+        } else {
+            // Focus the first visible item
+            focusItem(mLayout.findFirstVisibleItemPosition());
+        }
+    }
+
+    /**
+     * @return The adapter position of the last focused item.
+     */
+    public int getFocusPosition() {
+        return mLastFocusPosition;
     }
 
     /**
@@ -100,12 +134,27 @@
             case KeyEvent.KEYCODE_DPAD_DOWN:
                 searchDir = View.FOCUS_DOWN;
                 break;
-            case KeyEvent.KEYCODE_DPAD_LEFT:
-                searchDir = View.FOCUS_LEFT;
-                break;
-            case KeyEvent.KEYCODE_DPAD_RIGHT:
-                searchDir = View.FOCUS_RIGHT;
-                break;
+        }
+
+        if (inGridMode()) {
+            int currentPosition = mView.getChildAdapterPosition(view);
+            // Left and right arrow keys only work in grid mode.
+            switch (keyCode) {
+                case KeyEvent.KEYCODE_DPAD_LEFT:
+                    if (currentPosition > 0) {
+                        // Stop backward focus search at the first item, otherwise focus will wrap
+                        // around to the last visible item.
+                        searchDir = View.FOCUS_BACKWARD;
+                    }
+                    break;
+                case KeyEvent.KEYCODE_DPAD_RIGHT:
+                    if (currentPosition < mAdapter.getItemCount() - 1) {
+                        // Stop forward focus search at the last item, otherwise focus will wrap
+                        // around to the first visible item.
+                        searchDir = View.FOCUS_FORWARD;
+                    }
+                    break;
+            }
         }
 
         if (searchDir != -1) {
@@ -204,4 +253,11 @@
                     });
         }
     }
+
+    /**
+     * @return Whether the layout manager is currently in a grid-configuration.
+     */
+    private boolean inGridMode() {
+        return mLayout.getSpanCount() > 1;
+    }
 }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java
index d60825b..c8b6f85 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java
@@ -370,39 +370,41 @@
     }
 
     /**
-     * Handle a range selection event.
-     * <li> If the MSM is currently in single-select mode, only the last item in the range will
-     * actually be selected.
-     * <li>If a range selection is not already active, one will be started, and the given range of
-     * items will be selected.  The given startPos becomes the anchor for the range selection.
-     * <li>If a range selection is already active, the anchor is not changed. The range is extended
-     * from its current anchor to endPos.
+     * Starts a range selection. If a range selection is already active, this will start a new range
+     * selection (which will reset the range anchor).
      *
-     * @param startPos
-     * @param endPos
+     * @param pos The anchor position for the selection range.
      */
-    public void selectRange(int startPos, int endPos) {
-        // In single-select mode, just select the last item in the range.
-        if (mSingleSelect) {
-            attemptSelect(mAdapter.getModelId(endPos));
-            return;
-        }
+    void startRangeSelection(int pos) {
+      attemptSelect(mAdapter.getModelId(pos));
+      setSelectionRangeBegin(pos);
+    }
 
-        // In regular (i.e. multi-select) mode
-        if (!isRangeSelectionActive()) {
-            // If a range selection isn't active, start one up
-            attemptSelect(mAdapter.getModelId(startPos));
-            setSelectionRangeBegin(startPos);
-        }
-        // Extend the range selection
-        mRanger.snapSelection(endPos);
+    /**
+     * Sets the end point for the current range selection, started by a call to
+     * {@link #startRangeSelection(int)}. This function should only be called when a range selection
+     * is active (see {@link #isRangeSelectionActive()}. Items in the range [anchor, end] will be
+     * selected.
+     *
+     * @param pos The new end position for the selection range.
+     */
+    void snapRangeSelection(int pos) {
+        checkNotNull(mRanger);
+        mRanger.snapSelection(pos);
         notifySelectionChanged();
     }
 
     /**
+     * Stops an in-progress range selection.
+     */
+    void endRangeSelection() {
+        mRanger = null;
+    }
+
+    /**
      * @return Whether or not there is a current range selection active.
      */
-    private boolean isRangeSelectionActive() {
+    boolean isRangeSelectionActive() {
         return mRanger != null;
     }
 
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
index 77f16d9..609dc0c 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
@@ -21,6 +21,7 @@
 
 import android.os.RemoteException;
 import android.test.suitebuilder.annotation.LargeTest;
+import android.view.KeyEvent;
 
 @LargeTest
 public class FilesActivityUiTest extends ActivityTest<FilesActivity> {
@@ -115,4 +116,37 @@
         bot.waitForDeleteSnackbarGone();
         assertFalse(bot.hasDocuments("poodles.text"));
     }
+
+    // Tests that pressing tab switches focus between the roots and directory listings.
+    public void testKeyboard_tab() throws Exception {
+        bot.pressKey(KeyEvent.KEYCODE_TAB);
+        bot.assertHasFocus("com.android.documentsui:id/roots_list");
+        bot.pressKey(KeyEvent.KEYCODE_TAB);
+        bot.assertHasFocus("com.android.documentsui:id/dir_list");
+    }
+
+    // Tests that arrow keys do not switch focus away from the dir list.
+    public void testKeyboard_arrowsDirList() throws Exception {
+        for (int i = 0; i < 10; i++) {
+            bot.pressKey(KeyEvent.KEYCODE_DPAD_LEFT);
+            bot.assertHasFocus("com.android.documentsui:id/dir_list");
+        }
+        for (int i = 0; i < 10; i++) {
+            bot.pressKey(KeyEvent.KEYCODE_DPAD_RIGHT);
+            bot.assertHasFocus("com.android.documentsui:id/dir_list");
+        }
+    }
+
+    // Tests that arrow keys do not switch focus away from the roots list.
+    public void testKeyboard_arrowsRootsList() throws Exception {
+        bot.pressKey(KeyEvent.KEYCODE_TAB);
+        for (int i = 0; i < 10; i++) {
+            bot.pressKey(KeyEvent.KEYCODE_DPAD_RIGHT);
+            bot.assertHasFocus("com.android.documentsui:id/roots_list");
+        }
+        for (int i = 0; i < 10; i++) {
+            bot.pressKey(KeyEvent.KEYCODE_DPAD_LEFT);
+            bot.assertHasFocus("com.android.documentsui:id/roots_list");
+        }
+    }
 }
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/UiBot.java b/packages/DocumentsUI/tests/src/com/android/documentsui/UiBot.java
index 4534c40..d2f8403 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/UiBot.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/UiBot.java
@@ -71,7 +71,7 @@
     UiObject findRoot(String label) throws UiObjectNotFoundException {
         final UiSelector rootsList = new UiSelector().resourceId(
                 "com.android.documentsui:id/container_roots").childSelector(
-                new UiSelector().resourceId("android:id/list"));
+                new UiSelector().resourceId("com.android.documentsui:id/roots_list"));
 
         // We might need to expand drawer if not visible
         if (!new UiObject(rootsList).waitForExists(mTimeout)) {
@@ -195,6 +195,15 @@
         assertNotNull(getSnackbar(mContext.getString(id)));
     }
 
+    /**
+     * Asserts that the specified view or one of its descendents has focus.
+     */
+    void assertHasFocus(String resourceName) {
+        UiObject2 candidate = mDevice.findObject(By.res(resourceName));
+        assertNotNull("Expected " + resourceName + " to have focus, but it didn't.",
+            candidate.findObject(By.focused(true)));
+    }
+
     void openDocument(String label) throws UiObjectNotFoundException {
         int toolType = Configurator.getInstance().getToolType();
         Configurator.getInstance().setToolType(MotionEvent.TOOL_TYPE_FINGER);
@@ -309,7 +318,7 @@
     UiObject findDocument(String label) throws UiObjectNotFoundException {
         final UiSelector docList = new UiSelector().resourceId(
                 "com.android.documentsui:id/container_directory").childSelector(
-                        new UiSelector().resourceId("com.android.documentsui:id/list"));
+                        new UiSelector().resourceId("com.android.documentsui:id/dir_list"));
 
         // Wait for the first list item to appear
         new UiObject(docList.childSelector(new UiSelector())).waitForExists(mTimeout);
@@ -330,7 +339,7 @@
     UiObject findDocumentsList() {
         return findObject(
                 "com.android.documentsui:id/container_directory",
-                "com.android.documentsui:id/list");
+                "com.android.documentsui:id/dir_list");
     }
 
     UiObject findSearchView() {
@@ -416,4 +425,8 @@
         mDevice.wait(Until.hasObject(By.pkg(TARGET_PKG).depth(0)), mTimeout);
         mDevice.waitForIdle();
     }
+
+    void pressKey(int keyCode) {
+        mDevice.pressKeyCode(keyCode);
+    }
 }
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManagerTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManagerTest.java
index d95fb49..9447d9c1 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManagerTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManagerTest.java
@@ -189,6 +189,54 @@
         assertSelection(items.get(20));
     }
 
+    public void testRangeSelection() {
+        mManager.startRangeSelection(15);
+        mManager.snapRangeSelection(19);
+        assertRangeSelection(15, 19);
+    }
+
+    public void testRangeSelection_snapExpand() {
+        mManager.startRangeSelection(15);
+        mManager.snapRangeSelection(19);
+        mManager.snapRangeSelection(27);
+        assertRangeSelection(15, 27);
+    }
+
+    public void testRangeSelection_snapContract() {
+        mManager.startRangeSelection(15);
+        mManager.snapRangeSelection(27);
+        mManager.snapRangeSelection(19);
+        assertRangeSelection(15, 19);
+    }
+
+    public void testRangeSelection_snapInvert() {
+        mManager.startRangeSelection(15);
+        mManager.snapRangeSelection(27);
+        mManager.snapRangeSelection(3);
+        assertRangeSelection(3, 15);
+    }
+
+    public void testRangeSelection_multiple() {
+        mManager.startRangeSelection(15);
+        mManager.snapRangeSelection(27);
+        mManager.endRangeSelection();
+        mManager.startRangeSelection(42);
+        mManager.snapRangeSelection(57);
+        assertSelectionSize(29);
+        assertRangeSelected(15, 27);
+        assertRangeSelected(42, 57);
+
+    }
+
+    public void testRangeSelection_singleSelect() {
+        mManager = new MultiSelectManager(mEnv, mAdapter, MultiSelectManager.MODE_SINGLE, null);
+        mManager.addCallback(mCallback);
+        mManager.startRangeSelection(11);
+        mManager.snapRangeSelection(19);
+        assertSelectionSize(1);
+        assertSelection(items.get(19));
+    }
+
     public void testProvisionalSelection() {
         Selection s = mManager.getSelection();
         assertSelection();
diff --git a/packages/MtpDocumentsProvider/res/values/strings.xml b/packages/MtpDocumentsProvider/res/values/strings.xml
index 43a420c..f3a3fcf 100644
--- a/packages/MtpDocumentsProvider/res/values/strings.xml
+++ b/packages/MtpDocumentsProvider/res/values/strings.xml
@@ -25,4 +25,8 @@
     <string name="accessing_notification_title">Accessing files from <xliff:g id="device_model" example="Nexus 9">%1$s</xliff:g></string>
     <!-- Description of notification showing Files app is accessing files in a MTP device. [CHAR LIMIT=60]-->
     <string name="accessing_notification_description">Don\'t disconnect the device</string>
+    <!-- Error message shown in Files app when the connected MTP device is busy. [CHAR LIMIT=150]-->
+    <string name="error_busy_device">The other device is busy. You can\'t transfer files until it\'s available.</string>
+    <!-- Error message shown in Files app when the connected MTP device may be locked. [CHAR LIMIT=150]-->
+    <string name="error_locked_device">No files found. The other device may be locked. If so, unlock it and try again.</string>
 </resources>
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
index c456be9..5a11327 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
@@ -37,6 +37,7 @@
 import android.provider.DocumentsContract.Root;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
 
 import java.io.FileNotFoundException;
 import java.util.Objects;
@@ -244,15 +245,16 @@
     }
 
     /**
-     * Returns identifier of single storage if given document points device and it has only one
-     * storage. Otherwise null.
+     * Returns document IDs of storages under the given device document.
      *
-     * @param documentId Document ID that may point a device.
-     * @return Identifier for single storage or null.
+     * @param documentId Document ID that points a device.
+     * @return Storage document IDs.
      * @throws FileNotFoundException The given document ID is not registered in database.
      */
-    @Nullable Identifier getSingleStorageIdentifier(String documentId)
+    String[] getStorageDocumentIds(String documentId)
             throws FileNotFoundException {
+        Preconditions.checkArgument(createIdentifier(documentId).mDocumentType ==
+                DOCUMENT_TYPE_DEVICE);
         // Check if the parent document is device that has single storage.
         try (final Cursor cursor = mDatabase.query(
                 TABLE_DOCUMENTS,
@@ -267,12 +269,11 @@
                 null,
                 null,
                 null)) {
-            if (cursor.getCount() == 1) {
-                cursor.moveToNext();
-                return createIdentifier(cursor.getString(0));
-            } else {
-                return null;
+            final String[] ids = new String[cursor.getCount()];
+            for (int i = 0; cursor.moveToNext(); i++) {
+                ids[i] = cursor.getString(0);
             }
+            return ids;
         }
     }
 
@@ -342,6 +343,26 @@
         }
     }
 
+    String getDeviceDocumentId(int deviceId) throws FileNotFoundException {
+        try (final Cursor cursor = mDatabase.query(
+                TABLE_DOCUMENTS,
+                strings(Document.COLUMN_DOCUMENT_ID),
+                COLUMN_DEVICE_ID + " = ? AND " + COLUMN_DOCUMENT_TYPE + " = ? AND " +
+                COLUMN_ROW_STATE + " != ?",
+                strings(deviceId, DOCUMENT_TYPE_DEVICE, ROW_STATE_DISCONNECTED),
+                null,
+                null,
+                null,
+                "1")) {
+            if (cursor.getCount() > 0) {
+                cursor.moveToNext();
+                return cursor.getString(0);
+            } else {
+                throw new FileNotFoundException("The device ID not found: " + deviceId);
+            }
+        }
+    }
+
     /**
      * Adds new document under the parent.
      * The method does not affect invalidated and pending documents because we know the document is
@@ -557,13 +578,9 @@
 
         @Override
         public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
-            if (oldVersion == 1) {
-                db.execSQL("DROP TABLE " + TABLE_DOCUMENTS);
-                db.execSQL("DROP TABLE " + TABLE_ROOT_EXTRA);
-                onCreate(db);
-                return;
-            }
-            throw new UnsupportedOperationException();
+            db.execSQL("DROP TABLE " + TABLE_DOCUMENTS);
+            db.execSQL("DROP TABLE " + TABLE_ROOT_EXTRA);
+            onCreate(db);
         }
     }
 
@@ -680,7 +697,12 @@
         if (formatCodeMimeType != null) {
             return formatCodeMimeType;
         }
-        return MediaFile.getMimeTypeForFile(info.getName());
+        final String mediaFileMimeType = MediaFile.getMimeTypeForFile(info.getName());
+        if (mediaFileMimeType != null) {
+            return mediaFileMimeType;
+        }
+        // We don't know the file type.
+        return "application/octet-stream";
     }
 
     static String[] strings(Object... args) {
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java
index cb076af..ab356ce 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java
@@ -30,7 +30,7 @@
  * Class containing MtpDatabase constants.
  */
 class MtpDatabaseConstants {
-    static final int DATABASE_VERSION = 2;
+    static final int DATABASE_VERSION = 3;
     static final String DATABASE_NAME = "database";
 
     static final int FLAG_DATABASE_IN_MEMORY = 1;
@@ -125,7 +125,7 @@
             COLUMN_PARENT_DOCUMENT_ID + " INTEGER," +
             COLUMN_ROW_STATE + " INTEGER NOT NULL," +
             COLUMN_DOCUMENT_TYPE + " INTEGER NOT NULL," +
-            Document.COLUMN_MIME_TYPE + " TEXT," +
+            Document.COLUMN_MIME_TYPE + " TEXT NOT NULL," +
             Document.COLUMN_DISPLAY_NAME + " TEXT NOT NULL," +
             Document.COLUMN_SUMMARY + " TEXT," +
             Document.COLUMN_LAST_MODIFIED + " INTEGER," +
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
index 0338454..a512509 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
@@ -20,10 +20,12 @@
 import android.content.res.AssetFileDescriptor;
 import android.content.res.Resources;
 import android.database.Cursor;
+import android.database.MatrixCursor;
 import android.graphics.Point;
 import android.media.MediaFile;
 import android.mtp.MtpConstants;
 import android.mtp.MtpObjectInfo;
+import android.os.Bundle;
 import android.os.CancellationSignal;
 import android.os.ParcelFileDescriptor;
 import android.os.storage.StorageManager;
@@ -35,6 +37,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.mtp.exceptions.BusyDeviceException;
 
 import java.io.FileNotFoundException;
 import java.io.IOException;
@@ -163,17 +166,25 @@
         try {
             openDevice(parentIdentifier.mDeviceId);
             if (parentIdentifier.mDocumentType == MtpDatabaseConstants.DOCUMENT_TYPE_DEVICE) {
-                final Identifier singleStorageIdentifier =
-                        mDatabase.getSingleStorageIdentifier(parentDocumentId);
-                if (singleStorageIdentifier == null) {
+                final String[] storageDocIds = mDatabase.getStorageDocumentIds(parentDocumentId);
+                if (storageDocIds.length == 0) {
+                    // Remote device does not provide storages. Maybe it is locked.
+                    return createErrorCursor(projection, R.string.error_locked_device);
+                } else if (storageDocIds.length > 1) {
                     // Returns storage list from database.
                     return mDatabase.queryChildDocuments(projection, parentDocumentId);
                 }
-                parentIdentifier = singleStorageIdentifier;
+
+                // Exact one storage is found. Skip storage and returns object in the single
+                // storage.
+                parentIdentifier = mDatabase.createIdentifier(storageDocIds[0]);
             }
+
             // Returns object list from document loader.
             return getDocumentLoader(parentIdentifier).queryChildDocuments(
                     projection, parentIdentifier);
+        } catch (BusyDeviceException exception) {
+            return createErrorCursor(projection, R.string.error_busy_device);
         } catch (IOException exception) {
             Log.e(MtpDocumentsProvider.TAG, "queryChildDocuments", exception);
             throw new FileNotFoundException(exception.getMessage());
@@ -350,6 +361,16 @@
     }
 
     /**
+     * Obtains document ID for the given device ID.
+     * @param deviceId
+     * @return document ID
+     * @throws FileNotFoundException device ID has not been build.
+     */
+    public String getDeviceDocumentId(int deviceId) throws FileNotFoundException {
+        return mDatabase.getDeviceDocumentId(deviceId);
+    }
+
+    /**
      * Resumes root scanner to handle the update of device list.
      */
     void resumeRootScanner() {
@@ -442,6 +463,21 @@
         }
     }
 
+    /**
+     * Creates empty cursor with specific error message.
+     *
+     * @param projection Column names.
+     * @param stringResId String resource ID of error message.
+     * @return Empty cursor with error message.
+     */
+    private Cursor createErrorCursor(String[] projection, int stringResId) {
+        final Bundle bundle = new Bundle();
+        bundle.putString(DocumentsContract.EXTRA_ERROR, mResources.getString(stringResId));
+        final Cursor cursor = new MatrixCursor(projection);
+        cursor.setExtras(bundle);
+        return cursor;
+    }
+
     private static class DeviceToolkit {
         public final PipeManager mPipeManager;
         public final DocumentLoader mDocumentLoader;
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
index 5519efd..0527790 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
@@ -33,6 +33,7 @@
 import android.util.SparseArray;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.mtp.exceptions.BusyDeviceException;
 
 import java.io.FileNotFoundException;
 import java.io.IOException;
@@ -101,7 +102,8 @@
         }
 
         if (!device.open(connection)) {
-            throw new IOException("Failed to open a MTP device.");
+            // We cannot open connection when another application use the device.
+            throw new BusyDeviceException();
         }
 
         // Handle devices that fail to obtain storages just after opening a MTP session.
@@ -134,7 +136,7 @@
                 try {
                     roots = getRoots(device.getDeviceId());
                 } catch (IOException exp) {
-                    Log.e(MtpDocumentsProvider.TAG, exp.getMessage());
+                    Log.e(MtpDocumentsProvider.TAG, "Failed to open device", exp);
                     // If we failed to fetch roots for the device, we still returns device model
                     // with an empty set of roots so that the device is shown DocumentsUI as long as
                     // the device is physically connected.
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/ReceiverActivity.java b/packages/MtpDocumentsProvider/src/com/android/mtp/ReceiverActivity.java
index c7206a7..84745b2 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/ReceiverActivity.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/ReceiverActivity.java
@@ -17,10 +17,15 @@
 package com.android.mtp;
 
 import android.app.Activity;
-import android.content.ComponentName;
 import android.content.Intent;
+import android.hardware.usb.UsbDevice;
 import android.hardware.usb.UsbManager;
+import android.net.Uri;
 import android.os.Bundle;
+import android.provider.DocumentsContract;
+import android.util.Log;
+
+import java.io.IOException;
 
 /**
  * Invisible activity to receive intents.
@@ -33,14 +38,21 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(getIntent().getAction())) {
-            // TODO: To obtain data URI for the attached device, we need to wait until RootScanner
-            // found the device and add it to database. Set correct root URI, and use ACTION_BROWSE
-            // to launch Documents UI.
-            final Intent intent = new Intent(Intent.ACTION_MAIN);
-            intent.addCategory(Intent.CATEGORY_LAUNCHER);
-            intent.setComponent(new ComponentName(
-                    "com.android.documentsui", "com.android.documentsui.LauncherActivity"));
-            this.startActivity(intent);
+            final UsbDevice device = getIntent().getParcelableExtra(UsbManager.EXTRA_DEVICE);
+            try {
+                final MtpDocumentsProvider provider = MtpDocumentsProvider.getInstance();
+                provider.openDevice(device.getDeviceId());
+                final String deviceRootId = provider.getDeviceDocumentId(device.getDeviceId());
+                final Uri uri = DocumentsContract.buildRootUri(
+                        MtpDocumentsProvider.AUTHORITY, deviceRootId);
+
+                final Intent intent = new Intent(DocumentsContract.ACTION_BROWSE);
+                intent.setData(uri);
+                intent.addCategory(Intent.CATEGORY_DEFAULT);
+                this.startActivity(intent);
+            } catch (IOException exception) {
+                Log.e(MtpDocumentsProvider.TAG, "Failed to open device", exception);
+            }
         }
         finish();
     }
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/exceptions/BusyDeviceException.java b/packages/MtpDocumentsProvider/src/com/android/mtp/exceptions/BusyDeviceException.java
new file mode 100644
index 0000000..55f55b0
--- /dev/null
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/exceptions/BusyDeviceException.java
@@ -0,0 +1,25 @@
+/*
+ * 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.mtp.exceptions;
+
+import java.io.IOException;
+
+/**
+ * Exception thrown when the device is busy and the requested operation cannon be completed.
+ */
+public class BusyDeviceException extends IOException {
+}
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
index 5b0f557..01fcc55 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
@@ -29,6 +29,8 @@
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.MediumTest;
 
+import com.android.mtp.exceptions.BusyDeviceException;
+
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.util.Arrays;
@@ -526,6 +528,52 @@
         }
     }
 
+    public void testBusyDevice() throws Exception {
+        mMtpManager = new TestMtpManager(getContext()) {
+            @Override
+            void openDevice(int deviceId) throws IOException {
+                throw new BusyDeviceException();
+            }
+        };
+        setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
+        mMtpManager.addValidDevice(new MtpDeviceRecord(
+                0, "Device A", false /* unopened */, new MtpRoot[0], null, null));
+
+        mProvider.resumeRootScanner();
+        mResolver.waitForNotification(ROOTS_URI, 1);
+
+        try (final Cursor cursor = mProvider.queryRoots(null)) {
+            assertEquals(1, cursor.getCount());
+        }
+
+        try (final Cursor cursor = mProvider.queryChildDocuments("1", null, null)) {
+            assertEquals(0, cursor.getCount());
+            assertEquals(
+                    "error_busy_device",
+                    cursor.getExtras().getString(DocumentsContract.EXTRA_ERROR));
+        }
+    }
+
+    public void testLockedDevice() throws Exception {
+        setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
+        mMtpManager.addValidDevice(new MtpDeviceRecord(
+                0, "Device A", false /* unopened */, new MtpRoot[0], null, null));
+
+        mProvider.resumeRootScanner();
+        mResolver.waitForNotification(ROOTS_URI, 1);
+
+        try (final Cursor cursor = mProvider.queryRoots(null)) {
+            assertEquals(1, cursor.getCount());
+        }
+
+        try (final Cursor cursor = mProvider.queryChildDocuments("1", null, null)) {
+            assertEquals(0, cursor.getCount());
+            assertEquals(
+                    "error_locked_device",
+                    cursor.getExtras().getString(DocumentsContract.EXTRA_ERROR));
+        }
+    }
+
     private void setupProvider(int flag) {
         mDatabase = new MtpDatabase(getContext(), flag);
         mProvider = new MtpDocumentsProvider();
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResources.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResources.java
index b23038b..8676b5a 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResources.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResources.java
@@ -24,6 +24,10 @@
         switch (id) {
             case R.string.root_name:
                 return "%1$s %2$s";
+            case R.string.error_busy_device:
+                return "error_busy_device";
+            case R.string.error_locked_device:
+                return "error_locked_device";
         }
         throw new NotFoundException();
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerAdapter.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerAdapter.java
index 73171c7..1d6197a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerAdapter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerAdapter.java
@@ -65,7 +65,7 @@
     }
 
     public Tile getTile(int position) {
-        return mItems.get(position).tile;
+        return mItems.get(position) != null ? mItems.get(position).tile : null;
     }
 
     @Override
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 8c555a6..bad7e20 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -88,8 +88,8 @@
  * <p>
  * The workflow is:
  * <ol>
- * <li>When {@code dumpstate} starts, it sends a {@code BUGREPORT_STARTED} with its pid and the
- * estimated total effort.
+ * <li>When {@code dumpstate} starts, it sends a {@code BUGREPORT_STARTED} with a sequential id,
+ * its pid, and the estimated total effort.
  * <li>{@link BugreportReceiver} receives the intent and delegates it to this service.
  * <li>Upon start, this service:
  * <ol>
@@ -132,6 +132,7 @@
 
     static final String EXTRA_BUGREPORT = "android.intent.extra.BUGREPORT";
     static final String EXTRA_SCREENSHOT = "android.intent.extra.SCREENSHOT";
+    static final String EXTRA_ID = "android.intent.extra.ID";
     static final String EXTRA_PID = "android.intent.extra.PID";
     static final String EXTRA_MAX = "android.intent.extra.MAX";
     static final String EXTRA_NAME = "android.intent.extra.NAME";
@@ -177,7 +178,7 @@
      */
     private static final String SCREENSHOT_DIR = "bugreports";
 
-    /** Managed dumpstate processes (keyed by pid) */
+    /** Managed dumpstate processes (keyed by id) */
     private final SparseArray<BugreportInfo> mProcesses = new SparseArray<>();
 
     private Context mContext;
@@ -222,7 +223,7 @@
         }
 
         // If service is killed it cannot be recreated because it would not know which
-        // dumpstate PIDs it would have to watch.
+        // dumpstate IDs it would have to watch.
         return START_NOT_STICKY;
     }
 
@@ -299,38 +300,41 @@
             }
             final String action = intent.getAction();
             final int pid = intent.getIntExtra(EXTRA_PID, 0);
+            // TODO: temporarily using pid as id until test cases and dumpstate are changed.
+            final int id = intent.getIntExtra(EXTRA_ID, pid);
             final int max = intent.getIntExtra(EXTRA_MAX, -1);
             final String name = intent.getStringExtra(EXTRA_NAME);
 
-            if (DEBUG) Log.v(TAG, "action: " + action + ", name: " + name + ", pid: " + pid
-                    + ", max: "+ max);
+            if (DEBUG)
+                Log.v(TAG, "action: " + action + ", name: " + name + ", id: " + id + ", pid: "
+                        + pid + ", max: " + max);
             switch (action) {
                 case INTENT_BUGREPORT_STARTED:
-                    if (!startProgress(name, pid, max)) {
+                    if (!startProgress(name, id, pid, max)) {
                         stopSelfWhenDone();
                         return;
                     }
                     poll();
                     break;
                 case INTENT_BUGREPORT_FINISHED:
-                    if (pid == 0) {
+                    if (id == 0) {
                         // Shouldn't happen, unless BUGREPORT_FINISHED is received from a legacy,
                         // out-of-sync dumpstate process.
-                        Log.w(TAG, "Missing " + EXTRA_PID + " on intent " + intent);
+                        Log.w(TAG, "Missing " + EXTRA_ID + " on intent " + intent);
                     }
-                    onBugreportFinished(pid, intent);
+                    onBugreportFinished(id, intent);
                     break;
                 case INTENT_BUGREPORT_INFO_LAUNCH:
-                    launchBugreportInfoDialog(pid);
+                    launchBugreportInfoDialog(id);
                     break;
                 case INTENT_BUGREPORT_SCREENSHOT:
-                    takeScreenshot(pid, true);
+                    takeScreenshot(id, true);
                     break;
                 case INTENT_BUGREPORT_SHARE:
-                    shareBugreport(pid, (BugreportInfo) intent.getParcelableExtra(EXTRA_INFO));
+                    shareBugreport(id, (BugreportInfo) intent.getParcelableExtra(EXTRA_INFO));
                     break;
                 case INTENT_BUGREPORT_CANCEL:
-                    cancel(pid);
+                    cancel(id);
                     break;
                 default:
                     Log.w(TAG, "Unsupported intent: " + action);
@@ -367,10 +371,10 @@
         }
     }
 
-    private BugreportInfo getInfo(int pid) {
-        final BugreportInfo info = mProcesses.get(pid);
+    private BugreportInfo getInfo(int id) {
+        final BugreportInfo info = mProcesses.get(id);
         if (info == null) {
-            Log.w(TAG, "Not monitoring process with PID " + pid);
+            Log.w(TAG, "Not monitoring process with ID " + id);
         }
         return info;
     }
@@ -381,10 +385,14 @@
      *
      * @return whether it succeeded or not.
      */
-    private boolean startProgress(String name, int pid, int max) {
+    private boolean startProgress(String name, int id, int pid, int max) {
         if (name == null) {
             Log.w(TAG, "Missing " + EXTRA_NAME + " on start intent");
         }
+        if (id == -1) {
+            Log.e(TAG, "Missing " + EXTRA_ID + " on start intent");
+            return false;
+        }
         if (pid == -1) {
             Log.e(TAG, "Missing " + EXTRA_PID + " on start intent");
             return false;
@@ -394,14 +402,14 @@
             return false;
         }
 
-        final BugreportInfo info = new BugreportInfo(mContext, pid, name, max);
-        if (mProcesses.indexOfKey(pid) >= 0) {
-            Log.w(TAG, "PID " + pid + " already watched");
+        final BugreportInfo info = new BugreportInfo(mContext, id, pid, name, max);
+        if (mProcesses.indexOfKey(id) >= 0) {
+            Log.w(TAG, "ID " + id + " already watched");
         } else {
-            mProcesses.put(info.pid, info);
+            mProcesses.put(info.id, info);
         }
         // Take initial screenshot.
-        takeScreenshot(pid, false);
+        takeScreenshot(id, false);
         updateProgress(info);
         return true;
     }
@@ -423,22 +431,22 @@
                 com.android.internal.R.string.cancel), newCancelIntent(mContext, info)).build();
         final Intent infoIntent = new Intent(mContext, BugreportProgressService.class);
         infoIntent.setAction(INTENT_BUGREPORT_INFO_LAUNCH);
-        infoIntent.putExtra(EXTRA_PID, info.pid);
+        infoIntent.putExtra(EXTRA_ID, info.id);
         final Action infoAction = new Action.Builder(null,
                 mContext.getString(R.string.bugreport_info_action),
-                PendingIntent.getService(mContext, info.pid, infoIntent,
+                PendingIntent.getService(mContext, info.id, infoIntent,
                         PendingIntent.FLAG_UPDATE_CURRENT)).build();
         final Intent screenshotIntent = new Intent(mContext, BugreportProgressService.class);
         screenshotIntent.setAction(INTENT_BUGREPORT_SCREENSHOT);
-        screenshotIntent.putExtra(EXTRA_PID, info.pid);
+        screenshotIntent.putExtra(EXTRA_ID, info.id);
         PendingIntent screenshotPendingIntent = mTakingScreenshot ? null : PendingIntent
-                .getService(mContext, info.pid, screenshotIntent,
+                .getService(mContext, info.id, screenshotIntent,
                         PendingIntent.FLAG_UPDATE_CURRENT);
         final Action screenshotAction = new Action.Builder(null,
                 mContext.getString(R.string.bugreport_screenshot_action),
                 screenshotPendingIntent).build();
 
-        final String title = mContext.getString(R.string.bugreport_in_progress_title, info.pid);
+        final String title = mContext.getString(R.string.bugreport_in_progress_title, info.id);
 
         final String name =
                 info.name != null ? info.name : mContext.getString(R.string.bugreport_unnamed);
@@ -464,8 +472,8 @@
                     + info + ")");
             return;
         }
-        Log.v(TAG, "Sending 'Progress' notification for pid " + info.pid + ": " + percentText);
-        NotificationManager.from(mContext).notify(TAG, info.pid, notification);
+        Log.v(TAG, "Sending 'Progress' notification for id " + info.id + ": " + percentText);
+        NotificationManager.from(mContext).notify(TAG, info.id, notification);
     }
 
     /**
@@ -474,38 +482,38 @@
     private static PendingIntent newCancelIntent(Context context, BugreportInfo info) {
         final Intent intent = new Intent(INTENT_BUGREPORT_CANCEL);
         intent.setClass(context, BugreportProgressService.class);
-        intent.putExtra(EXTRA_PID, info.pid);
-        return PendingIntent.getService(context, info.pid, intent,
+        intent.putExtra(EXTRA_ID, info.id);
+        return PendingIntent.getService(context, info.id, intent,
                 PendingIntent.FLAG_UPDATE_CURRENT);
     }
 
     /**
      * Finalizes the progress on a given bugreport and cancel its notification.
      */
-    private void stopProgress(int pid) {
-        if (mProcesses.indexOfKey(pid) < 0) {
-            Log.w(TAG, "PID not watched: " + pid);
+    private void stopProgress(int id) {
+        if (mProcesses.indexOfKey(id) < 0) {
+            Log.w(TAG, "ID not watched: " + id);
         } else {
-            Log.d(TAG, "Removing PID " + pid);
-            mProcesses.remove(pid);
+            Log.d(TAG, "Removing ID " + id);
+            mProcesses.remove(id);
         }
         stopSelfWhenDone();
-        Log.v(TAG, "stopProgress(" + pid + "): cancel notification");
-        NotificationManager.from(mContext).cancel(TAG, pid);
+        Log.v(TAG, "stopProgress(" + id + "): cancel notification");
+        NotificationManager.from(mContext).cancel(TAG, id);
     }
 
     /**
      * Cancels a bugreport upon user's request.
      */
-    private void cancel(int pid) {
-        Log.v(TAG, "cancel: pid=" + pid);
-        final BugreportInfo info = getInfo(pid);
+    private void cancel(int id) {
+        Log.v(TAG, "cancel: ID=" + id);
+        final BugreportInfo info = getInfo(id);
         if (info != null && !info.finished) {
-            Log.i(TAG, "Cancelling bugreport service (pid=" + pid + ") on user's request");
+            Log.i(TAG, "Cancelling bugreport service (ID=" + id + ") on user's request");
             setSystemProperty(CTL_STOP, BUGREPORT_SERVICE);
             deleteScreenshots(info);
         }
-        stopProgress(pid);
+        stopProgress(id);
     }
 
     /**
@@ -522,14 +530,15 @@
         for (int i = 0; i < total; i++) {
             final BugreportInfo info = mProcesses.valueAt(i);
             if (info == null) {
-                Log.wtf(TAG, "pollProgress(): null info at index " + i + "(pid = "
+                Log.wtf(TAG, "pollProgress(): null info at index " + i + "(ID = "
                         + mProcesses.keyAt(i) + ")");
                 continue;
             }
 
             final int pid = info.pid;
+            final int id = info.id;
             if (info.finished) {
-                if (DEBUG) Log.v(TAG, "Skipping finished process " + pid);
+                if (DEBUG) Log.v(TAG, "Skipping finished process " + pid + "(id: " + id + ")");
                 continue;
             }
             activeProcesses++;
@@ -544,13 +553,13 @@
 
             if (progressChanged || maxChanged) {
                 if (progressChanged) {
-                    if (DEBUG) Log.v(TAG, "Updating progress for PID " + pid + " from "
-                            + info.progress + " to " + progress);
+                    if (DEBUG) Log.v(TAG, "Updating progress for PID " + pid + "(id: " + id
+                            + ") from " + info.progress + " to " + progress);
                     info.progress = progress;
                 }
                 if (maxChanged) {
-                    Log.i(TAG, "Updating max progress for PID " + pid + " from " + info.max
-                            + " to " + max);
+                    Log.i(TAG, "Updating max progress for PID " + pid + "(id: " + id
+                            + ") from " + info.max + " to " + max);
                     info.max = max;
                 }
                 info.lastUpdate = System.currentTimeMillis();
@@ -558,9 +567,9 @@
             } else {
                 long inactiveTime = System.currentTimeMillis() - info.lastUpdate;
                 if (inactiveTime >= INACTIVITY_TIMEOUT) {
-                    Log.w(TAG, "No progress update for process " + pid + " since "
+                    Log.w(TAG, "No progress update for PID " + pid + " since "
                             + info.getFormattedLastUpdate());
-                    stopProgress(info.pid);
+                    stopProgress(info.id);
                 }
             }
         }
@@ -572,19 +581,16 @@
      * Fetches a {@link BugreportInfo} for a given process and launches a dialog where the user can
      * change its values.
      */
-    private void launchBugreportInfoDialog(int pid) {
+    private void launchBugreportInfoDialog(int id) {
         // Copy values so it doesn't lock mProcesses while UI is being updated
         final String name, title, description;
-        final BugreportInfo info = getInfo(pid);
+        final BugreportInfo info = getInfo(id);
         if (info == null) {
             return;
         }
-        name = info.name;
-        title = info.title;
-        description = info.description;
 
         collapseNotificationBar();
-        mInfoDialog.initialize(mContext, pid, name, title, description);
+        mInfoDialog.initialize(mContext, info);
     }
 
     /**
@@ -597,7 +603,7 @@
      * Typical usage is delaying when taken from the notification action, and taking it right away
      * upon receiving a {@link #INTENT_BUGREPORT_STARTED}.
      */
-    private void takeScreenshot(int pid, boolean delayed) {
+    private void takeScreenshot(int id, boolean delayed) {
         setTakingScreenshot(true);
         if (delayed) {
             collapseNotificationBar();
@@ -608,28 +614,28 @@
             // Show a toast just once, otherwise it might be captured in the screenshot.
             Toast.makeText(mContext, msg, Toast.LENGTH_SHORT).show();
 
-            takeScreenshot(pid, SCREENSHOT_DELAY_SECONDS);
+            takeScreenshot(id, SCREENSHOT_DELAY_SECONDS);
         } else {
-            takeScreenshot(pid, 0);
+            takeScreenshot(id, 0);
         }
     }
 
     /**
      * Takes a screenshot after {@code delay} seconds.
      */
-    private void takeScreenshot(int pid, int delay) {
+    private void takeScreenshot(int id, int delay) {
         if (delay > 0) {
-            Log.d(TAG, "Taking screenshot for " + pid + " in " + delay + " seconds");
+            Log.d(TAG, "Taking screenshot for " + id + " in " + delay + " seconds");
             final Message msg = mMainHandler.obtainMessage();
             msg.what = MSG_DELAYED_SCREENSHOT;
-            msg.arg1 = pid;
+            msg.arg1 = id;
             msg.arg2 = delay - 1;
             mMainHandler.sendMessageDelayed(msg, DateUtils.SECOND_IN_MILLIS);
             return;
         }
 
         // It's time to take the screenshot: let the proper thread handle it
-        final BugreportInfo info = getInfo(pid);
+        final BugreportInfo info = getInfo(id);
         if (info == null) {
             return;
         }
@@ -638,7 +644,7 @@
 
         final Message requestMsg = new Message();
         requestMsg.what = MSG_SCREENSHOT_REQUEST;
-        requestMsg.arg1 = pid;
+        requestMsg.arg1 = id;
         requestMsg.obj = screenshotPath;
         mScreenshotHandler.sendMessage(requestMsg);
     }
@@ -715,30 +721,30 @@
      */
     private void stopSelfWhenDone() {
         if (mProcesses.size() > 0) {
-            if (DEBUG) Log.v(TAG, "Staying alive, waiting for pids " + mProcesses);
+            if (DEBUG) Log.d(TAG, "Staying alive, waiting for IDs " + mProcesses);
             return;
         }
-        Log.v(TAG, "No more pids to handle, shutting down");
+        Log.v(TAG, "No more processes to handle, shutting down");
         stopSelf();
     }
 
     /**
      * Handles the BUGREPORT_FINISHED intent sent by {@code dumpstate}.
      */
-    private void onBugreportFinished(int pid, Intent intent) {
+    private void onBugreportFinished(int id, Intent intent) {
         final File bugreportFile = getFileExtra(intent, EXTRA_BUGREPORT);
         if (bugreportFile == null) {
             // Should never happen, dumpstate always set the file.
             Log.wtf(TAG, "Missing " + EXTRA_BUGREPORT + " on intent " + intent);
             return;
         }
-        mInfoDialog.onBugreportFinished(pid);
-        BugreportInfo info = getInfo(pid);
+        mInfoDialog.onBugreportFinished(id);
+        BugreportInfo info = getInfo(id);
         if (info == null) {
             // Happens when BUGREPORT_FINISHED was received without a BUGREPORT_STARTED first.
-            Log.v(TAG, "Creating info for untracked pid " + pid);
-            info = new BugreportInfo(mContext, pid);
-            mProcesses.put(pid, info);
+            Log.v(TAG, "Creating info for untracked ID " + id);
+            info = new BugreportInfo(mContext, id);
+            mProcesses.put(id, info);
         }
         info.renameScreenshots(mScreenshotsDir);
         info.bugreportFile = bugreportFile;
@@ -765,7 +771,7 @@
         if (!info.bugreportFile.exists() || !info.bugreportFile.canRead()) {
             Log.e(TAG, "Could not read bugreport file " + info.bugreportFile);
             Toast.makeText(context, R.string.bugreport_unreadable_text, Toast.LENGTH_LONG).show();
-            stopProgress(info.pid);
+            stopProgress(info.id);
             return;
         }
 
@@ -837,12 +843,12 @@
      * Shares the bugreport upon user's request by issuing a {@link Intent#ACTION_SEND_MULTIPLE}
      * intent, but issuing a warning dialog the first time.
      */
-    private void shareBugreport(int pid, BugreportInfo sharedInfo) {
-        BugreportInfo info = getInfo(pid);
+    private void shareBugreport(int id, BugreportInfo sharedInfo) {
+        BugreportInfo info = getInfo(id);
         if (info == null) {
             // Service was terminated but notification persisted
             info = sharedInfo;
-            Log.d(TAG, "shareBugreport(): no info for PID " + pid + " on managed processes ("
+            Log.d(TAG, "shareBugreport(): no info for ID " + id + " on managed processes ("
                     + mProcesses + "), using info from intent instead (" + info + ")");
         }
 
@@ -863,7 +869,7 @@
         mContext.startActivity(notifIntent);
 
         // ... and stop watching this process.
-        stopProgress(pid);
+        stopProgress(id);
     }
 
     /**
@@ -877,16 +883,16 @@
         final Intent shareIntent = new Intent(INTENT_BUGREPORT_SHARE);
         shareIntent.setClass(context, BugreportProgressService.class);
         shareIntent.setAction(INTENT_BUGREPORT_SHARE);
-        shareIntent.putExtra(EXTRA_PID, info.pid);
+        shareIntent.putExtra(EXTRA_ID, info.id);
         shareIntent.putExtra(EXTRA_INFO, info);
 
-        final String title = context.getString(R.string.bugreport_finished_title, info.pid);
+        final String title = context.getString(R.string.bugreport_finished_title, info.id);
         final Notification.Builder builder = new Notification.Builder(context)
                 .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
                 .setContentTitle(title)
                 .setTicker(title)
                 .setContentText(context.getString(R.string.bugreport_finished_text))
-                .setContentIntent(PendingIntent.getService(context, info.pid, shareIntent,
+                .setContentIntent(PendingIntent.getService(context, info.id, shareIntent,
                         PendingIntent.FLAG_UPDATE_CURRENT))
                 .setDeleteIntent(newCancelIntent(context, info))
                 .setLocalOnly(true)
@@ -897,8 +903,8 @@
             builder.setContentInfo(info.name);
         }
 
-        Log.v(TAG, "Sending 'Share' notification for pid " + info.pid + ": " + title);
-        NotificationManager.from(context).notify(TAG, info.pid, builder.build());
+        Log.v(TAG, "Sending 'Share' notification for ID " + info.id + ": " + title);
+        NotificationManager.from(context).notify(TAG, info.id, builder.build());
     }
 
     /**
@@ -906,7 +912,7 @@
      * finishes - at this point there is nothing to be done other than waiting, hence it has no
      * pending action.
      */
-    private static void sendBugreportBeingUpdatedNotification(Context context, int pid) {
+    private static void sendBugreportBeingUpdatedNotification(Context context, int id) {
         final String title = context.getString(R.string.bugreport_updating_title);
         final Notification.Builder builder = new Notification.Builder(context)
                 .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
@@ -916,8 +922,8 @@
                 .setLocalOnly(true)
                 .setColor(context.getColor(
                         com.android.internal.R.color.system_notification_accent_color));
-        Log.v(TAG, "Sending 'Updating zip' notification for pid " + pid + ": " + title);
-        NotificationManager.from(context).notify(TAG, pid, builder.build());
+        Log.v(TAG, "Sending 'Updating zip' notification for ID " + id + ": " + title);
+        NotificationManager.from(context).notify(TAG, id, builder.build());
     }
 
     /**
@@ -985,7 +991,7 @@
 
         // It's not possible to add a new entry into an existing file, so we need to create a new
         // zip, copy all entries, then rename it.
-        sendBugreportBeingUpdatedNotification(context, info.pid); // ...and that takes time
+        sendBugreportBeingUpdatedNotification(context, info.id); // ...and that takes time
         final File dir = info.bugreportFile.getParentFile();
         final File tmpZip = new File(dir, "tmp-" + info.bugreportFile.getName());
         Log.d(TAG, "Writing temporary zip file (" + tmpZip + ") with title and/or description");
@@ -1113,8 +1119,8 @@
     /**
      * Updates the user-provided details of a bugreport.
      */
-    private void updateBugreportInfo(int pid, String name, String title, String description) {
-        final BugreportInfo info = getInfo(pid);
+    private void updateBugreportInfo(int id, String name, String title, String description) {
+        final BugreportInfo info = getInfo(id);
         if (info == null) {
             return;
         }
@@ -1179,6 +1185,7 @@
         private EditText mInfoDescription;
         private AlertDialog mDialog;
         private Button mOkButton;
+        private int mId;
         private int mPid;
 
         /**
@@ -1207,8 +1214,7 @@
         /**
          * Sets its internal state and displays the dialog.
          */
-        private void initialize(Context context, int pid, String name, String title,
-                String description) {
+        private void initialize(Context context, BugreportInfo info) {
             // First initializes singleton.
             if (mDialog == null) {
                 @SuppressLint("InflateParams")
@@ -1232,7 +1238,7 @@
 
                 mDialog = new AlertDialog.Builder(context)
                         .setView(view)
-                        .setTitle(context.getString(R.string.bugreport_info_dialog_title, pid))
+                        .setTitle(context.getString(R.string.bugreport_info_dialog_title, info.id))
                         .setCancelable(false)
                         .setPositiveButton(context.getString(com.android.internal.R.string.ok),
                                 null)
@@ -1258,16 +1264,17 @@
             }
 
             // Then set fields.
-            mSavedName = mTempName = name;
-            mPid = pid;
-            if (!TextUtils.isEmpty(name)) {
-                mInfoName.setText(name);
+            mSavedName = mTempName = info.name;
+            mId = info.id;
+            mPid = info.pid;
+            if (!TextUtils.isEmpty(info.name)) {
+                mInfoName.setText(info.name);
             }
-            if (!TextUtils.isEmpty(title)) {
-                mInfoTitle.setText(title);
+            if (!TextUtils.isEmpty(info.title)) {
+                mInfoTitle.setText(info.title);
             }
-            if (!TextUtils.isEmpty(description)) {
-                mInfoDescription.setText(description);
+            if (!TextUtils.isEmpty(info.description)) {
+                mInfoDescription.setText(info.description);
             }
 
             // And finally display it.
@@ -1290,7 +1297,7 @@
                         final String title = mInfoTitle.getText().toString();
                         final String description = mInfoDescription.getText().toString();
 
-                        updateBugreportInfo(mPid, name, title, description);
+                        updateBugreportInfo(mId, name, title, description);
                         mDialog.dismiss();
                     }
                 });
@@ -1328,7 +1335,7 @@
             // Must update system property for the cases where dumpstate finishes
             // while the user is still entering other fields (like title or
             // description)
-            setBugreportNameProperty(mPid, name);
+            setBugreportNameProperty(mId, name);
         }
 
        /**
@@ -1337,7 +1344,7 @@
          * <p>Once the bugreport is finished dumpstate has already generated the final files, so
          * changing the name would have no effect.
          */
-        private void onBugreportFinished(int pid) {
+        private void onBugreportFinished(int id) {
             if (mInfoName != null) {
                 mInfoName.setEnabled(false);
                 mInfoName.setText(mSavedName);
@@ -1353,6 +1360,11 @@
         private final Context context;
 
         /**
+         * Sequential, user-friendly id used to identify the bugreport.
+         */
+        final int id;
+
+        /**
          * {@code pid} of the {@code dumpstate} process generating the bugreport.
          */
         final int pid;
@@ -1426,8 +1438,9 @@
         /**
          * Constructor for tracked bugreports - typically called upon receiving BUGREPORT_STARTED.
          */
-        BugreportInfo(Context context, int pid, String name, int max) {
+        BugreportInfo(Context context, int id, int pid, String name, int max) {
             this.context = context;
+            this.id = id;
             this.pid = pid;
             this.name = name;
             this.max = max;
@@ -1437,8 +1450,8 @@
          * Constructor for untracked bugreports - typically called upon receiving BUGREPORT_FINISHED
          * without a previous call to BUGREPORT_STARTED.
          */
-        BugreportInfo(Context context, int pid) {
-            this(context, pid, null, 0);
+        BugreportInfo(Context context, int id) {
+            this(context, id, id, null, 0);
             this.finished = true;
         }
 
@@ -1494,7 +1507,7 @@
         @Override
         public String toString() {
             final float percent = ((float) progress * 100 / max);
-            return "pid: " + pid + ", name: " + name + ", finished: " + finished
+            return "id: " + id + ", pid: " + pid + ", name: " + name + ", finished: " + finished
                     + "\n\ttitle: " + title + "\n\tdescription: " + description
                     + "\n\tfile: " + bugreportFile + "\n\tscreenshots: " + screenshotFiles
                     + "\n\tprogress: " + progress + "/" + max + "(" + percent + ")"
@@ -1506,6 +1519,7 @@
         // Parcelable contract
         protected BugreportInfo(Parcel in) {
             context = null;
+            id = in.readInt();
             pid = in.readInt();
             name = in.readString();
             title = in.readString();
@@ -1527,6 +1541,7 @@
 
         @Override
         public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(id);
             dest.writeInt(pid);
             dest.writeString(name);
             dest.writeString(title);
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index a9b8df2..4cd920a 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -52,8 +52,12 @@
     <!-- Tint color for the content on the notification overflow card. -->
     <color name="keyguard_overflow_content_color">#ff686868</color>
 
+    <!-- The disabled recents task bar background color. -->
+    <color name="recents_task_bar_disabled_background_color">#ff676767</color>
     <!-- The default recents task bar background color. -->
     <color name="recents_task_bar_default_background_color">#ffe6e6e6</color>
+    <!-- The default recents task view background color. -->
+    <color name="recents_task_view_default_background_color">#fff3f3f3</color>
     <!-- The recents task bar light text color to be drawn on top of dark backgrounds. -->
     <color name="recents_task_bar_light_text_color">#ffeeeeee</color>
     <!-- The recents task bar dark text color to be drawn on top of light backgrounds. -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 5d4789a..4116962 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -714,6 +714,8 @@
     <string name="recents_search_bar_label">search</string>
     <!-- Recents: Launch error string. [CHAR LIMIT=NONE] -->
     <string name="recents_launch_error_message">Could not start <xliff:g id="app" example="Calendar">%s</xliff:g>.</string>
+    <!-- Recents: Launch disabled string. [CHAR LIMIT=NONE] -->
+    <string name="recents_launch_disabled_message"><xliff:g id="app" example="Calendar">%s</xliff:g> is disabled in safe-mode.</string>
     <!-- Recents: Show history string. [CHAR LIMIT=NONE] -->
     <string name="recents_history_button_label">History</string>
     <!-- Recents: History clear all string. [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
index 454d1ce..4845425 100755
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
@@ -62,6 +62,7 @@
             mPlusPaint;
     private float mTextHeight, mWarningTextHeight;
     private int mIconTint = Color.WHITE;
+    private float mOldDarkIntensity = 0f;
 
     private int mHeight;
     private int mWidth;
@@ -295,6 +296,9 @@
     }
 
     public void setDarkIntensity(float darkIntensity) {
+        if (darkIntensity == mOldDarkIntensity) {
+            return;
+        }
         int backgroundColor = getBackgroundColor(darkIntensity);
         int fillColor = getFillColor(darkIntensity);
         mIconTint = fillColor;
@@ -302,6 +306,7 @@
         mBoltPaint.setColor(fillColor);
         mChargeColor = fillColor;
         invalidateSelf();
+        mOldDarkIntensity = darkIntensity;
     }
 
     private int getBackgroundColor(float darkIntensity) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 958572f..90d56f7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -475,6 +475,23 @@
                     break;
             }
         }
+
+        @Override
+        public void onFingerprintAuthFailed() {
+            final int currentUser = KeyguardUpdateMonitor.getCurrentUser();
+            if (mLockPatternUtils.isSecure(currentUser)) {
+                mLockPatternUtils.getDevicePolicyManager().reportFailedFingerprintAttempt(
+                        currentUser);
+            }
+        }
+
+        @Override
+        public void onFingerprintAuthenticated(int userId) {
+            if (mLockPatternUtils.isSecure(userId)) {
+                mLockPatternUtils.getDevicePolicyManager().reportSuccessfulFingerprintAttempt(
+                        userId);
+            }
+        }
     };
 
     ViewMediatorCallback mViewMediatorCallback = new ViewMediatorCallback() {
@@ -1370,8 +1387,9 @@
      * @see #KEYGUARD_DONE
      */
     private void handleKeyguardDone(boolean authenticated) {
-        if (mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())) {
-            mLockPatternUtils.getDevicePolicyManager().reportKeyguardDismissed();
+        final int currentUser = KeyguardUpdateMonitor.getCurrentUser();
+        if (mLockPatternUtils.isSecure(currentUser)) {
+            mLockPatternUtils.getDevicePolicyManager().reportKeyguardDismissed(currentUser);
         }
         if (DEBUG) Log.d(TAG, "handleKeyguardDone");
         synchronized (this) {
@@ -1484,8 +1502,9 @@
      * @see #SHOW
      */
     private void handleShow(Bundle options) {
-        if (mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())) {
-            mLockPatternUtils.getDevicePolicyManager().reportKeyguardSecured();
+        final int currentUser = KeyguardUpdateMonitor.getCurrentUser();
+        if (mLockPatternUtils.isSecure(currentUser)) {
+            mLockPatternUtils.getDevicePolicyManager().reportKeyguardSecured(currentUser);
         }
         synchronized (KeyguardViewMediator.this) {
             if (!mSystemReady) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 9da5c2b..e0efaa5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -837,11 +837,13 @@
      * Draws the header of a task used for the window animation into a bitmap.
      */
     private Bitmap drawThumbnailTransitionBitmap(Task toTask, TaskViewTransform toTransform) {
+        SystemServicesProxy ssp = Recents.getSystemServices();
         if (toTransform != null && toTask.key != null) {
             Bitmap thumbnail;
             synchronized (mHeaderBarLock) {
                 int toHeaderWidth = (int) toTransform.rect.width();
                 int toHeaderHeight = (int) (mHeaderBar.getMeasuredHeight() * toTransform.scale);
+                boolean disabledInSafeMode = !toTask.isSystemApp && ssp.isInSafeMode();
                 mHeaderBar.onTaskViewSizeChanged((int) toTransform.rect.width(),
                         (int) toTransform.rect.height());
                 thumbnail = Bitmap.createBitmap(toHeaderWidth, toHeaderHeight,
@@ -851,7 +853,8 @@
                 } else {
                     Canvas c = new Canvas(thumbnail);
                     c.scale(toTransform.scale, toTransform.scale);
-                    mHeaderBar.rebindToTask(toTask, false /* touchExplorationEnabled */);
+                    mHeaderBar.rebindToTask(toTask, false /* touchExplorationEnabled */,
+                            disabledInSafeMode);
                     mHeaderBar.draw(c);
                     c.setBitmap(null);
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java b/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java
index 244c0df..95aa10f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java
@@ -17,6 +17,7 @@
 package com.android.systemui.recents.misc;
 
 import android.os.Handler;
+import android.view.ViewDebug;
 
 /**
  * A dozer is a class that fires a trigger after it falls asleep.
@@ -26,8 +27,11 @@
 
     Handler mHandler;
 
+    @ViewDebug.ExportedProperty(category="recents")
     boolean mIsDozing;
+    @ViewDebug.ExportedProperty(category="recents")
     boolean mHasTriggered;
+    @ViewDebug.ExportedProperty(category="recents")
     int mDozeDurationMilliseconds;
     Runnable mOnSleepRunnable;
 
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 22ab794..8b4474f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -112,6 +112,8 @@
     Display mDisplay;
     String mRecentsPackage;
     ComponentName mAssistComponent;
+
+    boolean mIsSafeMode;
     boolean mHasFreeformWorkspaceSupport;
 
     Bitmap mDummyIcon;
@@ -137,6 +139,7 @@
                 mPm.hasSystemFeature(PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT) ||
                         Settings.Global.getInt(context.getContentResolver(),
                                 DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0;
+        mIsSafeMode = mPm.isSafeMode();
 
         // Get the dummy thumbnail width/heights
         Resources res = context.getResources();
@@ -187,7 +190,8 @@
                 rti.firstActiveTime = rti.lastActiveTime = i;
                 if (i % 2 == 0) {
                     rti.taskDescription = new ActivityManager.TaskDescription(description,
-                        Bitmap.createBitmap(mDummyIcon),
+                        Bitmap.createBitmap(mDummyIcon), null,
+                        0xFF000000 | (0xFFFFFF & new Random().nextInt()),
                         0xFF000000 | (0xFFFFFF & new Random().nextInt()));
                 } else {
                     rti.taskDescription = new ActivityManager.TaskDescription();
@@ -260,6 +264,13 @@
         return mHasFreeformWorkspaceSupport;
     }
 
+    /**
+     * Returns whether this device is in the safe mode.
+     */
+    public boolean isInSafeMode() {
+        return mIsSafeMode;
+    }
+
     /** Returns whether the recents is currently running */
     public boolean isRecentsTopMost(ActivityManager.RunningTaskInfo topTask,
             MutableBoolean isHomeTopMost) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
index c51aa7c..016e6d3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -18,6 +18,7 @@
 
 import android.app.ActivityManager;
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
@@ -188,11 +189,15 @@
                     : null;
             Bitmap thumbnail = loader.getAndUpdateThumbnail(taskKey, false);
             int activityColor = loader.getActivityPrimaryColor(t.taskDescription);
+            int backgroundColor = loader.getActivityBackgroundColor(t.taskDescription);
+            boolean isSystemApp = (loader.getAndUpdateActivityInfo(taskKey).applicationInfo.flags
+                    & ApplicationInfo.FLAG_SYSTEM) != 0;
 
             // Add the task to the stack
             Task task = new Task(taskKey, t.affiliatedTaskId, t.affiliatedTaskColor, icon,
                     thumbnail, title, contentDescription, dismissDescription, activityColor,
-                    !isStackTask, isLaunchTarget, t.bounds, t.taskDescription);
+                    backgroundColor, !isStackTask, isLaunchTarget, isSystemApp, t.bounds,
+                    t.taskDescription);
 
             allTasks.add(task);
             affiliatedTaskCounts.put(taskKey.id, affiliatedTaskCounts.get(taskKey.id, 0) + 1);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
index 26130ab..5e1af12 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -264,13 +264,16 @@
     private int mNumVisibleThumbnailsLoaded;
 
     int mDefaultTaskBarBackgroundColor;
+    int mDefaultTaskViewBackgroundColor;
     BitmapDrawable mDefaultIcon;
     Bitmap mDefaultThumbnail;
 
     public RecentsTaskLoader(Context context) {
         Resources res = context.getResources();
         mDefaultTaskBarBackgroundColor =
-                res.getColor(R.color.recents_task_bar_default_background_color);
+                context.getColor(R.color.recents_task_bar_default_background_color);
+        mDefaultTaskViewBackgroundColor =
+                context.getColor(R.color.recents_task_view_default_background_color);
         mMaxThumbnailCacheSize = res.getInteger(R.integer.config_recents_max_thumbnail_count);
         mMaxIconCacheSize = res.getInteger(R.integer.config_recents_max_icon_count);
         int iconCacheSize = RecentsDebugFlags.Static.DisableBackgroundCache ? 1 :
@@ -556,10 +559,20 @@
     }
 
     /**
+     * Returns the task's background color if possible.
+     */
+    int getActivityBackgroundColor(ActivityManager.TaskDescription td) {
+        if (td != null && td.getBackgroundColor() != 0) {
+            return td.getBackgroundColor();
+        }
+        return mDefaultTaskViewBackgroundColor;
+    }
+
+    /**
      * Returns the activity info for the given task key, retrieving one from the system if the
      * task key is expired.
      */
-    private ActivityInfo getAndUpdateActivityInfo(Task.TaskKey taskKey) {
+    ActivityInfo getAndUpdateActivityInfo(Task.TaskKey taskKey) {
         SystemServicesProxy ssp = Recents.getSystemServices();
         ComponentName cn = taskKey.getComponent();
         ActivityInfo activityInfo = mActivityInfoCache.get(cn);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index 1c277d5..8ed6dd7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -23,6 +23,7 @@
 import android.graphics.Color;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.view.ViewDebug;
 
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.misc.SystemServicesProxy;
@@ -48,11 +49,17 @@
 
     /* The Task Key represents the unique primary key for the task */
     public static class TaskKey {
+        @ViewDebug.ExportedProperty(category="recents")
         public final int id;
+        @ViewDebug.ExportedProperty(category="recents")
         public int stackId;
+        @ViewDebug.ExportedProperty(category="recents")
         public final Intent baseIntent;
+        @ViewDebug.ExportedProperty(category="recents")
         public final int userId;
+        @ViewDebug.ExportedProperty(category="recents")
         public long firstActiveTime;
+        @ViewDebug.ExportedProperty(category="recents")
         public long lastActiveTime;
 
         private int mHashCode;
@@ -105,17 +112,21 @@
         }
     }
 
+    @ViewDebug.ExportedProperty(deepExport=true, prefix="key_")
     public TaskKey key;
 
     /**
      * The group will be computed separately from the initialization of the task
      */
+    @ViewDebug.ExportedProperty(deepExport=true, prefix="group_")
     public TaskGrouping group;
     /**
      * The affiliationTaskId is the task id of the parent task or itself if it is not affiliated
      * with any task.
      */
+    @ViewDebug.ExportedProperty(category="recents")
     public int affiliationTaskId;
+    @ViewDebug.ExportedProperty(category="recents")
     public int affiliationColor;
 
     /**
@@ -124,15 +135,23 @@
      */
     public Drawable icon;
     public Bitmap thumbnail;
+    @ViewDebug.ExportedProperty(category="recents")
     public String title;
+    @ViewDebug.ExportedProperty(category="recents")
     public String contentDescription;
+    @ViewDebug.ExportedProperty(category="recents")
     public String dismissDescription;
+    @ViewDebug.ExportedProperty(category="recents")
     public int colorPrimary;
+    @ViewDebug.ExportedProperty(category="recents")
+    public int colorBackground;
+    @ViewDebug.ExportedProperty(category="recents")
     public boolean useLightOnPrimaryColor;
 
     /**
      * The bounds of the task, used only if it is a freeform task.
      */
+    @ViewDebug.ExportedProperty(category="recents")
     public Rect bounds;
 
     /**
@@ -143,8 +162,12 @@
     /**
      * The state isLaunchTarget will be set for the correct task upon launching Recents.
      */
+    @ViewDebug.ExportedProperty(category="recents")
     public boolean isLaunchTarget;
+    @ViewDebug.ExportedProperty(category="recents")
     public boolean isHistorical;
+    @ViewDebug.ExportedProperty(category="recents")
+    public boolean isSystemApp;
 
     private ArrayList<TaskCallbacks> mCallbacks = new ArrayList<>();
 
@@ -154,8 +177,8 @@
 
     public Task(TaskKey key, int affiliationTaskId, int affiliationColor, Drawable icon,
                 Bitmap thumbnail, String title, String contentDescription,
-                String dismissDescription, int colorPrimary, boolean isHistorical,
-                boolean isLaunchTarget, Rect bounds,
+                String dismissDescription, int colorPrimary, int colorBackground,
+                boolean isHistorical, boolean isLaunchTarget, boolean isSystemApp, Rect bounds,
                 ActivityManager.TaskDescription taskDescription) {
         boolean isInAffiliationGroup = (affiliationTaskId != key.id);
         boolean hasAffiliationGroupColor = isInAffiliationGroup && (affiliationColor != 0);
@@ -168,12 +191,14 @@
         this.contentDescription = contentDescription;
         this.dismissDescription = dismissDescription;
         this.colorPrimary = hasAffiliationGroupColor ? affiliationColor : colorPrimary;
+        this.colorBackground = colorBackground;
         this.useLightOnPrimaryColor = Utilities.computeContrastBetweenColors(this.colorPrimary,
                 Color.WHITE) > 3f;
         this.bounds = bounds;
         this.taskDescription = taskDescription;
         this.isLaunchTarget = isLaunchTarget;
         this.isHistorical = isHistorical;
+        this.isSystemApp = isSystemApp;
     }
 
     /** Copies the other task. */
@@ -188,10 +213,12 @@
         this.contentDescription = o.contentDescription;
         this.dismissDescription = o.dismissDescription;
         this.colorPrimary = o.colorPrimary;
+        this.colorBackground = o.colorBackground;
         this.useLightOnPrimaryColor = o.useLightOnPrimaryColor;
         this.bounds = o.bounds;
         this.isLaunchTarget = o.isLaunchTarget;
         this.isHistorical = o.isHistorical;
+        this.isSystemApp = o.isSystemApp;
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java b/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
index 5842095..4a65374 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
@@ -19,16 +19,22 @@
 import android.graphics.Outline;
 import android.graphics.Rect;
 import android.view.View;
+import android.view.ViewDebug;
 import android.view.ViewOutlineProvider;
 
 /* An outline provider that has a clip and outline that can be animated. */
 public class AnimateableViewBounds extends ViewOutlineProvider {
 
     View mSourceView;
+    @ViewDebug.ExportedProperty(category="recents")
     Rect mClipRect = new Rect();
+    @ViewDebug.ExportedProperty(category="recents")
     Rect mClipBounds = new Rect();
+    @ViewDebug.ExportedProperty(category="recents")
     Rect mLastClipBounds = new Rect();
+    @ViewDebug.ExportedProperty(category="recents")
     int mCornerRadius;
+    @ViewDebug.ExportedProperty(category="recents")
     float mAlpha = 1f;
     final float mMinAlpha = 0.25f;
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 42aaa97..c4db485 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.recents.views;
 
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
@@ -32,6 +34,7 @@
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewDebug;
 import android.view.ViewOutlineProvider;
 import android.view.ViewPropertyAnimator;
 import android.view.WindowInsets;
@@ -79,8 +82,6 @@
 import java.util.ArrayList;
 import java.util.List;
 
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-
 /**
  * This view is the the top level layout that contains TaskStacks (which are laid out according
  * to their SpaceNode bounds.
@@ -103,6 +104,8 @@
 
     private boolean mAwaitingFirstLayout = true;
     private boolean mLastTaskLaunchedWasFreeform;
+
+    @ViewDebug.ExportedProperty(category="recents")
     private Rect mSystemInsets = new Rect();
     private int mDividerSize;
 
@@ -110,6 +113,7 @@
     private Animator mBackgroundScrimAnimator;
 
     private RecentsTransitionHelper mTransitionHelper;
+    @ViewDebug.ExportedProperty(deepExport=true, prefix="touch_")
     private RecentsViewTouchHandler mTouchHandler;
     private final FlingAnimationUtils mFlingAnimationUtils;
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
index 346ce16..016d937 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
@@ -20,6 +20,7 @@
 import android.graphics.Point;
 import android.view.MotionEvent;
 import android.view.ViewConfiguration;
+import android.view.ViewDebug;
 
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.RecentsConfiguration;
@@ -61,12 +62,18 @@
 
     private RecentsView mRv;
 
+    @ViewDebug.ExportedProperty(deepExport=true, prefix="drag_task")
     private Task mDragTask;
+    @ViewDebug.ExportedProperty(deepExport=true, prefix="drag_task_view_")
     private TaskView mTaskView;
 
+    @ViewDebug.ExportedProperty(category="recents")
     private Point mTaskViewOffset = new Point();
+    @ViewDebug.ExportedProperty(category="recents")
     private Point mDownPos = new Point();
+    @ViewDebug.ExportedProperty(category="recents")
     private boolean mDragRequested;
+    @ViewDebug.ExportedProperty(category="recents")
     private boolean mIsDragging;
     private float mDragSlop;
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
index 19ac1e7..98bc92f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
@@ -25,6 +25,7 @@
 import android.util.ArraySet;
 import android.util.FloatProperty;
 import android.util.Property;
+import android.view.ViewDebug;
 
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
@@ -216,15 +217,20 @@
     private TaskStackLayoutAlgorithmCallbacks mCb;
 
     // The task bounds (untransformed) for layout.  This rect is anchored at mTaskRoot.
+    @ViewDebug.ExportedProperty(category="recents")
     public Rect mTaskRect = new Rect();
     // The freeform workspace bounds, inset from the top by the search bar, and is a fixed height
+    @ViewDebug.ExportedProperty(category="recents")
     public Rect mFreeformRect = new Rect();
     // The stack bounds, inset from the top by the search bar, and runs to
     // the bottom of the screen
+    @ViewDebug.ExportedProperty(category="recents")
     public Rect mStackRect = new Rect();
     // This is the current system insets
+    @ViewDebug.ExportedProperty(category="recents")
     public Rect mSystemInsets = new Rect();
     // This is the bounds of the history button above the stack rect
+    @ViewDebug.ExportedProperty(category="recents")
     public Rect mHistoryButtonRect = new Rect();
 
     // The visible ranges when the stack is focused and unfocused
@@ -232,14 +238,17 @@
     private Range mFocusedRange;
 
     // The offset from the top when scrolled to the top of the stack
+    @ViewDebug.ExportedProperty(category="recents")
     private int mFocusedPeekHeight;
 
     // The offset from the top of the stack to the top of the bounds when the stack is scrolled to
     // the end
+    @ViewDebug.ExportedProperty(category="recents")
     private int mStackTopOffset;
 
     // The offset from the bottom of the stack to the bottom of the bounds when the stack is
     // scrolled to the front
+    @ViewDebug.ExportedProperty(category="recents")
     private int mStackBottomOffset;
 
     // The paths defining the motion of the tasks when the stack is focused and unfocused
@@ -250,27 +259,36 @@
 
     // The state of the stack focus (0..1), which controls the transition of the stack from the
     // focused to non-focused state
+    @ViewDebug.ExportedProperty(category="recents")
     private float mFocusState;
 
     // The animator used to reset the focused state
     private ObjectAnimator mFocusStateAnimator;
 
     // The smallest scroll progress, at this value, the back most task will be visible
+    @ViewDebug.ExportedProperty(category="recents")
     float mMinScrollP;
     // The largest scroll progress, at this value, the front most task will be visible above the
     // navigation bar
+    @ViewDebug.ExportedProperty(category="recents")
     float mMaxScrollP;
     // The initial progress that the scroller is set when you first enter recents
+    @ViewDebug.ExportedProperty(category="recents")
     float mInitialScrollP;
     // The task progress for the front-most task in the stack
+    @ViewDebug.ExportedProperty(category="recents")
     float mFrontMostTaskP;
 
     // The last computed task counts
+    @ViewDebug.ExportedProperty(category="recents")
     int mNumStackTasks;
+    @ViewDebug.ExportedProperty(category="recents")
     int mNumFreeformTasks;
 
     // The min/max z translations
+    @ViewDebug.ExportedProperty(category="recents")
     int mMinTranslationZ;
+    @ViewDebug.ExportedProperty(category="recents")
     int mMaxTranslationZ;
 
     // Optimization, allows for quick lookup of task -> index
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index fb3515a..9560eb4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -20,8 +20,6 @@
 import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.content.ComponentName;
@@ -40,11 +38,10 @@
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewDebug;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.animation.Interpolator;
-import android.view.animation.PathInterpolator;
 import android.widget.FrameLayout;
 
 import com.android.internal.logging.MetricsLogger;
@@ -121,8 +118,11 @@
 
     LayoutInflater mInflater;
     TaskStack mStack;
+    @ViewDebug.ExportedProperty(deepExport=true, prefix="layout_")
     TaskStackLayoutAlgorithm mLayoutAlgorithm;
+    @ViewDebug.ExportedProperty(deepExport=true, prefix="scroller_")
     TaskStackViewScroller mStackScroller;
+    @ViewDebug.ExportedProperty(deepExport=true, prefix="touch_")
     TaskStackViewTouchHandler mTouchHandler;
     TaskStackAnimationHelper mAnimationHelper;
     GradientDrawable mFreeformWorkspaceBackground;
@@ -134,25 +134,36 @@
     ArraySet<Task.TaskKey> mIgnoreTasks = new ArraySet<>();
     AnimationProps mDeferredTaskViewLayoutAnimation = null;
 
+    @ViewDebug.ExportedProperty(deepExport=true, prefix="doze_")
     DozeTrigger mUIDozeTrigger;
+    @ViewDebug.ExportedProperty(deepExport=true, prefix="focused_task_")
     Task mFocusedTask;
 
     int mTaskCornerRadiusPx;
     private int mDividerSize;
     private int mStartTimerIndicatorDuration;
 
+    @ViewDebug.ExportedProperty(category="recents")
     boolean mTaskViewsClipDirty = true;
+    @ViewDebug.ExportedProperty(category="recents")
     boolean mAwaitingFirstLayout = true;
+    @ViewDebug.ExportedProperty(category="recents")
     boolean mInMeasureLayout = false;
+    @ViewDebug.ExportedProperty(category="recents")
     boolean mEnterAnimationComplete = false;
+    @ViewDebug.ExportedProperty(category="recents")
     boolean mTouchExplorationEnabled;
+    @ViewDebug.ExportedProperty(category="recents")
     boolean mScreenPinningEnabled;
 
     // The stable stack bounds are the full bounds that we were measured with from RecentsView
+    @ViewDebug.ExportedProperty(category="recents")
     private Rect mStableStackBounds = new Rect();
     // The current stack bounds are dynamic and may change as the user drags and drops
+    @ViewDebug.ExportedProperty(category="recents")
     private Rect mStackBounds = new Rect();
 
+    @ViewDebug.ExportedProperty(category="recents")
     private int[] mTmpVisibleRange = new int[2];
     private Rect mTmpRect = new Rect();
     private ArrayMap<Task.TaskKey, TaskView> mTmpTaskViewMap = new ArrayMap<>();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
index d1bce55..b54ea1d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
@@ -23,6 +23,7 @@
 import android.util.FloatProperty;
 import android.util.Log;
 import android.util.Property;
+import android.view.ViewDebug;
 import android.widget.OverScroller;
 
 import com.android.systemui.Interpolators;
@@ -63,6 +64,7 @@
     TaskStackLayoutAlgorithm mLayoutAlgorithm;
     TaskStackViewScrollerCallbacks mCb;
 
+    @ViewDebug.ExportedProperty(category="recents")
     float mStackScrollP;
     float mFlingDownScrollP;
     int mFlingDownY;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index 5d1bb66..55f9fba 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -28,6 +28,7 @@
 import android.view.VelocityTracker;
 import android.view.View;
 import android.view.ViewConfiguration;
+import android.view.ViewDebug;
 import android.view.ViewParent;
 import android.view.animation.Animation;
 import android.view.animation.Interpolator;
@@ -69,6 +70,7 @@
     FlingAnimationUtils mFlingAnimUtils;
     ValueAnimator mScrollFlingAnimator;
 
+    @ViewDebug.ExportedProperty(category="recents")
     boolean mIsScrolling;
     float mDownScrollP;
     int mDownX, mDownY;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 850e36e7..0f485ac 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -35,8 +35,10 @@
 import android.util.Property;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewDebug;
 import android.view.ViewOutlineProvider;
 import android.view.animation.AccelerateInterpolator;
+import android.widget.Toast;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.MetricsProto.MetricsEvent;
@@ -105,31 +107,46 @@
                 }
             };
 
+    @ViewDebug.ExportedProperty(category="recents")
     float mTaskProgress;
+    @ViewDebug.ExportedProperty(category="recents")
     float mMaxDimScale;
+    @ViewDebug.ExportedProperty(category="recents")
     int mDimAlpha;
     AccelerateInterpolator mDimInterpolator = new AccelerateInterpolator(3f);
     PorterDuffColorFilter mDimColorFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_ATOP);
     Paint mDimLayerPaint = new Paint();
     float mActionButtonTranslationZ;
 
+    @ViewDebug.ExportedProperty(deepExport=true, prefix="task_")
     Task mTask;
+    @ViewDebug.ExportedProperty(category="recents")
     boolean mTaskDataLoaded;
+    @ViewDebug.ExportedProperty(category="recents")
     boolean mClipViewInStack = true;
+    @ViewDebug.ExportedProperty(category="recents")
     boolean mTouchExplorationEnabled;
+    @ViewDebug.ExportedProperty(category="recents")
+    boolean mIsDisabledInSafeMode;
+    @ViewDebug.ExportedProperty(deepExport=true, prefix="view_bounds_")
     AnimateableViewBounds mViewBounds;
 
     private AnimatorSet mTransformAnimation;
     private ArrayList<Animator> mTmpAnimators = new ArrayList<>();
 
     View mContent;
+    @ViewDebug.ExportedProperty(deepExport=true, prefix="thumbnail_")
     TaskViewThumbnail mThumbnailView;
+    @ViewDebug.ExportedProperty(deepExport=true, prefix="header_")
     TaskViewHeader mHeaderView;
     View mActionButtonView;
     TaskViewCallbacks mCb;
 
+    @ViewDebug.ExportedProperty(category="recents")
     Point mDownTouchPos = new Point();
 
+    private Toast mDisabledAppToast;
+
     public TaskView(Context context) {
         this(context, null);
     }
@@ -549,15 +566,17 @@
     /**** TaskCallbacks Implementation ****/
 
     public void onTaskBound(Task t) {
+        SystemServicesProxy ssp = Recents.getSystemServices();
         mTask = t;
         mTask.addCallback(this);
+        mIsDisabledInSafeMode = !mTask.isSystemApp && ssp.isInSafeMode();
     }
 
     @Override
     public void onTaskDataLoaded(Task task) {
         // Bind each of the views to the new task data
-        mThumbnailView.rebindToTask(mTask);
-        mHeaderView.rebindToTask(mTask, mTouchExplorationEnabled);
+        mThumbnailView.rebindToTask(mTask, mIsDisabledInSafeMode);
+        mHeaderView.rebindToTask(mTask, mTouchExplorationEnabled, mIsDisabledInSafeMode);
         mTaskDataLoaded = true;
     }
 
@@ -572,13 +591,24 @@
 
     @Override
     public void onTaskStackIdChanged() {
-        mHeaderView.rebindToTask(mTask, mTouchExplorationEnabled);
+        mHeaderView.rebindToTask(mTask, mTouchExplorationEnabled, mIsDisabledInSafeMode);
     }
 
     /**** View.OnClickListener Implementation ****/
 
     @Override
      public void onClick(final View v) {
+        if (mIsDisabledInSafeMode) {
+            Context context = getContext();
+            String msg = context.getString(R.string.recents_launch_disabled_message, mTask.title);
+            if (mDisabledAppToast != null) {
+                mDisabledAppToast.cancel();
+            }
+            mDisabledAppToast = Toast.makeText(context, msg, Toast.LENGTH_SHORT);
+            mDisabledAppToast.show();
+            return;
+        }
+
         boolean screenPinningRequested = false;
         if (v == mActionButtonView) {
             // Reset the translation of the action button before we animate it out
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index c91a833..bb56a52 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -36,6 +36,7 @@
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewAnimationUtils;
+import android.view.ViewDebug;
 import android.view.ViewStub;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
@@ -74,6 +75,8 @@
 
         private Paint mHighlightPaint = new Paint();
         private Paint mBackgroundPaint = new Paint();
+        private int mColor;
+        private float mDimAlpha;
 
         public HighlightColorDrawable() {
             mBackgroundPaint.setColor(Color.argb(255, 0, 0, 0));
@@ -83,15 +86,19 @@
         }
 
         public void setColorAndDim(int color, float dimAlpha) {
-            mBackgroundPaint.setColor(color);
+            if (mColor != color || Float.compare(mDimAlpha, dimAlpha) != 0) {
+                mColor = color;
+                mDimAlpha = dimAlpha;
+                mBackgroundPaint.setColor(color);
 
-            ColorUtils.colorToHSL(color, mTmpHSL);
-            // TODO: Consider using the saturation of the color to adjust the lightness as well
-            mTmpHSL[2] = Math.min(1f,
-                    mTmpHSL[2] + HIGHLIGHT_LIGHTNESS_INCREMENT * (1.0f - dimAlpha));
-            mHighlightPaint.setColor(ColorUtils.HSLToColor(mTmpHSL));
+                ColorUtils.colorToHSL(color, mTmpHSL);
+                // TODO: Consider using the saturation of the color to adjust the lightness as well
+                mTmpHSL[2] = Math.min(1f,
+                        mTmpHSL[2] + HIGHLIGHT_LIGHTNESS_INCREMENT * (1.0f - dimAlpha));
+                mHighlightPaint.setColor(ColorUtils.HSLToColor(mTmpHSL));
 
-            invalidateSelf();
+                invalidateSelf();
+            }
         }
 
         @Override
@@ -121,6 +128,10 @@
         public int getOpacity() {
             return PixelFormat.OPAQUE;
         }
+
+        public int getColor() {
+            return mColor;
+        }
     }
 
     Task mTask;
@@ -139,9 +150,11 @@
     ProgressBar mFocusTimerIndicator;
 
     // Header drawables
+    @ViewDebug.ExportedProperty(category="recents")
     Rect mTaskViewRect = new Rect();
     int mCornerRadius;
     int mHighlightHeight;
+    @ViewDebug.ExportedProperty(category="recents")
     float mDimAlpha;
     Drawable mLightDismissDrawable;
     Drawable mDarkDismissDrawable;
@@ -153,6 +166,7 @@
     Drawable mDarkInfoIcon;
     int mTaskBarViewLightTextColor;
     int mTaskBarViewDarkTextColor;
+    int mDisabledTaskBarBackgroundColor;
     int mMoveTaskTargetStackId = INVALID_STACK_ID;
 
     // Header background
@@ -195,6 +209,8 @@
         mDarkFullscreenIcon = context.getDrawable(R.drawable.recents_move_task_fullscreen_dark);
         mLightInfoIcon = context.getDrawable(R.drawable.recents_info_light);
         mDarkInfoIcon = context.getDrawable(R.drawable.recents_info_dark);
+        mDisabledTaskBarBackgroundColor =
+                context.getColor(R.color.recents_task_bar_disabled_background_color);
 
         // Configure the background and dim
         mBackground = new HighlightColorDrawable();
@@ -331,17 +347,17 @@
      */
     void setDimAlpha(float dimAlpha) {
         mDimAlpha = dimAlpha;
-        updateBackgroundColor(dimAlpha);
+        updateBackgroundColor(mBackground.getColor(), dimAlpha);
     }
 
     /**
      * Updates the background and highlight colors for this header.
      */
-    private void updateBackgroundColor(float dimAlpha) {
+    private void updateBackgroundColor(int color, float dimAlpha) {
         if (mTask != null) {
-            mBackground.setColorAndDim(mTask.colorPrimary, dimAlpha);
+            mBackground.setColorAndDim(color, dimAlpha);
             // TODO: Consider using the saturation of the color to adjust the lightness as well
-            ColorUtils.colorToHSL(mTask.colorPrimary, mTmpHSL);
+            ColorUtils.colorToHSL(color, mTmpHSL);
             mTmpHSL[2] = Math.min(1f, mTmpHSL[2] + OVERLAY_LIGHTNESS_INCREMENT * (1.0f - dimAlpha));
             mOverlayBackground.setColorAndDim(ColorUtils.HSLToColor(mTmpHSL), dimAlpha);
             mDimLayerPaint.setAlpha((int) (dimAlpha * 255));
@@ -350,12 +366,15 @@
     }
 
     /** Binds the bar view to the task */
-    public void rebindToTask(Task t, boolean touchExplorationEnabled) {
+    public void rebindToTask(Task t, boolean touchExplorationEnabled, boolean disabledInSafeMode) {
         mTask = t;
 
         // If an activity icon is defined, then we use that as the primary icon to show in the bar,
         // otherwise, we fall back to the application icon
-        updateBackgroundColor(mDimAlpha);
+        int primaryColor = disabledInSafeMode
+                ? mDisabledTaskBarBackgroundColor
+                : t.colorPrimary;
+        updateBackgroundColor(primaryColor, mDimAlpha);
         if (t.icon != null) {
             mIconView.setImageDrawable(t.icon);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
index f90951e..0fec9c3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
@@ -21,13 +21,17 @@
 import android.graphics.BitmapShader;
 import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
 import android.graphics.LightingColorFilter;
 import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.Rect;
+import android.graphics.Region;
 import android.graphics.Shader;
 import android.util.AttributeSet;
 import android.view.View;
+import android.view.ViewDebug;
 
 import com.android.systemui.R;
 import com.android.systemui.recents.model.Task;
@@ -39,27 +43,40 @@
  */
 public class TaskViewThumbnail extends View {
 
+
+    private static final ColorMatrix TMP_FILTER_COLOR_MATRIX = new ColorMatrix();
+    private static final ColorMatrix TMP_BRIGHTNESS_COLOR_MATRIX = new ColorMatrix();
+
     private Task mTask;
 
     // Drawing
+    @ViewDebug.ExportedProperty(category="recents")
     Rect mThumbnailRect = new Rect();
+    @ViewDebug.ExportedProperty(category="recents")
     Rect mTaskViewRect = new Rect();
     int mCornerRadius;
+    @ViewDebug.ExportedProperty(category="recents")
     float mDimAlpha;
     Matrix mScaleMatrix = new Matrix();
     Paint mDrawPaint = new Paint();
+    Paint mBgFillPaint = new Paint();
     BitmapShader mBitmapShader;
     LightingColorFilter mLightingColorFilter = new LightingColorFilter(0xffffffff, 0);
 
     // Task bar clipping, the top of this thumbnail can be clipped against the opaque header
     // bar that overlaps this thumbnail
     View mTaskBar;
+    @ViewDebug.ExportedProperty(category="recents")
     Rect mClipRect = new Rect();
 
     // Visibility optimization, if the thumbnail height is less than the height of the header
     // bar for the task view, then just mark this thumbnail view as invisible
+    @ViewDebug.ExportedProperty(category="recents")
     boolean mInvisible;
 
+    @ViewDebug.ExportedProperty(category="recents")
+    boolean mDisabledInSafeMode;
+
     public TaskViewThumbnail(Context context) {
         this(context, null);
     }
@@ -79,6 +96,7 @@
         mDrawPaint.setAntiAlias(true);
         mCornerRadius = getResources().getDimensionPixelSize(
                 R.dimen.recents_task_view_rounded_corners_radius);
+        mBgFillPaint.setColor(Color.WHITE);
     }
 
     /**
@@ -100,10 +118,39 @@
         if (mInvisible) {
             return;
         }
-        // Draw the thumbnail with the rounded corners
-        canvas.drawRoundRect(0, 0, mTaskViewRect.width(), mTaskViewRect.height(),
-                mCornerRadius,
-                mCornerRadius, mDrawPaint);
+
+        int thumbnailHeight = (int) (((float) mTaskViewRect.width() / mThumbnailRect.width()) *
+                mThumbnailRect.height());
+        if (thumbnailHeight >= mTaskViewRect.height()) {
+            // The thumbnail fills the full task view bounds, so just draw it
+            canvas.drawRoundRect(0, 0, mTaskViewRect.width(), mTaskViewRect.height(),
+                    mCornerRadius, mCornerRadius, mDrawPaint);
+        } else {
+            int count = 0;
+            if (thumbnailHeight > 0) {
+                // The thumbnail only covers part of the task view bounds, so fill in the
+                // non-thumbnail space with the default background color.  This is the equivalent of
+                // the GL border texture mode.
+                count = canvas.save();
+
+                // Since we only want the top corners to be rounded, draw slightly beyond the
+                // thumbnail height, but clip to the thumbnail height
+                canvas.clipRect(0, 0, mTaskViewRect.width(), thumbnailHeight, Region.Op.REPLACE);
+                canvas.drawRoundRect(0, 0, mTaskViewRect.width(), thumbnailHeight + mCornerRadius,
+                        mCornerRadius, mCornerRadius, mDrawPaint);
+            }
+
+            // In the remaining space, draw the background color
+            canvas.clipRect(0, thumbnailHeight, mTaskViewRect.width(), mTaskViewRect.height(),
+                    Region.Op.REPLACE);
+            canvas.drawRoundRect(0, Math.max(0, thumbnailHeight - mCornerRadius),
+                    mTaskViewRect.width(), mTaskViewRect.height(), mCornerRadius, mCornerRadius,
+                    mBgFillPaint);
+
+            if (thumbnailHeight > 0) {
+                canvas.restoreToCount(count);
+            }
+        }
     }
 
     /** Sets the thumbnail to a given bitmap. */
@@ -128,9 +175,27 @@
         }
         int mul = (int) ((1.0f - mDimAlpha) * 255);
         if (mBitmapShader != null) {
-            mLightingColorFilter.setColorMultiply(Color.argb(255, mul, mul, mul));
-            mDrawPaint.setColorFilter(mLightingColorFilter);
-            mDrawPaint.setColor(0xffffffff);
+            if (mDisabledInSafeMode) {
+                // Brightness: C-new = C-old*(1-amount) + amount
+                TMP_FILTER_COLOR_MATRIX.setSaturation(0);
+                float scale = 1f - mDimAlpha;
+                float[] mat = TMP_BRIGHTNESS_COLOR_MATRIX.getArray();
+                mat[0] = scale;
+                mat[6] = scale;
+                mat[12] = scale;
+                mat[4] = mDimAlpha;
+                mat[9] = mDimAlpha;
+                mat[14] = mDimAlpha;
+                TMP_FILTER_COLOR_MATRIX.preConcat(TMP_BRIGHTNESS_COLOR_MATRIX);
+                ColorMatrixColorFilter filter = new ColorMatrixColorFilter(TMP_FILTER_COLOR_MATRIX);
+                mDrawPaint.setColorFilter(filter);
+                mBgFillPaint.setColorFilter(filter);
+            } else {
+                mLightingColorFilter.setColorMultiply(Color.argb(255, mul, mul, mul));
+                mDrawPaint.setColorFilter(mLightingColorFilter);
+                mDrawPaint.setColor(0xFFffffff);
+                mBgFillPaint.setColorFilter(mLightingColorFilter);
+            }
         } else {
             int grey = mul;
             mDrawPaint.setColorFilter(null);
@@ -196,10 +261,14 @@
     }
 
     /** Binds the thumbnail view to the task */
-    void rebindToTask(Task t) {
+    void rebindToTask(Task t, boolean disabledInSafeMode) {
         mTask = t;
+        mDisabledInSafeMode = disabledInSafeMode;
         if (t.thumbnail != null) {
             setThumbnail(t.thumbnail);
+            if (t.colorBackground != 0) {
+                mBgFillPaint.setColor(t.colorBackground);
+            }
         } else {
             setThumbnail(null);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 7d37ad2..39a36cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -38,6 +38,7 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.database.ContentObserver;
+import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
 import android.os.AsyncTask;
@@ -116,7 +117,7 @@
         ExpandableNotificationRow.ExpansionLogger, NotificationData.Environment,
         ExpandableNotificationRow.OnExpandClickListener {
     public static final String TAG = "StatusBar";
-    public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+    public static final boolean DEBUG = false;
     public static final boolean MULTIUSER_DEBUG = false;
 
     public static final boolean ENABLE_REMOTE_INPUT =
@@ -194,6 +195,7 @@
     private final SparseBooleanArray mUsersAllowingPrivateNotifications = new SparseBooleanArray();
 
     private UserManager mUserManager;
+    private int mDensity;
 
     // UI-specific methods
 
@@ -633,18 +635,22 @@
         mLocale = currentConfig.locale;
         mLayoutDirection = TextUtils.getLayoutDirectionFromLocale(mLocale);
         mFontScale = currentConfig.fontScale;
+        mDensity = currentConfig.densityDpi;
 
         mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
 
         // Connect in to the status bar manager service
         mCommandQueue = new CommandQueue(this);
 
-        int[] switches = new int[8];
+        int[] switches = new int[9];
         ArrayList<IBinder> binders = new ArrayList<IBinder>();
         ArrayList<String> iconSlots = new ArrayList<>();
         ArrayList<StatusBarIcon> icons = new ArrayList<>();
+        Rect fullscreenStackBounds = new Rect();
+        Rect dockedStackBounds = new Rect();
         try {
-            mBarService.registerStatusBar(mCommandQueue, iconSlots, icons, switches, binders);
+            mBarService.registerStatusBar(mCommandQueue, iconSlots, icons, switches, binders,
+                    fullscreenStackBounds, dockedStackBounds);
         } catch (RemoteException ex) {
             // If the system process isn't there we're doomed anyway.
         }
@@ -653,7 +659,8 @@
 
         mSettingsObserver.onChange(false); // set up
         disable(switches[0], switches[6], false /* animate */);
-        setSystemUiVisibility(switches[1], 0xffffffff);
+        setSystemUiVisibility(switches[1], switches[7], switches[8], 0xffffffff,
+                fullscreenStackBounds, dockedStackBounds);
         topAppWindowChanged(switches[2] != 0);
         // StatusBarManagerService has a back up of IME token and it's restored here.
         setImeWindowStatus(binders.get(0), switches[3], switches[4], switches[5] != 0);
@@ -813,8 +820,13 @@
         final Locale locale = mContext.getResources().getConfiguration().locale;
         final int ld = TextUtils.getLayoutDirectionFromLocale(locale);
         final float fontScale = newConfig.fontScale;
-
-        if (! locale.equals(mLocale) || ld != mLayoutDirection || fontScale != mFontScale) {
+        final int density = newConfig.densityDpi;
+        if (density != mDensity || mFontScale != fontScale) {
+            reInflateViews();
+            mDensity = density;
+            mFontScale = fontScale;
+        }
+        if (! locale.equals(mLocale) || ld != mLayoutDirection) {
             if (DEBUG) {
                 Log.v(TAG, String.format(
                         "config changed locale/LD: %s (%d) -> %s (%d)", mLocale, mLayoutDirection,
@@ -826,6 +838,21 @@
         }
     }
 
+    protected void reInflateViews() {
+        ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
+        for (int i = 0; i < activeNotifications.size(); i++) {
+            Entry entry = activeNotifications.get(i);
+            boolean exposedGuts = entry.row.getGuts() == mNotificationGutsExposed;
+            entry.row.reInflateViews();
+            if (exposedGuts) {
+                mNotificationGutsExposed = entry.row.getGuts();
+                bindGuts(entry.row);
+            }
+            entry.cacheContentViews(mContext, null /* updatedNotification */);
+            inflateViews(entry, mStackScroller);
+        }
+    }
+
     protected View bindVetoButtonClickListener(View row, StatusBarNotification n) {
         View vetoButton = row.findViewById(R.id.veto);
         final String _pkg = n.getPackageName();
@@ -2080,8 +2107,25 @@
             return false;
         }
 
-        if (isSnoozedPackage(sbn)) {
-            if (DEBUG) Log.d(TAG, "No peeking: snoozed package: " + sbn.getKey());
+        boolean inUse = mPowerManager.isScreenOn()
+                && (!mStatusBarKeyguardViewManager.isShowing()
+                || mStatusBarKeyguardViewManager.isOccluded())
+                && !mStatusBarKeyguardViewManager.isInputRestricted();
+        try {
+            inUse = inUse && !mDreamManager.isDreaming();
+        } catch (RemoteException e) {
+            Log.d(TAG, "failed to query dream manager", e);
+        }
+
+        if (!inUse) {
+            if (DEBUG) {
+                Log.d(TAG, "No peeking: not in use: " + sbn.getKey());
+            }
+            return false;
+        }
+
+        if (mNotificationData.shouldSuppressScreenOn(sbn.getKey())) {
+            if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey());
             return false;
         }
 
@@ -2090,15 +2134,17 @@
             return false;
         }
 
-        if (sbn.getNotification().fullScreenIntent != null
-                && mAccessibilityManager.isTouchExplorationEnabled()) {
-            if (DEBUG) Log.d(TAG, "No peeking: accessible fullscreen: " + sbn.getKey());
-            return false;
+        if (sbn.getNotification().fullScreenIntent != null) {
+            if (mAccessibilityManager.isTouchExplorationEnabled()) {
+                if (DEBUG) Log.d(TAG, "No peeking: accessible fullscreen: " + sbn.getKey());
+                return false;
+            } else {
+                return true;
+            }
         }
 
-
-        if (mNotificationData.shouldSuppressScreenOn(sbn.getKey())) {
-            if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey());
+        if (isSnoozedPackage(sbn)) {
+            if (DEBUG) Log.d(TAG, "No peeking: snoozed package: " + sbn.getKey());
             return false;
         }
 
@@ -2107,17 +2153,7 @@
             return false;
         }
 
-        boolean inUse = mPowerManager.isScreenOn()
-                && (!mStatusBarKeyguardViewManager.isShowing()
-                        || mStatusBarKeyguardViewManager.isOccluded())
-                && !mStatusBarKeyguardViewManager.isInputRestricted();
-        try {
-            inUse = inUse && !mDreamManager.isDreaming();
-        } catch (RemoteException e) {
-            Log.d(TAG, "failed to query dream manager", e);
-        }
-        if (DEBUG) Log.d(TAG, "peek if device in use: " + inUse);
-        return inUse;
+        return true;
     }
 
     protected abstract boolean isSnoozedPackage(StatusBarNotification sbn);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 71347c4..3b960ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -17,12 +17,14 @@
 package com.android.systemui.statusbar;
 
 import android.content.ComponentName;
+import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
 import android.util.Pair;
 
+import com.android.internal.os.SomeArgs;
 import com.android.internal.statusbar.IStatusBar;
 import com.android.internal.statusbar.StatusBarIcon;
 
@@ -94,7 +96,8 @@
         public void animateExpandNotificationsPanel();
         public void animateCollapsePanels(int flags);
         public void animateExpandSettingsPanel(String obj);
-        public void setSystemUiVisibility(int vis, int mask);
+        public void setSystemUiVisibility(int vis, int fullscreenStackVis,
+                int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds);
         public void topAppWindowChanged(boolean visible);
         public void setImeWindowStatus(IBinder token, int vis, int backDisposition,
                 boolean showImeSwitcher);
@@ -169,11 +172,19 @@
         }
     }
 
-    public void setSystemUiVisibility(int vis, int mask) {
+    public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
+            int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
         synchronized (mLock) {
             // Don't coalesce these, since it might have one time flags set such as
             // STATUS_BAR_UNHIDE which might get lost.
-            mHandler.obtainMessage(MSG_SET_SYSTEMUI_VISIBILITY, vis, mask, null).sendToTarget();
+            SomeArgs args = SomeArgs.obtain();
+            args.argi1 = vis;
+            args.argi2 = fullscreenStackVis;
+            args.argi3 = dockedStackVis;
+            args.argi4 = mask;
+            args.arg1 = fullscreenStackBounds;
+            args.arg2 = dockedStackBounds;
+            mHandler.obtainMessage(MSG_SET_SYSTEMUI_VISIBILITY, args).sendToTarget();
         }
     }
 
@@ -377,7 +388,10 @@
                     mCallbacks.animateExpandSettingsPanel((String) msg.obj);
                     break;
                 case MSG_SET_SYSTEMUI_VISIBILITY:
-                    mCallbacks.setSystemUiVisibility(msg.arg1, msg.arg2);
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    mCallbacks.setSystemUiVisibility(args.argi1, args.argi2, args.argi3,
+                            args.argi4, (Rect) args.arg1, (Rect) args.arg2);
+                    args.recycle();
                     break;
                 case MSG_TOP_APP_WINDOW_CHANGED:
                     mCallbacks.topAppWindowChanged(msg.arg1 != 0);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 84b2031..264655c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -25,6 +25,7 @@
 import android.os.Build;
 import android.service.notification.StatusBarNotification;
 import android.util.AttributeSet;
+import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.NotificationHeaderView;
 import android.view.View;
@@ -50,11 +51,11 @@
 
     private static final int DEFAULT_DIVIDER_ALPHA = 0x29;
     private static final int COLORED_DIVIDER_ALPHA = 0x7B;
-    private final int mNotificationMinHeightLegacy;
-    private final int mMaxHeadsUpHeightLegacy;
-    private final int mMaxHeadsUpHeight;
-    private final int mNotificationMinHeight;
-    private final int mNotificationMaxHeight;
+    private int mNotificationMinHeightLegacy;
+    private int mMaxHeadsUpHeightLegacy;
+    private int mMaxHeadsUpHeight;
+    private int mNotificationMinHeight;
+    private int mNotificationMaxHeight;
 
     /** Does this row contain layouts that can adapt to row expansion */
     private boolean mExpandable;
@@ -507,6 +508,27 @@
         mHeadsUpManager = headsUpManager;
     }
 
+    public void reInflateViews() {
+        initDimens();
+        if (mIsSummaryWithChildren) {
+            removeView(mNotificationHeader);
+            mNotificationHeader = null;
+            recreateNotificationHeader();
+            if (mChildrenContainer != null) {
+                mChildrenContainer.reInflateViews();
+            }
+        }
+        if (mGuts != null) {
+            View oldGuts = mGuts;
+            int index = indexOfChild(oldGuts);
+            removeView(oldGuts);
+            mGuts = (NotificationGuts) LayoutInflater.from(mContext).inflate(
+                    R.layout.notification_guts, this, false);
+            mGuts.setVisibility(oldGuts.getVisibility());
+            addView(mGuts, index);
+        }
+    }
+
     public interface ExpansionLogger {
         public void logNotificationExpansion(String key, boolean userAction, boolean expanded);
     }
@@ -514,6 +536,10 @@
     public ExpandableNotificationRow(Context context, AttributeSet attrs) {
         super(context, attrs);
         mFalsingManager = FalsingManager.getInstance(context);
+        initDimens();
+    }
+
+    private void initDimens() {
         mNotificationMinHeightLegacy =  getResources().getDimensionPixelSize(
                 R.dimen.notification_min_height_legacy);
         mNotificationMinHeight =  getResources().getDimensionPixelSize(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
index 6801e5f..9aa5ea0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
@@ -21,6 +21,7 @@
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.graphics.Color;
+import android.graphics.Rect;
 import android.graphics.drawable.Animatable;
 import android.graphics.drawable.Drawable;
 import android.telephony.SubscriptionInfo;
@@ -80,6 +81,7 @@
     private ArrayList<PhoneState> mPhoneStates = new ArrayList<PhoneState>();
     private int mIconTint = Color.WHITE;
     private float mDarkIntensity;
+    private final Rect mTintArea = new Rect();
 
     ViewGroup mEthernetGroup, mWifiGroup;
     View mNoSimsCombo;
@@ -490,23 +492,31 @@
         }
     }
 
-    public void setIconTint(int tint, float darkIntensity) {
-        boolean changed = tint != mIconTint || darkIntensity != mDarkIntensity;
+    public void setIconTint(int tint, float darkIntensity, Rect tintArea) {
+        boolean changed = tint != mIconTint || darkIntensity != mDarkIntensity
+                || !mTintArea.equals(tintArea);
         mIconTint = tint;
         mDarkIntensity = darkIntensity;
+        mTintArea.set(tintArea);
         if (changed && isAttachedToWindow()) {
             applyIconTint();
         }
     }
 
     private void applyIconTint() {
-        setTint(mVpn, mIconTint);
-        setTint(mAirplane, mIconTint);
-        applyDarkIntensity(mDarkIntensity, mNoSims, mNoSimsDark);
-        applyDarkIntensity(mDarkIntensity, mWifi, mWifiDark);
-        applyDarkIntensity(mDarkIntensity, mEthernet, mEthernetDark);
+        setTint(mVpn, StatusBarIconController.getTint(mTintArea, mVpn, mIconTint));
+        setTint(mAirplane, StatusBarIconController.getTint(mTintArea, mAirplane, mIconTint));
+        applyDarkIntensity(
+                StatusBarIconController.getDarkIntensity(mTintArea, mNoSims, mDarkIntensity),
+                mNoSims, mNoSimsDark);
+        applyDarkIntensity(
+                StatusBarIconController.getDarkIntensity(mTintArea, mWifi, mDarkIntensity),
+                mWifi, mWifiDark);
+        applyDarkIntensity(
+                StatusBarIconController.getDarkIntensity(mTintArea, mEthernet, mDarkIntensity),
+                mEthernet, mEthernetDark);
         for (int i = 0; i < mPhoneStates.size(); i++) {
-            mPhoneStates.get(i).setIconTint(mIconTint, mDarkIntensity);
+            mPhoneStates.get(i).setIconTint(mIconTint, mDarkIntensity, mTintArea);
         }
     }
 
@@ -613,9 +623,11 @@
             }
         }
 
-        public void setIconTint(int tint, float darkIntensity) {
-            applyDarkIntensity(darkIntensity, mMobile, mMobileDark);
-            setTint(mMobileType, tint);
+        public void setIconTint(int tint, float darkIntensity, Rect tintArea) {
+            applyDarkIntensity(
+                    StatusBarIconController.getDarkIntensity(tintArea, mMobile, darkIntensity),
+                    mMobile, mMobileDark);
+            setTint(mMobileType, StatusBarIconController.getTint(tintArea, mMobileType, tint));
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightStatusBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightStatusBarController.java
new file mode 100644
index 0000000..f98b9e5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightStatusBarController.java
@@ -0,0 +1,126 @@
+/*
+ * 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.systemui.statusbar.phone;
+
+import android.graphics.Rect;
+import android.view.View;
+
+import com.android.systemui.statusbar.policy.BatteryController;
+
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
+
+/**
+ * Controls how light status bar flag applies to the icons.
+ */
+public class LightStatusBarController {
+
+    private final StatusBarIconController mIconController;
+    private final BatteryController mBatteryController;
+    private FingerprintUnlockController mFingerprintUnlockController;
+
+    private int mFullscreenStackVisibility;
+    private int mDockedStackVisibility;
+    private boolean mFullscreenLight;
+    private boolean mDockedLight;
+
+    private final Rect mLastFullscreenBounds = new Rect();
+    private final Rect mLastDockedBounds = new Rect();
+
+    public LightStatusBarController(StatusBarIconController iconController,
+            BatteryController batteryController) {
+        mIconController = iconController;
+        mBatteryController = batteryController;
+    }
+
+    public void setFingerprintUnlockController(
+            FingerprintUnlockController fingerprintUnlockController) {
+        mFingerprintUnlockController = fingerprintUnlockController;
+    }
+
+    public void onSystemUiVisibilityChanged(int fullscreenStackVis, int dockedStackVis, int mask,
+            Rect fullscreenStackBounds, Rect dockedStackBounds, boolean sbModeChanged,
+            int statusBarMode) {
+        int oldFullscreen = mFullscreenStackVisibility;
+        int newFullscreen = (oldFullscreen & ~mask) | (fullscreenStackVis & mask);
+        int diffFullscreen = newFullscreen ^ oldFullscreen;
+        int oldDocked = mDockedStackVisibility;
+        int newDocked = (oldDocked & ~mask) | (dockedStackVis & mask);
+        int diffDocked = newDocked ^ oldDocked;
+        if ((diffFullscreen & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0
+                || (diffDocked & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0
+                || sbModeChanged
+                || !mLastFullscreenBounds.equals(fullscreenStackBounds)
+                || !mLastDockedBounds.equals(dockedStackBounds)) {
+
+            mFullscreenLight = isLight(newFullscreen, statusBarMode);
+            mDockedLight = isLight(newDocked, statusBarMode);
+            update(fullscreenStackBounds, dockedStackBounds);
+        }
+        mFullscreenStackVisibility = newFullscreen;
+        mDockedStackVisibility = newDocked;
+        mLastFullscreenBounds.set(fullscreenStackBounds);
+        mLastDockedBounds.set(dockedStackBounds);
+    }
+
+    private boolean isLight(int vis, int statusBarMode) {
+        boolean isTransparentBar = (statusBarMode == MODE_TRANSPARENT
+                || statusBarMode == MODE_LIGHTS_OUT_TRANSPARENT);
+        boolean allowLight = isTransparentBar && !mBatteryController.isPowerSave();
+        boolean light = (vis & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0;
+        return allowLight && light;
+    }
+
+    private boolean animateChange() {
+        if (mFingerprintUnlockController == null) {
+            return false;
+        }
+        int unlockMode = mFingerprintUnlockController.getMode();
+        return unlockMode != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
+                && unlockMode != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK;
+    }
+
+    private void update(Rect fullscreenStackBounds, Rect dockedStackBounds) {
+        boolean hasDockedStack = !dockedStackBounds.isEmpty();
+
+        // If both are light or fullscreen is light and there is no docked stack, all icons get
+        // dark.
+        if ((mFullscreenLight && mDockedLight) || (mFullscreenLight && !hasDockedStack)) {
+            mIconController.setIconsDarkArea(null);
+            mIconController.setIconsDark(true, animateChange());
+
+        }
+
+        // If no one is light or the fullscreen is not light and there is no docked stack,
+        // all icons become white.
+        else if ((!mFullscreenLight && !mDockedLight) || (!mFullscreenLight && !hasDockedStack)) {
+            mIconController.setIconsDark(false, animateChange());
+
+        }
+
+        // Not the same for every stack, magic!
+        else {
+            Rect bounds = mFullscreenLight ? fullscreenStackBounds : dockedStackBounds;
+            if (bounds.isEmpty()) {
+                mIconController.setIconsDarkArea(null);
+            } else {
+                mIconController.setIconsDarkArea(bounds);
+            }
+            mIconController.setIconsDark(true, animateChange());
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 03a597c..6e345f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -4,6 +4,7 @@
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.graphics.Color;
+import android.graphics.Rect;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.ImageView;
@@ -32,6 +33,7 @@
     protected View mNotificationIconArea;
     private IconMerger mNotificationIcons;
     private ImageView mMoreIcon;
+    private final Rect mTintArea = new Rect();
 
     public NotificationIconAreaController(Context context, PhoneStatusBar phoneStatusBar) {
         mPhoneStatusBar = phoneStatusBar;
@@ -67,6 +69,20 @@
     }
 
     /**
+     * See {@link StatusBarIconController#setIconsDarkArea}.
+     *
+     * @param tintArea the area in which to tint the icons, specified in screen coordinates
+     */
+    public void setTintArea(Rect tintArea) {
+        if (tintArea == null) {
+            mTintArea.setEmpty();
+        } else {
+            mTintArea.set(tintArea);
+        }
+        applyNotificationIconsTint();
+    }
+
+    /**
      * Sets the color that should be used to tint any icons in the notification area. If this
      * method is not called, the default tint is {@link Color#WHITE}.
      */
@@ -145,7 +161,8 @@
             boolean isPreL = Boolean.TRUE.equals(v.getTag(R.id.icon_is_pre_L));
             boolean colorize = !isPreL || NotificationUtils.isGrayscale(v, mNotificationColorUtil);
             if (colorize) {
-                v.setImageTintList(ColorStateList.valueOf(mIconTint));
+                v.setImageTintList(ColorStateList.valueOf(
+                        StatusBarIconController.getTint(mTintArea, v, mIconTint)));
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index f382fe0..4ff7154 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -69,6 +69,7 @@
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.Vibrator;
@@ -296,6 +297,7 @@
     BrightnessMirrorController mBrightnessMirrorController;
     AccessibilityController mAccessibilityController;
     FingerprintUnlockController mFingerprintUnlockController;
+    LightStatusBarController mLightStatusBarController;
 
     int mNaturalBarHeight = -1;
 
@@ -361,6 +363,8 @@
 
     // tracking calls to View.setSystemUiVisibility()
     int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE;
+    private final Rect mLastFullscreenStackBounds = new Rect();
+    private final Rect mLastDockedStackBounds = new Rect();
 
     // last value sent to window manager
     private int mLastDispatchedSystemUiVisibility = ~View.SYSTEM_UI_FLAG_VISIBLE;
@@ -754,19 +758,8 @@
         mStackScroller.setOverflowContainer(mKeyguardIconOverflowContainer);
 
 
-        mEmptyShadeView = (EmptyShadeView) LayoutInflater.from(mContext).inflate(
-                R.layout.status_bar_no_notifications, mStackScroller, false);
-        mStackScroller.setEmptyShadeView(mEmptyShadeView);
-        mDismissView = (DismissView) LayoutInflater.from(mContext).inflate(
-                R.layout.status_bar_notification_dismiss_all, mStackScroller, false);
-        mDismissView.setOnButtonClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                MetricsLogger.action(mContext, MetricsEvent.ACTION_DISMISS_ALL_NOTES);
-                clearAllNotifications();
-            }
-        });
-        mStackScroller.setDismissView(mDismissView);
+        inflateEmptyShadeView();
+        inflateDismissView();
         mExpandedContents = mStackScroller;
 
         mBackdrop = (BackDropView) mStatusBarWindow.findViewById(R.id.backdrop);
@@ -855,6 +848,8 @@
         mAccessibilityController = new AccessibilityController(mContext);
         mKeyguardBottomArea.setAccessibilityController(mAccessibilityController);
         mNextAlarmController = new NextAlarmController(mContext);
+        mLightStatusBarController = new LightStatusBarController(mIconController,
+                mBatteryController);
         mKeyguardMonitor = new KeyguardMonitor(mContext);
         if (UserManager.get(mContext).isUserSwitcherEnabled()) {
             mUserSwitcherController = new UserSwitcherController(mContext, mKeyguardMonitor,
@@ -930,6 +925,34 @@
         return mStatusBarView;
     }
 
+    @Override
+    protected void reInflateViews() {
+        super.reInflateViews();
+        inflateDismissView();
+        updateClearAll();
+        inflateEmptyShadeView();
+        updateEmptyShadeView();
+    }
+
+    private void inflateEmptyShadeView() {
+        mEmptyShadeView = (EmptyShadeView) LayoutInflater.from(mContext).inflate(
+                R.layout.status_bar_no_notifications, mStackScroller, false);
+        mStackScroller.setEmptyShadeView(mEmptyShadeView);
+    }
+
+    private void inflateDismissView() {
+        mDismissView = (DismissView) LayoutInflater.from(mContext).inflate(
+                R.layout.status_bar_notification_dismiss_all, mStackScroller, false);
+        mDismissView.setOnButtonClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                MetricsLogger.action(mContext, MetricsEvent.ACTION_DISMISS_ALL_NOTES);
+                clearAllNotifications();
+            }
+        });
+        mStackScroller.setDismissView(mDismissView);
+    }
+
     protected void createUserSwitcher() {
         mKeyguardUserSwitcher = new KeyguardUserSwitcher(mContext,
                 (ViewStub) mStatusBarWindow.findViewById(R.id.keyguard_user_switcher),
@@ -1072,6 +1095,7 @@
         mFingerprintUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
         mRemoteInputController.addCallback(mStatusBarKeyguardViewManager);
         mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback();
+        mLightStatusBarController.setFingerprintUnlockController(mFingerprintUnlockController);
     }
 
     @Override
@@ -2507,7 +2531,8 @@
     }
 
     @Override // CommandQueue
-    public void setSystemUiVisibility(int vis, int mask) {
+    public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
+            int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
         final int oldVal = mSystemUiVisibility;
         final int newVal = (oldVal&~mask) | (vis&mask);
         final int diff = newVal ^ oldVal;
@@ -2516,6 +2541,7 @@
                 Integer.toHexString(vis), Integer.toHexString(mask),
                 Integer.toHexString(oldVal), Integer.toHexString(newVal),
                 Integer.toHexString(diff)));
+        boolean sbModeChanged = false;
         if (diff != 0) {
             // we never set the recents bit via this method, so save the prior state to prevent
             // clobbering the bit below
@@ -2549,7 +2575,7 @@
                     oldVal, newVal, mNavigationBarView.getBarTransitions(),
                     View.NAVIGATION_BAR_TRANSIENT, View.NAVIGATION_BAR_TRANSLUCENT,
                     View.NAVIGATION_BAR_TRANSPARENT);
-            final boolean sbModeChanged = sbMode != -1;
+            sbModeChanged = sbMode != -1;
             final boolean nbModeChanged = nbMode != -1;
             boolean checkBarModes = false;
             if (sbModeChanged && sbMode != mStatusBarMode) {
@@ -2576,18 +2602,6 @@
                 mSystemUiVisibility &= ~View.NAVIGATION_BAR_UNHIDE;
             }
 
-            if ((diff & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0 || sbModeChanged) {
-                boolean isTransparentBar = (mStatusBarMode == MODE_TRANSPARENT
-                        || mStatusBarMode == MODE_LIGHTS_OUT_TRANSPARENT);
-                boolean allowLight = isTransparentBar && !mBatteryController.isPowerSave();
-                boolean light = (vis & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0;
-                boolean animate = mFingerprintUnlockController == null
-                        || (mFingerprintUnlockController.getMode()
-                                != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
-                        && mFingerprintUnlockController.getMode()
-                                != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK);
-                mIconController.setIconsDark(allowLight && light, animate);
-            }
             // restore the recents bit
             if (wasRecentsVisible) {
                 mSystemUiVisibility |= View.RECENT_APPS_VISIBLE;
@@ -2596,6 +2610,9 @@
             // send updated sysui visibility to window manager
             notifyUiVisibilityChanged(mSystemUiVisibility);
         }
+
+        mLightStatusBarController.onSystemUiVisibilityChanged(fullscreenStackVis, dockedStackVis,
+                mask, fullscreenStackBounds, dockedStackBounds, sbModeChanged, mStatusBarMode);
     }
 
     private int computeBarMode(int oldVis, int newVis, BarTransitions transitions,
@@ -2723,9 +2740,12 @@
     public void setLightsOn(boolean on) {
         Log.v(TAG, "setLightsOn(" + on + ")");
         if (on) {
-            setSystemUiVisibility(0, View.SYSTEM_UI_FLAG_LOW_PROFILE);
+            setSystemUiVisibility(0, 0, 0, View.SYSTEM_UI_FLAG_LOW_PROFILE,
+                    mLastFullscreenStackBounds, mLastDockedStackBounds);
         } else {
-            setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE, View.SYSTEM_UI_FLAG_LOW_PROFILE);
+            setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE, 0, 0,
+                    View.SYSTEM_UI_FLAG_LOW_PROFILE, mLastFullscreenStackBounds,
+                    mLastDockedStackBounds);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index 45aae2d..f61f31e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -22,6 +22,7 @@
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.graphics.Color;
+import android.graphics.Rect;
 import android.graphics.drawable.Icon;
 import android.os.Bundle;
 import android.os.Handler;
@@ -58,8 +59,8 @@
 public class StatusBarIconController extends StatusBarIconList implements Tunable {
 
     public static final long DEFAULT_TINT_ANIMATION_DURATION = 120;
-
     public static final String ICON_BLACKLIST = "icon_blacklist";
+    public static final int DEFAULT_ICON_TINT = Color.WHITE;
 
     private Context mContext;
     private PhoneStatusBar mPhoneStatusBar;
@@ -79,8 +80,11 @@
     private int mIconSize;
     private int mIconHPadding;
 
-    private int mIconTint = Color.WHITE;
+    private int mIconTint = DEFAULT_ICON_TINT;
     private float mDarkIntensity;
+    private final Rect mTintArea = new Rect();
+    private static final Rect sTmpRect = new Rect();
+    private static final int[] sTmpInt2 = new int[2];
 
     private boolean mTransitionPending;
     private boolean mTintChangePending;
@@ -395,6 +399,25 @@
         }
     }
 
+    /**
+     * Sets the dark area so {@link #setIconsDark} only affects the icons in the specified area.
+     *
+     * @param darkArea the area in which icons should change it's tint, in logical screen
+     *                 coordinates
+     */
+    public void setIconsDarkArea(Rect darkArea) {
+        if (darkArea == null && mTintArea.isEmpty()) {
+            return;
+        }
+        if (darkArea == null) {
+            mTintArea.setEmpty();
+        } else {
+            mTintArea.set(darkArea);
+        }
+        applyIconTint();
+        mNotificationIconAreaController.setTintArea(darkArea);
+    }
+
     public void setIconsDark(boolean dark, boolean animate) {
         if (!animate) {
             setIconTintInternal(dark ? 1.0f : 0.0f);
@@ -446,14 +469,60 @@
         mPendingDarkIntensity = darkIntensity;
     }
 
+    /**
+     * @return the tint to apply to {@param view} depending on the desired tint {@param color} and
+     *         the screen {@param tintArea} in which to apply that tint
+     */
+    public static int getTint(Rect tintArea, View view, int color) {
+        if (isInArea(tintArea, view)) {
+            return color;
+        } else {
+            return DEFAULT_ICON_TINT;
+        }
+    }
+
+    /**
+     * @return the dark intensity to apply to {@param view} depending on the desired dark
+     *         {@param intensity} and the screen {@param tintArea} in which to apply that intensity
+     */
+    public static float getDarkIntensity(Rect tintArea, View view, float intensity) {
+        if (isInArea(tintArea, view)) {
+            return intensity;
+        } else {
+            return 0f;
+        }
+    }
+
+    /**
+     * @return true if more than half of the {@param view} area are in {@param area}, false
+     *         otherwise
+     */
+    private static boolean isInArea(Rect area, View view) {
+        if (area.isEmpty()) {
+            return true;
+        }
+        sTmpRect.set(area);
+        view.getLocationOnScreen(sTmpInt2);
+        int left = sTmpInt2[0];
+
+        int intersectStart = Math.max(left, area.left);
+        int intersectEnd = Math.min(left + view.getWidth(), area.right);
+        int intersectAmount = Math.max(0, intersectEnd - intersectStart);
+
+        boolean coversFullStatusBar = area.top <= 0;
+        boolean majorityOfWidth = 2 * intersectAmount > view.getWidth();
+        return majorityOfWidth && coversFullStatusBar;
+    }
+
     private void applyIconTint() {
         for (int i = 0; i < mStatusIcons.getChildCount(); i++) {
             StatusBarIconView v = (StatusBarIconView) mStatusIcons.getChildAt(i);
-            v.setImageTintList(ColorStateList.valueOf(mIconTint));
+            v.setImageTintList(ColorStateList.valueOf(getTint(mTintArea, v, mIconTint)));
         }
-        mSignalCluster.setIconTint(mIconTint, mDarkIntensity);
-        mBatteryMeterView.setDarkIntensity(mDarkIntensity);
-        mClock.setTextColor(mIconTint);
+        mSignalCluster.setIconTint(mIconTint, mDarkIntensity, mTintArea);
+        mBatteryMeterView.setDarkIntensity(
+                isInArea(mTintArea, mBatteryMeterView) ? mDarkIntensity : 0);
+        mClock.setTextColor(getTint(mTintArea, mClock, mIconTint));
     }
 
     public void appTransitionPending() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
index 5cfcd89..49aec42 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -43,16 +43,16 @@
     private static final int NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED = 5;
     private static final int NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED = 8;
 
-    private final int mChildPadding;
-    private final int mDividerHeight;
-    private final int mMaxNotificationHeight;
     private final List<View> mDividers = new ArrayList<>();
     private final List<ExpandableNotificationRow> mChildren = new ArrayList<>();
-    private final int mNotificationHeaderHeight;
-    private final int mNotificationAppearDistance;
-    private final int mNotificatonTopPadding;
     private final HybridNotificationViewManager mHybridViewManager;
-    private final float mCollapsedBottompadding;
+    private int mChildPadding;
+    private int mDividerHeight;
+    private int mMaxNotificationHeight;
+    private int mNotificationHeaderHeight;
+    private int mNotificationAppearDistance;
+    private int mNotificatonTopPadding;
+    private float mCollapsedBottompadding;
     private ViewInvertHelper mOverflowInvertHelper;
     private boolean mChildrenExpanded;
     private ExpandableNotificationRow mNotificationParent;
@@ -76,6 +76,11 @@
     public NotificationChildrenContainer(Context context, AttributeSet attrs, int defStyleAttr,
             int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
+        initDimens();
+        mHybridViewManager = new HybridNotificationViewManager(getContext(), this);
+    }
+
+    private void initDimens() {
         mChildPadding = getResources().getDimensionPixelSize(
                 R.dimen.notification_children_padding);
         mDividerHeight = Math.max(1, getResources().getDimensionPixelSize(
@@ -89,7 +94,6 @@
         mNotificatonTopPadding = getResources().getDimensionPixelSize(
                 R.dimen.notification_children_container_top_padding);
         mCollapsedBottompadding = 11.5f * getResources().getDisplayMetrics().density;
-        mHybridViewManager = new HybridNotificationViewManager(getContext(), this);
     }
 
     @Override
@@ -461,4 +465,16 @@
             mOverflowInvertHelper.setInverted(dark, fade, delay);
         }
     }
+
+    public void reInflateViews() {
+        initDimens();
+        for (int i = 0; i < mDividers.size(); i++) {
+            View prevDivider = mDividers.get(i);
+            int index = indexOfChild(prevDivider);
+            removeView(prevDivider);
+            View divider = inflateDivider();
+            addView(divider, index);
+            mDividers.set(i, divider);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index a75ac82..bf4245b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -2138,7 +2138,7 @@
     }
 
     private void updateAnimationState(View child) {
-        updateAnimationState((mAnimationsEnabled || isPinnedHeadsUp(child)) && mIsExpanded, child);
+        updateAnimationState(mAnimationsEnabled && (mIsExpanded || isPinnedHeadsUp(child)), child);
     }
 
 
@@ -2874,13 +2874,23 @@
     }
 
     public void setDismissView(DismissView dismissView) {
+        int index = -1;
+        if (mDismissView != null) {
+            index = indexOfChild(mDismissView);
+            removeView(mDismissView);
+        }
         mDismissView = dismissView;
-        addView(mDismissView);
+        addView(mDismissView, index);
     }
 
     public void setEmptyShadeView(EmptyShadeView emptyShadeView) {
+        int index = -1;
+        if (mEmptyShadeView != null) {
+            index = indexOfChild(mEmptyShadeView);
+            removeView(mEmptyShadeView);
+        }
         mEmptyShadeView = emptyShadeView;
-        addView(mEmptyShadeView);
+        addView(mEmptyShadeView, index);
     }
 
     public void updateEmptyShadeView(boolean visible) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index 784f610..110258c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.tv;
 
 import android.content.ComponentName;
+import android.graphics.Rect;
 import android.os.IBinder;
 import android.service.notification.NotificationListenerService.RankingMap;
 import android.service.notification.StatusBarNotification;
@@ -68,7 +69,8 @@
     }
 
     @Override
-    public void setSystemUiVisibility(int vis, int mask) {
+    public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
+            int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
     }
 
     @Override
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 830b0f2..f8bf59d2 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -87,6 +87,7 @@
 import android.util.AtomicFile;
 import android.util.EventLog;
 import android.util.Log;
+import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.StringBuilderPrinter;
@@ -142,6 +143,7 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
 import java.util.zip.Deflater;
 import java.util.zip.DeflaterOutputStream;
 import java.util.zip.InflaterInputStream;
@@ -786,8 +788,9 @@
             case MSG_OP_COMPLETE:
             {
                 try {
-                    BackupRestoreTask task = (BackupRestoreTask) msg.obj;
-                    task.operationComplete(msg.arg1);
+                    Pair<BackupRestoreTask, Long> taskWithResult =
+                            (Pair<BackupRestoreTask, Long>) msg.obj;
+                    taskWithResult.first.operationComplete(taskWithResult.second);
                 } catch (ClassCastException e) {
                     Slog.e(TAG, "Invalid completion in flight, obj=" + msg.obj);
                 }
@@ -1044,7 +1047,7 @@
 
         // If Encrypted file systems is enabled or disabled, this call will return the
         // correct directory.
-        mBaseStateDir = new File(Environment.getSecureDataDirectory(), "backup");
+        mBaseStateDir = new File(Environment.getDataDirectory(), "backup");
         mBaseStateDir.mkdirs();
         if (!SELinux.restorecon(mBaseStateDir)) {
             Slog.e(TAG, "SELinux restorecon failed on " + mBaseStateDir);
@@ -2460,7 +2463,7 @@
         void execute();
 
         // An operation that wanted a callback has completed
-        void operationComplete(int result);
+        void operationComplete(long result);
 
         // An operation that wanted a callback has timed out
         void handleTimeout();
@@ -3095,7 +3098,7 @@
         }
 
         @Override
-        public void operationComplete(int unusedResult) {
+        public void operationComplete(long unusedResult) {
             // The agent reported back to us!
 
             if (mBackupData == null) {
@@ -3494,7 +3497,7 @@
          */
         int preflightFullBackup(PackageInfo pkg, IBackupAgent agent);
 
-        long expectedSize();
+        long getExpectedSizeOrErrorCode();
     };
 
     class FullBackupEngine {
@@ -4532,7 +4535,7 @@
         // a standalone thread.  The  runner owns this half of the pipe, and closes
         // it to indicate EOD to the other end.
         class SinglePackageBackupPreflight implements BackupRestoreTask, FullBackupPreflight {
-            final AtomicInteger mResult = new AtomicInteger();
+            final AtomicLong mResult = new AtomicLong();
             final CountDownLatch mLatch = new CountDownLatch(1);
             final IBackupTransport mTransport;
 
@@ -4554,7 +4557,11 @@
 
                     // now wait to get our result back
                     mLatch.await();
-                    int totalSize = mResult.get();
+                    long totalSize = mResult.get();
+                    // If preflight timeouted, mResult will contain error code as int.
+                    if (totalSize < 0) {
+                        return (int) totalSize;
+                    }
                     if (MORE_DEBUG) {
                         Slog.v(TAG, "Got preflight response; size=" + totalSize);
                     }
@@ -4581,7 +4588,7 @@
             }
 
             @Override
-            public void operationComplete(int result) {
+            public void operationComplete(long result) {
                 // got the callback, and our preflightFullBackup() method is waiting for the result
                 if (MORE_DEBUG) {
                     Slog.i(TAG, "Preflight op complete, result=" + result);
@@ -4600,7 +4607,7 @@
             }
 
             @Override
-            public long expectedSize() {
+            public long getExpectedSizeOrErrorCode() {
                 try {
                     mLatch.await();
                     return mResult.get();
@@ -4649,7 +4656,7 @@
             }
 
             long expectedSize() {
-                return mPreflight.expectedSize();
+                return mPreflight.getExpectedSizeOrErrorCode();
             }
         }
     }
@@ -8558,7 +8565,7 @@
         }
 
         @Override
-        public void operationComplete(int unusedResult) {
+        public void operationComplete(long unusedResult) {
             if (MORE_DEBUG) {
                 Slog.i(TAG, "operationComplete() during restore: target="
                         + mCurrentPackage.packageName
@@ -9643,9 +9650,8 @@
 
         // The completion callback, if any, is invoked on the handler
         if (op != null && op.callback != null) {
-            Message msg = mBackupHandler.obtainMessage(MSG_OP_COMPLETE, op.callback);
-            // NB: this cannot distinguish between results > 2 gig
-            msg.arg1 = (result > Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) result;
+            Pair<BackupRestoreTask, Long> callbackAndResult = Pair.create(op.callback, result);
+            Message msg = mBackupHandler.obtainMessage(MSG_OP_COMPLETE, callbackAndResult);
             mBackupHandler.sendMessage(msg);
         }
     }
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index bbf881b..e745263 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -54,7 +54,7 @@
 
     public Trampoline(Context context) {
         mContext = context;
-        File dir = new File(Environment.getSecureDataDirectory(), "backup");
+        File dir = new File(Environment.getDataDirectory(), "backup");
         dir.mkdirs();
         mSuppressFile = new File(dir, BACKUP_SUPPRESS_FILENAME);
         mGlobalDisable = SystemProperties.getBoolean(BACKUP_DISABLE_PROPERTY, false);
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index a4455e9..07d472d 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -16,6 +16,8 @@
 
 package com.android.server;
 
+import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED;
+
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
@@ -882,6 +884,11 @@
 
     @Override
     public int checkAudioOperation(int code, int usage, int uid, String packageName) {
+        if (isApplicationSuspended(packageName, uid)) {
+            Log.i(TAG, "Audio disabled for suspended package=" + packageName + " for uid=" + uid);
+            return AppOpsManager.MODE_IGNORED;
+        }
+
         synchronized (this) {
             final int mode = checkRestrictionLocked(code, usage, uid, packageName);
             if (mode != AppOpsManager.MODE_ALLOWED) {
@@ -891,6 +898,23 @@
         return checkOperation(code, uid, packageName);
     }
 
+    private boolean isApplicationSuspended(String pkg, int uid) {
+        int userId = UserHandle.getUserId(uid);
+
+        ApplicationInfo ai;
+        try {
+            ai = AppGlobals.getPackageManager().getApplicationInfo(pkg, 0, userId);
+            if (ai == null) {
+                Log.w(TAG, "No application info for package " + pkg + " and user " + userId);
+                return false;
+            }
+        } catch (RemoteException re) {
+            throw new SecurityException("Could not talk to package manager service");
+        }
+
+        return ((ai.flags & FLAG_SUSPENDED) != 0);
+    }
+
     private int checkRestrictionLocked(int code, int usage, int uid, String packageName) {
         final SparseArray<Restriction> usageRestrictions = mAudioRestrictions.get(code);
         if (usageRestrictions != null) {
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index 4dbb490..c318140 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -17,6 +17,7 @@
 package com.android.server;
 
 import android.app.ActivityManagerNative;
+import android.app.KeyguardManager;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
@@ -34,6 +35,7 @@
 import android.content.res.Resources;
 
 import static android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE;
+import static android.content.Context.KEYGUARD_SERVICE;
 import static android.content.Context.USER_SERVICE;
 import static android.Manifest.permission.READ_CONTACTS;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
@@ -124,7 +126,7 @@
         @Override
         public void onBootPhase(int phase) {
             if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
-                mLockSettingsService.maybeShowEncryptionNotification(UserHandle.ALL);
+                mLockSettingsService.maybeShowEncryptionNotifications();
             } else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
                 // TODO
             }
@@ -176,22 +178,48 @@
      * If the account is credential-encrypted, show notification requesting the user to unlock
      * the device.
      */
-    private void maybeShowEncryptionNotification(UserHandle userHandle) {
-        if (UserHandle.ALL.equals(userHandle)) {
-            final List<UserInfo> users = mUserManager.getUsers();
-            for (int i = 0; i < users.size(); i++) {
-                UserHandle user = users.get(i).getUserHandle();
-                if (!mUserManager.isUserUnlocked(user)) {
-                    showEncryptionNotification(user);
+    private void maybeShowEncryptionNotifications() {
+        final List<UserInfo> users = mUserManager.getUsers();
+        for (int i = 0; i < users.size(); i++) {
+            UserInfo user = users.get(i);
+            UserHandle userHandle = user.getUserHandle();
+            if (!mUserManager.isUserUnlocked(userHandle)) {
+                if (!user.isManagedProfile()) {
+                    showEncryptionNotification(userHandle);
+                } else {
+                    UserInfo parent = mUserManager.getProfileParent(user.id);
+                    if (parent != null && mUserManager.isUserUnlocked(parent.getUserHandle())) {
+                        // Only show notifications for managed profiles once their parent
+                        // user is unlocked.
+                        showEncryptionNotificationForProfile(userHandle);
+                    }
                 }
             }
-        } else if (!mUserManager.isUserUnlocked(userHandle)){
-            showEncryptionNotification(userHandle);
         }
     }
 
+    private void showEncryptionNotificationForProfile(UserHandle user) {
+        Resources r = mContext.getResources();
+        CharSequence title = r.getText(
+                com.android.internal.R.string.user_encrypted_title);
+        CharSequence message = r.getText(
+                com.android.internal.R.string.profile_encrypted_message);
+        CharSequence detail = r.getText(
+                com.android.internal.R.string.profile_encrypted_detail);
+
+        final KeyguardManager km = (KeyguardManager) mContext.getSystemService(KEYGUARD_SERVICE);
+        final Intent unlockIntent = km.createConfirmDeviceCredentialIntent(null, null, user.getIdentifier());
+        if (unlockIntent == null) {
+            return;
+        }
+        unlockIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+        PendingIntent intent = PendingIntent.getActivity(mContext, 0, unlockIntent,
+                PendingIntent.FLAG_UPDATE_CURRENT);
+
+        showEncryptionNotification(user, title, message, detail, intent);
+    }
+
     private void showEncryptionNotification(UserHandle user) {
-        if (DEBUG) Slog.v(TAG, "showing encryption notification, user: " + user.getIdentifier());
         Resources r = mContext.getResources();
         CharSequence title = r.getText(
                 com.android.internal.R.string.user_encrypted_title);
@@ -203,6 +231,12 @@
         PendingIntent intent = PendingIntent.getBroadcast(mContext, 0, ACTION_NULL,
                 PendingIntent.FLAG_UPDATE_CURRENT);
 
+        showEncryptionNotification(user, title, message, detail, intent);
+    }
+
+    private void showEncryptionNotification(UserHandle user, CharSequence title, CharSequence message,
+            CharSequence detail, PendingIntent intent) {
+        if (DEBUG) Slog.v(TAG, "showing encryption notification, user: " + user.getIdentifier());
         Notification notification = new Notification.Builder(mContext)
                 .setSmallIcon(com.android.internal.R.drawable.ic_user_secure)
                 .setWhen(0)
@@ -230,8 +264,21 @@
         hideEncryptionNotification(new UserHandle(userId));
     }
 
-    public void onUnlockUser(int userHandle) {
-        hideEncryptionNotification(new UserHandle(userHandle));
+    public void onUnlockUser(int userId) {
+        hideEncryptionNotification(new UserHandle(userId));
+
+        // Now we have unlocked the parent user we should show notifications
+        // about any profiles that exist.
+        List<UserInfo> profiles = mUserManager.getProfiles(userId);
+        for (int i = 0; i < profiles.size(); i++) {
+            UserInfo profile = profiles.get(i);
+            if (profile.isManagedProfile()) {
+                UserHandle userHandle = profile.getUserHandle();
+                if (!mUserManager.isUserUnlocked(userHandle)) {
+                    showEncryptionNotificationForProfile(userHandle);
+                }
+            }
+        }
     }
 
     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index cbd477a..d09edc8 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -1473,7 +1473,7 @@
         }
 
         mSettingsFile = new AtomicFile(
-                new File(Environment.getSystemSecureDirectory(), "storage.xml"));
+                new File(Environment.getDataSystemDirectory(), "storage.xml"));
 
         synchronized (mLock) {
             readSettingsLocked();
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 95f5734..799d0bd 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -2086,7 +2086,7 @@
             final int oldUidFirewallRule = uidFirewallRules.get(uid, FIREWALL_RULE_DEFAULT);
             if (DBG) {
                 Slog.d(TAG, "oldRule = " + oldUidFirewallRule
-                        + ", newRule=" + rule + " for uid=" + uid);
+                        + ", newRule=" + rule + " for uid=" + uid + " on chain " + chain);
             }
             if (oldUidFirewallRule == rule) {
                 if (DBG) Slog.d(TAG, "!!!!! Skipping change");
diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java
index b984e19..879bb6f 100644
--- a/services/core/java/com/android/server/NetworkScoreService.java
+++ b/services/core/java/com/android/server/NetworkScoreService.java
@@ -234,12 +234,24 @@
             // safety as scores should never be compared across apps; in practice, Settings should
             // only be allowing valid apps to be set as scorers, so failure here should be rare.
             clearInternal();
+            // Get the scorer that is about to be replaced, if any, so we can notify it directly.
+            NetworkScorerAppData prevScorer = NetworkScorerAppManager.getActiveScorer(mContext);
             boolean result = NetworkScorerAppManager.setActiveScorer(mContext, packageName);
-            if (result) {
+            if (result) { // new scorer successfully set
                 registerPackageReceiverIfNeeded();
                 Intent intent = new Intent(NetworkScoreManager.ACTION_SCORER_CHANGED);
-                intent.putExtra(NetworkScoreManager.EXTRA_NEW_SCORER, packageName);
-                mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+                if (prevScorer != null) { // Directly notify the old scorer.
+                    intent.setPackage(prevScorer.mPackageName);
+                    // TODO: Need to update when we support per-user scorers. http://b/23422763
+                    mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
+                }
+
+                if (packageName != null) { // Then notify the new scorer
+                    intent.putExtra(NetworkScoreManager.EXTRA_NEW_SCORER, packageName);
+                    intent.setPackage(packageName);
+                    // TODO: Need to update when we support per-user scorers. http://b/23422763
+                    mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
+                }
             }
             return result;
         } finally {
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 2683be6..ca1e371 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -3809,7 +3809,7 @@
     }
 
     private static String getDatabaseName(int userId) {
-        File systemDir = Environment.getSystemSecureDirectory();
+        File systemDir = Environment.getDataSystemDirectory();
         File databaseFile = new File(Environment.getUserSystemDirectory(userId), DATABASE_NAME);
         if (userId == 0) {
             // Migrate old file, if it exists, to the new location.
diff --git a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
index bef6f0a..1b9d968 100644
--- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
+++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
@@ -62,7 +62,7 @@
     static final boolean DEBUG_LRU = DEBUG_ALL || false;
     static final boolean DEBUG_MU = DEBUG_ALL || false;
     static final boolean DEBUG_OOM_ADJ = DEBUG_ALL || false;
-    static final boolean DEBUG_PAUSE = DEBUG_ALL || true;
+    static final boolean DEBUG_PAUSE = DEBUG_ALL || false;
     static final boolean DEBUG_POWER = DEBUG_ALL || false;
     static final boolean DEBUG_POWER_QUICK = DEBUG_POWER || false;
     static final boolean DEBUG_PROCESS_OBSERVERS = DEBUG_ALL || false;
@@ -85,7 +85,7 @@
     static final boolean DEBUG_UID_OBSERVERS = DEBUG_ALL || false;
     static final boolean DEBUG_URI_PERMISSION = DEBUG_ALL || false;
     static final boolean DEBUG_USER_LEAVING = DEBUG_ALL || false;
-    static final boolean DEBUG_VISIBILITY = DEBUG_ALL || true;
+    static final boolean DEBUG_VISIBILITY = DEBUG_ALL || false;
     static final boolean DEBUG_VISIBLE_BEHIND = DEBUG_ALL_ACTIVITIES || false;
     static final boolean DEBUG_USAGE_STATS = DEBUG_ALL || false;
     static final boolean DEBUG_PERMISSIONS_REVIEW = DEBUG_ALL || false;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 104217a..5061901 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2851,31 +2851,39 @@
 
     @Override
     public void setFocusedStack(int stackId) {
+        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "setFocusedStack()");
         if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "setFocusedStack: stackId=" + stackId);
-        synchronized (ActivityManagerService.this) {
-            ActivityStack stack = mStackSupervisor.getStack(stackId);
-            if (stack != null) {
-                ActivityRecord r = stack.topRunningActivityLocked();
-                if (r != null) {
-                    setFocusedActivityLocked(r, "setFocusedStack");
+        final long callingId = Binder.clearCallingIdentity();
+        try {
+            synchronized (this) {
+                final ActivityStack stack = mStackSupervisor.getStack(stackId);
+                if (stack == null) {
+                    return;
+                }
+                final ActivityRecord r = stack.topRunningActivityLocked();
+                if (setFocusedActivityLocked(r, "setFocusedStack")) {
                     mStackSupervisor.resumeFocusedStackTopActivityLocked();
                 }
             }
+        } finally {
+            Binder.restoreCallingIdentity(callingId);
         }
     }
 
     @Override
     public void setFocusedTask(int taskId) {
+        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "setFocusedTask()");
         if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "setFocusedTask: taskId=" + taskId);
-        long callingId = Binder.clearCallingIdentity();
+        final long callingId = Binder.clearCallingIdentity();
         try {
-            synchronized (ActivityManagerService.this) {
-                TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
-                if (task != null) {
-                    final ActivityRecord r = task.topRunningActivityLocked();
-                    if (setFocusedActivityLocked(r, "setFocusedTask")) {
-                        mStackSupervisor.resumeFocusedStackTopActivityLocked();
-                    }
+            synchronized (this) {
+                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+                if (task == null) {
+                    return;
+                }
+                final ActivityRecord r = task.topRunningActivityLocked();
+                if (setFocusedActivityLocked(r, "setFocusedTask")) {
+                    mStackSupervisor.resumeFocusedStackTopActivityLocked();
                 }
             }
         } finally {
@@ -20794,7 +20802,7 @@
             pkgUid = pm.getPackageUid(packageName, MATCH_DEBUG_TRIAGED_MISSING, userId);
         } catch (RemoteException e) {
         }
-        if (pkgUid == -1) {
+        if (userId != UserHandle.USER_ALL && pkgUid == -1) {
             throw new IllegalArgumentException(
                     "Cannot kill dependents of non-existing package " + packageName);
         }
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 75dabc96..1103ea4 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -768,7 +768,6 @@
         r.state = ActivityState.RESUMED;
         if (DEBUG_STATES) Slog.v(TAG_STATES,
                 "Moving to RESUMED: " + r + " (starting new instance)");
-        r.stopped = false;
         mResumedActivity = r;
         r.task.touchActiveTime();
         mRecentTasks.addLocked(r.task);
@@ -1228,9 +1227,11 @@
      * this function updates the rest of our state to match that fact.
      */
     private void completeResumeLocked(ActivityRecord next) {
+        next.visible = true;
         next.idle = false;
         next.results = null;
         next.newIntents = null;
+        next.stopped = false;
 
         if (next.isHomeActivity()) {
             ProcessRecord app = next.task.mActivities.get(0).app;
@@ -1729,6 +1730,8 @@
 
         // This activity is not currently visible, but is running. Tell it to become visible.
         if (r.state == ActivityState.RESUMED || r == starting) {
+            Slog.d(TAG_VISIBILITY, "Not making visible, r=" + r + " state=" + r.state
+                    + " starting=" + starting);
             return;
         }
 
@@ -2291,7 +2294,6 @@
             // From this point on, if something goes wrong there is no way
             // to recover the activity.
             try {
-                next.visible = true;
                 completeResumeLocked(next);
             } catch (Exception e) {
                 // If any exception gets thrown, toss away this
@@ -2302,8 +2304,6 @@
                 if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
                 return true;
             }
-            next.stopped = false;
-
         } else {
             // Whoops, need to restart this activity!
             if (!next.hasBeenLaunched) {
@@ -4283,6 +4283,12 @@
                 // "restart!".
                 if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
                         "Config is relaunching resumed " + r);
+
+                if (DEBUG_STATES && !r.visible) {
+                    Slog.v(TAG_STATES, "Config is relaunching resumed invisible activity " + r
+                            + " called by " + Debug.getCallers(4));
+                }
+
                 relaunchActivityLocked(r, r.configChangeFlags, true, preserveWindow);
             } else {
                 if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
@@ -4433,9 +4439,21 @@
         }
 
         if (andResume) {
-            r.results = null;
-            r.newIntents = null;
+            if (DEBUG_STATES) {
+                Slog.d(TAG_STATES, "Resumed after relaunch " + r);
+            }
             r.state = ActivityState.RESUMED;
+            // Relaunch-resume could happen either when the app is already in the front,
+            // or while it's being brought to front. In the latter case, it's marked RESUMED
+            // but not yet visible (or stopped). We need to complete the resume here as the
+            // code in resumeTopActivityInnerLocked to complete the resume might be skipped.
+            if (!r.visible || r.stopped) {
+                mWindowManager.setAppVisibility(r.appToken, true);
+                completeResumeLocked(r);
+            } else {
+                r.results = null;
+                r.newIntents = null;
+            }
         } else {
             mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
             r.state = ActivityState.PAUSED;
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 26108a3..93a36eb 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1046,10 +1046,14 @@
     }
 
     ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId) {
+        return resolveIntent(intent, resolvedType, userId, 0);
+    }
+
+    ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId, int flags) {
         try {
             return AppGlobals.getPackageManager().resolveIntent(intent, resolvedType,
-                            PackageManager.MATCH_DEFAULT_ONLY
-                            | ActivityManagerService.STOCK_PM_FLAGS, userId);
+                    PackageManager.MATCH_DEFAULT_ONLY | flags
+                    | ActivityManagerService.STOCK_PM_FLAGS, userId);
         } catch (RemoteException e) {
         }
         return null;
@@ -1938,9 +1942,6 @@
         }
         final boolean updated = stack.ensureActivityConfigurationLocked(r, 0,
                 preserveWindows);
-        // And we need to make sure at this point that all other activities
-        // are made visible with the correct configuration.
-        ensureActivitiesVisibleLocked(r, 0, preserveWindows);
         if (!updated) {
             resumeFocusedStackTopActivityLocked();
         }
diff --git a/services/core/java/com/android/server/am/ActivityStartInterceptor.java b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
index 1ed749f..9b2bca0 100644
--- a/services/core/java/com/android/server/am/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
@@ -1,3 +1,19 @@
+/*
+ * 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.am;
 
 import static android.app.ActivityManager.INTENT_SENDER_ACTIVITY;
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 28be456..cca6fc5 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -1,3 +1,19 @@
+/*
+ * 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.am;
 
 import static android.app.Activity.RESULT_CANCELED;
@@ -74,7 +90,9 @@
 import android.content.IntentSender;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.content.pm.UserInfo;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Binder;
@@ -84,6 +102,7 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.service.voice.IVoiceInteractionSession;
 import android.util.EventLog;
 import android.util.Slog;
@@ -582,6 +601,22 @@
         intent = new Intent(intent);
 
         ResolveInfo rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId);
+        if (rInfo == null) {
+            UserInfo userInfo = mSupervisor.getUserInfo(userId);
+            if (userInfo != null && userInfo.isManagedProfile()) {
+                // Special case for managed profiles, if attempting to launch non-cryto aware
+                // app in a locked managed profile from an unlocked parent allow it to resolve
+                // as user will be sent via confirm credentials to unlock the profile.
+                UserManager userManager = UserManager.get(mService.mContext);
+                UserInfo parent = userManager.getProfileParent(userId);
+                if (parent != null
+                        && userManager.isUserUnlocked(parent.getUserHandle())
+                        && !userManager.isUserUnlocked(userInfo.getUserHandle())) {
+                    rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId,
+                            PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE);
+                }
+            }
+        }
         // Collect information about the target of the Intent.
         ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);
 
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 98a7ead..16fd909 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -70,6 +70,7 @@
 import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
 import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_CROP_WINDOWS;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
 import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
@@ -122,9 +123,6 @@
     private static final String ATTR_TASK_AFFILIATION_COLOR = "task_affiliation_color";
     private static final String ATTR_CALLING_UID = "calling_uid";
     private static final String ATTR_CALLING_PACKAGE = "calling_package";
-    // TODO(b/26847884): Currently needed while migrating to resize_mode.
-    // Can be removed at some later point.
-    private static final String ATTR_RESIZEABLE = "resizeable";
     private static final String ATTR_RESIZE_MODE = "resize_mode";
     private static final String ATTR_PRIVILEGED = "privileged";
     private static final String ATTR_NON_FULLSCREEN_BOUNDS = "non_fullscreen_bounds";
@@ -1011,6 +1009,7 @@
             String label = null;
             String iconFilename = null;
             int colorPrimary = 0;
+            int colorBackground = 0;
             for (--activityNdx; activityNdx >= 0; --activityNdx) {
                 final ActivityRecord r = mActivities.get(activityNdx);
                 if (r.taskDescription != null) {
@@ -1023,9 +1022,13 @@
                     if (colorPrimary == 0) {
                         colorPrimary = r.taskDescription.getPrimaryColor();
                     }
+                    if (colorBackground == 0) {
+                        colorBackground = r.taskDescription.getBackgroundColor();
+                    }
                 }
             }
-            lastTaskDescription = new TaskDescription(label, colorPrimary, iconFilename);
+            lastTaskDescription = new TaskDescription(label, null, iconFilename, colorPrimary,
+                    colorBackground);
             // Update the task affiliation color if we are the parent of the group
             if (taskId == mAffiliatedTaskId) {
                 mAffiliatedTaskColor = lastTaskDescription.getPrimaryColor();
@@ -1169,7 +1172,7 @@
         int nextTaskId = INVALID_TASK_ID;
         int callingUid = -1;
         String callingPackage = "";
-        int resizeMode = RESIZE_MODE_UNRESIZEABLE;
+        int resizeMode = RESIZE_MODE_FORCE_RESIZEABLE;
         boolean privileged = false;
         Rect bounds = null;
 
@@ -1231,11 +1234,10 @@
                 callingUid = Integer.valueOf(attrValue);
             } else if (ATTR_CALLING_PACKAGE.equals(attrName)) {
                 callingPackage = attrValue;
-            } else if (ATTR_RESIZEABLE.equals(attrName)) {
-                resizeMode = Boolean.valueOf(attrValue)
-                        ? RESIZE_MODE_RESIZEABLE : RESIZE_MODE_CROP_WINDOWS;
             } else if (ATTR_RESIZE_MODE.equals(attrName)) {
                 resizeMode = Integer.valueOf(attrValue);
+                resizeMode = (resizeMode == RESIZE_MODE_CROP_WINDOWS)
+                        ? RESIZE_MODE_FORCE_RESIZEABLE : resizeMode;
             } else if (ATTR_PRIVILEGED.equals(attrName)) {
                 privileged = Boolean.valueOf(attrValue);
             } else if (ATTR_NON_FULLSCREEN_BOUNDS.equals(attrName)) {
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index a355fa4..8e860fa 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -165,15 +165,15 @@
         void register(ContentResolver resolver) {
             resolver.registerContentObserver(mUserSetupComplete, false, this, UserHandle.USER_ALL);
             synchronized (mService) {
-                updateCurrentUserSetupCompleteLocked();
+                updateUserSetupCompleteLocked(UserHandle.USER_ALL);
             }
         }
 
         @Override
-        public void onChange(boolean selfChange, Uri uri) {
+        public void onChange(boolean selfChange, Uri uri, int userId) {
             if (mUserSetupComplete.equals(uri)) {
                 synchronized (mService) {
-                    updateCurrentUserSetupCompleteLocked();
+                    updateUserSetupCompleteLocked(userId);
                 }
             }
         }
@@ -297,6 +297,22 @@
                         null, null, AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
                         userId);
 
+                if (getUserInfo(userId).isManagedProfile()) {
+                    UserInfo parent = getUserManager().getProfileParent(userId);
+                    if (parent != null) {
+                        final Intent profileUnlockedIntent = new Intent(
+                                Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
+                        unlockedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId));
+                        unlockedIntent.addFlags(
+                                Intent.FLAG_RECEIVER_REGISTERED_ONLY
+                                | Intent.FLAG_RECEIVER_FOREGROUND);
+                        mService.broadcastIntentLocked(null, null, profileUnlockedIntent,
+                                null, null, 0, null, null, null, AppOpsManager.OP_NONE,
+                                null, false, false, MY_PID, SYSTEM_UID,
+                                parent.id);
+                    }
+                }
+
                 final Intent bootIntent = new Intent(Intent.ACTION_BOOT_COMPLETED, null);
                 bootIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
                 bootIntent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
@@ -655,7 +671,7 @@
                 final Integer userIdInt = userId;
                 mUserLru.remove(userIdInt);
                 mUserLru.add(userIdInt);
-                updateCurrentUserSetupCompleteLocked();
+                updateUserSetupCompleteLocked(userId);
 
                 if (foreground) {
                     mCurrentUserId = userId;
@@ -870,11 +886,22 @@
         mUserSwitchObservers.finishBroadcast();
     }
 
-    void updateCurrentUserSetupCompleteLocked() {
+    void updateUserSetupCompleteLocked(int userId) {
+        int[] users;
+        if (userId != UserHandle.USER_ALL) {
+            users = new int[] {userId};
+        } else {
+            users = new int[mStartedUsers.size()];
+            for (int i = mStartedUsers.size() - 1; i >= 0; i--) {
+                users[i] = mStartedUsers.keyAt(i);
+            }
+        }
         final ContentResolver cr = mService.mContext.getContentResolver();
-        final boolean setupComplete =
-                Settings.Secure.getIntForUser(cr, USER_SETUP_COMPLETE, 0, mCurrentUserId) != 0;
-        mSetupCompletedUsers.put(mCurrentUserId, setupComplete);
+        for (int i = 0; i < users.length; i++) {
+            final boolean setupComplete =
+                    Settings.Secure.getIntForUser(cr, USER_SETUP_COMPLETE, 0, users[i]) != 0;
+            mSetupCompletedUsers.put(users[i], setupComplete);
+        }
     }
 
     boolean isUserSetupCompleteLocked(int userId) {
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 8dec587..2f87f80 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -62,6 +62,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.os.Messenger;
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -70,41 +71,42 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.WorkSource;
-import android.os.Messenger;
 import android.provider.Settings;
 import android.text.format.DateUtils;
 import android.text.format.Time;
 import android.util.EventLog;
 import android.util.Log;
-import android.util.Slog;
 import android.util.Pair;
-
+import android.util.Slog;
 import android.util.SparseArray;
+
+import com.google.android.collect.Lists;
+import com.google.android.collect.Maps;
+
 import com.android.internal.R;
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.accounts.AccountManagerService;
+import com.android.server.backup.AccountSyncSettingsBackupHelper;
 import com.android.server.content.SyncStorageEngine.AuthorityInfo;
 import com.android.server.content.SyncStorageEngine.EndPoint;
 import com.android.server.content.SyncStorageEngine.OnSyncRequestListener;
-import com.google.android.collect.Lists;
-import com.google.android.collect.Maps;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.Random;
-import java.util.List;
-import java.util.Set;
-import java.util.HashSet;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
-import java.util.Map;
-import java.util.Arrays;
 import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
 import java.util.Objects;
+import java.util.Random;
+import java.util.Set;
 
 /**
  * Implementation details:
@@ -510,12 +512,12 @@
 
         mSyncStorageEngine.setPeriodicSyncAddedListener(
                 new SyncStorageEngine.PeriodicSyncAddedListener() {
-            @Override
-            public void onPeriodicSyncAdded(EndPoint target, Bundle extras, long pollFrequency,
-                                            long flex) {
-                updateOrAddPeriodicSync(target, pollFrequency, flex, extras);
-            }
-        });
+                    @Override
+                    public void onPeriodicSyncAdded(EndPoint target, Bundle extras, long pollFrequency,
+                            long flex) {
+                        updateOrAddPeriodicSync(target, pollFrequency, flex, extras);
+                    }
+                });
 
         mSyncStorageEngine.setOnAuthorityRemovedListener(new SyncStorageEngine.OnAuthorityRemovedListener() {
             @Override
@@ -750,8 +752,8 @@
      * @param onlyThoseWithUnkownSyncableState Only sync authorities that have unknown state.
      */
     public void scheduleSync(Account requestedAccount, int userId, int reason,
-                             String requestedAuthority, Bundle extras, long beforeRuntimeMillis,
-                             long runtimeMillis, boolean onlyThoseWithUnkownSyncableState) {
+            String requestedAuthority, Bundle extras, long beforeRuntimeMillis,
+            long runtimeMillis, boolean onlyThoseWithUnkownSyncableState) {
         final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
         if (extras == null) {
             extras = new Bundle();
@@ -941,7 +943,7 @@
      * flexMillis will be updated.
      */
     public void updateOrAddPeriodicSync(EndPoint target, long pollFrequency, long flex,
-                                        Bundle extras) {
+            Bundle extras) {
         UpdatePeriodicSyncMessagePayload payload = new UpdatePeriodicSyncMessagePayload(target,
                 pollFrequency, flex, extras);
         mSyncHandler.obtainMessage(SyncHandler.MESSAGE_UPDATE_PERIODIC_SYNC, payload)
@@ -995,7 +997,7 @@
     }
 
     private void sendSyncFinishedOrCanceledMessage(ActiveSyncContext syncContext,
-                                                   SyncResult syncResult) {
+            SyncResult syncResult) {
         if (Log.isLoggable(TAG, Log.VERBOSE)) Slog.v(TAG, "sending MESSAGE_SYNC_FINISHED");
         Message msg = mSyncHandler.obtainMessage();
         msg.what = SyncHandler.MESSAGE_SYNC_FINISHED;
@@ -1053,7 +1055,7 @@
         public final SyncResult syncResult;
 
         SyncFinishedOrCancelledMessagePayload(ActiveSyncContext syncContext,
-                                              SyncResult syncResult) {
+                SyncResult syncResult) {
             this.activeSyncContext = syncContext;
             this.syncResult = syncResult;
         }
@@ -1066,7 +1068,7 @@
         public final Bundle extras;
 
         UpdatePeriodicSyncMessagePayload(EndPoint target, long pollFrequency, long flex,
-                                         Bundle extras) {
+                Bundle extras) {
             this.target = target;
             this.pollFrequency = pollFrequency;
             this.flex = flex;
@@ -1297,7 +1299,7 @@
                 JobInfo.NETWORK_TYPE_UNMETERED : JobInfo.NETWORK_TYPE_ANY;
 
         JobInfo.Builder b = new JobInfo.Builder(syncOperation.jobId,
-                    new ComponentName(mContext, SyncJobService.class))
+                new ComponentName(mContext, SyncJobService.class))
                 .setExtras(syncOperation.toJobInfoExtras())
                 .setRequiredNetworkType(networkType)
                 .setPersisted(true)
@@ -1502,7 +1504,7 @@
          * for this sync. This is used to attribute the wakelock hold to that application.
          */
         public ActiveSyncContext(SyncOperation syncOperation, long historyRowId,
-                                 int syncAdapterUid) {
+                int syncAdapterUid) {
             super();
             mSyncAdapterUid = syncAdapterUid;
             mSyncOperation = syncOperation;
@@ -1731,7 +1733,7 @@
                     new Comparator<RegisteredServicesCache.ServiceInfo<SyncAdapterType>>() {
                         @Override
                         public int compare(RegisteredServicesCache.ServiceInfo<SyncAdapterType> lhs,
-                                           RegisteredServicesCache.ServiceInfo<SyncAdapterType> rhs) {
+                                RegisteredServicesCache.ServiceInfo<SyncAdapterType> rhs) {
                             return lhs.type.authority.compareTo(rhs.type.authority);
                         }
                     });
@@ -2546,6 +2548,7 @@
                             Slog.v(TAG, "Pushing back running sync due to a higher priority sync");
                         }
                         deferActiveSyncH(asc);
+                        break;
                     }
                 }
             }
@@ -2571,6 +2574,7 @@
         }
 
         private void updateRunningAccountsH(EndPoint syncTargets) {
+            AccountAndUser[] oldAccounts = mRunningAccounts;
             mRunningAccounts = AccountManagerService.getSingleton().getRunningAccounts();
             if (Log.isLoggable(TAG, Log.VERBOSE)) {
                 Slog.v(TAG, "Accounts list: ");
@@ -2594,6 +2598,17 @@
                 }
             }
 
+            // On account add, check if there are any settings to be restored.
+            for (AccountAndUser aau : mRunningAccounts) {
+                if (!containsAccountAndUser(oldAccounts, aau.account, aau.userId)) {
+                    if (Log.isLoggable(TAG, Log.DEBUG)) {
+                        Log.d(TAG, "Account " + aau.account + " added, checking sync restore data");
+                    }
+                    AccountSyncSettingsBackupHelper.accountAdded(mContext);
+                    break;
+                }
+            }
+
             List<SyncOperation> ops = getAllPendingSyncsFromCache();
             for (SyncOperation op: ops) {
                 if (!containsAccountAndUser(accounts, op.target.account, op.target.userId)) {
@@ -2617,7 +2632,7 @@
          * @param flexMillis new flex time in milliseconds.
          */
         private void maybeUpdateSyncPeriodH(SyncOperation syncOperation, long pollFrequencyMillis,
-                                            long flexMillis) {
+                long flexMillis) {
             if (!(pollFrequencyMillis == syncOperation.periodMillis
                     && flexMillis == syncOperation.flexMillis)) {
                 if (Log.isLoggable(TAG, Log.VERBOSE)) {
@@ -2632,7 +2647,7 @@
         }
 
         private void updateOrAddPeriodicSyncH(EndPoint target, long pollFrequency, long flex,
-                                              Bundle extras) {
+                Bundle extras) {
             final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
             verifyJobScheduler();  // Will fill in mScheduledSyncs cache if it is not already filled.
             final long pollFrequencyMillis = pollFrequency * 1000L;
@@ -2820,7 +2835,7 @@
         }
 
         private void runBoundToAdapterH(final ActiveSyncContext activeSyncContext,
-                                        IBinder syncAdapter) {
+                IBinder syncAdapter) {
             final SyncOperation syncOperation = activeSyncContext.mSyncOperation;
             try {
                 activeSyncContext.mIsLinkedToDeath = true;
@@ -2888,7 +2903,7 @@
         }
 
         private void runSyncFinishedOrCanceledH(SyncResult syncResult,
-                                                ActiveSyncContext activeSyncContext) {
+                ActiveSyncContext activeSyncContext) {
             final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
 
             final SyncOperation syncOperation = activeSyncContext.mSyncOperation;
@@ -3022,7 +3037,7 @@
         }
 
         private void installHandleTooManyDeletesNotification(Account account, String authority,
-                                                             long numDeletes, int userId) {
+                long numDeletes, int userId) {
             if (mNotificationMgr == null) return;
 
             final ProviderInfo providerInfo = mContext.getPackageManager().resolveContentProvider(
@@ -3098,7 +3113,7 @@
         }
 
         public void stopSyncEvent(long rowId, SyncOperation syncOperation, String resultMessage,
-                                  int upstreamActivity, int downstreamActivity, long elapsedTime) {
+                int upstreamActivity, int downstreamActivity, long elapsedTime) {
             EventLog.writeEvent(2720,
                     syncOperation.toEventLog(SyncStorageEngine.EVENT_STOP));
             mSyncStorageEngine.stopSyncEvent(rowId, elapsedTime,
@@ -3262,4 +3277,4 @@
             return mContext;
         }
     }
-}
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index f8e3e48..965054f 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -438,9 +438,7 @@
         if (sSyncStorageEngine != null) {
             return;
         }
-        // This call will return the correct directory whether Encrypted File Systems is
-        // enabled or not.
-        File dataDir = Environment.getSecureDataDirectory();
+        File dataDir = Environment.getDataDirectory();
         sSyncStorageEngine = new SyncStorageEngine(context, dataDir);
     }
 
diff --git a/services/core/java/com/android/server/firewall/IntentFirewall.java b/services/core/java/com/android/server/firewall/IntentFirewall.java
index 62114cd..7e19c66 100644
--- a/services/core/java/com/android/server/firewall/IntentFirewall.java
+++ b/services/core/java/com/android/server/firewall/IntentFirewall.java
@@ -52,7 +52,7 @@
     static final String TAG = "IntentFirewall";
 
     // e.g. /data/system/ifw or /data/secure/system/ifw
-    private static final File RULES_DIR = new File(Environment.getSystemSecureDirectory(), "ifw");
+    private static final File RULES_DIR = new File(Environment.getDataSystemDirectory(), "ifw");
 
     private static final int LOG_PACKAGES_MAX_LENGTH = 150;
     private static final int LOG_PACKAGES_SUFFICIENT_LENGTH = 125;
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index e5e86ac..4028372 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -330,7 +330,7 @@
 
     private void cancelJobImpl(JobStatus cancelled) {
         if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString());
-        stopTrackingJob(cancelled);
+        stopTrackingJob(cancelled, true /* writeBack */);
         synchronized (mJobs) {
             // Remove from pending queue.
             mPendingJobs.remove(cancelled);
@@ -509,12 +509,12 @@
      * Called when we want to remove a JobStatus object that we've finished executing. Returns the
      * object removed.
      */
-    private boolean stopTrackingJob(JobStatus jobStatus) {
+    private boolean stopTrackingJob(JobStatus jobStatus, boolean writeBack) {
         boolean removed;
         boolean rocking;
         synchronized (mJobs) {
             // Remove from store as well as controllers.
-            removed = mJobs.remove(jobStatus);
+            removed = mJobs.remove(jobStatus, writeBack);
             rocking = mReadyToRock;
         }
         if (removed && rocking) {
@@ -645,7 +645,9 @@
         if (DEBUG) {
             Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule);
         }
-        if (!stopTrackingJob(jobStatus)) {
+        // Do not write back immediately if this is a periodic job. The job may get lost if system
+        // shuts down before it is added back.
+        if (!stopTrackingJob(jobStatus, !jobStatus.getJob().isPeriodic())) {
             if (DEBUG) {
                 Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
             }
diff --git a/services/core/java/com/android/server/job/JobStore.java b/services/core/java/com/android/server/job/JobStore.java
index f796164..f6a6778 100644
--- a/services/core/java/com/android/server/job/JobStore.java
+++ b/services/core/java/com/android/server/job/JobStore.java
@@ -159,9 +159,10 @@
 
     /**
      * Remove the provided job. Will also delete the job if it was persisted.
+     * @param writeBack If true, the job will be deleted (if it was persisted) immediately.
      * @return Whether or not the job existed to be removed.
      */
-    public boolean remove(JobStatus jobStatus) {
+    public boolean remove(JobStatus jobStatus, boolean writeBack) {
         boolean removed = mJobSet.remove(jobStatus);
         if (!removed) {
             if (DEBUG) {
@@ -169,7 +170,7 @@
             }
             return false;
         }
-        if (jobStatus.isPersisted()) {
+        if (writeBack && jobStatus.isPersisted()) {
             maybeWriteStatusToDiskAsync();
         }
         return removed;
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index ffc52b3..c1eb844 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -1557,15 +1557,13 @@
      * called from native code to update SV info
      */
     private void reportSvStatus() {
-        int svCount = native_read_sv_status(mPrnWithFlags, mSnrs, mSvElevations, mSvAzimuths,
-                mConstellationTypes);
+        int svCount = native_read_sv_status(mSvidWithFlags, mSnrs, mSvElevations, mSvAzimuths);
         mListenerHelper.onSvStatusChanged(
                 svCount,
-                mPrnWithFlags,
+                mSvidWithFlags,
                 mSnrs,
                 mSvElevations,
-                mSvAzimuths,
-                mConstellationTypes);
+                mSvAzimuths);
 
         if (VERBOSE) {
             Log.v(TAG, "SV count: " + svCount);
@@ -1573,19 +1571,19 @@
         // Calculate number of sets used in fix.
         int usedInFixCount = 0;
         for (int i = 0; i < svCount; i++) {
-            if ((mPrnWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_USED_IN_FIX) != 0) {
+            if ((mSvidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_USED_IN_FIX) != 0) {
                 ++usedInFixCount;
             }
             if (VERBOSE) {
-                Log.v(TAG, "prn: " + (mPrnWithFlags[i] >> GnssStatus.PRN_SHIFT_WIDTH) +
+                Log.v(TAG, "svid: " + (mSvidWithFlags[i] >> GnssStatus.SVID_SHIFT_WIDTH) +
                         " snr: " + mSnrs[i]/10 +
                         " elev: " + mSvElevations[i] +
                         " azimuth: " + mSvAzimuths[i] +
-                        ((mPrnWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA) == 0
+                        ((mSvidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA) == 0
                                 ? "  " : " E") +
-                        ((mPrnWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_ALMANAC_DATA) == 0
+                        ((mSvidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_ALMANAC_DATA) == 0
                                 ? "  " : " A") +
-                        ((mPrnWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_USED_IN_FIX) == 0
+                        ((mSvidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_USED_IN_FIX) == 0
                                 ? "" : "U"));
             }
         }
@@ -2398,14 +2396,13 @@
     }
 
     // for GPS SV statistics
-    private static final int MAX_SVS = 512;
+    private static final int MAX_SVS = 64;
 
     // preallocated arrays, to avoid memory allocation in reportStatus()
-    private int mPrnWithFlags[] = new int[MAX_SVS];
+    private int mSvidWithFlags[] = new int[MAX_SVS];
     private float mSnrs[] = new float[MAX_SVS];
     private float mSvElevations[] = new float[MAX_SVS];
     private float mSvAzimuths[] = new float[MAX_SVS];
-    private int mConstellationTypes[] = new int[MAX_SVS];
     private int mSvCount;
     // preallocated to avoid memory allocation in reportNmea()
     private byte[] mNmeaBuffer = new byte[120];
@@ -2426,7 +2423,7 @@
     // returns number of SVs
     // mask[0] is ephemeris mask and mask[1] is almanac mask
     private native int native_read_sv_status(int[] prnWithFlags, float[] snrs, float[] elevations,
-            float[] azimuths, int[] constellationTypes);
+            float[] azimuths);
     private native int native_read_nmea(byte[] buffer, int bufferSize);
     private native void native_inject_location(double latitude, double longitude, float accuracy);
 
diff --git a/services/core/java/com/android/server/location/GnssStatusListenerHelper.java b/services/core/java/com/android/server/location/GnssStatusListenerHelper.java
index 9840c61..0b3111c 100644
--- a/services/core/java/com/android/server/location/GnssStatusListenerHelper.java
+++ b/services/core/java/com/android/server/location/GnssStatusListenerHelper.java
@@ -77,8 +77,7 @@
             final int[] prnWithFlags,
             final float[] snrs,
             final float[] elevations,
-            final float[] azimuths,
-            final int[] constellationTypes) {
+            final float[] azimuths) {
         Operation operation = new Operation() {
             @Override
             public void execute(IGnssStatusListener listener) throws RemoteException {
@@ -87,8 +86,7 @@
                         prnWithFlags,
                         snrs,
                         elevations,
-                        azimuths,
-                        constellationTypes);
+                        azimuths);
             }
         };
         foreach(operation);
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index b2e6adf..24bb845 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -1864,31 +1864,58 @@
     @Override
     public void addRestrictBackgroundWhitelistedUid(int uid) {
         mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
-        Slog.i(TAG, "adding uid " + uid + " to restrict background whitelist");
+        if (!isUidValidForRules(uid)) return;
+        final boolean changed;
         synchronized (mRulesLock) {
+            final boolean oldStatus = mRestrictBackgroundWhitelistUids.get(uid);
+            if (oldStatus) {
+                if (LOGD) Slog.d(TAG, "uid " + uid + " is already whitelisted");
+                return;
+            }
+            Slog.i(TAG, "adding uid " + uid + " to restrict background whitelist");
             mRestrictBackgroundWhitelistUids.append(uid, true);
-            updateRulesForGlobalChangeLocked(true);
+            changed = mRestrictBackground && !oldStatus;
+            if (changed && hasInternetPermissions(uid)) {
+                setUidNetworkRules(uid, false);
+            }
             writePolicyLocked();
         }
-        mHandler.obtainMessage(MSG_RESTRICT_BACKGROUND_WHITELIST_CHANGED, uid, 0).sendToTarget();
+        if (changed) {
+            mHandler.obtainMessage(MSG_RESTRICT_BACKGROUND_WHITELIST_CHANGED, uid, 0)
+                    .sendToTarget();
+        }
     }
 
     @Override
     public void removeRestrictBackgroundWhitelistedUid(int uid) {
         mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
-        Slog.i(TAG, "removing uid " + uid + " from restrict background whitelist");
+        if (!isUidValidForRules(uid)) return;
+        final boolean changed;
         synchronized (mRulesLock) {
-            removeRestrictBackgroundWhitelistedUidLocked(uid, true);
+            changed = removeRestrictBackgroundWhitelistedUidLocked(uid, true);
         }
-        mHandler.obtainMessage(MSG_RESTRICT_BACKGROUND_WHITELIST_CHANGED, uid, 0).sendToTarget();
+        if (changed) {
+            mHandler.obtainMessage(MSG_RESTRICT_BACKGROUND_WHITELIST_CHANGED, uid, 0)
+                    .sendToTarget();
+        }
     }
 
-    private void removeRestrictBackgroundWhitelistedUidLocked(int uid, boolean updateNow) {
+    private boolean removeRestrictBackgroundWhitelistedUidLocked(int uid, boolean updateNow) {
+        final boolean oldStatus = mRestrictBackgroundWhitelistUids.get(uid);
+        if (!oldStatus) {
+            if (LOGD) Slog.d(TAG, "uid " + uid + " was not whitelisted before");
+            return false;
+        }
+        Slog.i(TAG, "removing uid " + uid + " from restrict background whitelist");
+        final boolean changed = mRestrictBackground && oldStatus;
         mRestrictBackgroundWhitelistUids.delete(uid);
         if (updateNow) {
-            updateRulesForGlobalChangeLocked(true);
+            if (changed && hasInternetPermissions(uid)) {
+                setUidNetworkRules(uid, true);
+            }
             writePolicyLocked();
         }
+        return changed;
     }
 
     @Override
@@ -2408,22 +2435,28 @@
     }
 
     /**
-     * Applies network rules to bandwidth and firewall controllers based on uid policy.
-     * @param uid The uid for which to apply the latest policy
+     * Checks if an uid has INTERNET permissions.
+     * <p>
+     * Useful for the cases where the lack of network access can simplify the rules.
      */
-    void updateRulesForUidLocked(int uid) {
-        if (!isUidValidForRules(uid)) return;
-
-        // quick check: if this uid doesn't have INTERNET permission, it doesn't have
-        // network access anyway, so it is a waste to mess with it here.
+    private boolean hasInternetPermissions(int uid) {
         final IPackageManager ipm = AppGlobals.getPackageManager();
         try {
             if (ipm.checkUidPermission(Manifest.permission.INTERNET, uid)
                     != PackageManager.PERMISSION_GRANTED) {
-                return;
+                return false;
             }
         } catch (RemoteException e) {
         }
+        return true;
+    }
+
+    /**
+     * Applies network rules to bandwidth and firewall controllers based on uid policy.
+     * @param uid The uid for which to apply the latest policy
+     */
+    void updateRulesForUidLocked(int uid) {
+        if (!isUidValidForRules(uid) || !hasInternetPermissions(uid)) return;
 
         final int uidPolicy = mUidPolicy.get(uid, POLICY_NONE);
         final boolean uidForeground = isUidForegroundLocked(uid);
@@ -2598,7 +2631,6 @@
                         intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
                         mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
                     }
-
                     return true;
                 }
                 case MSG_ADVISE_PERSIST_THRESHOLD: {
@@ -2831,13 +2863,5 @@
                 removeRestrictBackgroundWhitelistedUidLocked(uid, true);
             }
         }
-
-        @Override
-        public void onPackageRemovedAllUsers(String packageName, int uid) {
-            if (LOGV) Slog.v(TAG, "onPackageRemovedAllUsers: " + packageName + " ->" + uid);
-            synchronized (mRulesLock) {
-                removeRestrictBackgroundWhitelistedUidLocked(uid, true);
-            }
-        }
     }
 }
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index d82bb3d..c6613f5 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -201,8 +201,7 @@
             long ident = Binder.clearCallingIdentity();
             try {
                 List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(mainIntent,
-                        PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
-                        user.getIdentifier());
+                        PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE, user.getIdentifier());
                 return new ParceledListSlice<>(apps);
             } finally {
                 Binder.restoreCallingIdentity(ident);
@@ -220,7 +219,7 @@
             long ident = Binder.clearCallingIdentity();
             try {
                 ResolveInfo app = mPm.resolveActivityAsUser(intent,
-                        PackageManager.MATCH_DEBUG_TRIAGED_MISSING, user.getIdentifier());
+                        PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE, user.getIdentifier());
                 return app;
             } finally {
                 Binder.restoreCallingIdentity(ident);
@@ -239,7 +238,7 @@
             try {
                 IPackageManager pm = AppGlobals.getPackageManager();
                 PackageInfo info = pm.getPackageInfo(packageName,
-                        PackageManager.MATCH_DEBUG_TRIAGED_MISSING, user.getIdentifier());
+                        PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE, user.getIdentifier());
                 return info != null && info.applicationInfo.enabled;
             } finally {
                 Binder.restoreCallingIdentity(ident);
@@ -277,7 +276,7 @@
             try {
                 IPackageManager pm = AppGlobals.getPackageManager();
                 ActivityInfo info = pm.getActivityInfo(component,
-                        PackageManager.MATCH_DEBUG_TRIAGED_MISSING, user.getIdentifier());
+                        PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE, user.getIdentifier());
                 return info != null;
             } finally {
                 Binder.restoreCallingIdentity(ident);
@@ -303,7 +302,7 @@
             try {
                 IPackageManager pm = AppGlobals.getPackageManager();
                 ActivityInfo info = pm.getActivityInfo(component,
-                        PackageManager.MATCH_DEBUG_TRIAGED_MISSING, user.getIdentifier());
+                        PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE, user.getIdentifier());
                 if (!info.exported) {
                     throw new SecurityException("Cannot launch non-exported components "
                             + component);
@@ -313,7 +312,7 @@
                 // as calling startActivityAsUser ignores the category and just
                 // resolves based on the component if present.
                 List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(launchIntent,
-                        PackageManager.MATCH_DEBUG_TRIAGED_MISSING, user.getIdentifier());
+                        PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE, user.getIdentifier());
                 final int size = apps.size();
                 for (int i = 0; i < size; ++i) {
                     ActivityInfo activityInfo = apps.get(i).activityInfo;
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index da62a2d..94b3b2d 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -55,8 +55,6 @@
 public class OtaDexoptService extends IOtaDexopt.Stub {
     private final static String TAG = "OTADexopt";
     private final static boolean DEBUG_DEXOPT = true;
-    // Apps used in the last 7 days.
-    private final static long DEXOPT_LRU_THRESHOLD_IN_MINUTES = 7 * 24 * 60;
 
     private final Context mContext;
     private final PackageDexOptimizer mPackageDexOptimizer;
@@ -94,69 +92,9 @@
         if (mDexoptPackages != null) {
             throw new IllegalStateException("already called prepare()");
         }
-
-        mDexoptPackages = new LinkedList<>();
-
-        ArrayList<PackageParser.Package> pkgs;
         synchronized (mPackageManagerService.mPackages) {
-            pkgs = new ArrayList<PackageParser.Package>(mPackageManagerService.mPackages.values());
-        }
-
-        // Sort apps by importance for dexopt ordering. Important apps are given more priority
-        // in case the device runs out of space.
-
-        // Give priority to core apps.
-        for (PackageParser.Package pkg : pkgs) {
-            if (pkg.coreApp) {
-                if (DEBUG_DEXOPT) {
-                    Log.i(TAG, "Adding core app " + mDexoptPackages.size() + ": " + pkg.packageName);
-                }
-                mDexoptPackages.add(pkg);
-            }
-        }
-        pkgs.removeAll(mDexoptPackages);
-
-        // Give priority to system apps that listen for pre boot complete.
-        Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
-        ArraySet<String> pkgNames = getPackageNamesForIntent(intent, UserHandle.USER_SYSTEM);
-        for (PackageParser.Package pkg : pkgs) {
-            if (pkgNames.contains(pkg.packageName)) {
-                if (DEBUG_DEXOPT) {
-                    Log.i(TAG, "Adding pre boot system app " + mDexoptPackages.size() + ": " +
-                            pkg.packageName);
-                }
-                mDexoptPackages.add(pkg);
-            }
-        }
-        pkgs.removeAll(mDexoptPackages);
-
-        // Filter out packages that aren't recently used, add all remaining apps.
-        // TODO: add a property to control this?
-        if (mPackageManagerService.isHistoricalPackageUsageAvailable()) {
-            filterRecentlyUsedApps(pkgs, DEXOPT_LRU_THRESHOLD_IN_MINUTES * 60 * 1000);
-        }
-        mDexoptPackages.addAll(pkgs);
-
-        // Now go ahead and also add the libraries required for these packages.
-        // TODO: Think about interleaving things.
-        Set<PackageParser.Package> dependencies = new HashSet<>();
-        for (PackageParser.Package p : mDexoptPackages) {
-            dependencies.addAll(mPackageManagerService.findSharedNonSystemLibraries(p));
-        }
-        if (!dependencies.isEmpty()) {
-            dependencies.removeAll(mDexoptPackages);
-        }
-        mDexoptPackages.addAll(dependencies);
-
-        if (DEBUG_DEXOPT) {
-            StringBuilder sb = new StringBuilder();
-            for (PackageParser.Package pkg : mDexoptPackages) {
-                if (sb.length() > 0) {
-                    sb.append(", ");
-                }
-                sb.append(pkg.packageName);
-            }
-            Log.i(TAG, "Packages to be optimized: " + sb.toString());
+            mDexoptPackages = PackageManagerServiceUtils.getPackagesForDexopt(
+                    mPackageManagerService.mPackages.values(), mPackageManagerService);
         }
     }
 
@@ -228,29 +166,6 @@
         return pkgNames;
     }
 
-    private void filterRecentlyUsedApps(Collection<PackageParser.Package> pkgs,
-            long dexOptLRUThresholdInMills) {
-        // Filter out packages that aren't recently used.
-        int total = pkgs.size();
-        int skipped = 0;
-        long now = System.currentTimeMillis();
-        for (Iterator<PackageParser.Package> i = pkgs.iterator(); i.hasNext();) {
-            PackageParser.Package pkg = i.next();
-            long then = pkg.mLastPackageUsageTimeInMills;
-            if (then + dexOptLRUThresholdInMills < now) {
-                if (DEBUG_DEXOPT) {
-                    Log.i(TAG, "Skipping dexopt of " + pkg.packageName + " last resumed: " +
-                          ((then == 0) ? "never" : new Date(then)));
-                }
-                i.remove();
-                skipped++;
-            }
-        }
-        if (DEBUG_DEXOPT) {
-            Log.i(TAG, "Skipped optimizing " + skipped + " of " + total);
-        }
-    }
-
     private static class OTADexoptPackageDexOptimizer extends
             PackageDexOptimizer.ForcedUpdatePackageDexOptimizer {
 
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 23a58d0..928c19f 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -212,8 +212,8 @@
         mCallbacks = new Callbacks(mInstallThread.getLooper());
 
         mSessionsFile = new AtomicFile(
-                new File(Environment.getSystemSecureDirectory(), "install_sessions.xml"));
-        mSessionsDir = new File(Environment.getSystemSecureDirectory(), "install_sessions");
+                new File(Environment.getDataSystemDirectory(), "install_sessions.xml"));
+        mSessionsDir = new File(Environment.getDataSystemDirectory(), "install_sessions");
         mSessionsDir.mkdirs();
 
         synchronized (mSessions) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e47d514..92343d6 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -282,6 +282,7 @@
 import java.util.Date;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -6778,32 +6779,19 @@
         // Extract pacakges only if profile-guided compilation is enabled because
         // otherwise BackgroundDexOptService will not dexopt them later.
         if (mUseJitProfiles) {
-            ArraySet<String> pkgs = getOptimizablePackages();
-            if (pkgs != null) {
-                for (String pkg : pkgs) {
-                    performDexOpt(pkg, null /* instructionSet */, false /* useProfiles */,
-                            true /* extractOnly */, false /* force */);
+            List<PackageParser.Package> pkgs;
+            synchronized (mPackages) {
+                pkgs = PackageManagerServiceUtils.getPackagesForDexopt(mPackages.values(), this);
+            }
+            for (PackageParser.Package pkg : pkgs) {
+                if (PackageDexOptimizer.canOptimizePackage(pkg)) {
+                    performDexOpt(pkg.packageName, null /* instructionSet */,
+                             false /* useProfiles */, true /* extractOnly */, false /* force */);
                 }
             }
         }
     }
 
-    private ArraySet<String> getPackageNamesForIntent(Intent intent, int userId) {
-        List<ResolveInfo> ris = null;
-        try {
-            ris = AppGlobals.getPackageManager().queryIntentReceivers(
-                    intent, null, 0, userId);
-        } catch (RemoteException e) {
-        }
-        ArraySet<String> pkgNames = new ArraySet<String>();
-        if (ris != null) {
-            for (ResolveInfo ri : ris) {
-                pkgNames.add(ri.activityInfo.packageName);
-            }
-        }
-        return pkgNames;
-    }
-
     @Override
     public void notifyPackageUse(String packageName) {
         synchronized (mPackages) {
@@ -12724,9 +12712,6 @@
         String pkgName = pkg.packageName;
 
         if (DEBUG_INSTALL) Slog.d(TAG, "installNewPackageLI: " + pkg);
-        // TODO: b/23350563
-        final boolean dataDirExists = Environment
-                .getDataUserPackageDirectory(volumeUuid, UserHandle.USER_SYSTEM, pkgName).exists();
 
         synchronized(mPackages) {
             if (mSettings.mRenamedPackages.containsKey(pkgName)) {
@@ -12752,18 +12737,15 @@
                     System.currentTimeMillis(), user);
 
             updateSettingsLI(newPackage, installerPackageName, null, null, res, user);
-            prepareAppDataAfterInstall(newPackage);
 
-            // delete the partially installed application. the data directory will have to be
-            // restored if it was already existing
-            if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
-                // remove package from internal structures.  Note that we want deletePackageX to
-                // delete the package data and cache directories that it created in
-                // scanPackageLocked, unless those directories existed before we even tried to
-                // install.
+            if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
+                prepareAppDataAfterInstall(newPackage);
+
+            } else {
+                // Remove package from internal structures, but keep around any
+                // data that might have already existed
                 deletePackageLI(pkgName, UserHandle.ALL, false, null, null,
-                        dataDirExists ? PackageManager.DELETE_KEEP_DATA : 0,
-                                res.removedInfo, true, null);
+                        PackageManager.DELETE_KEEP_DATA, res.removedInfo, true, null);
             }
 
         } catch (PackageManagerException e) {
@@ -13274,16 +13256,19 @@
             // of the package implies that the user actually wants to run that new code,
             // so we enable the package.
             PackageSetting ps = mSettings.mPackages.get(pkgName);
+            final int userId = user.getIdentifier();
             if (ps != null) {
                 if (isSystemApp(newPackage)) {
-                    // NB: implicit assumption that system package upgrades apply to all users
                     if (DEBUG_INSTALL) {
                         Slog.d(TAG, "Implicitly enabling system package on upgrade: " + pkgName);
                     }
+                    // Enable system package for requested users
                     if (res.origUsers != null) {
-                        for (int userHandle : res.origUsers) {
-                            ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT,
-                                    userHandle, installerPackageName);
+                        for (int origUserId : res.origUsers) {
+                            if (userId == UserHandle.USER_ALL || userId == origUserId) {
+                                ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT,
+                                        origUserId, installerPackageName);
+                            }
                         }
                     }
                     // Also convey the prior install/uninstall state
@@ -13301,7 +13286,6 @@
                 }
                 // It's implied that when a user requests installation, they want the app to be
                 // installed and enabled.
-                int userId = user.getIdentifier();
                 if (userId != UserHandle.USER_ALL) {
                     ps.setInstalled(true, userId);
                     ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, userId, installerPackageName);
@@ -17423,8 +17407,9 @@
      * recycled.
      */
     private void reconcileUsers(String volumeUuid) {
+        // TODO: also reconcile DE directories
         final File[] files = FileUtils
-                .listFilesOrEmpty(Environment.getDataUserDirectory(volumeUuid));
+                .listFilesOrEmpty(Environment.getDataUserCeDirectory(volumeUuid));
         for (File file : files) {
             if (!file.isDirectory()) continue;
 
@@ -17553,8 +17538,8 @@
         Slog.v(TAG, "reconcileAppsData for " + volumeUuid + " u" + userId + " 0x"
                 + Integer.toHexString(flags));
 
-        final File ceDir = Environment.getDataUserCredentialEncryptedDirectory(volumeUuid, userId);
-        final File deDir = Environment.getDataUserDeviceEncryptedDirectory(volumeUuid, userId);
+        final File ceDir = Environment.getDataUserCeDirectory(volumeUuid, userId);
+        final File deDir = Environment.getDataUserDeDirectory(volumeUuid, userId);
 
         boolean restoreconNeeded = false;
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
new file mode 100644
index 0000000..a3ac514
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -0,0 +1,153 @@
+/*
+ * 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.pm;
+
+import android.app.AppGlobals;
+import android.content.Intent;
+import android.content.pm.PackageParser;
+import android.content.pm.PackageParser.Package;
+import android.content.pm.ResolveInfo;
+import android.os.UserHandle;
+import android.os.RemoteException;
+import android.util.ArraySet;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT;
+import static com.android.server.pm.PackageManagerService.TAG;
+
+/**
+ * Class containing helper methods for the PackageManagerService.
+ *
+ * {@hide}
+ */
+public class PackageManagerServiceUtils {
+    private final static long SEVEN_DAYS_IN_MILLISECONDS = 7 * 24 * 60 * 60 * 1000;
+
+    private static ArraySet<String> getPackageNamesForIntent(Intent intent, int userId) {
+        List<ResolveInfo> ris = null;
+        try {
+            ris = AppGlobals.getPackageManager().queryIntentReceivers(intent, null, 0, userId);
+        } catch (RemoteException e) {
+        }
+        ArraySet<String> pkgNames = new ArraySet<String>();
+        if (ris != null) {
+            for (ResolveInfo ri : ris) {
+                pkgNames.add(ri.activityInfo.packageName);
+            }
+        }
+        return pkgNames;
+    }
+
+    private static void filterRecentlyUsedApps(Collection<PackageParser.Package> pkgs,
+            long dexOptLRUThresholdInMills) {
+        // Filter out packages that aren't recently used.
+        int total = pkgs.size();
+        int skipped = 0;
+        long now = System.currentTimeMillis();
+        for (Iterator<PackageParser.Package> i = pkgs.iterator(); i.hasNext();) {
+            PackageParser.Package pkg = i.next();
+            long then = pkg.mLastPackageUsageTimeInMills;
+            if (then + dexOptLRUThresholdInMills < now) {
+                if (DEBUG_DEXOPT) {
+                    Log.i(TAG, "Skipping dexopt of " + pkg.packageName + " last resumed: " +
+                          ((then == 0) ? "never" : new Date(then)));
+                }
+                i.remove();
+                skipped++;
+            }
+        }
+        if (DEBUG_DEXOPT) {
+            Log.i(TAG, "Skipped dexopt " + skipped + " of " + total);
+        }
+    }
+
+    // Sort apps by importance for dexopt ordering. Important apps are given
+    // more priority in case the device runs out of space.
+    public static List<PackageParser.Package> getPackagesForDexopt(
+            Collection<PackageParser.Package> packages,
+            PackageManagerService packageManagerService) {
+        ArrayList<PackageParser.Package> remainingPkgs = new ArrayList<>(packages);
+        LinkedList<PackageParser.Package> result = new LinkedList<>();
+
+        // Give priority to core apps.
+        for (PackageParser.Package pkg : remainingPkgs) {
+            if (pkg.coreApp) {
+                if (DEBUG_DEXOPT) {
+                    Log.i(TAG, "Adding core app " + result.size() + ": " + pkg.packageName);
+                }
+                result.add(pkg);
+            }
+        }
+        remainingPkgs.removeAll(result);
+
+        // Give priority to system apps that listen for pre boot complete.
+        Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
+        ArraySet<String> pkgNames = getPackageNamesForIntent(intent, UserHandle.USER_SYSTEM);
+        for (PackageParser.Package pkg : remainingPkgs) {
+            if (pkgNames.contains(pkg.packageName)) {
+                if (DEBUG_DEXOPT) {
+                    Log.i(TAG, "Adding pre boot system app " + result.size() + ": " +
+                            pkg.packageName);
+                }
+                result.add(pkg);
+            }
+        }
+        remainingPkgs.removeAll(result);
+
+        // Filter out packages that aren't recently used, add all remaining apps.
+        // TODO: add a property to control this?
+        if (packageManagerService.isHistoricalPackageUsageAvailable()) {
+            filterRecentlyUsedApps(remainingPkgs, SEVEN_DAYS_IN_MILLISECONDS);
+        }
+        result.addAll(remainingPkgs);
+
+        // Now go ahead and also add the libraries required for these packages.
+        // TODO: Think about interleaving things.
+        Set<PackageParser.Package> dependencies = new HashSet<>();
+        for (PackageParser.Package p : result) {
+            dependencies.addAll(packageManagerService.findSharedNonSystemLibraries(p));
+        }
+        if (!dependencies.isEmpty()) {
+            // We might have packages already in `result` that are dependencies
+            // of other packages. Make sure we don't add those to the list twice.
+            dependencies.removeAll(result);
+        }
+        result.addAll(dependencies);
+
+        if (DEBUG_DEXOPT) {
+            StringBuilder sb = new StringBuilder();
+            for (PackageParser.Package pkg : result) {
+                if (sb.length() > 0) {
+                    sb.append(", ");
+                }
+                sb.append(pkg.packageName);
+            }
+            Log.i(TAG, "Packages to be dexopted: " + sb.toString());
+        }
+
+        return result;
+    }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 3cc7b10..76d6b28 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1789,6 +1789,10 @@
             Log.w(LOG_TAG, "Cannot add user. DISALLOW_ADD_USER is enabled.");
             return null;
         }
+        return createUserInternalUnchecked(name, flags, parentId);
+    }
+
+    private UserInfo createUserInternalUnchecked(String name, int flags, int parentId) {
         if (ActivityManager.isLowRamDeviceStatic()) {
             return null;
         }
@@ -1930,13 +1934,18 @@
         if (user == null) {
             return null;
         }
-        setUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS, true, user.id);
-        // Change the setting before applying the DISALLOW_SHARE_LOCATION restriction, otherwise
-        // the putIntForUser() will fail.
-        android.provider.Settings.Secure.putIntForUser(mContext.getContentResolver(),
-                android.provider.Settings.Secure.LOCATION_MODE,
-                android.provider.Settings.Secure.LOCATION_MODE_OFF, user.id);
-        setUserRestriction(UserManager.DISALLOW_SHARE_LOCATION, true, user.id);
+        long identity = Binder.clearCallingIdentity();
+        try {
+            setUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS, true, user.id);
+            // Change the setting before applying the DISALLOW_SHARE_LOCATION restriction, otherwise
+            // the putIntForUser() will fail.
+            android.provider.Settings.Secure.putIntForUser(mContext.getContentResolver(),
+                    android.provider.Settings.Secure.LOCATION_MODE,
+                    android.provider.Settings.Secure.LOCATION_MODE_OFF, user.id);
+            setUserRestriction(UserManager.DISALLOW_SHARE_LOCATION, true, user.id);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
         return user;
     }
 
@@ -2975,6 +2984,17 @@
                 am.switchUser(UserHandle.USER_SYSTEM);
             }
         }
+
+        @Override
+        public UserInfo createUserEvenWhenDisallowed(String name, int flags) {
+            UserInfo user = createUserInternalUnchecked(name, flags, UserHandle.USER_NULL);
+            // Keep this in sync with UserManager.createUser
+            if (user != null && !user.isAdmin()) {
+                setUserRestriction(UserManager.DISALLOW_SMS, true, user.id);
+                setUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS, true, user.id);
+            }
+            return user;
+        }
     }
 
     /* Remove all the users except of the system one. */
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index c046ba6..bff0650 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -18,6 +18,8 @@
 
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
 import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.HOME_STACK_ID;
 import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
 import static android.content.pm.PackageManager.FEATURE_TELEVISION;
 import static android.content.pm.PackageManager.FEATURE_WATCH;
@@ -32,6 +34,7 @@
 import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
 
 import android.app.ActivityManager;
+import android.app.ActivityManager.StackId;
 import android.app.ActivityManagerInternal;
 import android.app.ActivityManagerInternal.SleepToken;
 import android.app.ActivityManagerNative;
@@ -135,6 +138,7 @@
 import com.android.server.LocalServices;
 import com.android.server.policy.keyguard.KeyguardServiceDelegate;
 import com.android.server.policy.keyguard.KeyguardServiceDelegate.DrawnListener;
+import com.android.server.statusbar.StatusBarManagerInternal;
 
 import java.io.File;
 import java.io.FileReader;
@@ -280,6 +284,7 @@
     DreamManagerInternal mDreamManagerInternal;
     PowerManagerInternal mPowerManagerInternal;
     IStatusBarService mStatusBarService;
+    StatusBarManagerInternal mStatusBarManagerInternal;
     boolean mPreloadedRecentApps;
     final Object mServiceAquireLock = new Object();
     Vibrator mVibrator; // Vibrator for giving feedback of orientation changes
@@ -488,6 +493,13 @@
     int mResettingSystemUiFlags = 0;
     // Bits that we are currently always keeping cleared.
     int mForceClearedSystemUiFlags = 0;
+    int mLastFullscreenStackSysUiFlags;
+    int mLastDockedStackSysUiFlags;
+    final Rect mNonDockedStackBounds = new Rect();
+    final Rect mDockedStackBounds = new Rect();
+    final Rect mLastNonDockedStackBounds = new Rect();
+    final Rect mLastDockedStackBounds = new Rect();
+
     // What we last reported to system UI about whether the compatibility
     // menu needs to be displayed.
     boolean mLastFocusNeedsMenu = false;
@@ -508,6 +520,8 @@
 
     WindowState mTopFullscreenOpaqueWindowState;
     WindowState mTopFullscreenOpaqueOrDimmingWindowState;
+    WindowState mTopDockedOpaqueWindowState;
+    WindowState mTopDockedOpaqueOrDimmingWindowState;
     HashSet<IApplicationToken> mAppsToBeHidden = new HashSet<IApplicationToken>();
     HashSet<IApplicationToken> mAppsThatDismissKeyguard = new HashSet<IApplicationToken>();
     boolean mTopIsFullscreen;
@@ -844,6 +858,16 @@
         }
     }
 
+    StatusBarManagerInternal getStatusBarManagerInternal() {
+        synchronized (mServiceAquireLock) {
+            if (mStatusBarManagerInternal == null) {
+                mStatusBarManagerInternal =
+                        LocalServices.getService(StatusBarManagerInternal.class);
+            }
+            return mStatusBarManagerInternal;
+        }
+    }
+
     /*
      * We always let the sensor be switched on by default except when
      * the user has explicitly disabled sensor based rotation or when the
@@ -4552,6 +4576,8 @@
     public void beginPostLayoutPolicyLw(int displayWidth, int displayHeight) {
         mTopFullscreenOpaqueWindowState = null;
         mTopFullscreenOpaqueOrDimmingWindowState = null;
+        mTopDockedOpaqueWindowState = null;
+        mTopDockedOpaqueOrDimmingWindowState = null;
         mAppsToBeHidden.clear();
         mAppsThatDismissKeyguard.clear();
         mForceStatusBar = false;
@@ -4597,7 +4623,7 @@
                 && attrs.type < FIRST_SYSTEM_WINDOW;
         final boolean showWhenLocked = (fl & FLAG_SHOW_WHEN_LOCKED) != 0;
         final boolean dismissKeyguard = (fl & FLAG_DISMISS_KEYGUARD) != 0;
-
+        final int stackId = win.getStackId();
         if (mTopFullscreenOpaqueWindowState == null &&
                 win.isVisibleOrBehindKeyguardLw() && !win.isGoneForLayoutLw()) {
             if ((fl & FLAG_FORCE_NOT_FULLSCREEN) != 0) {
@@ -4646,9 +4672,7 @@
                 } else {
                     mAppsToBeHidden.add(appToken);
                 }
-                if (attrs.x == 0 && attrs.y == 0
-                        && attrs.width == WindowManager.LayoutParams.MATCH_PARENT
-                        && attrs.height == WindowManager.LayoutParams.MATCH_PARENT) {
+                if (isFullscreen(attrs) && StackId.normallyFullscreenWindows(stackId)) {
                     if (DEBUG_LAYOUT) Slog.v(TAG, "Fullscreen window: " + win);
                     mTopFullscreenOpaqueWindowState = win;
                     if (mTopFullscreenOpaqueOrDimmingWindowState == null) {
@@ -4692,11 +4716,37 @@
                 mWinShowWhenLocked = win;
             }
         }
-        if (mTopFullscreenOpaqueOrDimmingWindowState == null
-                && win.isVisibleOrBehindKeyguardLw() && !win.isGoneForLayoutLw()
-                && win.isDimming()) {
+
+        // Keep track of the window if it's dimming but not necessarily fullscreen.
+        final boolean reallyVisible = win.isVisibleOrBehindKeyguardLw() && !win.isGoneForLayoutLw();
+        if (mTopFullscreenOpaqueOrDimmingWindowState == null &&  reallyVisible
+                && win.isDimming() && StackId.normallyFullscreenWindows(stackId)) {
             mTopFullscreenOpaqueOrDimmingWindowState = win;
         }
+
+        // We need to keep track of the top "fullscreen" opaque window for the docked stack
+        // separately, because both the "real fullscreen" opaque window and the one for the docked
+        // stack can control View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.
+        if (mTopDockedOpaqueWindowState == null && reallyVisible && appWindow && attached == null
+                && isFullscreen(attrs) && stackId == DOCKED_STACK_ID) {
+            mTopDockedOpaqueWindowState = win;
+            if (mTopDockedOpaqueOrDimmingWindowState == null) {
+                mTopDockedOpaqueOrDimmingWindowState = win;
+            }
+        }
+
+        // Also keep track of any windows that are dimming but not necessarily fullscreen in the
+        // docked stack.
+        if (mTopDockedOpaqueOrDimmingWindowState == null && reallyVisible && win.isDimming()
+                && stackId == DOCKED_STACK_ID) {
+            mTopDockedOpaqueOrDimmingWindowState = win;
+        }
+    }
+
+    private boolean isFullscreen(WindowManager.LayoutParams attrs) {
+        return attrs.x == 0 && attrs.y == 0
+                && attrs.width == WindowManager.LayoutParams.MATCH_PARENT
+                && attrs.height == WindowManager.LayoutParams.MATCH_PARENT;
     }
 
     /** {@inheritDoc} */
@@ -6838,42 +6888,52 @@
             tmpVisibility |= StatusBarManager.DISABLE_RECENT;
         }
 
-        tmpVisibility = updateLightStatusBarLw(tmpVisibility);
+        final int fullscreenVisibility = updateLightStatusBarLw(0 /* vis */,
+                mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState);
+        final int dockedVisibility = updateLightStatusBarLw(0 /* vis */,
+                mTopDockedOpaqueWindowState, mTopDockedOpaqueOrDimmingWindowState);
+        mWindowManagerFuncs.getStackBounds(HOME_STACK_ID, mNonDockedStackBounds);
+        mWindowManagerFuncs.getStackBounds(DOCKED_STACK_ID, mDockedStackBounds);
         final int visibility = updateSystemBarsLw(win, mLastSystemUiFlags, tmpVisibility);
         final int diff = visibility ^ mLastSystemUiFlags;
+        final int fullscreenDiff = fullscreenVisibility ^ mLastFullscreenStackSysUiFlags;
+        final int dockedDiff = dockedVisibility ^ mLastDockedStackSysUiFlags;
         final boolean needsMenu = win.getNeedsMenuLw(mTopFullscreenOpaqueWindowState);
-        if (diff == 0 && mLastFocusNeedsMenu == needsMenu
-                && mFocusedApp == win.getAppToken()) {
+        if (diff == 0 && fullscreenDiff == 0 && dockedDiff == 0 && mLastFocusNeedsMenu == needsMenu
+                && mFocusedApp == win.getAppToken()
+                && mLastNonDockedStackBounds.equals(mNonDockedStackBounds)
+                && mLastDockedStackBounds.equals(mDockedStackBounds)) {
             return 0;
         }
         mLastSystemUiFlags = visibility;
+        mLastFullscreenStackSysUiFlags = fullscreenVisibility;
+        mLastDockedStackSysUiFlags = dockedVisibility;
         mLastFocusNeedsMenu = needsMenu;
         mFocusedApp = win.getAppToken();
+        final Rect fullscreenStackBounds = new Rect(mNonDockedStackBounds);
+        final Rect dockedStackBounds = new Rect(mDockedStackBounds);
         mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    try {
-                        IStatusBarService statusbar = getStatusBarService();
-                        if (statusbar != null) {
-                            statusbar.setSystemUiVisibility(visibility, 0xffffffff, win.toString());
-                            statusbar.topAppWindowChanged(needsMenu);
-                        }
-                    } catch (RemoteException e) {
-                        // re-acquire status bar service next time it is needed.
-                        mStatusBarService = null;
+                    StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
+                    if (statusbar != null) {
+                        statusbar.setSystemUiVisibility(visibility, fullscreenVisibility,
+                                dockedVisibility, 0xffffffff, fullscreenStackBounds,
+                                dockedStackBounds, win.toString());
+                        statusbar.topAppWindowChanged(needsMenu);
                     }
                 }
             });
         return diff;
     }
 
-    private int updateLightStatusBarLw(int vis) {
+    private int updateLightStatusBarLw(int vis, WindowState opaque, WindowState opaqueOrDimming) {
         WindowState statusColorWin = isStatusBarKeyguard() && !mHideLockScreen
                 ? mStatusBar
-                : mTopFullscreenOpaqueOrDimmingWindowState;
+                : opaqueOrDimming;
 
         if (statusColorWin != null) {
-            if (statusColorWin == mTopFullscreenOpaqueWindowState) {
+            if (statusColorWin == opaque) {
                 // If the top fullscreen-or-dimming window is also the top fullscreen, respect
                 // its light flag.
                 vis &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 25d646d..cbbcdae 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -16,6 +16,7 @@
 
 package com.android.server.statusbar;
 
+import android.graphics.Rect;
 import android.os.Bundle;
 
 import com.android.server.notification.NotificationDelegate;
@@ -29,4 +30,7 @@
     void showAssistDisclosure();
     void startAssist(Bundle args);
     void onCameraLaunchGestureDetected(int source);
+    void topAppWindowChanged(boolean menuVisible);
+    void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis, int mask,
+            Rect fullscreenBounds, Rect dockedBounds, String cause);
 }
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 90340d5..6eab8d4 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -20,6 +20,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.graphics.Rect;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
@@ -70,6 +71,10 @@
     private Object mLock = new Object();
     // encompasses lights-out mode and other flags defined on View
     private int mSystemUiVisibility = 0;
+    private int mFullscreenStackSysUiVisibility;
+    private int mDockedStackSysUiVisibility;
+    private final Rect mFullscreenStackBounds = new Rect();
+    private final Rect mDockedStackBounds = new Rect();
     private boolean mMenuVisible = false;
     private int mImeWindowVis = 0;
     private int mImeBackDisposition;
@@ -186,6 +191,19 @@
                 }
             }
         }
+
+        @Override
+        public void topAppWindowChanged(boolean menuVisible) {
+            StatusBarManagerService.this.topAppWindowChanged(menuVisible);
+        }
+
+        @Override
+        public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
+                int mask,
+                Rect fullscreenBounds, Rect dockedBounds, String cause) {
+            StatusBarManagerService.this.setSystemUiVisibility(vis, fullscreenStackVis,
+                    dockedStackVis, mask, fullscreenBounds, dockedBounds, cause);
+        }
     };
 
     // ================================================================================
@@ -390,8 +408,7 @@
      * response to a window with {@link android.view.WindowManager.LayoutParams#needsMenuKey} set
      * to {@link android.view.WindowManager.LayoutParams#NEEDS_MENU_SET_TRUE}.
      */
-    @Override
-    public void topAppWindowChanged(final boolean menuVisible) {
+    private void topAppWindowChanged(final boolean menuVisible) {
         enforceStatusBar();
 
         if (SPEW) Slog.d(TAG, (menuVisible?"showing":"hiding") + " MENU key");
@@ -399,15 +416,15 @@
         synchronized(mLock) {
             mMenuVisible = menuVisible;
             mHandler.post(new Runnable() {
-                    public void run() {
-                        if (mBar != null) {
-                            try {
-                                mBar.topAppWindowChanged(menuVisible);
-                            } catch (RemoteException ex) {
-                            }
+                public void run() {
+                    if (mBar != null) {
+                        try {
+                            mBar.topAppWindowChanged(menuVisible);
+                        } catch (RemoteException ex) {
                         }
                     }
-                });
+                }
+            });
         }
     }
 
@@ -443,13 +460,19 @@
 
     @Override
     public void setSystemUiVisibility(int vis, int mask, String cause) {
+        setSystemUiVisibility(vis, 0, 0, mask, mFullscreenStackBounds, mDockedStackBounds, cause);
+    }
+
+    private void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis, int mask,
+            Rect fullscreenBounds, Rect dockedBounds, String cause) {
         // also allows calls from window manager which is in this process.
         enforceStatusBarService();
 
         if (SPEW) Slog.d(TAG, "setSystemUiVisibility(0x" + Integer.toHexString(vis) + ")");
 
         synchronized (mLock) {
-            updateUiVisibilityLocked(vis, mask);
+            updateUiVisibilityLocked(vis, fullscreenStackVis, dockedStackVis, mask,
+                    fullscreenBounds, dockedBounds);
             disableLocked(
                     mCurrentUserId,
                     vis & StatusBarManager.DISABLE_MASK,
@@ -458,14 +481,25 @@
         }
     }
 
-    private void updateUiVisibilityLocked(final int vis, final int mask) {
-        if (mSystemUiVisibility != vis) {
+    private void updateUiVisibilityLocked(final int vis,
+            final int fullscreenStackVis, final int dockedStackVis, final int mask,
+            final Rect fullscreenBounds, final Rect dockedBounds) {
+        if (mSystemUiVisibility != vis
+                || mFullscreenStackSysUiVisibility != fullscreenStackVis
+                || mDockedStackSysUiVisibility != dockedStackVis
+                || !mFullscreenStackBounds.equals(fullscreenBounds)
+                || !mDockedStackBounds.equals(dockedBounds)) {
             mSystemUiVisibility = vis;
+            mFullscreenStackSysUiVisibility = fullscreenStackVis;
+            mDockedStackSysUiVisibility = dockedStackVis;
+            mFullscreenStackBounds.set(fullscreenBounds);
+            mDockedStackBounds.set(dockedBounds);
             mHandler.post(new Runnable() {
                     public void run() {
                         if (mBar != null) {
                             try {
-                                mBar.setSystemUiVisibility(vis, mask);
+                                mBar.setSystemUiVisibility(vis, fullscreenStackVis, dockedStackVis,
+                                        mask, fullscreenBounds, dockedBounds);
                             } catch (RemoteException ex) {
                             }
                         }
@@ -617,7 +651,8 @@
     // ================================================================================
     @Override
     public void registerStatusBar(IStatusBar bar, List<String> iconSlots,
-            List<StatusBarIcon> iconList, int switches[], List<IBinder> binders) {
+            List<StatusBarIcon> iconList, int switches[], List<IBinder> binders,
+            Rect fullscreenStackBounds, Rect dockedStackBounds) {
         enforceStatusBarService();
 
         Slog.i(TAG, "registerStatusBar bar=" + bar);
@@ -636,7 +671,11 @@
             switches[4] = mImeBackDisposition;
             switches[5] = mShowImeSwitcher ? 1 : 0;
             switches[6] = gatherDisableActionsLocked(mCurrentUserId, 2);
+            switches[7] = mFullscreenStackSysUiVisibility;
+            switches[8] = mDockedStackSysUiVisibility;
             binders.add(mImeToken);
+            fullscreenStackBounds.set(mFullscreenStackBounds);
+            dockedStackBounds.set(mDockedStackBounds);
         }
     }
 
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
index c3a6f5d..f3b120f 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
@@ -29,8 +29,9 @@
 import android.os.Binder;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.provider.Settings;
-import android.provider.Settings.Secure;
+import android.provider.Settings.Global;
 import android.util.AndroidRuntimeException;
 import android.util.Slog;
 import android.webkit.IWebViewUpdateService;
@@ -90,7 +91,7 @@
                     // change provider when the new version of the package is being installed).
                     if (intent.getAction().equals(Intent.ACTION_PACKAGE_REMOVED)
                         && intent.getExtras().getBoolean(Intent.EXTRA_REPLACING)) {
-                        synchronized(this) {
+                        synchronized(WebViewUpdateService.this) {
                             if (mCurrentWebViewPackage == null) return;
 
                             String webViewPackage = "package:" + mCurrentWebViewPackage.packageName;
@@ -141,7 +142,7 @@
                                 // only kills dependents of packages that are being removed.
                                 try {
                                     ActivityManagerNative.getDefault().killPackageDependents(
-                                        oldProviderName, getContext().getUserId());
+                                        oldProviderName, UserHandle.USER_ALL);
                                 } catch (RemoteException e) {
                                 }
                             }
@@ -209,7 +210,7 @@
         try {
             if (oldPackage != null) {
                 ActivityManagerNative.getDefault().killPackageDependents(
-                        oldPackage.packageName, getContext().getUserId());
+                        oldPackage.packageName, UserHandle.USER_ALL);
             }
         } catch (RemoteException e) {
         }
@@ -267,13 +268,13 @@
     }
 
     private static String getUserChosenWebViewProvider() {
-        return Settings.Secure.getString(AppGlobals.getInitialApplication().getContentResolver(),
-                Settings.Secure.WEBVIEW_PROVIDER);
+        return Settings.Global.getString(AppGlobals.getInitialApplication().getContentResolver(),
+                Settings.Global.WEBVIEW_PROVIDER);
     }
 
     private void updateUserSetting(String newProviderName) {
-        Settings.Secure.putString(getContext().getContentResolver(),
-                Settings.Secure.WEBVIEW_PROVIDER,
+        Settings.Global.putString(getContext().getContentResolver(),
+                Settings.Global.WEBVIEW_PROVIDER,
                 newProviderName == null ? "" : newProviderName);
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java b/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
index 0979cd3..e229c5e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
+++ b/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
@@ -37,8 +37,8 @@
     static final boolean DEBUG = false;
     static final boolean DEBUG_ADD_REMOVE = false;
     static final boolean DEBUG_FOCUS = false;
-    static final boolean DEBUG_FOCUS_LIGHT = DEBUG_FOCUS || false;
-    static final boolean DEBUG_ANIM = false;
+    static final boolean DEBUG_FOCUS_LIGHT = DEBUG_FOCUS || true;
+    static final boolean DEBUG_ANIM = true;
     static final boolean DEBUG_KEYGUARD = false;
     static final boolean DEBUG_LAYOUT = false;
     static final boolean DEBUG_LAYERS = false;
@@ -50,7 +50,7 @@
     static final boolean DEBUG_ORIENTATION = false;
     static final boolean DEBUG_APP_ORIENTATION = false;
     static final boolean DEBUG_CONFIGURATION = false;
-    static final boolean DEBUG_APP_TRANSITIONS = false;
+    static final boolean DEBUG_APP_TRANSITIONS = true;
     static final boolean DEBUG_STARTING_WINDOW = false;
     static final boolean DEBUG_WALLPAPER = false;
     static final boolean DEBUG_WALLPAPER_LIGHT = false || DEBUG_WALLPAPER;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index d1ffaa0..f01ffb5 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1192,7 +1192,7 @@
                 break;
             }
         }
-        if (DEBUG_FOCUS_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
+        if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
                 "Based on layer: Adding window " + win + " at " + (i + 1) + " of "
                         + windows.size());
         windows.add(i + 1, win);
@@ -1224,7 +1224,7 @@
                 //apptoken note that the window could be a floating window
                 //that was created later or a window at the top of the list of
                 //windows associated with this token.
-                if (DEBUG_FOCUS_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
+                if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
                         "not Base app: Adding window " + win + " at " + (newIdx + 1) + " of "
                                 + windows.size());
                 windows.add(newIdx + 1, win);
@@ -1262,7 +1262,7 @@
             }
         }
         i++;
-        if (DEBUG_FOCUS_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
+        if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
                 "Free window: Adding window " + win + " at " + i + " of " + windows.size());
         windows.add(i, win);
         mWindowsChanged = true;
@@ -1333,7 +1333,7 @@
     }
 
     private void addWindowToListInOrderLocked(final WindowState win, boolean addToToken) {
-        if (DEBUG_FOCUS_LIGHT) Slog.d(TAG_WM, "addWindowToListInOrderLocked: win=" + win +
+        if (DEBUG_FOCUS) Slog.d(TAG_WM, "addWindowToListInOrderLocked: win=" + win +
                 " Callers=" + Debug.getCallers(4));
         if (win.mAttachedWindow == null) {
             final WindowToken token = win.mToken;
@@ -4236,7 +4236,6 @@
 
             mOpeningApps.remove(wtoken);
             mClosingApps.remove(wtoken);
-            wtoken.mAppStopped = false;
             wtoken.waitingToShow = false;
             wtoken.hiddenRequested = !visible;
 
@@ -4246,6 +4245,8 @@
                 // if made visible again.
                 wtoken.appDied = false;
                 wtoken.removeAllWindows();
+            } else if (visible) {
+                wtoken.mAppStopped = false;
             }
 
             // If we are preparing an app transition, then delay changing
@@ -4863,6 +4864,7 @@
         }
     }
 
+    @Override
     public void getStackBounds(int stackId, Rect bounds) {
         synchronized (mWindowMap) {
             final TaskStack stack = mStackIdToStack.get(stackId);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 2096ea0..5b9206d 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2088,7 +2088,8 @@
         return mTmpRect;
     }
 
-    private int getStackId() {
+    @Override
+    public int getStackId() {
         final TaskStack stack = getStack();
         if (stack == null) {
             return INVALID_STACK_ID;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 0201296..f8f8363 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1347,7 +1347,7 @@
             }
         } else {
             if (DEBUG_ANIM && isAnimating()) {
-                Slog.v(TAG, "prepareSurface: No changes in animation for " + this);
+                //Slog.v(TAG, "prepareSurface: No changes in animation for " + this);
             }
             displayed = true;
         }
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index e75775f..cdd5519 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -68,13 +68,14 @@
 static const GpsNavigationMessageInterface* sGpsNavigationMessageInterface = NULL;
 static const GnssConfigurationInterface* sGnssConfigurationInterface = NULL;
 
-#define MAX_SATELLITE_COUNT 512
-#define MAX_GPS_SATELLITE_COUNT 512
+#define GPS_MAX_SATELLITE_COUNT 32
+#define GNSS_MAX_SATELLITE_COUNT 64
 
-#define PRN_SHIFT_WIDTH 3
+#define SVID_SHIFT_WIDTH 7
+#define CONSTELLATION_TYPE_SHIFT_WIDTH 3
 
 // temporary storage for GPS callbacks
-static GnssSvInfo sGnssSvList[MAX_SATELLITE_COUNT];
+static GnssSvInfo sGnssSvList[GNSS_MAX_SATELLITE_COUNT];
 static size_t sGnssSvListSize;
 static const char* sNmeaString;
 static int sNmeaStringLength;
@@ -113,56 +114,75 @@
 {
     JNIEnv* env = AndroidRuntime::getJNIEnv();
     size_t status_size = sv_status->size;
-    // Some drive doesn't set the size field correctly. Assume GpsSvStatus_v1 if
-    // it doesn't provide a valid size.
+    // Some drives doesn't set the size field correctly. Assume GpsSvStatus_v1
+    // if it doesn't provide a valid size.
     if (status_size == 0) {
-        status_size = sizeof(GpsSvStatus_v1);
+        ALOGW("Invalid size of GpsSvStatus found: %zd.", status_size);
     }
-    if (status_size == sizeof(GpsSvStatus)) {
-        sGnssSvListSize = sv_status->gnss_sv_list_size;
-        // Cramp the list size
-        if (sGnssSvListSize > MAX_SATELLITE_COUNT) {
-            sGnssSvListSize = MAX_SATELLITE_COUNT;
-        }
-        // Copy GNSS SV info into sGnssSvList, if any.
-        if (sGnssSvListSize > 0 && sv_status->gnss_sv_list) {
-            memcpy(sGnssSvList, sv_status->gnss_sv_list, sizeof(GnssSvInfo) * sGnssSvListSize);
-        }
-    } else if (status_size == sizeof(GpsSvStatus_v1)) {
-        sGnssSvListSize = sv_status->num_svs;
-        // Cramp the list size
-        if (sGnssSvListSize > MAX_GPS_SATELLITE_COUNT) {
-            sGnssSvListSize = MAX_GPS_SATELLITE_COUNT;
-        }
-        uint32_t ephemeris_mask = sv_status->ephemeris_mask;
-        uint32_t almanac_mask = sv_status->almanac_mask;
-        uint32_t used_in_fix_mask = sv_status->used_in_fix_mask;
-        for (size_t i = 0; i < sGnssSvListSize; i++) {
-            GnssSvInfo& info = sGnssSvList[i];
+    sGnssSvListSize = sv_status->num_svs;
+    // Clamp the list size. Legacy GpsSvStatus has only 32 elements in sv_list.
+    if (sGnssSvListSize > GPS_MAX_SATELLITE_COUNT) {
+        ALOGW("Too many satellites %zd. Clamps to %d.",
+              sGnssSvListSize,
+              GPS_MAX_SATELLITE_COUNT);
+        sGnssSvListSize = GPS_MAX_SATELLITE_COUNT;
+    }
+    uint32_t ephemeris_mask = sv_status->ephemeris_mask;
+    uint32_t almanac_mask = sv_status->almanac_mask;
+    uint32_t used_in_fix_mask = sv_status->used_in_fix_mask;
+    for (size_t i = 0; i < sGnssSvListSize; i++) {
+        GnssSvInfo& info = sGnssSvList[i];
+        info.svid = sv_status->sv_list[i].prn;
+        // TODO: implement the correct logic to derive the constellation type
+        // based on PRN ranges.
+        if (info.svid >=1 && info.svid <= 32) {
             info.constellation = GNSS_CONSTELLATION_GPS;
-            info.prn = sv_status->sv_list[i].prn;
-            info.snr = sv_status->sv_list[i].snr;
-            info.elevation = sv_status->sv_list[i].elevation;
-            info.azimuth = sv_status->sv_list[i].azimuth;
-            info.flags = GNSS_SV_FLAGS_NONE;
-            if (info.prn > 0 && info.prn <= 32) {
-              int32_t this_prn_mask = (1 << (info.prn - 1));
-              if ((ephemeris_mask & this_prn_mask) != 0) {
+        } else {
+            info.constellation = GNSS_CONSTELLATION_UNKNOWN;
+        }
+        info.snr = sv_status->sv_list[i].snr;
+        info.elevation = sv_status->sv_list[i].elevation;
+        info.azimuth = sv_status->sv_list[i].azimuth;
+        info.flags = GNSS_SV_FLAGS_NONE;
+        if (info.svid > 0 && info.svid <= 32) {
+            int32_t this_svid_mask = (1 << (info.svid - 1));
+            if ((ephemeris_mask & this_svid_mask) != 0) {
                 info.flags |= GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA;
-              }
-              if ((almanac_mask & this_prn_mask) != 0) {
+            }
+            if ((almanac_mask & this_svid_mask) != 0) {
                 info.flags |= GNSS_SV_FLAGS_HAS_ALMANAC_DATA;
-              }
-              if ((used_in_fix_mask & this_prn_mask) != 0) {
+            }
+            if ((used_in_fix_mask & this_svid_mask) != 0) {
                 info.flags |= GNSS_SV_FLAGS_USED_IN_FIX;
-              }
             }
         }
-    } else {
-        sGnssSvListSize = 0;
-        ALOGE("Invalid size of GpsSvStatus found: %zd.", status_size);
+    }
+    env->CallVoidMethod(mCallbacksObj, method_reportSvStatus);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+static void gnss_sv_status_callback(GnssSvStatus* sv_status) {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    size_t status_size = sv_status->size;
+    // Check the size, and reject the object that has invalid size.
+    if (status_size != sizeof(GnssSvStatus)) {
+        ALOGE("Invalid size of GnssSvStatus found: %zd.", status_size);
         return;
     }
+    sGnssSvListSize = sv_status->num_svs;
+    // Clamp the list size
+    if (sGnssSvListSize > GNSS_MAX_SATELLITE_COUNT) {
+        ALOGD("Too many satellites %zd. Clamps to %d.",
+              sGnssSvListSize,
+              GNSS_MAX_SATELLITE_COUNT);
+        sGnssSvListSize = GNSS_MAX_SATELLITE_COUNT;
+    }
+    // Copy GNSS SV info into sGnssSvList, if any.
+    if (sGnssSvListSize > 0) {
+        memcpy(sGnssSvList,
+               sv_status->gnss_sv_list,
+               sizeof(GnssSvInfo) * sGnssSvListSize);
+    }
     env->CallVoidMethod(mCallbacksObj, method_reportSvStatus);
     checkAndClearExceptionFromCallback(env, __FUNCTION__);
 }
@@ -228,6 +248,7 @@
     create_thread_callback,
     request_utc_time_callback,
     set_system_info_callback,
+    gnss_sv_status_callback,
 };
 
 static void xtra_download_request_callback()
@@ -677,31 +698,30 @@
 }
 
 static jint android_location_GnssLocationProvider_read_sv_status(JNIEnv* env, jobject /* obj */,
-        jintArray prnWithFlagArray, jfloatArray snrArray, jfloatArray elevArray,
-        jfloatArray azumArray, jintArray constellationTypeArray)
+        jintArray svidWithFlagArray, jfloatArray snrArray, jfloatArray elevArray,
+        jfloatArray azumArray)
 {
     // this should only be called from within a call to reportSvStatus
-    jint* prnWithFlags = env->GetIntArrayElements(prnWithFlagArray, 0);
+    jint* svidWithFlags = env->GetIntArrayElements(svidWithFlagArray, 0);
     jfloat* snrs = env->GetFloatArrayElements(snrArray, 0);
     jfloat* elev = env->GetFloatArrayElements(elevArray, 0);
     jfloat* azim = env->GetFloatArrayElements(azumArray, 0);
-    jint* constellationTypes = env->GetIntArrayElements(constellationTypeArray, 0);
 
     // GNSS SV info.
     for (size_t i = 0; i < sGnssSvListSize; ++i) {
         const GnssSvInfo& info = sGnssSvList[i];
-        constellationTypes[i] = info.constellation;
-        prnWithFlags[i] = (info.prn << PRN_SHIFT_WIDTH) | info.flags;
+        svidWithFlags[i] = (info.svid << SVID_SHIFT_WIDTH) |
+            (info.constellation << CONSTELLATION_TYPE_SHIFT_WIDTH) |
+            info.flags;
         snrs[i] = info.snr;
         elev[i] = info.elevation;
         azim[i] = info.azimuth;
     }
 
-    env->ReleaseIntArrayElements(prnWithFlagArray, prnWithFlags, 0);
+    env->ReleaseIntArrayElements(svidWithFlagArray, svidWithFlags, 0);
     env->ReleaseFloatArrayElements(snrArray, snrs, 0);
     env->ReleaseFloatArrayElements(elevArray, elev, 0);
     env->ReleaseFloatArrayElements(azumArray, azim, 0);
-    env->ReleaseIntArrayElements(constellationTypeArray, constellationTypes, 0);
     return (jint) sGnssSvListSize;
 }
 
@@ -968,370 +988,341 @@
     return JNI_FALSE;
 }
 
-static jobject translate_gps_clock(JNIEnv* env, void* data, size_t size) {
-    const char* doubleSignature = "(D)V";
-    const char* longSignature = "(J)V";
+template<class T>
+class JavaMethodHelper {
+  public:
+   // Helper function to call setter on a Java object.
+   static void callJavaMethod(
+           JNIEnv* env,
+           jclass clazz,
+           jobject object,
+           const char* method_name,
+           T value);
 
-    GpsClock* clock = reinterpret_cast<GpsClock*>(data);
+  private:
+    static const char *const signature_;
+};
 
-    jclass gpsClockClass = env->FindClass("android/location/GnssClock");
-    jmethodID gpsClockCtor = env->GetMethodID(gpsClockClass, "<init>", "()V");
+template<class T>
+void JavaMethodHelper<T>::callJavaMethod(
+        JNIEnv* env,
+        jclass clazz,
+        jobject object,
+        const char* method_name,
+        T value) {
+    jmethodID method = env->GetMethodID(clazz, method_name, signature_);
+    env->CallVoidMethod(object, method, value);
+}
 
-    jobject gpsClockObject = env->NewObject(gpsClockClass, gpsClockCtor);
+class JavaObject {
+  public:
+   JavaObject(JNIEnv* env, const char* class_name);
+   virtual ~JavaObject();
+
+   template<class T>
+   void callSetter(const char* method_name, T value);
+   template<class T>
+   void callSetter(const char* method_name, T* value, size_t size);
+   jobject get();
+
+  private:
+   JNIEnv* env_;
+   jclass clazz_;
+   jobject object_;
+};
+
+JavaObject::JavaObject(JNIEnv* env, const char* class_name) : env_(env) {
+    clazz_ = env_->FindClass(class_name);
+    jmethodID ctor = env->GetMethodID(clazz_, "<init>", "()V");
+    object_ = env_->NewObject(clazz_, ctor);
+}
+
+JavaObject::~JavaObject() {
+    env_->DeleteLocalRef(clazz_);
+}
+
+template<class T>
+void JavaObject::callSetter(const char* method_name, T value) {
+    JavaMethodHelper<T>::callJavaMethod(
+            env_, clazz_, object_, method_name, value);
+}
+
+template<>
+void JavaObject::callSetter(
+        const char* method_name, uint8_t* value, size_t size) {
+    jbyteArray array = env_->NewByteArray(size);
+    env_->SetByteArrayRegion(array, 0, size, (jbyte*) value);
+    jmethodID method = env_->GetMethodID(
+            clazz_,
+            method_name,
+            "([B)V");
+    env_->CallVoidMethod(object_, method, array);
+}
+
+jobject JavaObject::get() {
+    return object_;
+}
+
+// Define Java method signatures for all known types.
+
+template<>
+const char *const JavaMethodHelper<uint8_t>::signature_ = "(B)V";
+template<>
+const char *const JavaMethodHelper<int8_t>::signature_ = "(B)V";
+template<>
+const char *const JavaMethodHelper<int16_t>::signature_ = "(S)V";
+template<>
+const char *const JavaMethodHelper<uint16_t>::signature_ = "(S)V";
+template<>
+const char *const JavaMethodHelper<int>::signature_ = "(I)V";
+template<>
+const char *const JavaMethodHelper<int64_t>::signature_ = "(J)V";
+template<>
+const char *const JavaMethodHelper<float>::signature_ = "(F)V";
+template<>
+const char *const JavaMethodHelper<double>::signature_ = "(D)V";
+template<>
+const char *const JavaMethodHelper<bool>::signature_ = "(Z)V";
+
+#define SET(setter, value) object.callSetter("set" # setter, (value))
+#define SET_IF(flag, setter, value) \
+        if (flags & (flag)) object.callSetter("set" # setter, (value))
+
+static jobject translate_gps_clock(JNIEnv* env, GpsClock* clock) {
+    JavaObject object(env, "android/location/GnssClock");
     GpsClockFlags flags = clock->flags;
 
-    if (flags & GPS_CLOCK_HAS_LEAP_SECOND) {
-        jmethodID setterMethod = env->GetMethodID(gpsClockClass, "setLeapSecond", "(S)V");
-        env->CallVoidMethod(gpsClockObject, setterMethod, clock->leap_second);
-   }
+    SET_IF(GPS_CLOCK_HAS_LEAP_SECOND, LeapSecond, clock->leap_second);
+    SET(Type, clock->type);
+    SET(TimeInNs, clock->time_ns);
+    SET_IF(GPS_CLOCK_HAS_TIME_UNCERTAINTY,
+           TimeUncertaintyInNs,
+           clock->time_uncertainty_ns);
+    SET_IF(GPS_CLOCK_HAS_FULL_BIAS, FullBiasInNs, clock->full_bias_ns);
+    SET_IF(GPS_CLOCK_HAS_BIAS, BiasInNs, clock->bias_ns);
+    SET_IF(GPS_CLOCK_HAS_BIAS_UNCERTAINTY,
+           BiasUncertaintyInNs,
+           clock->bias_uncertainty_ns);
+    SET_IF(GPS_CLOCK_HAS_DRIFT, DriftInNsPerSec, clock->drift_nsps);
+    SET_IF(GPS_CLOCK_HAS_DRIFT_UNCERTAINTY,
+           DriftUncertaintyInNsPerSec,
+           clock->drift_uncertainty_nsps);
 
-   jmethodID typeSetterMethod = env->GetMethodID(gpsClockClass, "setType", "(B)V");
-   env->CallVoidMethod(gpsClockObject, typeSetterMethod, clock->type);
-
-    jmethodID setterMethod = env->GetMethodID(gpsClockClass, "setTimeInNs", longSignature);
-    env->CallVoidMethod(gpsClockObject, setterMethod, clock->time_ns);
-
-    if (flags & GPS_CLOCK_HAS_TIME_UNCERTAINTY) {
-        jmethodID setterMethod =
-                env->GetMethodID(gpsClockClass, "setTimeUncertaintyInNs", doubleSignature);
-        env->CallVoidMethod(gpsClockObject, setterMethod, clock->time_uncertainty_ns);
-    }
-
-    if (flags & GPS_CLOCK_HAS_FULL_BIAS) {
-        jmethodID setterMethod = env->GetMethodID(gpsClockClass, "setFullBiasInNs", longSignature);
-        env->CallVoidMethod(gpsClockObject, setterMethod, clock->full_bias_ns);
-    }
-
-    if (flags & GPS_CLOCK_HAS_BIAS) {
-        jmethodID setterMethod = env->GetMethodID(gpsClockClass, "setBiasInNs", doubleSignature);
-        env->CallVoidMethod(gpsClockObject, setterMethod, clock->bias_ns);
-    }
-
-    if (flags & GPS_CLOCK_HAS_BIAS_UNCERTAINTY) {
-        jmethodID setterMethod =
-                env->GetMethodID(gpsClockClass, "setBiasUncertaintyInNs", doubleSignature);
-        env->CallVoidMethod(gpsClockObject, setterMethod, clock->bias_uncertainty_ns);
-    }
-
-    if (flags & GPS_CLOCK_HAS_DRIFT) {
-        jmethodID setterMethod =
-                env->GetMethodID(gpsClockClass, "setDriftInNsPerSec", doubleSignature);
-        env->CallVoidMethod(gpsClockObject, setterMethod, clock->drift_nsps);
-    }
-
-    if (flags & GPS_CLOCK_HAS_DRIFT_UNCERTAINTY) {
-        jmethodID setterMethod =
-                env->GetMethodID(gpsClockClass, "setDriftUncertaintyInNsPerSec", doubleSignature);
-        env->CallVoidMethod(gpsClockObject, setterMethod, clock->drift_uncertainty_nsps);
-    }
-
+    /*
     if (flags & GPS_CLOCK_TYPE_LOCAL_HW_TIME) {
-        if (size == sizeof(GpsClock)) {
+        if (size == sizeof(GnssClock)) {
             jmethodID setterMethod =
                     env->GetMethodID(gpsClockClass,
                                      "setTimeOfLastHwClockDiscontinuityInNs",
                                      longSignature);
             env->CallVoidMethod(gpsClockObject,
                                 setterMethod,
-                                clock->time_of_last_hw_clock_discontinuity_ns);
+                                reinterpret_cast<GnssClock*>(clock)->time_of_last_hw_clock_discontinuity_ns);
         }
     }
+    */
 
-    env->DeleteLocalRef(gpsClockClass);
-    return gpsClockObject;
+    return object.get();
 }
 
-static jobject translate_gps_measurement(JNIEnv* env, void* data, size_t size) {
-    const char* byteSignature = "(B)V";
-    const char* shortSignature = "(S)V";
-    const char* intSignature = "(I)V";
-    const char* longSignature = "(J)V";
-    const char* floatSignature = "(F)V";
-    const char* doubleSignature = "(D)V";
+static jobject translate_gnss_clock(JNIEnv* env, GnssClock* clock) {
+    JavaObject object(env, "android/location/GnssClock");
+    GpsClockFlags flags = clock->flags;
 
-    jclass gnssMeasurementClass = env->FindClass("android/location/GnssMeasurement");
-    jmethodID gnssMeasurementCtor = env->GetMethodID(gnssMeasurementClass, "<init>", "()V");
-    GpsMeasurement* measurement = reinterpret_cast<GpsMeasurement*>(data);
+    SET_IF(GPS_CLOCK_HAS_LEAP_SECOND, LeapSecond, clock->leap_second);
+    SET(Type, clock->type);
+    SET(TimeInNs, clock->time_ns);
+    SET_IF(GPS_CLOCK_HAS_TIME_UNCERTAINTY,
+           TimeUncertaintyInNs,
+           clock->time_uncertainty_ns);
+    SET_IF(GPS_CLOCK_HAS_FULL_BIAS, FullBiasInNs, clock->full_bias_ns);
+    SET_IF(GPS_CLOCK_HAS_BIAS, BiasInNs, clock->bias_ns);
+    SET_IF(GPS_CLOCK_HAS_BIAS_UNCERTAINTY,
+           BiasUncertaintyInNs,
+           clock->bias_uncertainty_ns);
+    SET_IF(GPS_CLOCK_HAS_DRIFT, DriftInNsPerSec, clock->drift_nsps);
+    SET_IF(GPS_CLOCK_HAS_DRIFT_UNCERTAINTY,
+           DriftUncertaintyInNsPerSec,
+           clock->drift_uncertainty_nsps);
 
-    jobject gnssMeasurementObject = env->NewObject(gnssMeasurementClass, gnssMeasurementCtor);
+    SET_IF(GPS_CLOCK_TYPE_LOCAL_HW_TIME,
+           TimeOfLastHwClockDiscontinuityInNs,
+           clock->time_of_last_hw_clock_discontinuity_ns);
+
+    return object.get();
+}
+
+static jobject translate_gps_measurement(JNIEnv* env,
+                                         GpsMeasurement* measurement) {
+    JavaObject object(env, "android/location/GnssMeasurement");
     GpsMeasurementFlags flags = measurement->flags;
 
-    jmethodID prnSetterMethod = env->GetMethodID(gnssMeasurementClass, "setPrn", byteSignature);
-    env->CallVoidMethod(gnssMeasurementObject, prnSetterMethod, measurement->prn);
+    SET(Svid, static_cast<int16_t>(measurement->prn));
+    SET(TimeOffsetInNs, measurement->time_offset_ns);
+    SET(State, measurement->state);
+    SET(ReceivedGpsTowInNs, measurement->received_gps_tow_ns);
+    SET(ReceivedGpsTowUncertaintyInNs,
+        measurement->received_gps_tow_uncertainty_ns);
+    SET(Cn0InDbHz, measurement->c_n0_dbhz);
+    SET(PseudorangeRateInMetersPerSec, measurement->pseudorange_rate_mps);
+    SET(PseudorangeRateUncertaintyInMetersPerSec,
+        measurement->pseudorange_rate_uncertainty_mps);
+    SET(AccumulatedDeltaRangeState, measurement->accumulated_delta_range_state);
+    SET(AccumulatedDeltaRangeInMeters, measurement->accumulated_delta_range_m);
+    SET(AccumulatedDeltaRangeUncertaintyInMeters,
+        measurement->accumulated_delta_range_uncertainty_m);
+    SET_IF(GPS_MEASUREMENT_HAS_PSEUDORANGE,
+           PseudorangeInMeters,
+           measurement->pseudorange_m);
+    SET_IF(GPS_MEASUREMENT_HAS_PSEUDORANGE_UNCERTAINTY,
+           PseudorangeUncertaintyInMeters,
+           measurement->pseudorange_uncertainty_m);
+    SET_IF(GPS_MEASUREMENT_HAS_CODE_PHASE,
+           CodePhaseInChips,
+           measurement->code_phase_chips);
+    SET_IF(GPS_MEASUREMENT_HAS_CODE_PHASE_UNCERTAINTY,
+           CodePhaseUncertaintyInChips,
+           measurement->code_phase_uncertainty_chips);
+    SET_IF(GPS_MEASUREMENT_HAS_CARRIER_FREQUENCY,
+           CarrierFrequencyInHz,
+           measurement->carrier_frequency_hz);
+    SET_IF(GPS_MEASUREMENT_HAS_CARRIER_CYCLES,
+           CarrierCycles,
+           measurement->carrier_cycles);
+    SET_IF(GPS_MEASUREMENT_HAS_CARRIER_PHASE,
+           CarrierPhase,
+           measurement->carrier_phase);
+    SET_IF(GPS_MEASUREMENT_HAS_CARRIER_PHASE_UNCERTAINTY,
+           CarrierPhaseUncertainty,
+           measurement->carrier_phase_uncertainty);
+    SET(LossOfLock, measurement->loss_of_lock);
+    SET_IF(GPS_MEASUREMENT_HAS_BIT_NUMBER, BitNumber, measurement->bit_number);
+    SET_IF(GPS_MEASUREMENT_HAS_TIME_FROM_LAST_BIT,
+           TimeFromLastBitInMs,
+           measurement->time_from_last_bit_ms);
+    SET_IF(GPS_MEASUREMENT_HAS_DOPPLER_SHIFT,
+           DopplerShiftInHz,
+           measurement->doppler_shift_hz);
+    SET_IF(GPS_MEASUREMENT_HAS_DOPPLER_SHIFT_UNCERTAINTY,
+           DopplerShiftUncertaintyInHz,
+           measurement->doppler_shift_uncertainty_hz);
+    SET(MultipathIndicator, measurement->multipath_indicator);
+    SET_IF(GPS_MEASUREMENT_HAS_SNR, SnrInDb, measurement->snr_db);
+    SET_IF(GPS_MEASUREMENT_HAS_ELEVATION,
+           ElevationInDeg,
+           measurement->elevation_deg);
+    SET_IF(GPS_MEASUREMENT_HAS_ELEVATION_UNCERTAINTY,
+           ElevationUncertaintyInDeg,
+           measurement->elevation_uncertainty_deg);
+    SET_IF(GPS_MEASUREMENT_HAS_AZIMUTH,
+           AzimuthInDeg,
+           measurement->azimuth_deg);
+    SET_IF(GPS_MEASUREMENT_HAS_AZIMUTH_UNCERTAINTY,
+           AzimuthUncertaintyInDeg,
+           measurement->azimuth_uncertainty_deg);
+    SET(UsedInFix,
+        (flags & GPS_MEASUREMENT_HAS_USED_IN_FIX) && measurement->used_in_fix);
 
-    jmethodID timeOffsetSetterMethod =
-            env->GetMethodID(gnssMeasurementClass, "setTimeOffsetInNs", doubleSignature);
-    env->CallVoidMethod(
-            gnssMeasurementObject,
-            timeOffsetSetterMethod,
-            measurement->time_offset_ns);
-
-    jmethodID stateSetterMethod = env->GetMethodID(gnssMeasurementClass, "setState", shortSignature);
-    env->CallVoidMethod(gnssMeasurementObject, stateSetterMethod, measurement->state);
-
-    jmethodID receivedGpsTowSetterMethod =
-            env->GetMethodID(gnssMeasurementClass, "setReceivedGpsTowInNs", longSignature);
-    env->CallVoidMethod(
-            gnssMeasurementObject,
-            receivedGpsTowSetterMethod,
-            measurement->received_gps_tow_ns);
-
-    jmethodID receivedGpsTowUncertaintySetterMethod = env->GetMethodID(
-            gnssMeasurementClass,
-            "setReceivedGpsTowUncertaintyInNs",
-            longSignature);
-    env->CallVoidMethod(
-            gnssMeasurementObject,
-            receivedGpsTowUncertaintySetterMethod,
-            measurement->received_gps_tow_uncertainty_ns);
-
-    jmethodID cn0SetterMethod =
-            env->GetMethodID(gnssMeasurementClass, "setCn0InDbHz", doubleSignature);
-    env->CallVoidMethod(gnssMeasurementObject, cn0SetterMethod, measurement->c_n0_dbhz);
-
-    jmethodID pseudorangeRateSetterMethod = env->GetMethodID(
-            gnssMeasurementClass,
-            "setPseudorangeRateInMetersPerSec",
-            doubleSignature);
-    env->CallVoidMethod(
-            gnssMeasurementObject,
-            pseudorangeRateSetterMethod,
-            measurement->pseudorange_rate_mps);
-
-    jmethodID pseudorangeRateUncertaintySetterMethod = env->GetMethodID(
-            gnssMeasurementClass,
-            "setPseudorangeRateUncertaintyInMetersPerSec",
-            doubleSignature);
-    env->CallVoidMethod(
-            gnssMeasurementObject,
-            pseudorangeRateUncertaintySetterMethod,
-            measurement->pseudorange_rate_uncertainty_mps);
-
-    jmethodID accumulatedDeltaRangeStateSetterMethod =
-            env->GetMethodID(gnssMeasurementClass, "setAccumulatedDeltaRangeState", shortSignature);
-    env->CallVoidMethod(
-            gnssMeasurementObject,
-            accumulatedDeltaRangeStateSetterMethod,
-            measurement->accumulated_delta_range_state);
-
-    jmethodID accumulatedDeltaRangeSetterMethod = env->GetMethodID(
-            gnssMeasurementClass,
-            "setAccumulatedDeltaRangeInMeters",
-            doubleSignature);
-    env->CallVoidMethod(
-            gnssMeasurementObject,
-            accumulatedDeltaRangeSetterMethod,
-            measurement->accumulated_delta_range_m);
-
-    jmethodID accumulatedDeltaRangeUncertaintySetterMethod = env->GetMethodID(
-            gnssMeasurementClass,
-            "setAccumulatedDeltaRangeUncertaintyInMeters",
-            doubleSignature);
-    env->CallVoidMethod(
-            gnssMeasurementObject,
-            accumulatedDeltaRangeUncertaintySetterMethod,
-            measurement->accumulated_delta_range_uncertainty_m);
-
-    if (flags & GPS_MEASUREMENT_HAS_PSEUDORANGE) {
-        jmethodID setterMethod =
-                env->GetMethodID(gnssMeasurementClass, "setPseudorangeInMeters", doubleSignature);
-        env->CallVoidMethod(gnssMeasurementObject, setterMethod, measurement->pseudorange_m);
-    }
-
-    if (flags & GPS_MEASUREMENT_HAS_PSEUDORANGE_UNCERTAINTY) {
-        jmethodID setterMethod = env->GetMethodID(
-                gnssMeasurementClass,
-                "setPseudorangeUncertaintyInMeters",
-                doubleSignature);
-        env->CallVoidMethod(
-                gnssMeasurementObject,
-                setterMethod,
-                measurement->pseudorange_uncertainty_m);
-    }
-
-    if (flags & GPS_MEASUREMENT_HAS_CODE_PHASE) {
-        jmethodID setterMethod =
-                env->GetMethodID(gnssMeasurementClass, "setCodePhaseInChips", doubleSignature);
-        env->CallVoidMethod(gnssMeasurementObject, setterMethod, measurement->code_phase_chips);
-    }
-
-    if (flags & GPS_MEASUREMENT_HAS_CODE_PHASE_UNCERTAINTY) {
-        jmethodID setterMethod = env->GetMethodID(
-                gnssMeasurementClass,
-                "setCodePhaseUncertaintyInChips",
-                doubleSignature);
-        env->CallVoidMethod(
-                gnssMeasurementObject,
-                setterMethod,
-                measurement->code_phase_uncertainty_chips);
-    }
-
-    if (flags & GPS_MEASUREMENT_HAS_CARRIER_FREQUENCY) {
-        jmethodID setterMethod =
-                env->GetMethodID(gnssMeasurementClass, "setCarrierFrequencyInHz", floatSignature);
-        env->CallVoidMethod(
-                gnssMeasurementObject,
-                setterMethod,
-                measurement->carrier_frequency_hz);
-    }
-
-    if (flags & GPS_MEASUREMENT_HAS_CARRIER_CYCLES) {
-        jmethodID setterMethod =
-                env->GetMethodID(gnssMeasurementClass, "setCarrierCycles", longSignature);
-        env->CallVoidMethod(gnssMeasurementObject, setterMethod, measurement->carrier_cycles);
-    }
-
-    if (flags & GPS_MEASUREMENT_HAS_CARRIER_PHASE) {
-        jmethodID setterMethod =
-                env->GetMethodID(gnssMeasurementClass, "setCarrierPhase", doubleSignature);
-        env->CallVoidMethod(gnssMeasurementObject, setterMethod, measurement->carrier_phase);
-    }
-
-    if (flags & GPS_MEASUREMENT_HAS_CARRIER_PHASE_UNCERTAINTY) {
-        jmethodID setterMethod = env->GetMethodID(
-                gnssMeasurementClass,
-                "setCarrierPhaseUncertainty",
-                doubleSignature);
-        env->CallVoidMethod(
-                gnssMeasurementObject,
-                setterMethod,
-                measurement->carrier_phase_uncertainty);
-    }
-
-    jmethodID lossOfLockSetterMethod =
-            env->GetMethodID(gnssMeasurementClass, "setLossOfLock", byteSignature);
-    env->CallVoidMethod(gnssMeasurementObject, lossOfLockSetterMethod, measurement->loss_of_lock);
-
-    if (flags & GPS_MEASUREMENT_HAS_BIT_NUMBER) {
-        jmethodID setterMethod =
-                env->GetMethodID(gnssMeasurementClass, "setBitNumber", intSignature);
-        env->CallVoidMethod(gnssMeasurementObject, setterMethod, measurement->bit_number);
-    }
-
-    if (flags & GPS_MEASUREMENT_HAS_TIME_FROM_LAST_BIT) {
-        jmethodID setterMethod =
-                env->GetMethodID(gnssMeasurementClass, "setTimeFromLastBitInMs", shortSignature);
-        env->CallVoidMethod(
-                gnssMeasurementObject,
-                setterMethod,
-                measurement->time_from_last_bit_ms);
-    }
-
-    if (flags & GPS_MEASUREMENT_HAS_DOPPLER_SHIFT) {
-        jmethodID setterMethod =
-                env->GetMethodID(gnssMeasurementClass, "setDopplerShiftInHz", doubleSignature);
-        env->CallVoidMethod(gnssMeasurementObject, setterMethod, measurement->doppler_shift_hz);
-    }
-
-    if (flags & GPS_MEASUREMENT_HAS_DOPPLER_SHIFT_UNCERTAINTY) {
-        jmethodID setterMethod = env->GetMethodID(
-                gnssMeasurementClass,
-                "setDopplerShiftUncertaintyInHz",
-                doubleSignature);
-        env->CallVoidMethod(
-                gnssMeasurementObject,
-                setterMethod,
-                measurement->doppler_shift_uncertainty_hz);
-    }
-
-    jmethodID multipathIndicatorSetterMethod =
-            env->GetMethodID(gnssMeasurementClass, "setMultipathIndicator", byteSignature);
-    env->CallVoidMethod(
-            gnssMeasurementObject,
-            multipathIndicatorSetterMethod,
-            measurement->multipath_indicator);
-
-    if (flags & GPS_MEASUREMENT_HAS_SNR) {
-        jmethodID setterMethod =
-                env->GetMethodID(gnssMeasurementClass, "setSnrInDb", doubleSignature);
-        env->CallVoidMethod(gnssMeasurementObject, setterMethod, measurement->snr_db);
-    }
-
-    if (flags & GPS_MEASUREMENT_HAS_ELEVATION) {
-        jmethodID setterMethod =
-                env->GetMethodID(gnssMeasurementClass, "setElevationInDeg", doubleSignature);
-        env->CallVoidMethod(gnssMeasurementObject, setterMethod, measurement->elevation_deg);
-    }
-
-    if (flags & GPS_MEASUREMENT_HAS_ELEVATION_UNCERTAINTY) {
-        jmethodID setterMethod =
-                env->GetMethodID(gnssMeasurementClass, "setElevationUncertaintyInDeg", doubleSignature);
-        env->CallVoidMethod(
-                gnssMeasurementObject,
-                setterMethod,
-                measurement->elevation_uncertainty_deg);
-    }
-
-    if (flags & GPS_MEASUREMENT_HAS_AZIMUTH) {
-        jmethodID setterMethod =
-                env->GetMethodID(gnssMeasurementClass, "setAzimuthInDeg", doubleSignature);
-        env->CallVoidMethod(gnssMeasurementObject, setterMethod, measurement->azimuth_deg);
-    }
-
-    if (flags & GPS_MEASUREMENT_HAS_AZIMUTH_UNCERTAINTY) {
-        jmethodID setterMethod = env->GetMethodID(
-                gnssMeasurementClass,
-                "setAzimuthUncertaintyInDeg",
-                doubleSignature);
-        env->CallVoidMethod(
-                gnssMeasurementObject,
-                setterMethod,
-                measurement->azimuth_uncertainty_deg);
-    }
-
-    jmethodID usedInFixSetterMethod = env->GetMethodID(gnssMeasurementClass, "setUsedInFix", "(Z)V");
-    env->CallVoidMethod(
-            gnssMeasurementObject,
-            usedInFixSetterMethod,
-            (flags & GPS_MEASUREMENT_HAS_USED_IN_FIX) && measurement->used_in_fix);
-
-    if (size == sizeof(GpsMeasurement)) {
-      jmethodID setterMethod =
-          env->GetMethodID(gnssMeasurementClass,
-                           "setPseudorangeRateCarrierInMetersPerSec",
-                           doubleSignature);
-      env->CallVoidMethod(
-          gnssMeasurementObject,
-          setterMethod,
-          measurement->pseudorange_rate_carrier_mps);
-
-      setterMethod =
-          env->GetMethodID(gnssMeasurementClass,
-                           "setPseudorangeRateCarrierUncertaintyInMetersPerSec",
-                           doubleSignature);
-      env->CallVoidMethod(
-          gnssMeasurementObject,
-          setterMethod,
-          measurement->pseudorange_rate_carrier_uncertainty_mps);
-    }
-
-    env->DeleteLocalRef(gnssMeasurementClass);
-    return gnssMeasurementObject;
+    return object.get();
 }
 
-/**
- * <T> can only be GpsData or GpsData_v1. Must rewrite this function if more
- * types are introduced in the future releases.
- */
-template<class T>
-static jobjectArray translate_gps_measurements(JNIEnv* env, void* data) {
-    T* gps_data = reinterpret_cast<T*>(data);
-    size_t measurementCount = gps_data->measurement_count;
-    if (measurementCount == 0) {
+static jobject translate_gnss_measurement(JNIEnv* env,
+                                          GnssMeasurement* measurement) {
+    JavaObject object(env, "android/location/GnssMeasurement");
+    GpsMeasurementFlags flags = measurement->flags;
+
+    SET(Svid, measurement->svid);
+    SET(TimeOffsetInNs, measurement->time_offset_ns);
+    SET(State, measurement->state);
+    SET(ReceivedGpsTowInNs, measurement->received_gps_tow_ns);
+    SET(ReceivedGpsTowUncertaintyInNs,
+        measurement->received_gps_tow_uncertainty_ns);
+    SET(Cn0InDbHz, measurement->c_n0_dbhz);
+    SET(PseudorangeRateInMetersPerSec, measurement->pseudorange_rate_mps);
+    SET(PseudorangeRateUncertaintyInMetersPerSec,
+        measurement->pseudorange_rate_uncertainty_mps);
+    SET(AccumulatedDeltaRangeState, measurement->accumulated_delta_range_state);
+    SET(AccumulatedDeltaRangeInMeters, measurement->accumulated_delta_range_m);
+    SET(AccumulatedDeltaRangeUncertaintyInMeters,
+        measurement->accumulated_delta_range_uncertainty_m);
+    SET_IF(GPS_MEASUREMENT_HAS_PSEUDORANGE,
+           PseudorangeInMeters,
+           measurement->pseudorange_m);
+    SET_IF(GPS_MEASUREMENT_HAS_PSEUDORANGE_UNCERTAINTY,
+           PseudorangeUncertaintyInMeters,
+           measurement->pseudorange_uncertainty_m);
+    SET_IF(GPS_MEASUREMENT_HAS_CODE_PHASE,
+           CodePhaseInChips,
+           measurement->code_phase_chips);
+    SET_IF(GPS_MEASUREMENT_HAS_CODE_PHASE_UNCERTAINTY,
+           CodePhaseUncertaintyInChips,
+           measurement->code_phase_uncertainty_chips);
+    SET_IF(GPS_MEASUREMENT_HAS_CARRIER_FREQUENCY,
+           CarrierFrequencyInHz,
+           measurement->carrier_frequency_hz);
+    SET_IF(GPS_MEASUREMENT_HAS_CARRIER_CYCLES,
+           CarrierCycles,
+           measurement->carrier_cycles);
+    SET_IF(GPS_MEASUREMENT_HAS_CARRIER_PHASE,
+           CarrierPhase,
+           measurement->carrier_phase);
+    SET_IF(GPS_MEASUREMENT_HAS_CARRIER_PHASE_UNCERTAINTY,
+           CarrierPhaseUncertainty,
+           measurement->carrier_phase_uncertainty);
+    SET(LossOfLock, measurement->loss_of_lock);
+    SET_IF(GPS_MEASUREMENT_HAS_BIT_NUMBER, BitNumber, measurement->bit_number);
+    SET_IF(GPS_MEASUREMENT_HAS_TIME_FROM_LAST_BIT,
+           TimeFromLastBitInMs,
+           measurement->time_from_last_bit_ms);
+    SET_IF(GPS_MEASUREMENT_HAS_DOPPLER_SHIFT,
+           DopplerShiftInHz,
+           measurement->doppler_shift_hz);
+    SET_IF(GPS_MEASUREMENT_HAS_DOPPLER_SHIFT_UNCERTAINTY,
+           DopplerShiftUncertaintyInHz,
+           measurement->doppler_shift_uncertainty_hz);
+    SET(MultipathIndicator, measurement->multipath_indicator);
+    SET_IF(GPS_MEASUREMENT_HAS_SNR, SnrInDb, measurement->snr_db);
+    SET_IF(GPS_MEASUREMENT_HAS_ELEVATION,
+           ElevationInDeg,
+           measurement->elevation_deg);
+    SET_IF(GPS_MEASUREMENT_HAS_ELEVATION_UNCERTAINTY,
+           ElevationUncertaintyInDeg,
+           measurement->elevation_uncertainty_deg);
+    SET_IF(GPS_MEASUREMENT_HAS_AZIMUTH,
+           AzimuthInDeg,
+           measurement->azimuth_deg);
+    SET_IF(GPS_MEASUREMENT_HAS_AZIMUTH_UNCERTAINTY,
+           AzimuthUncertaintyInDeg,
+           measurement->azimuth_uncertainty_deg);
+    SET(UsedInFix,
+        (flags & GPS_MEASUREMENT_HAS_USED_IN_FIX) && measurement->used_in_fix);
+
+    SET(PseudorangeRateCarrierInMetersPerSec,
+        measurement->pseudorange_rate_carrier_mps);
+    SET(PseudorangeRateCarrierUncertaintyInMetersPerSec,
+        measurement->pseudorange_rate_carrier_uncertainty_mps);
+
+    return object.get();
+}
+
+static jobjectArray translate_gps_measurements(JNIEnv* env,
+                                               GpsMeasurement* measurements,
+                                               size_t count) {
+    if (count == 0) {
         return NULL;
     }
 
-    jclass gnssMeasurementClass = env->FindClass("android/location/GnssMeasurement");
+    jclass gnssMeasurementClass = env->FindClass(
+            "android/location/GnssMeasurement");
     jobjectArray gnssMeasurementArray = env->NewObjectArray(
-            measurementCount,
+            count,
             gnssMeasurementClass,
             NULL /* initialElement */);
 
-    for (uint16_t i = 0; i < measurementCount; ++i) {
+    for (uint16_t i = 0; i < count; ++i) {
         jobject gnssMeasurement = translate_gps_measurement(
             env,
-            &(gps_data->measurements[i]),
-            sizeof(gps_data->measurements[0]));
+            &measurements[i]);
         env->SetObjectArrayElement(gnssMeasurementArray, i, gnssMeasurement);
         env->DeleteLocalRef(gnssMeasurement);
     }
@@ -1340,27 +1331,37 @@
     return gnssMeasurementArray;
 }
 
-static void measurement_callback(GpsData* data) {
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
-    if (data == NULL) {
-        ALOGE("Invalid data provided to gps_measurement_callback");
-        return;
-    }
-    if (data->size != sizeof(GpsData) && data->size != sizeof(GpsData_v1)) {
-        ALOGE("Invalid GpsData size found in gps_measurement_callback, size=%zd", data->size);
-        return;
+static jobjectArray translate_gnss_measurements(JNIEnv* env,
+                                                GnssMeasurement* measurements,
+                                                size_t count) {
+    if (count == 0) {
+        return NULL;
     }
 
-    jobject gpsClock;
-    jobjectArray measurementArray;
-    if (data->size == sizeof(GpsData)) {
-        gpsClock = translate_gps_clock(env, &data->clock, sizeof(GpsClock));
-        measurementArray = translate_gps_measurements<GpsData>(env, data);
-    } else {
-        gpsClock = translate_gps_clock(env, &data->clock, sizeof(GpsClock_v1));
-        measurementArray = translate_gps_measurements<GpsData_v1>(env, data);
+    jclass gnssMeasurementClass = env->FindClass(
+            "android/location/GnssMeasurement");
+    jobjectArray gnssMeasurementArray = env->NewObjectArray(
+            count,
+            gnssMeasurementClass,
+            NULL /* initialElement */);
+
+    for (uint16_t i = 0; i < count; ++i) {
+        jobject gnssMeasurement = translate_gnss_measurement(
+            env,
+            &measurements[i]);
+        env->SetObjectArrayElement(gnssMeasurementArray, i, gnssMeasurement);
+        env->DeleteLocalRef(gnssMeasurement);
     }
-    jclass gnssMeasurementsEventClass = env->FindClass("android/location/GnssMeasurementsEvent");
+
+    env->DeleteLocalRef(gnssMeasurementClass);
+    return gnssMeasurementArray;
+}
+
+static void set_measurement_data(JNIEnv *env,
+                                 jobject clock,
+                                 jobjectArray measurementArray) {
+    jclass gnssMeasurementsEventClass = env->FindClass(
+            "android/location/GnssMeasurementsEvent");
     jmethodID gnssMeasurementsEventCtor = env->GetMethodID(
         gnssMeasurementsEventClass,
         "<init>",
@@ -1369,21 +1370,68 @@
     jobject gnssMeasurementsEvent = env->NewObject(
         gnssMeasurementsEventClass,
         gnssMeasurementsEventCtor,
-        gpsClock,
+        clock,
         measurementArray);
-
-    env->CallVoidMethod(mCallbacksObj, method_reportMeasurementData, gnssMeasurementsEvent);
+    env->CallVoidMethod(mCallbacksObj,
+                        method_reportMeasurementData,
+                        gnssMeasurementsEvent);
     checkAndClearExceptionFromCallback(env, __FUNCTION__);
-
-    env->DeleteLocalRef(gpsClock);
-    env->DeleteLocalRef(measurementArray);
     env->DeleteLocalRef(gnssMeasurementsEventClass);
     env->DeleteLocalRef(gnssMeasurementsEvent);
 }
 
+static void measurement_callback(GpsData* data) {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    if (data == NULL) {
+        ALOGE("Invalid data provided to gps_measurement_callback");
+        return;
+    }
+    if (data->size != sizeof(GpsData)) {
+        ALOGE("Invalid GpsData size found in gps_measurement_callback, "
+              "size=%zd",
+              data->size);
+        return;
+    }
+
+    jobject clock;
+    jobjectArray measurementArray;
+    clock = translate_gps_clock(env, &data->clock);
+    measurementArray = translate_gps_measurements(
+            env, data->measurements, data->measurement_count);
+    set_measurement_data(env, clock, measurementArray);
+
+    env->DeleteLocalRef(clock);
+    env->DeleteLocalRef(measurementArray);
+}
+
+static void gnss_measurement_callback(GnssData* data) {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    if (data == NULL) {
+        ALOGE("Invalid data provided to gps_measurement_callback");
+        return;
+    }
+    if (data->size != sizeof(GpsData)) {
+        ALOGE("Invalid GpsData size found in gps_measurement_callback, "
+              "size=%zd",
+              data->size);
+        return;
+    }
+
+    jobject clock;
+    jobjectArray measurementArray;
+    clock = translate_gnss_clock(env, &data->clock);
+    measurementArray = translate_gnss_measurements(
+            env, data->measurements, data->measurement_count);
+    set_measurement_data(env, clock, measurementArray);
+
+    env->DeleteLocalRef(clock);
+    env->DeleteLocalRef(measurementArray);
+}
+
 GpsMeasurementCallbacks sGpsMeasurementCallbacks = {
     sizeof(GpsMeasurementCallbacks),
     measurement_callback,
+    gnss_measurement_callback,
 };
 
 static jboolean android_location_GnssLocationProvider_is_measurement_supported(
@@ -1431,69 +1479,86 @@
         ALOGE("Invalid Navigation Message found: data=%p, length=%zd", data, dataLength);
         return NULL;
     }
+    JavaObject object(env, "android/location/GnssNavigationMessage");
+    SET(Type, message->type);
+    SET(Svid, static_cast<int16_t>(message->prn));
+    SET(MessageId, message->message_id);
+    SET(SubmessageId, message->submessage_id);
+    object.callSetter("setData", data, dataLength);
+    return object.get();
+}
 
-    jclass navigationMessageClass = env->FindClass("android/location/GnssNavigationMessage");
-    jmethodID navigationMessageCtor = env->GetMethodID(navigationMessageClass, "<init>", "()V");
-    jobject navigationMessageObject = env->NewObject(navigationMessageClass, navigationMessageCtor);
+static jobject translate_gnss_navigation_message(
+        JNIEnv* env, GnssNavigationMessage* message) {
+    size_t dataLength = message->data_length;
+    uint8_t* data = message->data;
+    if (dataLength == 0 || data == NULL) {
+        ALOGE("Invalid Navigation Message found: data=%p, length=%zd", data, dataLength);
+        return NULL;
+    }
+    JavaObject object(env, "android/location/GnssNavigationMessage");
+    SET(Type, message->type);
+    SET(Svid, message->svid);
+    SET(MessageId, message->message_id);
+    SET(SubmessageId, message->submessage_id);
+    object.callSetter("setData", data, dataLength);
+    return object.get();
+}
 
-    jmethodID setTypeMethod = env->GetMethodID(navigationMessageClass, "setType", "(B)V");
-    env->CallVoidMethod(navigationMessageObject, setTypeMethod, message->type);
-
-    jmethodID setPrnMethod = env->GetMethodID(navigationMessageClass, "setPrn", "(B)V");
-    env->CallVoidMethod(navigationMessageObject, setPrnMethod, message->prn);
-
-    jmethodID setMessageIdMethod = env->GetMethodID(navigationMessageClass, "setMessageId", "(S)V");
-    env->CallVoidMethod(navigationMessageObject, setMessageIdMethod, message->message_id);
-
-    jmethodID setSubmessageIdMethod =
-            env->GetMethodID(navigationMessageClass, "setSubmessageId", "(S)V");
-    env->CallVoidMethod(navigationMessageObject, setSubmessageIdMethod, message->submessage_id);
-
-    jbyteArray dataArray = env->NewByteArray(dataLength);
-    env->SetByteArrayRegion(dataArray, 0, dataLength, (jbyte*) data);
-    jmethodID setDataMethod = env->GetMethodID(navigationMessageClass, "setData", "([B)V");
-    env->CallVoidMethod(navigationMessageObject, setDataMethod, dataArray);
-
-    env->DeleteLocalRef(navigationMessageClass);
-    env->DeleteLocalRef(dataArray);
-    return navigationMessageObject;
+static void set_navigation_message(jobject navigationMessage) {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    jclass navigationMessageEventClass =
+            env->FindClass("android/location/GnssNavigationMessageEvent");
+    jmethodID navigationMessageEventCtor = env->GetMethodID(
+            navigationMessageEventClass,
+            "<init>",
+            "(Landroid/location/GnssNavigationMessage;)V");
+    jobject navigationMessageEvent = env->NewObject(
+            navigationMessageEventClass,
+            navigationMessageEventCtor,
+            navigationMessage);
+    env->CallVoidMethod(mCallbacksObj,
+                        method_reportNavigationMessages,
+                        navigationMessageEvent);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    env->DeleteLocalRef(navigationMessageEventClass);
+    env->DeleteLocalRef(navigationMessageEvent);
 }
 
 static void navigation_message_callback(GpsNavigationMessage* message) {
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
     if (message == NULL) {
         ALOGE("Invalid Navigation Message provided to callback");
         return;
     }
-
-    if (message->size == sizeof(GpsNavigationMessage)) {
-        jobject navigationMessage = translate_gps_navigation_message(env, message);
-
-        jclass navigationMessageEventClass =
-                env->FindClass("android/location/GnssNavigationMessageEvent");
-        jmethodID navigationMessageEventCtor = env->GetMethodID(
-                navigationMessageEventClass,
-                "<init>",
-                "(Landroid/location/GnssNavigationMessage;)V");
-        jobject navigationMessageEvent = env->NewObject(
-                navigationMessageEventClass,
-                navigationMessageEventCtor,
-                navigationMessage);
-
-        env->CallVoidMethod(mCallbacksObj, method_reportNavigationMessages, navigationMessageEvent);
-        checkAndClearExceptionFromCallback(env, __FUNCTION__);
-
-        env->DeleteLocalRef(navigationMessage);
-        env->DeleteLocalRef(navigationMessageEventClass);
-        env->DeleteLocalRef(navigationMessageEvent);
-    } else {
+    if (message->size != sizeof(GpsNavigationMessage)) {
         ALOGE("Invalid GpsNavigationMessage size found: %zd", message->size);
+        return;
     }
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    jobject navigationMessage = translate_gps_navigation_message(env, message);
+    set_navigation_message(navigationMessage);
+    env->DeleteLocalRef(navigationMessage);
+}
+
+static void gnss_navigation_message_callback(GnssNavigationMessage* message) {
+    if (message == NULL) {
+        ALOGE("Invalid Navigation Message provided to callback");
+        return;
+    }
+    if (message->size != sizeof(GnssNavigationMessage)) {
+        ALOGE("Invalid GnssNavigationMessage size found: %zd", message->size);
+        return;
+    }
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    jobject navigationMessage = translate_gnss_navigation_message(env, message);
+    set_navigation_message(navigationMessage);
+    env->DeleteLocalRef(navigationMessage);
 }
 
 GpsNavigationMessageCallbacks sGpsNavigationMessageCallbacks = {
     sizeof(GpsNavigationMessageCallbacks),
     navigation_message_callback,
+    gnss_navigation_message_callback,
 };
 
 static jboolean android_location_GnssLocationProvider_is_navigation_message_supported(
@@ -1567,7 +1632,7 @@
             "(I)V",
             (void*)android_location_GnssLocationProvider_delete_aiding_data},
     {"native_read_sv_status",
-            "([I[F[F[F[I)I",
+            "([I[F[F[F)I",
             (void*)android_location_GnssLocationProvider_read_sv_status},
     {"native_read_nmea", "([BI)I", (void*)android_location_GnssLocationProvider_read_nmea},
     {"native_inject_time", "(JJI)V", (void*)android_location_GnssLocationProvider_inject_time},
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index eacf11f..916b66d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -116,6 +116,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
+import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.Xml;
@@ -274,6 +275,8 @@
     private static final int PROFILE_KEYGUARD_FEATURES =
             PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER | PROFILE_KEYGUARD_FEATURES_AFFECT_PROFILE;
 
+    private static final int DEVICE_ADMIN_DEACTIVATE_TIMEOUT = 10000;
+
     final Context mContext;
     final Injector mInjector;
     final IPackageManager mIPackageManager;
@@ -281,6 +284,13 @@
     final UserManagerInternal mUserManagerInternal;
     private final LockPatternUtils mLockPatternUtils;
 
+    /**
+     * Contains (package-user) pairs to remove. An entry (p, u) implies that removal of package p
+     * is requested for user u.
+     */
+    private final Set<Pair<String, Integer>> mPackagesToRemove =
+            new ArraySet<Pair<String, Integer>>();
+
     final LocalService mLocalService;
 
     // Stores and loads state on device and profile owners.
@@ -1239,7 +1249,9 @@
                     if (packageName == null || packageName.equals(adminPackage)) {
                         if (mIPackageManager.getPackageInfo(adminPackage, 0, userHandle) == null
                                 || mIPackageManager.getReceiverInfo(
-                                    aa.info.getComponent(), 0, userHandle) == null) {
+                                    aa.info.getComponent(),
+                                    PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE,
+                                    userHandle) == null) {
                             removed = true;
                             policy.mAdminList.remove(i);
                             policy.mAdminMap.remove(aa.info.getComponent());
@@ -2015,35 +2027,19 @@
         final ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver, userHandle);
         if (admin != null) {
             getUserData(userHandle).mRemovingAdmins.add(adminReceiver);
-
             sendAdminCommandLocked(admin,
                     DeviceAdminReceiver.ACTION_DEVICE_ADMIN_DISABLED,
                     new BroadcastReceiver() {
                         @Override
                         public void onReceive(Context context, Intent intent) {
-                            synchronized (DevicePolicyManagerService.this) {
-                                int userHandle = admin.getUserHandle().getIdentifier();
-                                DevicePolicyData policy = getUserData(userHandle);
-                                boolean doProxyCleanup = admin.info.usesPolicy(
-                                        DeviceAdminInfo.USES_POLICY_SETS_GLOBAL_PROXY);
-                                policy.mAdminList.remove(admin);
-                                policy.mAdminMap.remove(adminReceiver);
-                                validatePasswordOwnerLocked(policy);
-                                if (doProxyCleanup) {
-                                    resetGlobalProxyLocked(getUserData(userHandle));
-                                }
-                                saveSettingsLocked(userHandle);
-                                updateMaximumTimeToLockLocked(userHandle);
-                                policy.mRemovingAdmins.remove(adminReceiver);
-                            }
-                            // The removed admin might have disabled camera, so update user
-                            // restrictions.
-                            pushUserRestrictions(userHandle);
+                            removeAdminArtifacts(adminReceiver, userHandle);
+                            removePackageIfRequired(adminReceiver.getPackageName(), userHandle);
                         }
                     });
         }
     }
 
+
     public DeviceAdminInfo findAdmin(ComponentName adminName, int userHandle,
             boolean throwForMissiongPermission) {
         if (!mHasFeature) {
@@ -3528,11 +3524,14 @@
 
     @Override
     public int getCurrentFailedPasswordAttempts(int userHandle, boolean parent) {
+        enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
-            // This API can only be called by an active device admin,
-            // so try to retrieve it to check that the caller is one.
-            getActiveAdminForCallerLocked(
-                    null, DeviceAdminInfo.USES_POLICY_WATCH_LOGIN, parent);
+            if (!isCallerWithSystemUid()) {
+                // This API can only be called by an active device admin,
+                // so try to retrieve it to check that the caller is one.
+                getActiveAdminForCallerLocked(
+                        null, DeviceAdminInfo.USES_POLICY_WATCH_LOGIN, parent);
+            }
 
             DevicePolicyData policy = getUserDataUnchecked(getCredentialOwner(userHandle, parent));
 
@@ -4492,7 +4491,8 @@
         }
 
         if (mInjector.securityLogIsLoggingEnabled()) {
-            SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT, /*result*/ 0);
+            SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT, /*result*/ 0,
+                    /*method strength*/ 1);
         }
     }
 
@@ -4522,23 +4522,50 @@
         }
 
         if (mInjector.securityLogIsLoggingEnabled()) {
-            SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT, /*result*/ 1);
+            SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT, /*result*/ 1,
+                    /*method strength*/ 1);
         }
     }
 
     @Override
-    public void reportKeyguardDismissed() {
+    public void reportFailedFingerprintAttempt(int userHandle) {
+        enforceFullCrossUsersPermission(userHandle);
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
         if (mInjector.securityLogIsLoggingEnabled()) {
+            SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT, /*result*/ 0,
+                    /*method strength*/ 0);
+        }
+    }
+
+    @Override
+    public void reportSuccessfulFingerprintAttempt(int userHandle) {
+        enforceFullCrossUsersPermission(userHandle);
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.BIND_DEVICE_ADMIN, null);
+        if (mInjector.securityLogIsLoggingEnabled()) {
+            SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT, /*result*/ 1,
+                    /*method strength*/ 0);
+        }
+    }
+
+    @Override
+    public void reportKeyguardDismissed(int userHandle) {
+        enforceFullCrossUsersPermission(userHandle);
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.BIND_DEVICE_ADMIN, null);
+
+        if (mInjector.securityLogIsLoggingEnabled()) {
             SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISSED);
         }
     }
 
     @Override
-    public void reportKeyguardSecured() {
+    public void reportKeyguardSecured(int userHandle) {
+        enforceFullCrossUsersPermission(userHandle);
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
+
         if (mInjector.securityLogIsLoggingEnabled()) {
             SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_SECURED);
         }
@@ -6769,6 +6796,10 @@
             throw new IllegalArgumentException("profileOwner " + profileOwner + " and admin "
                     + admin + " are not in the same package");
         }
+        // Only allow the system user to use this method
+        if (!mInjector.binderGetCallingUserHandle().isSystem()) {
+            throw new SecurityException("createAndManageUser was called from non-system user");
+        }
         // Create user.
         UserHandle user = null;
         synchronized (this) {
@@ -6780,7 +6811,8 @@
                 if ((flags & DevicePolicyManager.MAKE_USER_EPHEMERAL) != 0) {
                     userInfoFlags |= UserInfo.FLAG_EPHEMERAL;
                 }
-                UserInfo userInfo = mUserManager.createUser(name, userInfoFlags);
+                UserInfo userInfo = mUserManagerInternal.createUserEvenWhenDisallowed(name,
+                        userInfoFlags);
                 if (userInfo != null) {
                     user = userInfo.getUserHandle();
                 }
@@ -7110,7 +7142,7 @@
                 List<ResolveInfo> activitiesToEnable = mIPackageManager.queryIntentActivities(
                         intent,
                         intent.resolveTypeIfNeeded(mContext.getContentResolver()),
-                        0, // no flags
+                        PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE,
                         parentUserId);
 
                 if (VERBOSE_LOG) {
@@ -8009,7 +8041,8 @@
 
     boolean isPackageInstalledForUser(String packageName, int userHandle) {
         try {
-            PackageInfo pi = mIPackageManager.getPackageInfo(packageName, 0, userHandle);
+            PackageInfo pi = mInjector.getIPackageManager().getPackageInfo(packageName, 0,
+                    userHandle);
             return (pi != null) && (pi.applicationInfo.flags != 0);
         } catch (RemoteException re) {
             throw new RuntimeException("Package manager has died", re);
@@ -8465,4 +8498,137 @@
         List<SecurityEvent> logs = mSecurityLogMonitor.retrieveLogs();
         return logs != null ? new ParceledListSlice<SecurityEvent>(logs) : null;
     }
+
+    private void enforceCanManageDeviceAdmin() {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS,
+                null);
+    }
+
+    @Override
+    public boolean isUninstallInQueue(final String packageName) {
+        enforceCanManageDeviceAdmin();
+        final int userId = mInjector.userHandleGetCallingUserId();
+        Pair<String, Integer> packageUserPair = new Pair<>(packageName, userId);
+        synchronized (this) {
+            return mPackagesToRemove.contains(packageUserPair);
+        }
+    }
+
+    @Override
+    public void uninstallPackageWithActiveAdmins(final String packageName) {
+        enforceCanManageDeviceAdmin();
+        Preconditions.checkArgument(!TextUtils.isEmpty(packageName));
+
+        final int userId = mInjector.userHandleGetCallingUserId();
+
+        final ComponentName profileOwner = getProfileOwner(userId);
+        if (profileOwner != null && packageName.equals(profileOwner.getPackageName())) {
+            throw new IllegalArgumentException("Cannot uninstall a package with a profile owner");
+        }
+
+        final ComponentName deviceOwner = getDeviceOwnerComponent(/* callingUserOnly= */ false);
+        if (getDeviceOwnerUserId() == userId && deviceOwner != null
+                && packageName.equals(deviceOwner.getPackageName())) {
+            throw new IllegalArgumentException("Cannot uninstall a package with a device owner");
+        }
+
+        final Pair<String, Integer> packageUserPair = new Pair<>(packageName, userId);
+        synchronized (this) {
+            mPackagesToRemove.add(packageUserPair);
+        }
+
+        // All active admins on the user.
+        final List<ComponentName> allActiveAdmins = getActiveAdmins(userId);
+
+        // Active admins in the target package.
+        final List<ComponentName> packageActiveAdmins = new ArrayList<>();
+        if (allActiveAdmins != null) {
+            for (ComponentName activeAdmin : allActiveAdmins) {
+                if (packageName.equals(activeAdmin.getPackageName())) {
+                    packageActiveAdmins.add(activeAdmin);
+                    removeActiveAdmin(activeAdmin, userId);
+                }
+            }
+        }
+        if (packageActiveAdmins.size() == 0) {
+            startUninstallIntent(packageName, userId);
+        } else {
+            mHandler.postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    for (ComponentName activeAdmin : packageActiveAdmins) {
+                        removeAdminArtifacts(activeAdmin, userId);
+                    }
+                    startUninstallIntent(packageName, userId);
+                }
+            }, DEVICE_ADMIN_DEACTIVATE_TIMEOUT); // Start uninstall after timeout anyway.
+        }
+    }
+
+    private void removePackageIfRequired(final String packageName, final int userId) {
+        if (!packageHasActiveAdmins(packageName, userId)) {
+            // Will not do anything if uninstall was not requested or was already started.
+            startUninstallIntent(packageName, userId);
+        }
+    }
+
+    private void startUninstallIntent(final String packageName, final int userId) {
+        final Pair<String, Integer> packageUserPair = new Pair<>(packageName, userId);
+        synchronized (this) {
+            if (!mPackagesToRemove.contains(packageUserPair)) {
+                // Do nothing if uninstall was not requested or was already started.
+                return;
+            }
+            mPackagesToRemove.remove(packageUserPair);
+        }
+        try {
+            if (mInjector.getIPackageManager().getPackageInfo(packageName, 0, userId) == null) {
+                // Package does not exist. Nothing to do.
+                return;
+            }
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Failure talking to PackageManager while getting package info");
+        }
+
+        try { // force stop the package before uninstalling
+            mInjector.getIActivityManager().forceStopPackage(packageName, userId);
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Failure talking to ActivityManager while force stopping package");
+        }
+        final Uri packageURI = Uri.parse("package:" + packageName);
+        final Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageURI);
+        uninstallIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        mContext.startActivityAsUser(uninstallIntent, UserHandle.of(userId));
+    }
+
+    /**
+     * Removes the admin from the policy. Ideally called after the admin's
+     * {@link DeviceAdminReceiver#onDisabled(Context, Intent)} has been successfully completed.
+     *
+     * @param adminReceiver The admin to remove
+     * @param userHandle The user for which this admin has to be removed.
+     */
+    private void removeAdminArtifacts(final ComponentName adminReceiver, final int userHandle) {
+        synchronized (this) {
+            final ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver, userHandle);
+            if (admin == null) {
+                return;
+            }
+            final DevicePolicyData policy = getUserData(userHandle);
+            final boolean doProxyCleanup = admin.info.usesPolicy(
+                    DeviceAdminInfo.USES_POLICY_SETS_GLOBAL_PROXY);
+            policy.mAdminList.remove(admin);
+            policy.mAdminMap.remove(adminReceiver);
+            validatePasswordOwnerLocked(policy);
+            if (doProxyCleanup) {
+                resetGlobalProxyLocked(policy);
+            }
+            saveSettingsLocked(userHandle);
+            updateMaximumTimeToLockLocked(userHandle);
+            policy.mRemovingAdmins.remove(adminReceiver);
+        }
+        // The removed admin might have disabled camera, so update user
+        // restrictions.
+        pushUserRestrictions(userHandle);
+    }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index 880f810..68fd0f6 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -716,11 +716,11 @@
     }
 
     File getLegacyConfigFileWithTestOverride() {
-        return new File(Environment.getSystemSecureDirectory(), DEVICE_OWNER_XML_LEGACY);
+        return new File(Environment.getDataSystemDirectory(), DEVICE_OWNER_XML_LEGACY);
     }
 
     File getDeviceOwnerFileWithTestOverride() {
-        return new File(Environment.getSystemSecureDirectory(), DEVICE_OWNER_XML);
+        return new File(Environment.getDataSystemDirectory(), DEVICE_OWNER_XML);
     }
 
     File getProfileOwnerFileWithTestOverride(int userId) {
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index 2329b42..ceabfce 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -27,7 +27,6 @@
 import android.content.IntentFilter;
 import android.net.DhcpResults;
 import android.net.BaseDhcpStateMachine;
-import android.net.DhcpStateMachine;
 import android.net.InterfaceConfiguration;
 import android.net.LinkAddress;
 import android.net.NetworkUtils;
@@ -103,12 +102,35 @@
     // t=0, t=2, t=6, t=14, t=30, allowing for 10% jitter.
     private static final int DHCP_TIMEOUT_MS    =  36 * SECONDS;
 
+    private static final int PUBLIC_BASE = Protocol.BASE_DHCP;
+
+    /* Commands from controller to start/stop DHCP */
+    public static final int CMD_START_DHCP                  = PUBLIC_BASE + 1;
+    public static final int CMD_STOP_DHCP                   = PUBLIC_BASE + 2;
+    public static final int CMD_RENEW_DHCP                  = PUBLIC_BASE + 3;
+
+    /* Notification from DHCP state machine prior to DHCP discovery/renewal */
+    public static final int CMD_PRE_DHCP_ACTION             = PUBLIC_BASE + 4;
+    /* Notification from DHCP state machine post DHCP discovery/renewal. Indicates
+     * success/failure */
+    public static final int CMD_POST_DHCP_ACTION            = PUBLIC_BASE + 5;
+    /* Notification from DHCP state machine before quitting */
+    public static final int CMD_ON_QUIT                     = PUBLIC_BASE + 6;
+
+    /* Command from controller to indicate DHCP discovery/renewal can continue
+     * after pre DHCP action is complete */
+    public static final int CMD_PRE_DHCP_ACTION_COMPLETE    = PUBLIC_BASE + 7;
+
+    /* Message.arg1 arguments to CMD_POST_DHCP notification */
+    public static final int DHCP_SUCCESS = 1;
+    public static final int DHCP_FAILURE = 2;
+
     // Messages.
-    private static final int BASE                 = Protocol.BASE_DHCP + 100;
-    private static final int CMD_KICK             = BASE + 1;
-    private static final int CMD_RECEIVED_PACKET  = BASE + 2;
-    private static final int CMD_TIMEOUT          = BASE + 3;
-    private static final int CMD_ONESHOT_TIMEOUT  = BASE + 4;
+    private static final int PRIVATE_BASE         = Protocol.BASE_DHCP + 100;
+    private static final int CMD_KICK             = PRIVATE_BASE + 1;
+    private static final int CMD_RECEIVED_PACKET  = PRIVATE_BASE + 2;
+    private static final int CMD_TIMEOUT          = PRIVATE_BASE + 3;
+    private static final int CMD_ONESHOT_TIMEOUT  = PRIVATE_BASE + 4;
 
     // DHCP parameters that we request.
     private static final byte[] REQUESTED_PARAMS = new byte[] {
@@ -211,7 +233,7 @@
         // Used to time out PacketRetransmittingStates.
         mTimeoutAlarm = makeWakeupMessage("TIMEOUT", CMD_TIMEOUT);
         // Used to schedule DHCP renews.
-        mRenewAlarm = makeWakeupMessage("RENEW", DhcpStateMachine.CMD_RENEW_DHCP);
+        mRenewAlarm = makeWakeupMessage("RENEW", CMD_RENEW_DHCP);
         // Used to tell the caller when its request (CMD_START_DHCP or CMD_RENEW_DHCP) timed out.
         // TODO: when the legacy DHCP client is gone, make the client fully asynchronous and
         // remove this.
@@ -400,13 +422,12 @@
     }
 
     private void notifySuccess() {
-        mController.sendMessage(DhcpStateMachine.CMD_POST_DHCP_ACTION,
-                DhcpStateMachine.DHCP_SUCCESS, 0, new DhcpResults(mDhcpLease));
+        mController.sendMessage(
+                CMD_POST_DHCP_ACTION, DHCP_SUCCESS, 0, new DhcpResults(mDhcpLease));
     }
 
     private void notifyFailure() {
-        mController.sendMessage(DhcpStateMachine.CMD_POST_DHCP_ACTION,
-                DhcpStateMachine.DHCP_FAILURE, 0, null);
+        mController.sendMessage(CMD_POST_DHCP_ACTION, DHCP_FAILURE, 0, null);
     }
 
     private void clearDhcpState() {
@@ -428,7 +449,7 @@
 
     protected void onQuitting() {
         Log.d(TAG, "onQuitting");
-        mController.sendMessage(DhcpStateMachine.CMD_ON_QUIT);
+        mController.sendMessage(CMD_ON_QUIT);
     }
 
     private void maybeLog(String msg) {
@@ -442,17 +463,17 @@
 
         private String messageName(int what) {
             switch (what) {
-                case DhcpStateMachine.CMD_START_DHCP:
+                case CMD_START_DHCP:
                     return "CMD_START_DHCP";
-                case DhcpStateMachine.CMD_STOP_DHCP:
+                case CMD_STOP_DHCP:
                     return "CMD_STOP_DHCP";
-                case DhcpStateMachine.CMD_RENEW_DHCP:
+                case CMD_RENEW_DHCP:
                     return "CMD_RENEW_DHCP";
-                case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
+                case CMD_PRE_DHCP_ACTION:
                     return "CMD_PRE_DHCP_ACTION";
-                case DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE:
+                case CMD_PRE_DHCP_ACTION_COMPLETE:
                     return "CMD_PRE_DHCP_ACTION_COMPLETE";
-                case DhcpStateMachine.CMD_POST_DHCP_ACTION:
+                case CMD_POST_DHCP_ACTION:
                     return "CMD_POST_DHCP_ACTION";
                 case CMD_KICK:
                     return "CMD_KICK";
@@ -495,14 +516,14 @@
         @Override
         public void enter() {
             super.enter();
-            mController.sendMessage(DhcpStateMachine.CMD_PRE_DHCP_ACTION);
+            mController.sendMessage(CMD_PRE_DHCP_ACTION);
         }
 
         @Override
         public boolean processMessage(Message message) {
             super.processMessage(message);
             switch (message.what) {
-                case DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE:
+                case CMD_PRE_DHCP_ACTION_COMPLETE:
                     transitionTo(mOtherState);
                     return HANDLED;
                 default:
@@ -532,7 +553,7 @@
         public boolean processMessage(Message message) {
             super.processMessage(message);
             switch (message.what) {
-                case DhcpStateMachine.CMD_START_DHCP:
+                case CMD_START_DHCP:
                     scheduleOneshotTimeout();
                     if (mRegisteredForPreDhcpNotification) {
                         transitionTo(mWaitBeforeStartState);
@@ -588,7 +609,7 @@
         public boolean processMessage(Message message) {
             super.processMessage(message);
             switch (message.what) {
-                case DhcpStateMachine.CMD_STOP_DHCP:
+                case CMD_STOP_DHCP:
                     transitionTo(mStoppedState);
                     return HANDLED;
                 case CMD_ONESHOT_TIMEOUT:
@@ -810,7 +831,7 @@
         public boolean processMessage(Message message) {
             super.processMessage(message);
             switch (message.what) {
-                case DhcpStateMachine.CMD_RENEW_DHCP:
+                case CMD_RENEW_DHCP:
                     if (mRegisteredForPreDhcpNotification) {
                         transitionTo(mWaitBeforeRenewalState);
                     } else {
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
index 06b6ee7..a388a26 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -61,7 +61,6 @@
  * @hide
  */
 public class IpManager extends StateMachine {
-    private static final String TAG = IpManager.class.getSimpleName();
     private static final boolean DBG = true;
     private static final boolean VDBG = false;
 
@@ -92,13 +91,18 @@
          */
 
         // Implementations must call IpManager#completedPreDhcpAction().
+        // TODO: Remove this requirement, perhaps via some
+        // registerForPreDhcpAction()-style mechanism.
         public void onPreDhcpAction() {}
         public void onPostDhcpAction() {}
 
-        // TODO: Kill with fire once DHCP and static configuration are moved
-        // out of WifiStateMachine.
-        public void onIPv4ProvisioningSuccess(DhcpResults dhcpResults) {}
-        public void onIPv4ProvisioningFailure() {}
+        // This is purely advisory and not an indication of provisioning
+        // success or failure.  This is only here for callers that want to
+        // expose DHCPv4 results to other APIs (e.g., WifiInfo#setInetAddress).
+        // DHCPv4 or static IPv4 configuration failure or success can be
+        // determined by whether or not the passed-in DhcpResults object is
+        // null or not.
+        public void onNewDhcpResults(DhcpResults dhcpResults) {}
 
         public void onProvisioningSuccess(LinkProperties newLp) {}
         public void onProvisioningFailure(LinkProperties newLp) {}
@@ -122,8 +126,10 @@
 
     private final Object mLock = new Object();
     private final State mStoppedState = new StoppedState();
+    private final State mStoppingState = new StoppingState();
     private final State mStartedState = new StartedState();
 
+    private final String mTag;
     private final Context mContext;
     private final String mInterfaceName;
     @VisibleForTesting
@@ -150,11 +156,11 @@
 
     public IpManager(Context context, String ifName, Callback callback)
                 throws IllegalArgumentException {
-        super(TAG + "." + ifName);
+        super(IpManager.class.getSimpleName() + "." + ifName);
+        mTag = getName();
 
         mContext = context;
         mInterfaceName = ifName;
-
         mCallback = callback;
 
         mNwService = INetworkManagementService.Stub.asInterface(
@@ -171,7 +177,7 @@
         try {
             mNwService.registerObserver(mNetlinkTracker);
         } catch (RemoteException e) {
-            Log.e(TAG, "Couldn't register NetlinkTracker: " + e.toString());
+            Log.e(mTag, "Couldn't register NetlinkTracker: " + e.toString());
         }
 
         resetLinkProperties();
@@ -179,6 +185,8 @@
         // Super simple StateMachine.
         addState(mStoppedState);
         addState(mStartedState);
+        addState(mStoppingState);
+
         setInitialState(mStoppedState);
         setLogRecSize(MAX_LOG_RECORDS);
         super.start();
@@ -192,7 +200,9 @@
      */
     @VisibleForTesting
     protected IpManager(String ifName, Callback callback) {
-        super(TAG + ".test-" + ifName);
+        super(IpManager.class.getSimpleName() + ".test-" + ifName);
+        mTag = getName();
+
         mInterfaceName = ifName;
         mCallback = callback;
 
@@ -203,13 +213,11 @@
 
     public void startProvisioning(StaticIpConfiguration staticIpConfig) {
         getInterfaceIndex();
-
         sendMessage(CMD_START, staticIpConfig);
     }
 
     public void startProvisioning() {
         getInterfaceIndex();
-
         sendMessage(CMD_START);
     }
 
@@ -236,12 +244,48 @@
      * Internals.
      */
 
+    @Override
+    protected String getWhatToString(int what) {
+        // TODO: Investigate switching to reflection.
+        switch (what) {
+            case CMD_STOP:
+                return "CMD_STOP";
+            case CMD_START:
+                return "CMD_START";
+            case CMD_CONFIRM:
+                return "CMD_CONFIRM";
+            case EVENT_PRE_DHCP_ACTION_COMPLETE:
+                return "EVENT_PRE_DHCP_ACTION_COMPLETE";
+            case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
+                return "EVENT_NETLINK_LINKPROPERTIES_CHANGED";
+            case DhcpClient.CMD_PRE_DHCP_ACTION:
+                return "DhcpClient.CMD_PRE_DHCP_ACTION";
+            case DhcpClient.CMD_POST_DHCP_ACTION:
+                return "DhcpClient.CMD_POST_DHCP_ACTION";
+            case DhcpClient.CMD_ON_QUIT:
+                return "DhcpClient.CMD_ON_QUIT";
+        }
+        return "UNKNOWN:" + Integer.toString(what);
+    }
+
+    @Override
+    protected String getLogRecString(Message msg) {
+        final String logLine = String.format(
+                "iface{%s/%d} arg1{%d} arg2{%d} obj{%s}",
+                mInterfaceName, mInterfaceIndex,
+                msg.arg1, msg.arg2, Objects.toString(msg.obj));
+        if (VDBG) {
+            Log.d(mTag, getWhatToString(msg.what) + " " + logLine);
+        }
+        return logLine;
+    }
+
     private void getInterfaceIndex() {
         try {
             mInterfaceIndex = NetworkInterface.getByName(mInterfaceName).getIndex();
         } catch (SocketException | NullPointerException e) {
             // TODO: throw new IllegalStateException.
-            Log.e(TAG, "ALERT: Failed to get interface index: ", e);
+            Log.e(mTag, "ALERT: Failed to get interface index: ", e);
         }
     }
 
@@ -260,16 +304,93 @@
         }
     }
 
+    // For now: use WifiStateMachine's historical notion of provisioned.
+    private static boolean isProvisioned(LinkProperties lp) {
+        // For historical reasons, we should connect even if all we have is
+        // an IPv4 address and nothing else.
+        return lp.isProvisioned() || lp.hasIPv4Address();
+    }
+
+    // TODO: Investigate folding all this into the existing static function
+    // LinkProperties.compareProvisioning() or some other single function that
+    // takes two LinkProperties objects and returns a ProvisioningChange
+    // object that is a correct and complete assessment of what changed, taking
+    // account of the asymmetries described in the comments in this function.
+    // Then switch to using it everywhere (IpReachabilityMonitor, etc.).
+    private static ProvisioningChange compareProvisioning(
+            LinkProperties oldLp, LinkProperties newLp) {
+        ProvisioningChange delta;
+
+        final boolean wasProvisioned = isProvisioned(oldLp);
+        final boolean isProvisioned = isProvisioned(newLp);
+
+        if (!wasProvisioned && isProvisioned) {
+            delta = ProvisioningChange.GAINED_PROVISIONING;
+        } else if (wasProvisioned && isProvisioned) {
+            delta = ProvisioningChange.STILL_PROVISIONED;
+        } else if (!wasProvisioned && !isProvisioned) {
+            delta = ProvisioningChange.STILL_NOT_PROVISIONED;
+        } else {
+            // (wasProvisioned && !isProvisioned)
+            //
+            // Note that this is true even if we lose a configuration element
+            // (e.g., a default gateway) that would not be required to advance
+            // into provisioned state. This is intended: if we have a default
+            // router and we lose it, that's a sure sign of a problem, but if
+            // we connect to a network with no IPv4 DNS servers, we consider
+            // that to be a network without DNS servers and connect anyway.
+            //
+            // See the comment below.
+            delta = ProvisioningChange.LOST_PROVISIONING;
+        }
+
+        // Additionally:
+        //
+        // Partial configurations (e.g., only an IPv4 address with no DNS
+        // servers and no default route) are accepted as long as DHCPv4
+        // succeeds. On such a network, isProvisioned() will always return
+        // false, because the configuration is not complete, but we want to
+        // connect anyway. It might be a disconnected network such as a
+        // Chromecast or a wireless printer, for example.
+        //
+        // Because on such a network isProvisioned() will always return false,
+        // delta will never be LOST_PROVISIONING. So check for loss of
+        // provisioning here too.
+        if ((oldLp.hasIPv4Address() && !newLp.hasIPv4Address()) ||
+                (oldLp.isIPv6Provisioned() && !newLp.isIPv6Provisioned())) {
+            delta = ProvisioningChange.LOST_PROVISIONING;
+        }
+
+        return delta;
+    }
+
+    private void dispatchCallback(ProvisioningChange delta, LinkProperties newLp) {
+        switch (delta) {
+            case GAINED_PROVISIONING:
+                if (VDBG) { Log.d(mTag, "onProvisioningSuccess()"); }
+                mCallback.onProvisioningSuccess(newLp);
+                break;
+
+            case LOST_PROVISIONING:
+                if (VDBG) { Log.d(mTag, "onProvisioningFailure()"); }
+                mCallback.onProvisioningFailure(newLp);
+                break;
+
+            default:
+                if (VDBG) { Log.d(mTag, "onLinkPropertiesChange()"); }
+                mCallback.onLinkPropertiesChange(newLp);
+                break;
+        }
+    }
+
     private ProvisioningChange setLinkProperties(LinkProperties newLp) {
         if (mIpReachabilityMonitor != null) {
             mIpReachabilityMonitor.updateLinkProperties(newLp);
         }
 
-        // TODO: Figure out whether and how to incorporate static configuration
-        // into the notion of provisioning.
         ProvisioningChange delta;
         synchronized (mLock) {
-            delta = LinkProperties.compareProvisioning(mLinkProperties, newLp);
+            delta = compareProvisioning(mLinkProperties, newLp);
             mLinkProperties = new LinkProperties(newLp);
         }
 
@@ -277,7 +398,7 @@
             switch (delta) {
                 case GAINED_PROVISIONING:
                 case LOST_PROVISIONING:
-                    Log.d(TAG, "provisioning: " + delta);
+                    Log.d(mTag, "provisioning: " + delta);
                     break;
             }
         }
@@ -333,7 +454,7 @@
         }
 
         if (VDBG) {
-            Log.d(TAG, "newLp{" + newLp + "}");
+            Log.d(mTag, "newLp{" + newLp + "}");
         }
 
         return newLp;
@@ -345,21 +466,51 @@
             ifcg.setLinkAddress(new LinkAddress("0.0.0.0/0"));
             mNwService.setInterfaceConfig(mInterfaceName, ifcg);
         } catch (RemoteException e) {
-            Log.e(TAG, "ALERT: Failed to clear IPv4 address on interface " + mInterfaceName, e);
+            Log.e(mTag, "ALERT: Failed to clear IPv4 address on interface " + mInterfaceName, e);
         }
     }
 
     private void handleIPv4Success(DhcpResults dhcpResults) {
         mDhcpResults = new DhcpResults(dhcpResults);
-        setLinkProperties(assembleLinkProperties());
-        mCallback.onIPv4ProvisioningSuccess(dhcpResults);
+        final LinkProperties newLp = assembleLinkProperties();
+        final ProvisioningChange delta = setLinkProperties(newLp);
+
+        if (VDBG) {
+            Log.d(mTag, "onNewDhcpResults(" + Objects.toString(dhcpResults) + ")");
+        }
+        mCallback.onNewDhcpResults(dhcpResults);
+
+        dispatchCallback(delta, newLp);
     }
 
     private void handleIPv4Failure() {
+        // TODO: Figure out to de-dup this and the same code in DhcpClient.
         clearIPv4Address();
         mDhcpResults = null;
-        setLinkProperties(assembleLinkProperties());
-        mCallback.onIPv4ProvisioningFailure();
+        final LinkProperties newLp = assembleLinkProperties();
+        ProvisioningChange delta = setLinkProperties(newLp);
+        // If we've gotten here and we're still not provisioned treat that as
+        // a total loss of provisioning.
+        //
+        // Either (a) static IP configuration failed or (b) DHCPv4 failed AND
+        // there was no usable IPv6 obtained before the DHCPv4 timeout.
+        //
+        // Regardless: GAME OVER.
+        //
+        // TODO: Make the DHCP client not time out and just continue in
+        // exponential backoff. Callers such as Wi-Fi which need a timeout
+        // should implement it themselves.
+        if (delta == ProvisioningChange.STILL_NOT_PROVISIONED) {
+            delta = ProvisioningChange.LOST_PROVISIONING;
+        }
+
+        if (VDBG) { Log.d(mTag, "onNewDhcpResults(null)"); }
+        mCallback.onNewDhcpResults(null);
+
+        dispatchCallback(delta, newLp);
+        if (delta == ProvisioningChange.LOST_PROVISIONING) {
+            transitionTo(mStoppingState);
+        }
     }
 
     class StoppedState extends State {
@@ -369,7 +520,7 @@
                 mNwService.disableIpv6(mInterfaceName);
                 mNwService.clearInterfaceAddresses(mInterfaceName);
             } catch (Exception e) {
-                Log.e(TAG, "Failed to clear addresses or disable IPv6" + e);
+                Log.e(mTag, "Failed to clear addresses or disable IPv6" + e);
             }
 
             resetLinkProperties();
@@ -390,14 +541,9 @@
                     setLinkProperties(assembleLinkProperties());
                     break;
 
-                case DhcpStateMachine.CMD_ON_QUIT:
-                    // CMD_ON_QUIT is really more like "EVENT_ON_QUIT".
-                    // Shutting down DHCPv4 progresses simultaneously with
-                    // transitioning to StoppedState, so we can receive this
-                    // message after we've already transitioned here.
-                    //
-                    // TODO: Figure out if this is actually useful and if not
-                    // expunge it.
+                case DhcpClient.CMD_ON_QUIT:
+                    // Everything is already stopped.
+                    Log.e(mTag, "Unexpected CMD_ON_QUIT (already stopped).");
                     break;
 
                 default:
@@ -407,6 +553,30 @@
         }
     }
 
+    class StoppingState extends State {
+        @Override
+        public void enter() {
+            if (mDhcpStateMachine == null) {
+                // There's no DHCPv4 for which to wait; proceed to stopped.
+                transitionTo(mStoppedState);
+            }
+        }
+
+        @Override
+        public boolean processMessage(Message msg) {
+            switch (msg.what) {
+                case DhcpClient.CMD_ON_QUIT:
+                    mDhcpStateMachine = null;
+                    transitionTo(mStoppedState);
+                    break;
+
+                default:
+                    deferMessage(msg);
+            }
+            return HANDLED;
+        }
+    }
+
     class StartedState extends State {
         @Override
         public void enter() {
@@ -416,9 +586,9 @@
                 mNwService.enableIpv6(mInterfaceName);
                 // TODO: Perhaps clearIPv4Address() as well.
             } catch (RemoteException re) {
-                Log.e(TAG, "Unable to change interface settings: " + re);
+                Log.e(mTag, "Unable to change interface settings: " + re);
             } catch (IllegalStateException ie) {
-                Log.e(TAG, "Unable to change interface settings: " + ie);
+                Log.e(mTag, "Unable to change interface settings: " + ie);
             }
 
             mIpReachabilityMonitor = new IpReachabilityMonitor(
@@ -439,13 +609,15 @@
                 if (applyStaticIpConfig()) {
                     handleIPv4Success(new DhcpResults(mStaticIpConfig));
                 } else {
-                    handleIPv4Failure();
+                    if (VDBG) { Log.d(mTag, "onProvisioningFailure()"); }
+                    mCallback.onProvisioningFailure(getLinkProperties());
+                    transitionTo(mStoppingState);
                 }
             } else {
                 // Start DHCPv4.
                 makeDhcpStateMachine();
                 mDhcpStateMachine.registerForPreDhcpNotification();
-                mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP);
+                mDhcpStateMachine.sendMessage(DhcpClient.CMD_START_DHCP);
             }
         }
 
@@ -455,9 +627,8 @@
             mIpReachabilityMonitor = null;
 
             if (mDhcpStateMachine != null) {
-                mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_STOP_DHCP);
+                mDhcpStateMachine.sendMessage(DhcpClient.CMD_STOP_DHCP);
                 mDhcpStateMachine.doQuit();
-                mDhcpStateMachine = null;
             }
 
             resetLinkProperties();
@@ -471,7 +642,7 @@
                     break;
 
                 case CMD_START:
-                    Log.e(TAG, "ALERT: START received in StartedState. Please fix caller.");
+                    Log.e(mTag, "ALERT: START received in StartedState. Please fix caller.");
                     break;
 
                 case CMD_CONFIRM:
@@ -489,8 +660,7 @@
                     // calls completedPreDhcpAction() after provisioning with
                     // a static IP configuration.
                     if (mDhcpStateMachine != null) {
-                        mDhcpStateMachine.sendMessage(
-                                DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE);
+                        mDhcpStateMachine.sendMessage(DhcpClient.CMD_PRE_DHCP_ACTION_COMPLETE);
                     }
                     break;
 
@@ -500,57 +670,43 @@
                         break;
                     }
                     final ProvisioningChange delta = setLinkProperties(newLp);
-
-                    // NOTE: The only receiver of these callbacks currently
-                    // treats all three of them identically, namely it calls
-                    // IpManager#getLinkProperties() and makes its own determination.
-                    switch (delta) {
-                        case GAINED_PROVISIONING:
-                            mCallback.onProvisioningSuccess(newLp);
-                            break;
-
-                        case LOST_PROVISIONING:
-                            mCallback.onProvisioningFailure(newLp);
-                            break;
-
-                        default:
-                            // TODO: Only notify on STILL_PROVISIONED?
-                            mCallback.onLinkPropertiesChange(newLp);
-                            break;
+                    dispatchCallback(delta, newLp);
+                    if (delta == ProvisioningChange.LOST_PROVISIONING) {
+                        transitionTo(mStoppedState);
                     }
                     break;
                 }
 
-                case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
+                case DhcpClient.CMD_PRE_DHCP_ACTION:
+                    if (VDBG) { Log.d(mTag, "onPreDhcpAction()"); }
                     mCallback.onPreDhcpAction();
                     break;
 
-                case DhcpStateMachine.CMD_POST_DHCP_ACTION: {
+                case DhcpClient.CMD_POST_DHCP_ACTION: {
                     // Note that onPostDhcpAction() is likely to be
                     // asynchronous, and thus there is no guarantee that we
                     // will be able to observe any of its effects here.
+                    if (VDBG) { Log.d(mTag, "onPostDhcpAction()"); }
                     mCallback.onPostDhcpAction();
 
                     final DhcpResults dhcpResults = (DhcpResults) msg.obj;
                     switch (msg.arg1) {
-                        case DhcpStateMachine.DHCP_SUCCESS:
+                        case DhcpClient.DHCP_SUCCESS:
                             handleIPv4Success(dhcpResults);
                             break;
-                        case DhcpStateMachine.DHCP_FAILURE:
+                        case DhcpClient.DHCP_FAILURE:
                             handleIPv4Failure();
                             break;
                         default:
-                            Log.e(TAG, "Unknown CMD_POST_DHCP_ACTION status:" + msg.arg1);
+                            Log.e(mTag, "Unknown CMD_POST_DHCP_ACTION status:" + msg.arg1);
                     }
                     break;
                 }
 
-                case DhcpStateMachine.CMD_ON_QUIT:
-                    // CMD_ON_QUIT is really more like "EVENT_ON_QUIT".
-                    // Regardless, we ignore it.
-                    //
-                    // TODO: Figure out if this is actually useful and if not
-                    // expunge it.
+                case DhcpClient.CMD_ON_QUIT:
+                    // DHCPv4 quit early for some reason.
+                    Log.e(mTag, "Unexpected CMD_ON_QUIT.");
+                    mDhcpStateMachine = null;
                     break;
 
                 default:
@@ -565,9 +721,9 @@
             ifcg.setInterfaceUp();
             try {
                 mNwService.setInterfaceConfig(mInterfaceName, ifcg);
-                if (DBG) Log.d(TAG, "Static IP configuration succeeded");
+                if (DBG) Log.d(mTag, "Static IP configuration succeeded");
             } catch (IllegalStateException | RemoteException e) {
-                Log.e(TAG, "Static IP configuration failed: ", e);
+                Log.e(mTag, "Static IP configuration failed: ", e);
                 return false;
             }
 
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 64f60d93..467ecd7 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -22,9 +22,12 @@
 import android.app.admin.DevicePolicyManagerInternal;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.net.wifi.WifiInfo;
 import android.os.Build.VERSION_CODES;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Process;
 import android.os.UserHandle;
@@ -995,6 +998,7 @@
     public void testApplicationRestrictionsManagingApp() throws Exception {
         setAsProfileOwner(admin1);
 
+        final String nonExistAppRestrictionsManagerPackage = "com.google.app.restrictions.manager2";
         final String appRestrictionsManagerPackage = "com.google.app.restrictions.manager";
         final int appRestrictionsManagerAppId = 20987;
         final int appRestrictionsManagerUid = UserHandle.getUid(
@@ -1004,6 +1008,14 @@
                 eq(DpmMockContext.CALLER_USER_HANDLE));
         mContext.binder.callingUid = appRestrictionsManagerUid;
 
+        final PackageInfo pi = new PackageInfo();
+        pi.applicationInfo = new ApplicationInfo();
+        pi.applicationInfo.flags = ApplicationInfo.FLAG_HAS_CODE;
+        doReturn(pi).when(mContext.ipackageManager).getPackageInfo(
+                eq(appRestrictionsManagerPackage),
+                anyInt(),
+                eq(DpmMockContext.CALLER_USER_HANDLE));
+
         // appRestrictionsManager package shouldn't be able to manage restrictions as the PO hasn't
         // delegated that permission yet.
         assertFalse(dpm.isCallerApplicationRestrictionsManagingPackage());
@@ -1028,6 +1040,16 @@
         mContext.binder.callingUid = DpmMockContext.CALLER_UID;
         assertEquals(0, dpm.getApplicationRestrictions(admin1, "pkg1").size());
 
+        // Check the API does not allow setting a non-existent package
+        try {
+            dpm.setApplicationRestrictionsManagingPackage(admin1,
+                    nonExistAppRestrictionsManagerPackage);
+            fail("Non-existent app set as app restriction manager.");
+        } catch (IllegalArgumentException expected) {
+            MoreAsserts.assertContainsRegex(
+                    "is not installed on the current user", expected.getMessage());
+        }
+
         // Let appRestrictionsManagerPackage manage app restrictions
         dpm.setApplicationRestrictionsManagingPackage(admin1, appRestrictionsManagerPackage);
         assertEquals(appRestrictionsManagerPackage,
diff --git a/telecomm/java/android/telecom/Log.java b/telecomm/java/android/telecom/Log.java
index 73cc4a5..3f32dbe 100644
--- a/telecomm/java/android/telecom/Log.java
+++ b/telecomm/java/android/telecom/Log.java
@@ -16,6 +16,8 @@
 
 package android.telecom;
 
+import android.os.AsyncTask;
+
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.util.IllegalFormatException;
@@ -38,8 +40,26 @@
     public static final boolean WARN = isLoggable(android.util.Log.WARN);
     public static final boolean ERROR = isLoggable(android.util.Log.ERROR);
 
+    private static MessageDigest sMessageDigest;
+
     private Log() {}
 
+    public static void initMd5Sum() {
+        new AsyncTask<Void, Void, Void>() {
+            @Override
+            public Void doInBackground(Void... args) {
+                MessageDigest md;
+                try {
+                    md = MessageDigest.getInstance("SHA-1");
+                } catch (NoSuchAlgorithmException e) {
+                    md = null;
+                }
+                sMessageDigest = md;
+                return null;
+            }
+        }.execute();
+    }
+
     public static boolean isLoggable(int level) {
         return FORCE_LOGGING || android.util.Log.isLoggable(TAG, level);
     }
@@ -137,15 +157,14 @@
     }
 
     private static String secureHash(byte[] input) {
-        MessageDigest messageDigest;
-        try {
-            messageDigest = MessageDigest.getInstance("SHA-1");
-        } catch (NoSuchAlgorithmException e) {
-            return null;
+        if (sMessageDigest != null) {
+            sMessageDigest.reset();
+            sMessageDigest.update(input);
+            byte[] result = sMessageDigest.digest();
+            return encodeHex(result);
+        } else {
+            return "Uninitialized SHA1";
         }
-        messageDigest.update(input);
-        byte[] result = messageDigest.digest();
-        return encodeHex(result);
     }
 
     private static String encodeHex(byte[] bytes) {
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index f1cbb9a..9f478df 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -498,6 +498,7 @@
             mContext = context;
         }
         mTelecomServiceOverride = telecomServiceImpl;
+        android.telecom.Log.initMd5Sum();
     }
 
     /**
diff --git a/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl b/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl
index 23a69d1..69259d0 100644
--- a/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl
+++ b/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl
@@ -17,6 +17,7 @@
 package com.android.ims.internal;
 
 import com.android.ims.ImsReasonInfo;
+
 /**
  * A listener type for receiving notifications about the changes to
  * the IMS connection(registration).
@@ -26,15 +27,36 @@
 interface IImsRegistrationListener {
     /**
      * Notifies the application when the device is connected to the IMS network.
+     *
+     * @deprecated see {@link registrationConnectedWithRadioTech}
      */
     void registrationConnected();
 
     /**
      * Notifies the application when the device is trying to connect the IMS network.
+     *
+     * @deprecated see {@link registrationProgressingWithRadioTech}
      */
     void registrationProgressing();
 
     /**
+     * Notifies the application when the device is connected to the IMS network.
+     *
+     * @param imsRadioTech the radio access technology. Valid values are {@code
+     * RIL_RADIO_TECHNOLOGY_*} defined in {@link ServiceState}.
+     */
+    void registrationConnectedWithRadioTech(int imsRadioTech);
+
+    /**
+     * Notifies the application when the device is trying to connect the IMS network.
+     *
+     * @param imsRadioTech the radio access technology. Valid values are {@code
+     * RIL_RADIO_TECHNOLOGY_*} defined in {@link ServiceState}.
+     */
+    void registrationProgressingWithRadioTech(int imsRadioTech);
+
+
+    /**
      * Notifies the application when the device is disconnected from the IMS network.
      */
     void registrationDisconnected(in ImsReasonInfo imsReasonInfo);
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 429839f..0d6c70b 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -92,7 +92,33 @@
     int NO_SMS_TO_ACK = 48;                   /* ACK received when there is no SMS to ack */
     int NETWORK_ERR = 49;                     /* Received error from network */
     int REQUEST_RATE_LIMITED = 50;            /* Operation denied due to overly-frequent requests */
-
+    // Below is list of OEM specific error codes which can by used by OEMs in case they don't want to
+    // reveal particular replacement for Generic failure
+    int OEM_ERROR_1 = 501;
+    int OEM_ERROR_2 = 502;
+    int OEM_ERROR_3 = 503;
+    int OEM_ERROR_4 = 504;
+    int OEM_ERROR_5 = 505;
+    int OEM_ERROR_6 = 506;
+    int OEM_ERROR_7 = 507;
+    int OEM_ERROR_8 = 508;
+    int OEM_ERROR_9 = 509;
+    int OEM_ERROR_10 = 510;
+    int OEM_ERROR_11 = 511;
+    int OEM_ERROR_12 = 512;
+    int OEM_ERROR_13 = 513;
+    int OEM_ERROR_14 = 514;
+    int OEM_ERROR_15 = 515;
+    int OEM_ERROR_16 = 516;
+    int OEM_ERROR_17 = 517;
+    int OEM_ERROR_18 = 518;
+    int OEM_ERROR_19 = 519;
+    int OEM_ERROR_20 = 520;
+    int OEM_ERROR_21 = 521;
+    int OEM_ERROR_22 = 522;
+    int OEM_ERROR_23 = 523;
+    int OEM_ERROR_24 = 524;
+    int OEM_ERROR_25 = 525;
 
     /* NETWORK_MODE_* See ril.h RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE */
     int NETWORK_MODE_WCDMA_PREF     = 0; /* GSM/WCDMA (WCDMA preferred) */
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 4d9ba6c..18a1943 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -1883,8 +1883,6 @@
                     //printf("Comment of %s: %s\n", String8(e).string(),
                     //        String8(cmt).string());
                     syms->appendComment(String8(e), String16(cmt), srcPos);
-                } else {
-                    //printf("No comment for %s\n", String8(e).string());
                 }
                 syms->makeSymbolPublic(String8(e), srcPos);
             } else if (strcmp16(block.getElementName(&len), uses_permission16.string()) == 0) {
@@ -2535,10 +2533,6 @@
             fprintf(fp,
                     "%s/** %s\n",
                     getIndentSpace(indent), cmt.string());
-        } else if (sym.isPublic && !includePrivate) {
-            sym.sourcePos.warning("No comment for public symbol %s:%s/%s",
-                assets->getPackage().string(), className.string(),
-                String8(sym.name).string());
         }
         String16 typeComment(sym.typeComment);
         if (typeComment.size() > 0) {
@@ -2581,10 +2575,6 @@
                      "%s */\n",
                     getIndentSpace(indent), cmt.string(),
                     getIndentSpace(indent));
-        } else if (sym.isPublic && !includePrivate) {
-            sym.sourcePos.warning("No comment for public symbol %s:%s/%s",
-                assets->getPackage().string(), className.string(),
-                String8(sym.name).string());
         }
         ann.printAnnotations(fp, getIndentSpace(indent));
         fprintf(fp, "%spublic static final String %s=\"%s\";\n",
diff --git a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
index d0dd22f..a10ac00 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
@@ -388,21 +388,18 @@
     @LayoutlibDelegate
     /*package*/ static void native_addRoundRect(long nPath, float left, float top, float right,
             float bottom, float[] radii, int dir) {
-        // Java2D doesn't support different rounded corners in each corner, so just use the
-        // first value.
-        native_addRoundRect(nPath, left, top, right, bottom, radii[0], radii[1], dir);
 
-        // there can be a case where this API is used but with similar values for all corners, so
-        // in that case we don't warn.
-        // we only care if 2 corners are different so just compare to the next one.
-        for (int i = 0 ; i < 3 ; i++) {
-            if (radii[i * 2] != radii[(i + 1) * 2] || radii[i * 2 + 1] != radii[(i + 1) * 2 + 1]) {
-                Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
-                        "Different corner sizes are not supported in Path.addRoundRect.",
-                        null, null /*data*/);
-                break;
-            }
+        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
+        if (pathDelegate == null) {
+            return;
         }
+
+        float[] cornerDimensions = new float[radii.length];
+        for (int i = 0; i < radii.length; i++) {
+            cornerDimensions[i] = 2 * radii[i];
+        }
+        pathDelegate.mPath.append(new RoundRectangle(left, top, right - left, bottom - top,
+                cornerDimensions), false);
     }
 
     @LayoutlibDelegate
diff --git a/tools/layoutlib/bridge/src/android/graphics/RoundRectangle.java b/tools/layoutlib/bridge/src/android/graphics/RoundRectangle.java
new file mode 100644
index 0000000..edd36e5
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/RoundRectangle.java
@@ -0,0 +1,370 @@
+/*
+ * 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.graphics;
+
+import java.awt.geom.AffineTransform;
+import java.awt.geom.PathIterator;
+import java.awt.geom.Rectangle2D;
+import java.awt.geom.RectangularShape;
+import java.awt.geom.RoundRectangle2D;
+import java.util.EnumSet;
+import java.util.NoSuchElementException;
+
+/**
+ * Defines a rectangle with rounded corners, where the sizes of the corners
+ * are potentially different.
+ */
+public class RoundRectangle extends RectangularShape {
+    public double x;
+    public double y;
+    public double width;
+    public double height;
+    public double ulWidth;
+    public double ulHeight;
+    public double urWidth;
+    public double urHeight;
+    public double lrWidth;
+    public double lrHeight;
+    public double llWidth;
+    public double llHeight;
+
+    private enum Zone {
+        CLOSE_OUTSIDE,
+        CLOSE_INSIDE,
+        MIDDLE,
+        FAR_INSIDE,
+        FAR_OUTSIDE
+    }
+
+    private final EnumSet<Zone> close = EnumSet.of(Zone.CLOSE_OUTSIDE, Zone.CLOSE_INSIDE);
+    private final EnumSet<Zone> far = EnumSet.of(Zone.FAR_OUTSIDE, Zone.FAR_INSIDE);
+
+    /**
+     * @param cornerDimensions array of 8 floating-point number corresponding to the width and
+     * the height of each corner in the following order: upper-left, upper-right, lower-right,
+     * lower-left. It assumes for the size the same convention as {@link RoundRectangle2D}, that
+     * is that the width and height of a corner correspond to the total width and height of the
+     * ellipse that corner is a quarter of.
+     */
+    public RoundRectangle(float x, float y, float width, float height, float[] cornerDimensions) {
+        if (cornerDimensions.length != 8) {
+            throw new IllegalArgumentException("The array of corner dimensions must have eight " +
+                    "elements");
+        }
+
+        this.x = x;
+        this.y = y;
+        this.width = width;
+        this.height = height;
+
+        float[] dimensions = cornerDimensions.clone();
+        // If a value is negative, the corresponding corner is squared
+        for (int i = 0; i < dimensions.length; i += 2) {
+            if (dimensions[i] < 0 || dimensions[i + 1] < 0) {
+                dimensions[i] = 0;
+                dimensions[i + 1] = 0;
+            }
+        }
+
+        double topCornerWidth = (dimensions[0] + dimensions[2]) / 2d;
+        double bottomCornerWidth = (dimensions[4] + dimensions[6]) / 2d;
+        double leftCornerHeight = (dimensions[1] + dimensions[7]) / 2d;
+        double rightCornerHeight = (dimensions[3] + dimensions[5]) / 2d;
+
+        // Rescale the corner dimensions if they are bigger than the rectangle
+        double scale = Math.min(1.0, width / topCornerWidth);
+        scale = Math.min(scale, width / bottomCornerWidth);
+        scale = Math.min(scale, height / leftCornerHeight);
+        scale = Math.min(scale, height / rightCornerHeight);
+
+        this.ulWidth = dimensions[0] * scale;
+        this.ulHeight = dimensions[1] * scale;
+        this.urWidth = dimensions[2] * scale;
+        this.urHeight = dimensions[3] * scale;
+        this.lrWidth = dimensions[4] * scale;
+        this.lrHeight = dimensions[5] * scale;
+        this.llWidth = dimensions[6] * scale;
+        this.llHeight = dimensions[7] * scale;
+    }
+
+    @Override
+    public double getX() {
+        return x;
+    }
+
+    @Override
+    public double getY() {
+        return y;
+    }
+
+    @Override
+    public double getWidth() {
+        return width;
+    }
+
+    @Override
+    public double getHeight() {
+        return height;
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return (width <= 0d) || (height <= 0d);
+    }
+
+    @Override
+    public void setFrame(double x, double y, double w, double h) {
+        this.x = x;
+        this.y = y;
+        this.width = w;
+        this.height = h;
+    }
+
+    @Override
+    public Rectangle2D getBounds2D() {
+        return new Rectangle2D.Double(x, y, width, height);
+    }
+
+    @Override
+    public boolean contains(double x, double y) {
+        if (isEmpty()) {
+            return false;
+        }
+
+        double x0 = getX();
+        double y0 = getY();
+        double x1 = x0 + getWidth();
+        double y1 = y0 + getHeight();
+        // Check for trivial rejection - point is outside bounding rectangle
+        if (x < x0 || y < y0 || x >= x1 || y >= y1) {
+            return false;
+        }
+
+        double insideTopX0 = x0 + ulWidth / 2d;
+        double insideLeftY0 = y0 + ulHeight / 2d;
+        if (x < insideTopX0 && y < insideLeftY0) {
+            // In the upper-left corner
+            return isInsideCorner(x - insideTopX0, y - insideLeftY0, ulWidth / 2d, ulHeight / 2d);
+        }
+
+        double insideTopX1 = x1 - urWidth / 2d;
+        double insideRightY0 = y0 + urHeight / 2d;
+        if (x > insideTopX1 && y < insideRightY0) {
+            // In the upper-right corner
+            return isInsideCorner(x - insideTopX1, y - insideRightY0, urWidth / 2d, urHeight / 2d);
+        }
+
+        double insideBottomX1 = x1 - lrWidth / 2d;
+        double insideRightY1 = y1 - lrHeight / 2d;
+        if (x > insideBottomX1 && y > insideRightY1) {
+            // In the lower-right corner
+            return isInsideCorner(x - insideBottomX1, y - insideRightY1, lrWidth / 2d,
+                    lrHeight / 2d);
+        }
+
+        double insideBottomX0 = x0 + llWidth / 2d;
+        double insideLeftY1 = y1 - llHeight / 2d;
+        if (x < insideBottomX0 && y > insideLeftY1) {
+            // In the lower-left corner
+            return isInsideCorner(x - insideBottomX0, y - insideLeftY1, llWidth / 2d,
+                    llHeight / 2d);
+        }
+
+        // In the central part of the rectangle
+        return true;
+    }
+
+    private boolean isInsideCorner(double x, double y, double width, double height) {
+        double squareDist = height * height * x * x + width * width * y * y;
+        return squareDist <= width * width * height * height;
+    }
+
+    private Zone classify(double coord, double side1, double arcSize1, double side2,
+            double arcSize2) {
+        if (coord < side1) {
+            return Zone.CLOSE_OUTSIDE;
+        } else if (coord < side1 + arcSize1) {
+            return Zone.CLOSE_INSIDE;
+        } else if (coord < side2 - arcSize2) {
+            return Zone.MIDDLE;
+        } else if (coord < side2) {
+            return Zone.FAR_INSIDE;
+        } else {
+            return Zone.FAR_OUTSIDE;
+        }
+    }
+
+    public boolean intersects(double x, double y, double w, double h) {
+        if (isEmpty() || w <= 0 || h <= 0) {
+            return false;
+        }
+        double x0 = getX();
+        double y0 = getY();
+        double x1 = x0 + getWidth();
+        double y1 = y0 + getHeight();
+        // Check for trivial rejection - bounding rectangles do not intersect
+        if (x + w <= x0 || x >= x1 || y + h <= y0 || y >= y1) {
+            return false;
+        }
+
+        double maxLeftCornerWidth = Math.max(ulWidth, llWidth) / 2d;
+        double maxRightCornerWidth = Math.max(urWidth, lrWidth) / 2d;
+        double maxUpperCornerHeight = Math.max(ulHeight, urHeight) / 2d;
+        double maxLowerCornerHeight = Math.max(llHeight, lrHeight) / 2d;
+        Zone x0class = classify(x, x0, maxLeftCornerWidth, x1, maxRightCornerWidth);
+        Zone x1class = classify(x + w, x0, maxLeftCornerWidth, x1, maxRightCornerWidth);
+        Zone y0class = classify(y, y0, maxUpperCornerHeight, y1, maxLowerCornerHeight);
+        Zone y1class = classify(y + h, y0, maxUpperCornerHeight, y1, maxLowerCornerHeight);
+
+        // Trivially accept if any point is inside inner rectangle
+        if (x0class == Zone.MIDDLE || x1class == Zone.MIDDLE || y0class == Zone.MIDDLE || y1class == Zone.MIDDLE) {
+            return true;
+        }
+        // Trivially accept if either edge spans inner rectangle
+        if ((close.contains(x0class) && far.contains(x1class)) || (close.contains(y0class) &&
+                far.contains(y1class))) {
+            return true;
+        }
+
+        // Since neither edge spans the center, then one of the corners
+        // must be in one of the rounded edges.  We detect this case if
+        // a [xy]0class is 3 or a [xy]1class is 1.  One of those two cases
+        // must be true for each direction.
+        // We now find a "nearest point" to test for being inside a rounded
+        // corner.
+        if (x1class == Zone.CLOSE_INSIDE && y1class == Zone.CLOSE_INSIDE) {
+            // Potentially in upper-left corner
+            x = x + w - x0 - ulWidth / 2d;
+            y = y + h - y0 - ulHeight / 2d;
+            return x > 0 || y > 0 || isInsideCorner(x, y, ulWidth / 2d, ulHeight / 2d);
+        }
+        if (x1class == Zone.CLOSE_INSIDE) {
+            // Potentially in lower-left corner
+            x = x + w - x0 - llWidth / 2d;
+            y = y - y1 + llHeight / 2d;
+            return x > 0 || y < 0 || isInsideCorner(x, y, llWidth / 2d, llHeight / 2d);
+        }
+        if (y1class == Zone.CLOSE_INSIDE) {
+            //Potentially in the upper-right corner
+            x = x - x1 + urWidth / 2d;
+            y = y + h - y0 - urHeight / 2d;
+            return x < 0 || y > 0 || isInsideCorner(x, y, urWidth / 2d, urHeight / 2d);
+        }
+        // Potentially in the lower-right corner
+        x = x - x1 + lrWidth / 2d;
+        y = y - y1 + lrHeight / 2d;
+        return x < 0 || y < 0 || isInsideCorner(x, y, lrWidth / 2d, lrHeight / 2d);
+    }
+
+    @Override
+    public boolean contains(double x, double y, double w, double h) {
+        if (isEmpty() || w <= 0 || h <= 0) {
+            return false;
+        }
+        return (contains(x, y) &&
+                contains(x + w, y) &&
+                contains(x, y + h) &&
+                contains(x + w, y + h));
+    }
+
+    @Override
+    public PathIterator getPathIterator(final AffineTransform at) {
+        return new PathIterator() {
+            int index;
+
+            // ArcIterator.btan(Math.PI/2)
+            public static final double CtrlVal = 0.5522847498307933;
+            private final double ncv = 1.0 - CtrlVal;
+
+            // Coordinates of control points for Bezier curves approximating the straight lines
+            // and corners of the rounded rectangle.
+            private final double[][] ctrlpts = {
+                    {0.0, 0.0, 0.0, ulHeight},
+                    {0.0, 0.0, 1.0, -llHeight},
+                    {0.0, 0.0, 1.0, -llHeight * ncv, 0.0, ncv * llWidth, 1.0, 0.0, 0.0, llWidth,
+                            1.0, 0.0},
+                    {1.0, -lrWidth, 1.0, 0.0},
+                    {1.0, -lrWidth * ncv, 1.0, 0.0, 1.0, 0.0, 1.0, -lrHeight * ncv, 1.0, 0.0, 1.0,
+                            -lrHeight},
+                    {1.0, 0.0, 0.0, urHeight},
+                    {1.0, 0.0, 0.0, ncv * urHeight, 1.0, -urWidth * ncv, 0.0, 0.0, 1.0, -urWidth,
+                            0.0, 0.0},
+                    {0.0, ulWidth, 0.0, 0.0},
+                    {0.0, ncv * ulWidth, 0.0, 0.0, 0.0, 0.0, 0.0, ncv * ulHeight, 0.0, 0.0, 0.0,
+                            ulHeight},
+                    {}
+            };
+            private final int[] types = {
+                    SEG_MOVETO,
+                    SEG_LINETO, SEG_CUBICTO,
+                    SEG_LINETO, SEG_CUBICTO,
+                    SEG_LINETO, SEG_CUBICTO,
+                    SEG_LINETO, SEG_CUBICTO,
+                    SEG_CLOSE,
+            };
+
+            @Override
+            public int getWindingRule() {
+                return WIND_NON_ZERO;
+            }
+
+            @Override
+            public boolean isDone() {
+                return index >= ctrlpts.length;
+            }
+
+            @Override
+            public void next() {
+                index++;
+            }
+
+            @Override
+            public int currentSegment(float[] coords) {
+                if (isDone()) {
+                    throw new NoSuchElementException("roundrect iterator out of bounds");
+                }
+                int nc = 0;
+                double ctrls[] = ctrlpts[index];
+                for (int i = 0; i < ctrls.length; i += 4) {
+                    coords[nc++] = (float) (x + ctrls[i] * width + ctrls[i + 1] / 2d);
+                    coords[nc++] = (float) (y + ctrls[i + 2] * height + ctrls[i + 3] / 2d);
+                }
+                if (at != null) {
+                    at.transform(coords, 0, coords, 0, nc / 2);
+                }
+                return types[index];
+            }
+
+            @Override
+            public int currentSegment(double[] coords) {
+                if (isDone()) {
+                    throw new NoSuchElementException("roundrect iterator out of bounds");
+                }
+                int nc = 0;
+                double ctrls[] = ctrlpts[index];
+                for (int i = 0; i < ctrls.length; i += 4) {
+                    coords[nc++] = x + ctrls[i] * width + ctrls[i + 1] / 2d;
+                    coords[nc++] = y + ctrls[i + 2] * height + ctrls[i + 3] / 2d;
+                }
+                if (at != null) {
+                    at.transform(coords, 0, coords, 0, nc / 2);
+                }
+                return types[index];
+            }
+        };
+    }
+}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 2ad3c2e..4921073 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -44,6 +44,7 @@
 import com.android.internal.util.Protocol;
 
 import java.net.InetAddress;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
 
@@ -892,6 +893,24 @@
     }
 
     /**
+     * Sets whether or not the given network is metered from a network policy
+     * point of view. A network should be classified as metered when the user is
+     * sensitive to heavy data usage on that connection due to monetary costs,
+     * data limitations or battery/performance issues. A typical example would
+     * be a wifi connection where the user was being charged for usage.
+     * @param netId the integer that identifies the network configuration
+     * to the supplicant.
+     * @param isMetered True to mark the network as metered.
+     * @return {@code true} if the operation succeeded.
+     * @hide
+     */
+    @SystemApi
+    public boolean setMetered(int netId, boolean isMetered) {
+        // TODO(jjoslin): Implement
+        return false;
+    }
+
+    /**
      * Remove the specified network from the list of configured networks.
      * This may result in the asynchronous delivery of state change
      * events.
@@ -1301,13 +1320,15 @@
      * @return the list of access points found in the most recent scan. An app must hold
      * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or
      * {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission
-     * in order to get valid results.
+     * in order to get valid results.  If there is a remote exception (e.g., either a communication
+     * problem with the system service or an exception within the framework) an empty list will be
+     * returned.
      */
     public List<ScanResult> getScanResults() {
         try {
             return mService.getScanResults(mContext.getOpPackageName());
         } catch (RemoteException e) {
-            return null;
+            return new ArrayList<ScanResult>();
         }
     }
 
diff --git a/wifi/java/android/net/wifi/nan/WifiNanEventListener.java b/wifi/java/android/net/wifi/nan/WifiNanEventListener.java
index 5c18bd7..9e6ed4e 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanEventListener.java
+++ b/wifi/java/android/net/wifi/nan/WifiNanEventListener.java
@@ -36,7 +36,7 @@
  */
 public class WifiNanEventListener {
     private static final String TAG = "WifiNanEventListener";
-    private static final boolean DBG = true;
+    private static final boolean DBG = false;
     private static final boolean VDBG = false; // STOPSHIP if true
 
     /**
diff --git a/wifi/java/android/net/wifi/nan/WifiNanManager.java b/wifi/java/android/net/wifi/nan/WifiNanManager.java
index cb82268..667c4b1 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanManager.java
+++ b/wifi/java/android/net/wifi/nan/WifiNanManager.java
@@ -38,7 +38,7 @@
  */
 public class WifiNanManager {
     private static final String TAG = "WifiNanManager";
-    private static final boolean DBG = true;
+    private static final boolean DBG = false;
     private static final boolean VDBG = false; // STOPSHIP if true
 
     private IBinder mBinder;
diff --git a/wifi/java/android/net/wifi/nan/WifiNanSession.java b/wifi/java/android/net/wifi/nan/WifiNanSession.java
index d0a9410..bc1787f 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanSession.java
+++ b/wifi/java/android/net/wifi/nan/WifiNanSession.java
@@ -27,7 +27,7 @@
  */
 public class WifiNanSession {
     private static final String TAG = "WifiNanSession";
-    private static final boolean DBG = true;
+    private static final boolean DBG = false;
     private static final boolean VDBG = false; // STOPSHIP if true
 
     /**
diff --git a/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java b/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java
index 0925087..b9af7def 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java
+++ b/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java
@@ -43,7 +43,7 @@
  */
 public class WifiNanSessionListener {
     private static final String TAG = "WifiNanSessionListener";
-    private static final boolean DBG = true;
+    private static final boolean DBG = false;
     private static final boolean VDBG = false; // STOPSHIP if true
 
     /**