diff options
133 files changed, 3347 insertions, 1699 deletions
diff --git a/api/current.txt b/api/current.txt index db77d3365737..7f4075a24c3d 100644 --- a/api/current.txt +++ b/api/current.txt @@ -19221,7 +19221,6 @@ package android.location { 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 +19231,7 @@ package android.location { 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 +19291,6 @@ package android.location { 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 +19301,7 @@ package android.location { 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 +19356,17 @@ package android.location { 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 +19412,8 @@ package android.location { 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 +22950,8 @@ package android.media.tv { 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 +23051,8 @@ package android.media.tv { 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 +23067,7 @@ package android.media.tv { 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 +23087,7 @@ package android.media.tv { 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 +29064,7 @@ package android.os { 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 +29245,7 @@ package android.os { 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); @@ -52150,7 +52154,6 @@ package java.net { 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 @@ package java.net { 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 { 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 @@ package java.nio.channels { 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 @@ package java.nio.channels { 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 @@ package java.nio.channels { 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 @@ package java.nio.channels { 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 @@ package java.nio.channels { 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 @@ package java.nio.channels { 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 @@ package java.nio.channels { 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 @@ package java.nio.channels.spi { 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; diff --git a/api/system-current.txt b/api/system-current.txt index 86419ce86261..75988ababdc9 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -20401,7 +20401,6 @@ package android.location { 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 +20411,7 @@ package android.location { 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 +20471,6 @@ package android.location { 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 +20481,7 @@ package android.location { 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 +20536,17 @@ package android.location { 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 +20592,8 @@ package android.location { 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 +24649,8 @@ package android.media.tv { 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 +24809,8 @@ package android.media.tv { 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 +24852,7 @@ package android.media.tv { 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 +24872,7 @@ package android.media.tv { 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 +26682,7 @@ package android.net.wifi { 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 +31358,7 @@ package android.os { 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 +31585,7 @@ package android.os { 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(); @@ -55238,7 +55243,6 @@ package java.net { 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 @@ package java.net { 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 { 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 @@ package java.nio.channels { 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 @@ package java.nio.channels { 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 @@ package java.nio.channels { 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 @@ package java.nio.channels { 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 @@ package java.nio.channels { 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 @@ package java.nio.channels { 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 @@ package java.nio.channels { 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 @@ package java.nio.channels.spi { 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; diff --git a/api/test-current.txt b/api/test-current.txt index 675b9938dc83..dfbea5f59d04 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -19229,7 +19229,6 @@ package android.location { 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 +19239,7 @@ package android.location { 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 +19299,6 @@ package android.location { 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 +19309,7 @@ package android.location { 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 +19364,17 @@ package android.location { 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 +19420,8 @@ package android.location { 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 +22959,8 @@ package android.media.tv { 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 +23060,8 @@ package android.media.tv { 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 +23076,7 @@ package android.media.tv { 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 +23096,7 @@ package android.media.tv { 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 +29073,7 @@ package android.os { 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 +29255,7 @@ package android.os { 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); @@ -52167,7 +52171,6 @@ package java.net { 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 @@ package java.net { 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 { 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 @@ package java.nio.channels { 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 @@ package java.nio.channels { 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 @@ package java.nio.channels { 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 @@ package java.nio.channels { 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 @@ package java.nio.channels { 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 @@ package java.nio.channels { 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 @@ package java.nio.channels { 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 @@ package java.nio.channels.spi { 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; diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 622012ecf2e2..ea58e292e545 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.NonNull; 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.content.res.TypedArray; 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.Handler; 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.Selection; 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.ViewGroup.LayoutParams; 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.ArrayList; 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,14 +3978,49 @@ public class Activity extends ContextThemeWrapper // 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; } /** @@ -5612,8 +5651,8 @@ public class Activity extends ContextThemeWrapper 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 1eb2fe2c89ec..f64765cb3ddd 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -863,8 +863,10 @@ public class ActivityManager { 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 +874,21 @@ public class ActivityManager { 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 +898,7 @@ public class ActivityManager { * @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 +907,24 @@ public class ActivityManager { * @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 +933,9 @@ public class ActivityManager { 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 +963,18 @@ public class ActivityManager { } /** + * 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 +1023,8 @@ public class ActivityManager { 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 +1038,26 @@ public class ActivityManager { 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 +1068,10 @@ public class ActivityManager { 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 +1097,7 @@ public class ActivityManager { mIcon.writeToParcel(dest, 0); } dest.writeInt(mColorPrimary); + dest.writeInt(mColorBackground); if (mIconFilename == null) { dest.writeInt(0); } else { @@ -1076,6 +1110,7 @@ public class ActivityManager { 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 +1127,8 @@ public class ActivityManager { @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 55c635360433..35b7c39338fc 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -3358,13 +3358,9 @@ public class Notification implements Parcelable 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 @@ public class Notification implements Parcelable } 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 @@ public class Notification implements Parcelable 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 @@ public class Notification implements Parcelable 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 83f9357d2909..1b2322ff0c6c 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -3023,13 +3023,39 @@ public class DevicePolicyManager { } /** + * @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 +3066,10 @@ public class DevicePolicyManager { * 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 +5756,32 @@ public class DevicePolicyManager { 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 c6a53443b51c..bd6818264448 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -116,9 +116,10 @@ interface IDevicePolicyManager { 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 @@ interface IDevicePolicyManager { 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 8d8d2f59c677..f1703d644bb8 100644 --- a/core/java/android/auditing/SecurityLog.java +++ b/core/java/android/auditing/SecurityLog.java @@ -77,8 +77,10 @@ public class SecurityLog { 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 cf858940058b..ccc37995972c 100644 --- a/core/java/android/auditing/SecurityLogTags.logtags +++ b/core/java/android/auditing/SecurityLogTags.logtags @@ -8,5 +8,5 @@ option java_package android.auditing 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/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index 43a0cc77d492..f58b16a7204f 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -178,6 +178,11 @@ public class ActivityInfo extends ComponentInfo */ 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 @@ public class ActivityInfo extends ComponentInfo /** @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 @@ public class ActivityInfo extends ComponentInfo 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/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 5dddebd3cae7..5ae8d4cf0ce4 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -80,6 +80,7 @@ import libcore.io.IoUtils; 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 @@ public class PackageParser { 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 766868da9193..8724a96ee30a 100644 --- a/core/java/android/hardware/camera2/CameraCaptureSession.java +++ b/core/java/android/hardware/camera2/CameraCaptureSession.java @@ -847,6 +847,9 @@ public abstract class CameraCaptureSession implements AutoCloseable { * 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 3f36d65e577c..b6fe68af51ac 100644 --- a/core/java/android/net/NetworkScoreManager.java +++ b/core/java/android/net/NetworkScoreManager.java @@ -105,7 +105,8 @@ public class NetworkScoreManager { /** * 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/Process.java b/core/java/android/os/Process.java index b51d2dfb8694..9984755d316f 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -823,6 +823,16 @@ public class Process { } /** + * 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 24666fe71724..b3f44536214b 100644 --- a/core/java/android/os/UserHandle.java +++ b/core/java/android/os/UserHandle.java @@ -130,6 +130,15 @@ public final class UserHandle implements Parcelable { } /** + * 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 dc0e249d72ec..69d564fc610b 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -1141,6 +1141,8 @@ public class UserManager { 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 58a026904373..d2ece8bba3ad 100644 --- a/core/java/android/os/UserManagerInternal.java +++ b/core/java/android/os/UserManagerInternal.java @@ -17,6 +17,7 @@ package android.os; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.pm.UserInfo; import android.graphics.Bitmap; /** @@ -106,4 +107,12 @@ public abstract class UserManagerInternal { * 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 8468040b1bad..2ca758935feb 100644 --- a/core/java/android/provider/DocumentsContract.java +++ b/core/java/android/provider/DocumentsContract.java @@ -1201,7 +1201,7 @@ public final class DocumentsContract { 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/text/BidiFormatter.java b/core/java/android/text/BidiFormatter.java index 675803c7b7bd..707c0fcb75ee 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 @@ public final class BidiFormatter { * @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 82f69efaac45..e9d12f511a9a 100644 --- a/core/java/android/text/Html.java +++ b/core/java/android/text/Html.java @@ -356,24 +356,48 @@ public class Html { } } - private static String getTextStyles(Spanned text, int start, int end) { - final StringBuilder style = new StringBuilder(" style=\"margin-top:0; margin-bottom:0;"); - - 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;"); + private static String getTextStyles(Spanned text, int start, int end, + boolean forceNoVerticalMargin, boolean includeTextAlign) { + String margin = null; + String textAlign = null; + + 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 +419,55 @@ public class Html { 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"); + } + 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>\n"); - } + 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"); - } + 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(">"); + String tagType = isListItem ? "li" : "p"; + out.append("<").append(tagType) + .append(getTextDirection(text, i, next)) + .append(getTextStyles(text, i, next, !isListItem, true)) + .append(">"); - if (next - i == 0) { - out.append("<br>"); - } else { 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 +687,9 @@ class HtmlToSpannedConverter implements ContentHandler { private int mFlags; private static Pattern sTextAlignPattern; + private static Pattern sForegroundColorPattern; + private static Pattern sBackgroundColorPattern; + private static Pattern sTextDecorationPattern; private static Pattern getTextAlignPattern() { if (sTextAlignPattern == null) { @@ -662,6 +698,30 @@ class HtmlToSpannedConverter implements ContentHandler { 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 +775,15 @@ class HtmlToSpannedConverter implements ContentHandler { // 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 +835,16 @@ class HtmlToSpannedConverter implements ContentHandler { 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 +898,14 @@ class HtmlToSpannedConverter implements ContentHandler { 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); } @@ -905,6 +987,18 @@ class HtmlToSpannedConverter implements ContentHandler { 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()); @@ -970,6 +1064,55 @@ class HtmlToSpannedConverter implements ContentHandler { } } + private static void startCssStyle(Editable text, Attributes attributes) { + String style = attributes.getValue("", "style"); + if (style != null) { + final int len = text.length(); + Matcher m = getForegroundColorPattern().matcher(style); + if (m.find()) { + int c = Color.getHtmlColor(m.group(1)); + if (c != -1) { + text.setSpan(new Foreground(c | 0xFF000000), len, len, + Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + } + } + + m = getBackgroundColorPattern().matcher(style); + if (m.find()) { + int c = Color.getHtmlColor(m.group(1)); + if (c != -1) { + text.setSpan(new Background(c | 0xFF000000), len, len, + Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + } + } + + m = getTextDecorationPattern().matcher(style); + if (m.find()) { + String textDecoration = m.group(1); + if (textDecoration.equalsIgnoreCase("line-through")) { + text.setSpan(new Strikethrough(), len, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + } + } + } + } + + 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; @@ -1132,6 +1275,7 @@ class HtmlToSpannedConverter implements ContentHandler { private static class Blockquote { } private static class Super { } private static class Sub { } + private static class Bullet { } private static class Font { public String mColor; @@ -1151,6 +1295,22 @@ class HtmlToSpannedConverter implements ContentHandler { } } + 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 2612ab2f8eaf..bba5a173d12c 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -6874,6 +6874,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @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/com/android/internal/app/LocaleStore.java b/core/java/com/android/internal/app/LocaleStore.java index 210adce4d0af..465c4d833aa1 100644 --- a/core/java/com/android/internal/app/LocaleStore.java +++ b/core/java/com/android/internal/app/LocaleStore.java @@ -104,6 +104,9 @@ public class LocaleStore { } private boolean isSuggestionOfType(int suggestionMask) { + if (!mIsTranslated) { // Never suggest an untranslated locale + return false; + } return (mSuggestionFlags & suggestionMask) == suggestionMask; } @@ -207,6 +210,27 @@ public class LocaleStore { } } + /* + * 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 @@ public class LocaleStore { 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 88af920df2d4..f1c79fa60db9 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -1718,8 +1718,13 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind 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 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind * 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 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind 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/util/LineBreakBufferedWriter.java b/core/java/com/android/internal/util/LineBreakBufferedWriter.java index f831e7a84d2f..552a93f6666a 100644 --- a/core/java/com/android/internal/util/LineBreakBufferedWriter.java +++ b/core/java/com/android/internal/util/LineBreakBufferedWriter.java @@ -96,7 +96,7 @@ public class LineBreakBufferedWriter extends PrintWriter { @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/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp index dd0e45636ee7..ac7700786ea0 100644 --- a/core/jni/android_view_ThreadedRenderer.cpp +++ b/core/jni/android_view_ThreadedRenderer.cpp @@ -322,7 +322,11 @@ private: 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 dd161e38486e..ca0373773577 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 17e93d0a9eae..b2589da8ea5b 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 78268036c827..20a80489239d 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 63df5beff76d..61753b1f0211 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 96a81d138457..2fe4f6652a87 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/tests/coretests/src/com/android/internal/util/LineBreakBufferedWriterTest.java b/core/tests/coretests/src/com/android/internal/util/LineBreakBufferedWriterTest.java index 49ae10401983..4845c4ef28f8 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 @@ public class LineBreakBufferedWriterTest extends TestCase { 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/hwui/BakedOpState.h b/libs/hwui/BakedOpState.h index 3db28c982469..5a5845af81b9 100644 --- a/libs/hwui/BakedOpState.h +++ b/libs/hwui/BakedOpState.h @@ -100,7 +100,7 @@ public: 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 @@ public: ? (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 @@ public: 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 @@ public: 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 c2e14a29f29e..6d5833b3be86 100644 --- a/libs/hwui/DamageAccumulator.cpp +++ b/libs/hwui/DamageAccumulator.cpp @@ -45,7 +45,7 @@ struct DirtyStack { }; 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::computeCurrentTransform(Matrix4* outMatrix) const { 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 2d5979f2f1a7..98ccf11b1c2a 100644 --- a/libs/hwui/DeferredDisplayList.h +++ b/libs/hwui/DeferredDisplayList.h @@ -49,11 +49,6 @@ typedef const void* mergeid_t; 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 @@ private: 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 e5711e35a88b..a703e227fc8d 100644 --- a/libs/hwui/DisplayListCanvas.h +++ b/libs/hwui/DisplayListCanvas.h @@ -251,7 +251,7 @@ private: 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 @@ private: // 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 20501ba3c1d9..98315d0a416a 100644 --- a/libs/hwui/DisplayListOp.h +++ b/libs/hwui/DisplayListOp.h @@ -64,7 +64,9 @@ public: 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 185accec1ef6..4f51036b336e 100644 --- a/libs/hwui/FrameBuilder.cpp +++ b/libs/hwui/FrameBuilder.cpp @@ -209,7 +209,7 @@ void FrameBuilder::deferNodePropsAndOps(RenderNode& node) { // 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 @@ void FrameBuilder::deferNodePropsAndOps(RenderNode& node) { // (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::deferBitmapRectOp(const BitmapRectOp& op) { 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 @@ void FrameBuilder::deferCirclePropsOp(const CirclePropsOp& op) { 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::deferRoundRectOp(const RoundRectOp& op) { 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 @@ void FrameBuilder::deferEndLayerOp(const EndLayerOp& /* ignored */) { // 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 @@ void FrameBuilder::deferBeginUnclippedLayerOp(const BeginUnclippedLayerOp& op) { /** * 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 @@ void FrameBuilder::deferBeginUnclippedLayerOp(const BeginUnclippedLayerOp& op) { * 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 11293d61211b..c8f5e9435594 100644 --- a/libs/hwui/GradientCache.cpp +++ b/libs/hwui/GradientCache.cpp @@ -165,6 +165,10 @@ Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient, 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 7170d4fbeea7..1ba3bf26c0d4 100644 --- a/libs/hwui/LayerBuilder.cpp +++ b/libs/hwui/LayerBuilder.cpp @@ -64,10 +64,6 @@ protected: 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 @@ public: 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 @@ void LayerBuilder::flushLayerClears(LinearAllocator& allocator) { // 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 @@ void LayerBuilder::flushLayerClears(LinearAllocator& allocator) { // 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 @@ void LayerBuilder::deferUnmergeableOp(LinearAllocator& allocator, 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 @@ void LayerBuilder::deferMergeableOp(LinearAllocator& allocator, 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 16929b8ac8ee..269e590892d3 100644 --- a/libs/hwui/RecordingCanvas.cpp +++ b/libs/hwui/RecordingCanvas.cpp @@ -83,9 +83,9 @@ void RecordingCanvas::onViewportInitialized() { 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 @@ int RecordingCanvas::saveLayer(float left, float top, float right, float bottom, 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 @@ int RecordingCanvas::saveLayer(float left, float top, float right, float bottom, } 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::drawColor(int color, SkXfermode::Mode mode) { } 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 @@ void RecordingCanvas::drawPoints(const float* points, int floatCount, const SkPa 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 @@ void RecordingCanvas::drawLines(const float* points, int floatCount, const SkPai 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::drawLines(const float* points, int floatCount, const SkPai } 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::drawRect(float left, float top, float right, float bottom, 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 @@ void RecordingCanvas::drawSimpleRects(const float* rects, int vertexCount, const 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::drawRegion(const SkRegion& region, const SkPaint& paint) { } 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 @@ void RecordingCanvas::drawRoundRect( 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 @@ void RecordingCanvas::drawCircle( 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::drawCircle( } 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::drawOval(float left, float top, float right, float bottom, 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::drawArc(float left, float top, float right, float bottom, } 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::drawPath(const SkPath& path, const SkPaint& paint) { 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 @@ void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, float srcLeft, float sr 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::drawBitmap(const SkBitmap& bitmap, float srcLeft, float sr 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::drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int 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 @@ void RecordingCanvas::drawText(const uint16_t* glyphs, const float* positions, i 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 @@ void RecordingCanvas::drawTextOnPath(const uint16_t* glyphs, int glyphCount, con 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::drawTextOnPath(const uint16_t* glyphs, int glyphCount, con } 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::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) { 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 @@ void RecordingCanvas::drawLayer(DeferredLayerUpdater* layerHandle) { 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::drawLayer(DeferredLayerUpdater* layerHandle) { 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 cc14e6111cec..719872d35169 100644 --- a/libs/hwui/RecordingCanvas.h +++ b/libs/hwui/RecordingCanvas.h @@ -219,7 +219,7 @@ private: 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 @@ private: // 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 dbaa905b0728..0ac2f1411140 100644 --- a/libs/hwui/Snapshot.h +++ b/libs/hwui/Snapshot.h @@ -46,7 +46,7 @@ class RoundRectClipState { 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 @@ class ProjectionPathMask { 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 c09b6dd89e4e..49a103c01108 100644 --- a/libs/hwui/Texture.cpp +++ b/libs/hwui/Texture.cpp @@ -33,8 +33,11 @@ static int bytesPerPixel(GLint glFormat) { 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/tests/unit/LinearAllocatorTests.cpp b/libs/hwui/tests/unit/LinearAllocatorTests.cpp index 5c442901045e..402a09c55e8f 100644 --- a/libs/hwui/tests/unit/LinearAllocatorTests.cpp +++ b/libs/hwui/tests/unit/LinearAllocatorTests.cpp @@ -30,7 +30,7 @@ struct SimplePair { 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 @@ TEST(LinearAllocator, dtor) { 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 @@ TEST(LinearAllocator, rewind) { 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 e6a4c03156b4..5bba420a258f 100644 --- a/libs/hwui/utils/LinearAllocator.cpp +++ b/libs/hwui/utils/LinearAllocator.cpp @@ -81,10 +81,6 @@ static void _addAllocation(int count) { #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 @@ void LinearAllocator::ensureNext(size_t size) { 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 @@ void LinearAllocator::addToDestructionList(Destructor dtor, void* addr) { "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 dcbc0dda951a..0a0e1858cd91 100644 --- a/libs/hwui/utils/LinearAllocator.h +++ b/libs/hwui/utils/LinearAllocator.h @@ -52,30 +52,36 @@ public: * 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 @@ private: 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 @@ public: : 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 @@ public: }; // 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 d8f507c9b70e..a490685a3a8b 100644 --- a/location/java/android/location/GnssMeasurement.java +++ b/location/java/android/location/GnssMeasurement.java @@ -28,7 +28,7 @@ import java.lang.annotation.RetentionPolicy; */ 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 final class GnssMeasurement implements Parcelable { */ 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 @@ public final class GnssMeasurement implements Parcelable { * 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 @@ public final class GnssMeasurement implements Parcelable { 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 @@ public final class GnssMeasurement implements Parcelable { } }; + @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 @@ public final class GnssMeasurement implements Parcelable { 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 @@ public final class GnssMeasurement implements Parcelable { 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 0e011d5c98a8..86328eb5dcd7 100644 --- a/location/java/android/location/GnssNavigationMessage.java +++ b/location/java/android/location/GnssNavigationMessage.java @@ -26,7 +26,7 @@ import java.lang.annotation.RetentionPolicy; 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 @@ public final class GnssNavigationMessage implements Parcelable { // 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 final class GnssNavigationMessage implements Parcelable { */ 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 @@ public final class GnssNavigationMessage implements Parcelable { * 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 @@ public final class GnssNavigationMessage implements Parcelable { 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 @@ public final class GnssNavigationMessage implements Parcelable { } }; + @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 @@ public final class GnssNavigationMessage implements Parcelable { 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 @@ public final class GnssNavigationMessage implements Parcelable { 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 77e8a5bbaca0..906e944f06ab 100644 --- a/location/java/android/location/GnssStatus.java +++ b/location/java/android/location/GnssStatus.java @@ -47,24 +47,26 @@ public final class GnssStatus { 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 @@ public final class GnssStatus { * @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 @@ public final class GnssStatus { * @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 @@ public final class GnssStatus { * @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 @@ public final class GnssStatus { * @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 8d2f781e497f..e41e20c172f7 100644 --- a/location/java/android/location/GpsStatus.java +++ b/location/java/android/location/GpsStatus.java @@ -138,15 +138,19 @@ public final class GpsStatus { // 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 @@ public final class GpsStatus { 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 @@ public final class GpsStatus { */ 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 d1c6a85a9fdd..8c7d06eee5e6 100644 --- a/location/java/android/location/IGnssStatusListener.aidl +++ b/location/java/android/location/IGnssStatusListener.aidl @@ -26,7 +26,7 @@ oneway interface IGnssStatusListener 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 30cf101397db..23f0710a0a9d 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -1562,10 +1562,9 @@ public class LocationManager { @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/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java index 1320e382a7ea..00083e650314 100644 --- a/media/java/android/media/tv/TvInputManager.java +++ b/media/java/android/media/tv/TvInputManager.java @@ -157,35 +157,22 @@ public final class TvInputManager { 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 334c84b1f35e..4ebd0fccc5c8 100644 --- a/media/java/android/media/tv/TvInputService.java +++ b/media/java/android/media/tv/TvInputService.java @@ -1584,6 +1584,10 @@ public abstract class TvInputService extends Service { * 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 @@ public abstract class TvInputService extends Service { } /** - * 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 72606f59f53b..1c920f57de2a 100644 --- a/media/java/android/media/tv/TvRecordingClient.java +++ b/media/java/android/media/tv/TvRecordingClient.java @@ -68,7 +68,7 @@ public class TvRecordingClient { /** * 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 @@ public class TvRecordingClient { /** * 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 class TvRecordingClient { */ 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 @@ public class TvRecordingClient { * @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 @@ public class TvRecordingClient { mSession.tune(mChannelUri, mConnectionParams); } else { mSessionCallback = null; - mCallback.onError(TvInputManager.RECORDING_ERROR_CONNECTION_FAILED); + if (mCallback != null) { + mCallback.onConnectionFailed(mInputId); + } } } @@ -331,11 +348,13 @@ public class TvRecordingClient { 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 ed432c463b8a..6a44b1e6e4e9 100644 --- a/media/java/android/media/tv/TvTrackInfo.java +++ b/media/java/android/media/tv/TvTrackInfo.java @@ -52,11 +52,14 @@ public final class TvTrackInfo implements Parcelable { 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 @@ public final class TvTrackInfo implements Parcelable { mVideoHeight = videoHeight; mVideoFrameRate = videoFrameRate; mVideoPixelAspectRatio = videoPixelAspectRatio; + mVideoActiveFormatDescription = videoActiveFormatDescription; mExtra = extra; } @@ -81,6 +85,7 @@ public final class TvTrackInfo implements Parcelable { mVideoHeight = in.readInt(); mVideoFrameRate = in.readFloat(); mVideoPixelAspectRatio = in.readFloat(); + mVideoActiveFormatDescription = in.readByte(); mExtra = in.readBundle(); } @@ -179,6 +184,20 @@ public final class TvTrackInfo implements Parcelable { } /** + * 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 @@ public final class TvTrackInfo implements Parcelable { dest.writeInt(mVideoHeight); dest.writeFloat(mVideoFrameRate); dest.writeFloat(mVideoPixelAspectRatio); + dest.writeByte(mVideoActiveFormatDescription); dest.writeBundle(mExtra); } @@ -238,6 +258,7 @@ public final class TvTrackInfo implements Parcelable { private int mVideoHeight; private float mVideoFrameRate; private float mVideoPixelAspectRatio = 1.0f; + private byte mVideoActiveFormatDescription; private Bundle mExtra; /** @@ -368,6 +389,25 @@ public final class TvTrackInfo implements Parcelable { } /** + * 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 final class TvTrackInfo implements Parcelable { 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 8414febfb7ca..84a928dc3d9b 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 d0364ff44294..0fb74e5eca34 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 f3de3b43be50..b33b8d09b992 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 f53d69808929..235d22d0737b 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 475387bb224b..4a55906e56c1 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.CallSuper; 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 @@ public abstract class BaseActivity extends Activity // 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 @@ public abstract class BaseActivity extends Activity } } + /** + * 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/RecentsCreateFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java index 7dac0c10e077..0e2762291b09 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java @@ -83,7 +83,7 @@ public class RecentsCreateFragment extends Fragment { 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 26bda31261ef..53f82972738c 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java @@ -89,7 +89,7 @@ public class RootsFragment extends Fragment { 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 @@ public class RootsFragment extends Fragment { } } + /** + * 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 000000000000..bf03ffd1e6d6 --- /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 174984cec8ed..f8735b2f99a1 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.model.RootInfo; 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 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi 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 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi 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 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi 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 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi @Override public void initDocumentHolder(DocumentHolder holder) { holder.addEventListener(mItemEventListener); + holder.itemView.setOnFocusChangeListener(mFocusManager); } @Override @@ -1054,6 +1060,13 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi } } + /** + * 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 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi } 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 86b9146ba48c..93ec8426e74f 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 @@ import com.android.documentsui.Events; /** * 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 @@ class FocusManager { * @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 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 handled; + } + + /** + * @return The adapter position of the last focused item. + */ + public int getFocusPosition() { + return mLastFocusPosition; } /** @@ -100,12 +134,27 @@ class FocusManager { 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 @@ class FocusManager { }); } } + + /** + * @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 d60825baa7cf..c8b6f8528272 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 @@ public final class MultiSelectManager { } /** - * 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 77f16d9ca9e6..609dc0c67e50 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 static com.android.documentsui.StubProvider.ROOT_1_ID; 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 @@ public class FilesActivityUiTest extends ActivityTest<FilesActivity> { 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 4534c40c6e0e..d2f84034593c 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 @@ class UiBot { 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 @@ class UiBot { 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 @@ class UiBot { 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 @@ class UiBot { 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 @@ class UiBot { 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 d95fb490d81e..9447d9c18a83 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 @@ public class MultiSelectManagerTest extends AndroidTestCase { 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/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerAdapter.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerAdapter.java index 73171c70d856..1d6197a9333f 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 class SettingsDrawerAdapter extends BaseAdapter { } 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 8c555a655bef..bad7e202e1d1 100644 --- a/packages/Shell/src/com/android/shell/BugreportProgressService.java +++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java @@ -88,8 +88,8 @@ import android.widget.Toast; * <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 @@ public class BugreportProgressService extends Service { 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 @@ public class BugreportProgressService extends Service { */ 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 @@ public class BugreportProgressService extends Service { } // 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 @@ public class BugreportProgressService extends Service { } 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 @@ public class BugreportProgressService extends Service { } } - 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 @@ public class BugreportProgressService extends Service { * * @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 @@ public class BugreportProgressService extends Service { 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 @@ public class BugreportProgressService extends Service { 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 @@ public class BugreportProgressService extends Service { + 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 @@ public class BugreportProgressService extends Service { 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 @@ public class BugreportProgressService extends Service { 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 @@ public class BugreportProgressService extends Service { 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 @@ public class BugreportProgressService extends Service { } 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 @@ public class BugreportProgressService extends Service { * 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 @@ public class BugreportProgressService extends Service { * 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 @@ public class BugreportProgressService extends Service { // 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 @@ public class BugreportProgressService extends Service { 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 @@ public class BugreportProgressService extends Service { */ 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 @@ public class BugreportProgressService extends Service { 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 @@ public class BugreportProgressService extends Service { * 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 @@ public class BugreportProgressService extends Service { mContext.startActivity(notifIntent); // ... and stop watching this process. - stopProgress(pid); + stopProgress(id); } /** @@ -877,16 +883,16 @@ public class BugreportProgressService extends Service { 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 @@ public class BugreportProgressService extends Service { 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 @@ public class BugreportProgressService extends Service { * 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 @@ public class BugreportProgressService extends Service { .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 @@ public class BugreportProgressService extends Service { // 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 @@ public class BugreportProgressService extends Service { /** * 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 @@ public class BugreportProgressService extends Service { private EditText mInfoDescription; private AlertDialog mDialog; private Button mOkButton; + private int mId; private int mPid; /** @@ -1207,8 +1214,7 @@ public class BugreportProgressService extends Service { /** * 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 @@ public class BugreportProgressService extends Service { 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 @@ public class BugreportProgressService extends Service { } // 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 @@ public class BugreportProgressService extends Service { 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 @@ public class BugreportProgressService extends Service { // 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 @@ public class BugreportProgressService extends Service { * <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 @@ public class BugreportProgressService extends Service { 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 @@ public class BugreportProgressService extends Service { /** * 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 @@ public class BugreportProgressService extends Service { * 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 @@ public class BugreportProgressService extends Service { @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 @@ public class BugreportProgressService extends Service { // Parcelable contract protected BugreportInfo(Parcel in) { context = null; + id = in.readInt(); pid = in.readInt(); name = in.readString(); title = in.readString(); @@ -1527,6 +1541,7 @@ public class BugreportProgressService extends Service { @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 a9b8df2934c7..4cd920a86733 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 5d4789a0f53d..4116962ffa20 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/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 958572fd4fd5..90d56f78697e 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 @@ public class KeyguardViewMediator extends SystemUI { 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 @@ public class KeyguardViewMediator extends SystemUI { * @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 @@ public class KeyguardViewMediator extends SystemUI { * @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 9da5c2bd3f02..e0efaa5ae38d 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 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener * 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 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener } 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 244c0df30da4..95aa10f4876e 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 @@ public class DozeTrigger { 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 22ab79430cd2..8b4474f1cea5 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 @@ public class SystemServicesProxy { Display mDisplay; String mRecentsPackage; ComponentName mAssistComponent; + + boolean mIsSafeMode; boolean mHasFreeformWorkspaceSupport; Bitmap mDummyIcon; @@ -137,6 +139,7 @@ public class SystemServicesProxy { 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 @@ public class SystemServicesProxy { 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 @@ public class SystemServicesProxy { 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 c51aa7ce38c9..016e6d30843d 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 @@ package com.android.systemui.recents.model; 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 @@ public class RecentsTaskLoadPlan { : 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 26130abc138b..5e1af1280400 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 @@ public class RecentsTaskLoader { 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 @@ public class RecentsTaskLoader { } /** + * 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 1c277d5a3d92..8ed6dd78a357 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.Bitmap; 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 @@ public class Task { /* 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 @@ public class Task { } } + @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 class Task { */ 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 @@ public class Task { /** * 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 class Task { 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 @@ public class Task { 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 @@ public class Task { 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 584209510e45..4a6537464bc0 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 @@ package com.android.systemui.recents.views; 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 42aaa9782712..c4db48552377 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.util.AttributeSet; 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 com.android.systemui.statusbar.FlingAnimationUtils; 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 @@ public class RecentsView extends FrameLayout { private boolean mAwaitingFirstLayout = true; private boolean mLastTaskLaunchedWasFreeform; + + @ViewDebug.ExportedProperty(category="recents") private Rect mSystemInsets = new Rect(); private int mDividerSize; @@ -110,6 +113,7 @@ public class RecentsView extends FrameLayout { 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 346ce167cd17..016d9370364d 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.content.res.Configuration; 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 @@ public class RecentsViewTouchHandler { 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 19ac1e7dd44d..98bc92f8de8d 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.ArrayMap; 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 @@ public class TaskStackLayoutAlgorithm { 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 @@ public class TaskStackLayoutAlgorithm { 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 @@ public class TaskStackLayoutAlgorithm { // 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 fb3515a26380..9560eb458542 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.FREEFORM_WORKSPACE_STACK_ID; 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.util.MutableBoolean; 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal 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 d1bce55c324e..b54ea1ddc449 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.content.Context; 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 @@ public class TaskStackViewScroller { 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 5d1bb66f0dac..55f9fbaba1bd 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.MotionEvent; 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 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback { 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 850e36e73e36..0f485acc3bad 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.IntProperty; 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 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks } }; + @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 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks /**** 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 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks @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 c91a833e943a..bb56a520fbdd 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.support.v4.graphics.ColorUtils; 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 @@ public class TaskViewHeader extends FrameLayout 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 class TaskViewHeader extends FrameLayout } public void setColorAndDim(int color, float 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)); - - invalidateSelf(); + 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)); + + invalidateSelf(); + } } @Override @@ -121,6 +128,10 @@ public class TaskViewHeader extends FrameLayout public int getOpacity() { return PixelFormat.OPAQUE; } + + public int getColor() { + return mColor; + } } Task mTask; @@ -139,9 +150,11 @@ public class TaskViewHeader extends FrameLayout 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 @@ public class TaskViewHeader extends FrameLayout Drawable mDarkInfoIcon; int mTaskBarViewLightTextColor; int mTaskBarViewDarkTextColor; + int mDisabledTaskBarBackgroundColor; int mMoveTaskTargetStackId = INVALID_STACK_ID; // Header background @@ -195,6 +209,8 @@ public class TaskViewHeader extends FrameLayout 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 @@ public class TaskViewHeader extends FrameLayout */ 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 @@ public class TaskViewHeader extends FrameLayout } /** 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 f90951e4ccd1..0fec9c38b5a4 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.Bitmap; 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 @@ import com.android.systemui.recents.model.Task; */ 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 @@ public class TaskViewThumbnail extends View { mDrawPaint.setAntiAlias(true); mCornerRadius = getResources().getDimensionPixelSize( R.dimen.recents_task_view_rounded_corners_radius); + mBgFillPaint.setColor(Color.WHITE); } /** @@ -100,10 +118,39 @@ public class TaskViewThumbnail extends View { 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 @@ public class TaskViewThumbnail extends View { } 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 @@ public class TaskViewThumbnail extends View { } /** 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 7d37ad22223f..c9ebfa0a7829 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -194,6 +194,7 @@ public abstract class BaseStatusBar extends SystemUI implements private final SparseBooleanArray mUsersAllowingPrivateNotifications = new SparseBooleanArray(); private UserManager mUserManager; + private int mDensity; // UI-specific methods @@ -633,6 +634,7 @@ public abstract class BaseStatusBar extends SystemUI implements mLocale = currentConfig.locale; mLayoutDirection = TextUtils.getLayoutDirectionFromLocale(mLocale); mFontScale = currentConfig.fontScale; + mDensity = currentConfig.densityDpi; mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); @@ -813,8 +815,13 @@ public abstract class BaseStatusBar extends SystemUI implements 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 +833,21 @@ public abstract class BaseStatusBar extends SystemUI implements } } + 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,25 +2102,44 @@ public abstract class BaseStatusBar extends SystemUI implements 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 (entry.hasJustLaunchedFullScreenIntent()) { - if (DEBUG) Log.d(TAG, "No peeking: recent fullscreen: " + sbn.getKey()); + if (mNotificationData.shouldSuppressScreenOn(sbn.getKey())) { + if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey()); return false; } - if (sbn.getNotification().fullScreenIntent != null - && mAccessibilityManager.isTouchExplorationEnabled()) { - if (DEBUG) Log.d(TAG, "No peeking: accessible fullscreen: " + sbn.getKey()); + if (entry.hasJustLaunchedFullScreenIntent()) { + if (DEBUG) Log.d(TAG, "No peeking: recent 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 +2148,7 @@ public abstract class BaseStatusBar extends SystemUI implements 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/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java index 84b2031491fd..264655c77301 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.graphics.drawable.Drawable; 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 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { 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 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { 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 class ExpandableNotificationRow extends ActivatableNotificationView { 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/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index f382fe05650a..14376acf8e83 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -754,19 +754,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, 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); @@ -930,6 +919,34 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, 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), 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 5cfcd8981890..49aec423db1c 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 @@ public class NotificationChildrenContainer extends ViewGroup { 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 class NotificationChildrenContainer extends ViewGroup { 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 @@ public class NotificationChildrenContainer extends ViewGroup { 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 @@ public class NotificationChildrenContainer extends ViewGroup { 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 a75ac8219aa3..bf4245b5e7ed 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 @@ public class NotificationStackScrollLayout extends ViewGroup } private void updateAnimationState(View child) { - updateAnimationState((mAnimationsEnabled || isPinnedHeadsUp(child)) && mIsExpanded, child); + updateAnimationState(mAnimationsEnabled && (mIsExpanded || isPinnedHeadsUp(child)), child); } @@ -2874,13 +2874,23 @@ public class NotificationStackScrollLayout extends ViewGroup } 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/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java index 7149065e1f8a..4c3b61b200f0 100644 --- a/services/backup/java/com/android/server/backup/BackupManagerService.java +++ b/services/backup/java/com/android/server/backup/BackupManagerService.java @@ -3494,7 +3494,7 @@ public class BackupManagerService { */ int preflightFullBackup(PackageInfo pkg, IBackupAgent agent); - long expectedSize(); + long getExpectedSizeOrErrorCode(); }; class FullBackupEngine { @@ -4555,6 +4555,10 @@ public class BackupManagerService { // now wait to get our result back mLatch.await(); int totalSize = mResult.get(); + // If preflight timeouted, mResult will contain error code. + if (totalSize < 0) { + return totalSize; + } if (MORE_DEBUG) { Slog.v(TAG, "Got preflight response; size=" + totalSize); } @@ -4600,7 +4604,7 @@ public class BackupManagerService { } @Override - public long expectedSize() { + public long getExpectedSizeOrErrorCode() { try { mLatch.await(); return mResult.get(); @@ -4649,7 +4653,7 @@ public class BackupManagerService { } long expectedSize() { - return mPreflight.expectedSize(); + return mPreflight.getExpectedSizeOrErrorCode(); } } } diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java index a4455e91f294..07d472dc5375 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 @@ public class AppOpsService extends IAppOpsService.Stub { @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 @@ public class AppOpsService extends IAppOpsService.Stub { 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/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index 95f57346dcfc..799d0bda895f 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -2086,7 +2086,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub 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 b984e1938758..879bb6f72895 100644 --- a/services/core/java/com/android/server/NetworkScoreService.java +++ b/services/core/java/com/android/server/NetworkScoreService.java @@ -234,12 +234,24 @@ public class NetworkScoreService extends INetworkScoreService.Stub { // 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/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java index bef6f0aabf5d..1b9d968f18db 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 @@ class ActivityManagerDebugConfig { 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 @@ class ActivityManagerDebugConfig { 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 104217a63104..50619015bff9 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 @@ public final class ActivityManagerService extends ActivityManagerNative @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 @@ public final class ActivityManagerService extends ActivityManagerNative 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 75dabc96c2c3..1103ea4874d2 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 @@ final class ActivityStack { 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 @@ final class ActivityStack { * 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 @@ final class ActivityStack { // 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 @@ final class ActivityStack { // 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 @@ final class ActivityStack { if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked(); return true; } - next.stopped = false; - } else { // Whoops, need to restart this activity! if (!next.hasBeenLaunched) { @@ -4283,6 +4283,12 @@ final class ActivityStack { // "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 @@ final class ActivityStack { } 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 26108a362273..e90b5db5ac4e 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -1938,9 +1938,6 @@ public final class ActivityStackSupervisor implements DisplayListener { } 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/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index 98a7ead339df..16fd909b329a 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_DEFAULT; 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 @@ final class TaskRecord { 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 @@ final class TaskRecord { 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 @@ final class TaskRecord { 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 @@ final class TaskRecord { 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 @@ final class TaskRecord { 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/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index 8dec5870427c..218e529d5366 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -2546,6 +2546,7 @@ public class SyncManager { Slog.v(TAG, "Pushing back running sync due to a higher priority sync"); } deferActiveSyncH(asc); + break; } } } diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java index e5e86ac44a26..402837281609 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 @@ public class JobSchedulerService extends com.android.server.SystemService 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 @@ public class JobSchedulerService extends com.android.server.SystemService * 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 @@ public class JobSchedulerService extends com.android.server.SystemService 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 f796164ef8ce..f6a6778aa496 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 @@ public class JobStore { /** * 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 @@ public class JobStore { } 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 ffc52b31a7d5..c1eb844f09fa 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 @@ public class GnssLocationProvider implements LocationProviderInterface { * 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 @@ public class GnssLocationProvider implements LocationProviderInterface { // 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 @@ public class GnssLocationProvider implements LocationProviderInterface { } // 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 @@ public class GnssLocationProvider implements LocationProviderInterface { // 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 9840c61ec516..0b3111cd726c 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 @@ abstract class GnssStatusListenerHelper extends RemoteListenerHelper<IGnssStatus 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 @@ abstract class GnssStatusListenerHelper extends RemoteListenerHelper<IGnssStatus 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 b2e6adfd028a..24bb845de427 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 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @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); + } + if (changed) { + mHandler.obtainMessage(MSG_RESTRICT_BACKGROUND_WHITELIST_CHANGED, uid, 0) + .sendToTarget(); } - 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 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } /** - * 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 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); mContext.sendBroadcastAsUser(intent, UserHandle.of(userId)); } - return true; } case MSG_ADVISE_PERSIST_THRESHOLD: { @@ -2831,13 +2863,5 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { 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/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java index da62a2d6fc84..94b3b2d613cf 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 @@ import static com.android.server.pm.Installer.DEXOPT_OTA; 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 @@ public class OtaDexoptService extends IOtaDexopt.Stub { 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 @@ public class OtaDexoptService extends IOtaDexopt.Stub { 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/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 31d16cddee76..92343d6fbcaf 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.Comparator; 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,30 +6779,17 @@ public class PackageManagerService extends IPackageManager.Stub { // 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); } - } - } - - 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); + for (PackageParser.Package pkg : pkgs) { + if (PackageDexOptimizer.canOptimizePackage(pkg)) { + performDexOpt(pkg.packageName, null /* instructionSet */, + false /* useProfiles */, true /* extractOnly */, false /* force */); + } } } - return pkgNames; } @Override @@ -13268,16 +13256,19 @@ public class PackageManagerService extends IPackageManager.Stub { // 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 @@ -13295,7 +13286,6 @@ public class PackageManagerService extends IPackageManager.Stub { } // 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); 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 000000000000..a3ac514ccd22 --- /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 3cc7b10c6f24..22d09944d13f 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 @@ public class UserManagerService extends IUserManager.Stub { 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; } @@ -2975,6 +2979,17 @@ public class UserManagerService extends IUserManager.Stub { 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/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java index c3a6f5d2f637..3e8269acce73 100644 --- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java +++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java @@ -29,6 +29,7 @@ import android.content.pm.Signature; 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.util.AndroidRuntimeException; @@ -141,7 +142,7 @@ public class WebViewUpdateService extends SystemService { // 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 @@ public class WebViewUpdateService extends SystemService { try { if (oldPackage != null) { ActivityManagerNative.getDefault().killPackageDependents( - oldPackage.packageName, getContext().getUserId()); + oldPackage.packageName, UserHandle.USER_ALL); } } catch (RemoteException e) { } diff --git a/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java b/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java index 0979cd32a1e2..e229c5e3fc35 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 @@ public class WindowManagerDebugConfig { 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 @@ public class WindowManagerDebugConfig { 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 d1ffaa07ed13..db533de9b4b6 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 @@ public class WindowManagerService extends IWindowManager.Stub 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 @@ public class WindowManagerService extends IWindowManager.Stub //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 @@ public class WindowManagerService extends IWindowManager.Stub } } 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 @@ public class WindowManagerService extends IWindowManager.Stub } 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 @@ public class WindowManagerService extends IWindowManager.Stub mOpeningApps.remove(wtoken); mClosingApps.remove(wtoken); - wtoken.mAppStopped = false; wtoken.waitingToShow = false; wtoken.hiddenRequested = !visible; @@ -4246,6 +4245,8 @@ public class WindowManagerService extends IWindowManager.Stub // 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 diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 02012961f5be..f8f8363c6412 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 @@ class WindowStateAnimator { } } 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 e75775fa9237..cdd551936511 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 GpsMeasurementInterface* sGpsMeasurementInterface = NULL; 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 @@ static void sv_status_callback(GpsSvStatus* sv_status) { 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); - } - 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]; + ALOGW("Invalid size of GpsSvStatus found: %zd.", status_size); + } + 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 @@ GpsCallbacks sGpsCallbacks = { 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 void android_location_GnssLocationProvider_delete_aiding_data(JNIEnv* /* } 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,367 @@ static jboolean android_location_GnssLocationProvider_resume_geofence(JNIEnv* /* 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"; - - GpsClock* clock = reinterpret_cast<GpsClock*>(data); - - jclass gpsClockClass = env->FindClass("android/location/GnssClock"); - jmethodID gpsClockCtor = env->GetMethodID(gpsClockClass, "<init>", "()V"); - - jobject gpsClockObject = env->NewObject(gpsClockClass, gpsClockCtor); - 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); - } +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); + + private: + static const char *const signature_; +}; - jmethodID typeSetterMethod = env->GetMethodID(gpsClockClass, "setType", "(B)V"); - env->CallVoidMethod(gpsClockObject, typeSetterMethod, clock->type); +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); +} - jmethodID setterMethod = env->GetMethodID(gpsClockClass, "setTimeInNs", longSignature); - env->CallVoidMethod(gpsClockObject, setterMethod, clock->time_ns); +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_; +}; - if (flags & GPS_CLOCK_HAS_TIME_UNCERTAINTY) { - jmethodID setterMethod = - env->GetMethodID(gpsClockClass, "setTimeUncertaintyInNs", doubleSignature); - env->CallVoidMethod(gpsClockObject, setterMethod, clock->time_uncertainty_ns); - } +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); +} - if (flags & GPS_CLOCK_HAS_FULL_BIAS) { - jmethodID setterMethod = env->GetMethodID(gpsClockClass, "setFullBiasInNs", longSignature); - env->CallVoidMethod(gpsClockObject, setterMethod, clock->full_bias_ns); - } +JavaObject::~JavaObject() { + env_->DeleteLocalRef(clazz_); +} - if (flags & GPS_CLOCK_HAS_BIAS) { - jmethodID setterMethod = env->GetMethodID(gpsClockClass, "setBiasInNs", doubleSignature); - env->CallVoidMethod(gpsClockObject, setterMethod, clock->bias_ns); - } +template<class T> +void JavaObject::callSetter(const char* method_name, T value) { + JavaMethodHelper<T>::callJavaMethod( + env_, clazz_, object_, method_name, value); +} - if (flags & GPS_CLOCK_HAS_BIAS_UNCERTAINTY) { - jmethodID setterMethod = - env->GetMethodID(gpsClockClass, "setBiasUncertaintyInNs", doubleSignature); - env->CallVoidMethod(gpsClockObject, setterMethod, clock->bias_uncertainty_ns); - } +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); +} - if (flags & GPS_CLOCK_HAS_DRIFT) { - jmethodID setterMethod = - env->GetMethodID(gpsClockClass, "setDriftInNsPerSec", doubleSignature); - env->CallVoidMethod(gpsClockObject, setterMethod, clock->drift_nsps); - } +jobject JavaObject::get() { + return object_; +} - if (flags & GPS_CLOCK_HAS_DRIFT_UNCERTAINTY) { - jmethodID setterMethod = - env->GetMethodID(gpsClockClass, "setDriftUncertaintyInNsPerSec", doubleSignature); - env->CallVoidMethod(gpsClockObject, setterMethod, clock->drift_uncertainty_nsps); - } +// 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; + 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); + + /* 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); + + SET_IF(GPS_CLOCK_TYPE_LOCAL_HW_TIME, + TimeOfLastHwClockDiscontinuityInNs, + clock->time_of_last_hw_clock_discontinuity_ns); + + return object.get(); +} - jobject gnssMeasurementObject = env->NewObject(gnssMeasurementClass, gnssMeasurementCtor); +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); + + return object.get(); +} - jmethodID timeOffsetSetterMethod = - env->GetMethodID(gnssMeasurementClass, "setTimeOffsetInNs", doubleSignature); - env->CallVoidMethod( - gnssMeasurementObject, - timeOffsetSetterMethod, - measurement->time_offset_ns); +static jobject translate_gnss_measurement(JNIEnv* env, + GnssMeasurement* measurement) { + JavaObject object(env, "android/location/GnssMeasurement"); + GpsMeasurementFlags flags = measurement->flags; - jmethodID stateSetterMethod = env->GetMethodID(gnssMeasurementClass, "setState", shortSignature); - env->CallVoidMethod(gnssMeasurementObject, stateSetterMethod, measurement->state); + 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(); +} - jmethodID receivedGpsTowSetterMethod = - env->GetMethodID(gnssMeasurementClass, "setReceivedGpsTowInNs", longSignature); - env->CallVoidMethod( - gnssMeasurementObject, - receivedGpsTowSetterMethod, - measurement->received_gps_tow_ns); +static jobjectArray translate_gps_measurements(JNIEnv* env, + GpsMeasurement* measurements, + size_t count) { + if (count == 0) { + return NULL; + } - 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( + jclass gnssMeasurementClass = env->FindClass( + "android/location/GnssMeasurement"); + jobjectArray gnssMeasurementArray = env->NewObjectArray( + count, 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); + NULL /* initialElement */); + + for (uint16_t i = 0; i < count; ++i) { + jobject gnssMeasurement = translate_gps_measurement( + env, + &measurements[i]); + env->SetObjectArrayElement(gnssMeasurementArray, i, gnssMeasurement); + env->DeleteLocalRef(gnssMeasurement); } env->DeleteLocalRef(gnssMeasurementClass); - return gnssMeasurementObject; + return gnssMeasurementArray; } -/** - * <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 jobjectArray translate_gnss_measurements(JNIEnv* env, + GnssMeasurement* 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) { - jobject gnssMeasurement = translate_gps_measurement( + for (uint16_t i = 0; i < count; ++i) { + jobject gnssMeasurement = translate_gnss_measurement( env, - &(gps_data->measurements[i]), - sizeof(gps_data->measurements[0])); + &measurements[i]); env->SetObjectArrayElement(gnssMeasurementArray, i, gnssMeasurement); env->DeleteLocalRef(gnssMeasurement); } @@ -1340,50 +1357,81 @@ static jobjectArray translate_gps_measurements(JNIEnv* env, void* data) { 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>", + "(Landroid/location/GnssClock;[Landroid/location/GnssMeasurement;)V"); + + jobject gnssMeasurementsEvent = env->NewObject( + gnssMeasurementsEventClass, + gnssMeasurementsEventCtor, + clock, + measurementArray); + env->CallVoidMethod(mCallbacksObj, + method_reportMeasurementData, + gnssMeasurementsEvent); + checkAndClearExceptionFromCallback(env, __FUNCTION__); + 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) && data->size != sizeof(GpsData_v1)) { - ALOGE("Invalid GpsData size found in gps_measurement_callback, size=%zd", data->size); + if (data->size != sizeof(GpsData)) { + ALOGE("Invalid GpsData size found in gps_measurement_callback, " + "size=%zd", + data->size); return; } - jobject gpsClock; + jobject clock; 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 gnssMeasurementsEventClass = env->FindClass("android/location/GnssMeasurementsEvent"); - jmethodID gnssMeasurementsEventCtor = env->GetMethodID( - gnssMeasurementsEventClass, - "<init>", - "(Landroid/location/GnssClock;[Landroid/location/GnssMeasurement;)V"); + clock = translate_gps_clock(env, &data->clock); + measurementArray = translate_gps_measurements( + env, data->measurements, data->measurement_count); + set_measurement_data(env, clock, measurementArray); - jobject gnssMeasurementsEvent = env->NewObject( - gnssMeasurementsEventClass, - gnssMeasurementsEventCtor, - gpsClock, - measurementArray); + env->DeleteLocalRef(clock); + env->DeleteLocalRef(measurementArray); +} - env->CallVoidMethod(mCallbacksObj, method_reportMeasurementData, gnssMeasurementsEvent); - checkAndClearExceptionFromCallback(env, __FUNCTION__); +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; + } - env->DeleteLocalRef(gpsClock); + 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); - env->DeleteLocalRef(gnssMeasurementsEventClass); - env->DeleteLocalRef(gnssMeasurementsEvent); } GpsMeasurementCallbacks sGpsMeasurementCallbacks = { sizeof(GpsMeasurementCallbacks), measurement_callback, + gnss_measurement_callback, }; static jboolean android_location_GnssLocationProvider_is_measurement_supported( @@ -1431,69 +1479,86 @@ static jobject translate_gps_navigation_message(JNIEnv* env, GpsNavigationMessag 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); - - 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); +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(); +} - 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 @@ static const JNINativeMethod sMethods[] = { "(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 eacf11f47415..d7222a5d548d 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.text.TextUtils; 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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) { @@ -4492,7 +4488,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } 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 +4519,50 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } 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 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 reportKeyguardDismissed() { + 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 +6793,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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 +6808,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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 +7139,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { List<ResolveInfo> activitiesToEnable = mIPackageManager.queryIntentActivities( intent, intent.resolveTypeIfNeeded(mContext.getContentResolver()), - 0, // no flags + PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE, parentUserId); if (VERBOSE_LOG) { @@ -8465,4 +8494,137 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java index 027c3254396f..4fe97c110691 100644 --- a/services/net/java/android/net/ip/IpManager.java +++ b/services/net/java/android/net/ip/IpManager.java @@ -61,7 +61,6 @@ import java.util.Objects; * @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; @@ -130,6 +129,7 @@ public class IpManager extends StateMachine { 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 @@ -156,11 +156,11 @@ public class IpManager extends StateMachine { 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( @@ -177,7 +177,7 @@ public class IpManager extends StateMachine { 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(); @@ -200,7 +200,9 @@ public class IpManager extends StateMachine { */ @VisibleForTesting protected IpManager(String ifName, Callback callback) { - super(TAG + ".test-" + ifName); + super(IpManager.class.getSimpleName() + ".test-" + ifName); + mTag = getName(); + mInterfaceName = ifName; mCallback = callback; @@ -273,7 +275,7 @@ public class IpManager extends StateMachine { mInterfaceName, mInterfaceIndex, msg.arg1, msg.arg2, Objects.toString(msg.obj)); if (VDBG) { - Log.d(TAG, getWhatToString(msg.what) + " " + logLine); + Log.d(mTag, getWhatToString(msg.what) + " " + logLine); } return logLine; } @@ -283,7 +285,7 @@ public class IpManager extends StateMachine { 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); } } @@ -365,17 +367,17 @@ public class IpManager extends StateMachine { private void dispatchCallback(ProvisioningChange delta, LinkProperties newLp) { switch (delta) { case GAINED_PROVISIONING: - if (VDBG) { Log.d(TAG, "onProvisioningSuccess()"); } + if (VDBG) { Log.d(mTag, "onProvisioningSuccess()"); } mCallback.onProvisioningSuccess(newLp); break; case LOST_PROVISIONING: - if (VDBG) { Log.d(TAG, "onProvisioningFailure()"); } + if (VDBG) { Log.d(mTag, "onProvisioningFailure()"); } mCallback.onProvisioningFailure(newLp); break; default: - if (VDBG) { Log.d(TAG, "onLinkPropertiesChange()"); } + if (VDBG) { Log.d(mTag, "onLinkPropertiesChange()"); } mCallback.onLinkPropertiesChange(newLp); break; } @@ -396,7 +398,7 @@ public class IpManager extends StateMachine { switch (delta) { case GAINED_PROVISIONING: case LOST_PROVISIONING: - Log.d(TAG, "provisioning: " + delta); + Log.d(mTag, "provisioning: " + delta); break; } } @@ -452,7 +454,7 @@ public class IpManager extends StateMachine { } if (VDBG) { - Log.d(TAG, "newLp{" + newLp + "}"); + Log.d(mTag, "newLp{" + newLp + "}"); } return newLp; @@ -464,7 +466,7 @@ public class IpManager extends StateMachine { 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); } } @@ -474,7 +476,7 @@ public class IpManager extends StateMachine { final ProvisioningChange delta = setLinkProperties(newLp); if (VDBG) { - Log.d(TAG, "onNewDhcpResults(" + Objects.toString(dhcpResults) + ")"); + Log.d(mTag, "onNewDhcpResults(" + Objects.toString(dhcpResults) + ")"); } mCallback.onNewDhcpResults(dhcpResults); @@ -502,7 +504,7 @@ public class IpManager extends StateMachine { delta = ProvisioningChange.LOST_PROVISIONING; } - if (VDBG) { Log.d(TAG, "onNewDhcpResults(null)"); } + if (VDBG) { Log.d(mTag, "onNewDhcpResults(null)"); } mCallback.onNewDhcpResults(null); dispatchCallback(delta, newLp); @@ -518,7 +520,7 @@ public class IpManager extends StateMachine { 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(); @@ -541,7 +543,7 @@ public class IpManager extends StateMachine { case DhcpStateMachine.CMD_ON_QUIT: // Everything is already stopped. - Log.e(TAG, "Unexpected CMD_ON_QUIT (already stopped)."); + Log.e(mTag, "Unexpected CMD_ON_QUIT (already stopped)."); break; default: @@ -584,9 +586,9 @@ public class IpManager extends StateMachine { 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( @@ -607,7 +609,7 @@ public class IpManager extends StateMachine { if (applyStaticIpConfig()) { handleIPv4Success(new DhcpResults(mStaticIpConfig)); } else { - if (VDBG) { Log.d(TAG, "onProvisioningFailure()"); } + if (VDBG) { Log.d(mTag, "onProvisioningFailure()"); } mCallback.onProvisioningFailure(getLinkProperties()); transitionTo(mStoppingState); } @@ -640,7 +642,7 @@ public class IpManager extends StateMachine { 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: @@ -677,7 +679,7 @@ public class IpManager extends StateMachine { } case DhcpStateMachine.CMD_PRE_DHCP_ACTION: - if (VDBG) { Log.d(TAG, "onPreDhcpAction()"); } + if (VDBG) { Log.d(mTag, "onPreDhcpAction()"); } mCallback.onPreDhcpAction(); break; @@ -685,7 +687,7 @@ public class IpManager extends StateMachine { // 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(TAG, "onPostDhcpAction()"); } + if (VDBG) { Log.d(mTag, "onPostDhcpAction()"); } mCallback.onPostDhcpAction(); final DhcpResults dhcpResults = (DhcpResults) msg.obj; @@ -697,14 +699,14 @@ public class IpManager extends StateMachine { 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: // DHCPv4 quit early for some reason. - Log.e(TAG, "Unexpected CMD_ON_QUIT."); + Log.e(mTag, "Unexpected CMD_ON_QUIT."); mDhcpStateMachine = null; break; @@ -720,9 +722,9 @@ public class IpManager extends StateMachine { 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/telecomm/java/android/telecom/Log.java b/telecomm/java/android/telecom/Log.java index 73cc4a5f6138..3f32dbe0fdbe 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 @@ final public class Log { 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 @@ final public class Log { } 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 f1cbb9ab73d3..9f478df2063e 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -498,6 +498,7 @@ public class TelecomManager { mContext = context; } mTelecomServiceOverride = telecomServiceImpl; + android.telecom.Log.initMd5Sum(); } /** diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java index 429839f6f6f9..0d6c70b9a4c6 100644 --- a/telephony/java/com/android/internal/telephony/RILConstants.java +++ b/telephony/java/com/android/internal/telephony/RILConstants.java @@ -92,7 +92,33 @@ public interface RILConstants { 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/layoutlib/bridge/src/android/graphics/Path_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java index d0dd22f8faad..a10ac00fc356 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 @@ public final class Path_Delegate { @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 000000000000..edd36e54aa77 --- /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 2ad3c2e16e21..4921073cce6c 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.AsyncChannel; 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 @@ public class WifiManager { } /** + * 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 @@ public class WifiManager { * @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 5c18bd7e0f07..9e6ed4ee9634 100644 --- a/wifi/java/android/net/wifi/nan/WifiNanEventListener.java +++ b/wifi/java/android/net/wifi/nan/WifiNanEventListener.java @@ -36,7 +36,7 @@ import android.util.Log; */ 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 cb82268ec195..667c4b1de6d0 100644 --- a/wifi/java/android/net/wifi/nan/WifiNanManager.java +++ b/wifi/java/android/net/wifi/nan/WifiNanManager.java @@ -38,7 +38,7 @@ import android.util.Log; */ 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 d0a94109d0d8..bc1787fee478 100644 --- a/wifi/java/android/net/wifi/nan/WifiNanSession.java +++ b/wifi/java/android/net/wifi/nan/WifiNanSession.java @@ -27,7 +27,7 @@ import android.util.Log; */ 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 092508766570..b9af7def6868 100644 --- a/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java +++ b/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java @@ -43,7 +43,7 @@ import android.util.Log; */ 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 /** |