Merge "Dynamic audio policies: JNI support for UID rules" into nyc-dev
diff --git a/api/current.txt b/api/current.txt
index db77d33..617aac0 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -8512,6 +8512,7 @@
field public static final java.lang.String ACTION_MANAGED_PROFILE_ADDED = "android.intent.action.MANAGED_PROFILE_ADDED";
field public static final java.lang.String ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED = "android.intent.action.MANAGED_PROFILE_AVAILABILITY_CHANGED";
field public static final java.lang.String ACTION_MANAGED_PROFILE_REMOVED = "android.intent.action.MANAGED_PROFILE_REMOVED";
+ field public static final java.lang.String ACTION_MANAGED_PROFILE_UNLOCKED = "android.intent.action.MANAGED_PROFILE_UNLOCKED";
field public static final java.lang.String ACTION_MANAGE_NETWORK_USAGE = "android.intent.action.MANAGE_NETWORK_USAGE";
field public static final java.lang.String ACTION_MANAGE_PACKAGE_STORAGE = "android.intent.action.MANAGE_PACKAGE_STORAGE";
field public static final java.lang.String ACTION_MEDIA_BAD_REMOVAL = "android.intent.action.MEDIA_BAD_REMOVAL";
@@ -19221,7 +19222,6 @@
method public double getElevationUncertaintyInDeg();
method public byte getLossOfLock();
method public byte getMultipathIndicator();
- method public byte getPrn();
method public double getPseudorangeInMeters();
method public double getPseudorangeRateCarrierInMetersPerSec();
method public double getPseudorangeRateCarrierUncertaintyInMetersPerSec();
@@ -19232,6 +19232,7 @@
method public long getReceivedGpsTowUncertaintyInNs();
method public double getSnrInDb();
method public short getState();
+ method public short getSvid();
method public short getTimeFromLastBitInMs();
method public double getTimeOffsetInNs();
method public boolean hasAzimuthInDeg();
@@ -19291,7 +19292,6 @@
method public void setElevationUncertaintyInDeg(double);
method public void setLossOfLock(byte);
method public void setMultipathIndicator(byte);
- method public void setPrn(byte);
method public void setPseudorangeInMeters(double);
method public void setPseudorangeRateCarrierInMetersPerSec(double);
method public void setPseudorangeRateCarrierUncertaintyInMetersPerSec(double);
@@ -19302,6 +19302,7 @@
method public void setReceivedGpsTowUncertaintyInNs(long);
method public void setSnrInDb(double);
method public void setState(short);
+ method public void setSvid(short);
method public void setTimeFromLastBitInMs(short);
method public void setTimeOffsetInNs(double);
method public void setUsedInFix(boolean);
@@ -19356,17 +19357,17 @@
method public int describeContents();
method public byte[] getData();
method public short getMessageId();
- method public byte getPrn();
method public short getStatus();
method public short getSubmessageId();
+ method public short getSvid();
method public byte getType();
method public void reset();
method public void set(android.location.GnssNavigationMessage);
method public void setData(byte[]);
method public void setMessageId(short);
- method public void setPrn(byte);
method public void setStatus(short);
method public void setSubmessageId(short);
+ method public void setSvid(short);
method public void setType(byte);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.location.GnssNavigationMessage> CREATOR;
@@ -19412,8 +19413,8 @@
method public int getConstellationType(int);
method public float getElevation(int);
method public int getNumSatellites();
- method public int getPrn(int);
method public float getSnr(int);
+ method public int getSvid(int);
method public boolean hasAlmanac(int);
method public boolean hasEphemeris(int);
method public boolean usedInFix(int);
@@ -22950,10 +22951,8 @@
field public static final int INPUT_STATE_CONNECTED_STANDBY = 1; // 0x1
field public static final int INPUT_STATE_DISCONNECTED = 2; // 0x2
field public static final java.lang.String META_DATA_CONTENT_RATING_SYSTEMS = "android.media.tv.metadata.CONTENT_RATING_SYSTEMS";
- field public static final int RECORDING_ERROR_CONNECTION_FAILED = 1; // 0x1
- field public static final int RECORDING_ERROR_DISCONNECTED = 2; // 0x2
- field public static final int RECORDING_ERROR_INSUFFICIENT_SPACE = 3; // 0x3
- field public static final int RECORDING_ERROR_RESOURCE_BUSY = 4; // 0x4
+ field public static final int RECORDING_ERROR_INSUFFICIENT_SPACE = 1; // 0x1
+ field public static final int RECORDING_ERROR_RESOURCE_BUSY = 2; // 0x2
field public static final int RECORDING_ERROR_UNKNOWN = 0; // 0x0
field public static final long TIME_SHIFT_INVALID_TIME = -9223372036854775808L; // 0x8000000000000000L
field public static final int TIME_SHIFT_STATUS_AVAILABLE = 3; // 0x3
@@ -23053,6 +23052,8 @@
public static abstract class TvRecordingClient.RecordingCallback {
ctor public TvRecordingClient.RecordingCallback();
+ method public void onConnectionFailed(java.lang.String);
+ method public void onDisconnected(java.lang.String);
method public void onError(int);
method public void onRecordingStopped(android.net.Uri);
method public void onTuned();
@@ -23067,6 +23068,7 @@
method public final java.lang.String getId();
method public final java.lang.String getLanguage();
method public final int getType();
+ method public final byte getVideoActiveFormatDescription();
method public final float getVideoFrameRate();
method public final int getVideoHeight();
method public final float getVideoPixelAspectRatio();
@@ -23086,6 +23088,7 @@
method public final android.media.tv.TvTrackInfo.Builder setDescription(java.lang.CharSequence);
method public final android.media.tv.TvTrackInfo.Builder setExtra(android.os.Bundle);
method public final android.media.tv.TvTrackInfo.Builder setLanguage(java.lang.String);
+ method public final android.media.tv.TvTrackInfo.Builder setVideoActiveFormatDescription(byte);
method public final android.media.tv.TvTrackInfo.Builder setVideoFrameRate(float);
method public final android.media.tv.TvTrackInfo.Builder setVideoHeight(int);
method public final android.media.tv.TvTrackInfo.Builder setVideoPixelAspectRatio(float);
@@ -29062,6 +29065,7 @@
method public static final int getThreadPriority(int) throws java.lang.IllegalArgumentException;
method public static final int getUidForName(java.lang.String);
method public static final boolean is64Bit();
+ method public static boolean isApplicationUid(int);
method public static final void killProcess(int);
method public static final int myPid();
method public static final int myTid();
@@ -29242,6 +29246,7 @@
public final class UserHandle implements android.os.Parcelable {
ctor public UserHandle(android.os.Parcel);
method public int describeContents();
+ method public static android.os.UserHandle getUserHandleForUid(int);
method public static android.os.UserHandle readFromParcel(android.os.Parcel);
method public void writeToParcel(android.os.Parcel, int);
method public static void writeToParcel(android.os.UserHandle, android.os.Parcel);
@@ -51474,7 +51479,6 @@
method public static int getLength(java.lang.Object);
method public static long getLong(java.lang.Object, int) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
method public static short getShort(java.lang.Object, int) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
- method public static java.lang.Object newArray(java.lang.Class<?>, int) throws java.lang.NegativeArraySizeException;
method public static java.lang.Object newInstance(java.lang.Class<?>, int) throws java.lang.NegativeArraySizeException;
method public static java.lang.Object newInstance(java.lang.Class<?>, int...) throws java.lang.IllegalArgumentException, java.lang.NegativeArraySizeException;
method public static void set(java.lang.Object, int, java.lang.Object) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
@@ -52150,7 +52154,6 @@
public class InetAddress implements java.io.Serializable {
method public byte[] getAddress();
- method public byte[] getAddressInternal();
method public static java.net.InetAddress[] getAllByName(java.lang.String) throws java.net.UnknownHostException;
method public static java.net.InetAddress getByAddress(java.lang.String, byte[]) throws java.net.UnknownHostException;
method public static java.net.InetAddress getByAddress(byte[]) throws java.net.UnknownHostException;
@@ -52424,7 +52427,7 @@
method protected abstract void connect(java.net.InetAddress, int) throws java.io.IOException;
method protected abstract void connect(java.net.SocketAddress, int) throws java.io.IOException;
method protected abstract void create(boolean) throws java.io.IOException;
- method public java.io.FileDescriptor getFileDescriptor();
+ method protected java.io.FileDescriptor getFileDescriptor();
method protected java.net.InetAddress getInetAddress();
method protected abstract java.io.InputStream getInputStream() throws java.io.IOException;
method protected int getLocalPort();
@@ -52966,10 +52969,6 @@
package java.nio.channels {
- public class AcceptPendingException extends java.lang.IllegalStateException {
- ctor public AcceptPendingException();
- }
-
public class AlreadyBoundException extends java.lang.IllegalStateException {
ctor public AlreadyBoundException();
}
@@ -52978,68 +52977,10 @@
ctor public AlreadyConnectedException();
}
- public abstract interface AsynchronousByteChannel implements java.nio.channels.AsynchronousChannel {
- method public abstract void read(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
- method public abstract java.util.concurrent.Future<java.lang.Integer> read(java.nio.ByteBuffer);
- method public abstract void write(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
- method public abstract java.util.concurrent.Future<java.lang.Integer> write(java.nio.ByteBuffer);
- }
-
- public abstract interface AsynchronousChannel implements java.nio.channels.Channel {
- method public abstract void close() throws java.io.IOException;
- }
-
- public abstract class AsynchronousChannelGroup {
- ctor protected AsynchronousChannelGroup(java.nio.channels.spi.AsynchronousChannelProvider);
- method public abstract boolean awaitTermination(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
- method public abstract boolean isShutdown();
- method public abstract boolean isTerminated();
- method public final java.nio.channels.spi.AsynchronousChannelProvider provider();
- method public abstract void shutdown();
- method public abstract void shutdownNow() throws java.io.IOException;
- method public static java.nio.channels.AsynchronousChannelGroup withCachedThreadPool(java.util.concurrent.ExecutorService, int) throws java.io.IOException;
- method public static java.nio.channels.AsynchronousChannelGroup withFixedThreadPool(int, java.util.concurrent.ThreadFactory) throws java.io.IOException;
- method public static java.nio.channels.AsynchronousChannelGroup withThreadPool(java.util.concurrent.ExecutorService) throws java.io.IOException;
- }
-
public class AsynchronousCloseException extends java.nio.channels.ClosedChannelException {
ctor public AsynchronousCloseException();
}
- public abstract class AsynchronousServerSocketChannel implements java.nio.channels.AsynchronousChannel java.nio.channels.NetworkChannel {
- ctor protected AsynchronousServerSocketChannel(java.nio.channels.spi.AsynchronousChannelProvider);
- method public abstract void accept(A, java.nio.channels.CompletionHandler<java.nio.channels.AsynchronousSocketChannel, ? super A>);
- method public abstract java.util.concurrent.Future<java.nio.channels.AsynchronousSocketChannel> accept();
- method public final java.nio.channels.AsynchronousServerSocketChannel bind(java.net.SocketAddress) throws java.io.IOException;
- method public abstract java.nio.channels.AsynchronousServerSocketChannel bind(java.net.SocketAddress, int) throws java.io.IOException;
- method public static java.nio.channels.AsynchronousServerSocketChannel open(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
- method public static java.nio.channels.AsynchronousServerSocketChannel open() throws java.io.IOException;
- method public final java.nio.channels.spi.AsynchronousChannelProvider provider();
- method public abstract java.nio.channels.AsynchronousServerSocketChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
- }
-
- public abstract class AsynchronousSocketChannel implements java.nio.channels.AsynchronousByteChannel java.nio.channels.NetworkChannel {
- ctor protected AsynchronousSocketChannel(java.nio.channels.spi.AsynchronousChannelProvider);
- method public abstract java.nio.channels.AsynchronousSocketChannel bind(java.net.SocketAddress) throws java.io.IOException;
- method public abstract void connect(java.net.SocketAddress, A, java.nio.channels.CompletionHandler<java.lang.Void, ? super A>);
- method public abstract java.util.concurrent.Future<java.lang.Void> connect(java.net.SocketAddress);
- method public abstract java.net.SocketAddress getRemoteAddress() throws java.io.IOException;
- method public static java.nio.channels.AsynchronousSocketChannel open(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
- method public static java.nio.channels.AsynchronousSocketChannel open() throws java.io.IOException;
- method public final java.nio.channels.spi.AsynchronousChannelProvider provider();
- method public abstract void read(java.nio.ByteBuffer, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
- method public final void read(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
- method public abstract java.util.concurrent.Future<java.lang.Integer> read(java.nio.ByteBuffer);
- method public abstract void read(java.nio.ByteBuffer[], int, int, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Long, ? super A>);
- method public abstract java.nio.channels.AsynchronousSocketChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
- method public abstract java.nio.channels.AsynchronousSocketChannel shutdownInput() throws java.io.IOException;
- method public abstract java.nio.channels.AsynchronousSocketChannel shutdownOutput() throws java.io.IOException;
- method public abstract void write(java.nio.ByteBuffer, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
- method public final void write(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
- method public abstract java.util.concurrent.Future<java.lang.Integer> write(java.nio.ByteBuffer);
- method public abstract void write(java.nio.ByteBuffer[], int, int, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Long, ? super A>);
- }
-
public abstract interface ByteChannel implements java.nio.channels.ReadableByteChannel java.nio.channels.WritableByteChannel {
}
@@ -53056,9 +52997,7 @@
method public static java.nio.channels.ReadableByteChannel newChannel(java.io.InputStream);
method public static java.nio.channels.WritableByteChannel newChannel(java.io.OutputStream);
method public static java.io.InputStream newInputStream(java.nio.channels.ReadableByteChannel);
- method public static java.io.InputStream newInputStream(java.nio.channels.AsynchronousByteChannel);
method public static java.io.OutputStream newOutputStream(java.nio.channels.WritableByteChannel);
- method public static java.io.OutputStream newOutputStream(java.nio.channels.AsynchronousByteChannel);
method public static java.io.Reader newReader(java.nio.channels.ReadableByteChannel, java.nio.charset.CharsetDecoder, int);
method public static java.io.Reader newReader(java.nio.channels.ReadableByteChannel, java.lang.String);
method public static java.io.Writer newWriter(java.nio.channels.WritableByteChannel, java.nio.charset.CharsetEncoder, int);
@@ -53077,16 +53016,11 @@
ctor public ClosedSelectorException();
}
- public abstract interface CompletionHandler {
- method public abstract void completed(V, A);
- method public abstract void failed(java.lang.Throwable, A);
- }
-
public class ConnectionPendingException extends java.lang.IllegalStateException {
ctor public ConnectionPendingException();
}
- public abstract class DatagramChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.MulticastChannel java.nio.channels.ScatteringByteChannel {
+ public abstract class DatagramChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.ScatteringByteChannel {
ctor protected DatagramChannel(java.nio.channels.spi.SelectorProvider);
method public abstract java.nio.channels.DatagramChannel bind(java.net.SocketAddress) throws java.io.IOException;
method public abstract java.nio.channels.DatagramChannel connect(java.net.SocketAddress) throws java.io.IOException;
@@ -53165,40 +53099,14 @@
ctor public IllegalBlockingModeException();
}
- public class IllegalChannelGroupException extends java.lang.IllegalArgumentException {
- ctor public IllegalChannelGroupException();
- }
-
public class IllegalSelectorException extends java.lang.IllegalArgumentException {
ctor public IllegalSelectorException();
}
- public class InterruptedByTimeoutException extends java.io.IOException {
- ctor public InterruptedByTimeoutException();
- }
-
public abstract interface InterruptibleChannel implements java.nio.channels.Channel {
method public abstract void close() throws java.io.IOException;
}
- public abstract class MembershipKey {
- ctor protected MembershipKey();
- method public abstract java.nio.channels.MembershipKey block(java.net.InetAddress) throws java.io.IOException;
- method public abstract java.nio.channels.MulticastChannel channel();
- method public abstract void drop();
- method public abstract java.net.InetAddress group();
- method public abstract boolean isValid();
- method public abstract java.net.NetworkInterface networkInterface();
- method public abstract java.net.InetAddress sourceAddress();
- method public abstract java.nio.channels.MembershipKey unblock(java.net.InetAddress);
- }
-
- public abstract interface MulticastChannel implements java.nio.channels.NetworkChannel {
- method public abstract void close() throws java.io.IOException;
- method public abstract java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface) throws java.io.IOException;
- method public abstract java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface, java.net.InetAddress) throws java.io.IOException;
- }
-
public abstract interface NetworkChannel implements java.nio.channels.Channel {
method public abstract java.nio.channels.NetworkChannel bind(java.net.SocketAddress) throws java.io.IOException;
method public abstract java.net.SocketAddress getLocalAddress() throws java.io.IOException;
@@ -53248,10 +53156,6 @@
method public final int validOps();
}
- public class ReadPendingException extends java.lang.IllegalStateException {
- ctor public ReadPendingException();
- }
-
public abstract interface ReadableByteChannel implements java.nio.channels.Channel {
method public abstract int read(java.nio.ByteBuffer) throws java.io.IOException;
}
@@ -53329,10 +53233,6 @@
method public final int validOps();
}
- public class ShutdownChannelGroupException extends java.lang.IllegalStateException {
- ctor public ShutdownChannelGroupException();
- }
-
public abstract class SocketChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.NetworkChannel java.nio.channels.ScatteringByteChannel {
ctor protected SocketChannel(java.nio.channels.spi.SelectorProvider);
method public abstract java.nio.channels.SocketChannel bind(java.net.SocketAddress) throws java.io.IOException;
@@ -53368,10 +53268,6 @@
method public abstract int write(java.nio.ByteBuffer) throws java.io.IOException;
}
- public class WritePendingException extends java.lang.IllegalStateException {
- ctor public WritePendingException();
- }
-
}
package java.nio.channels.spi {
@@ -53418,15 +53314,6 @@
method protected abstract java.nio.channels.SelectionKey register(java.nio.channels.spi.AbstractSelectableChannel, int, java.lang.Object);
}
- public abstract class AsynchronousChannelProvider {
- ctor protected AsynchronousChannelProvider();
- method public abstract java.nio.channels.AsynchronousChannelGroup openAsynchronousChannelGroup(int, java.util.concurrent.ThreadFactory) throws java.io.IOException;
- method public abstract java.nio.channels.AsynchronousChannelGroup openAsynchronousChannelGroup(java.util.concurrent.ExecutorService, int) throws java.io.IOException;
- method public abstract java.nio.channels.AsynchronousServerSocketChannel openAsynchronousServerSocketChannel(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
- method public abstract java.nio.channels.AsynchronousSocketChannel openAsynchronousSocketChannel(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
- method public static java.nio.channels.spi.AsynchronousChannelProvider provider();
- }
-
public abstract class SelectorProvider {
ctor protected SelectorProvider();
method public java.nio.channels.Channel inheritedChannel() throws java.io.IOException;
@@ -56781,13 +56668,11 @@
method public static final java.text.DecimalFormatSymbols getInstance(java.util.Locale);
method public java.lang.String getInternationalCurrencySymbol();
method public char getMinusSign();
- method public java.lang.String getMinusSignString();
method public char getMonetaryDecimalSeparator();
method public java.lang.String getNaN();
method public char getPatternSeparator();
method public char getPerMill();
method public char getPercent();
- method public java.lang.String getPercentString();
method public char getZeroDigit();
method public void setCurrency(java.util.Currency);
method public void setCurrencySymbol(java.lang.String);
diff --git a/api/system-current.txt b/api/system-current.txt
index 86419ce..ec422c0 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -8818,6 +8818,7 @@
field public static final java.lang.String ACTION_MANAGED_PROFILE_ADDED = "android.intent.action.MANAGED_PROFILE_ADDED";
field public static final java.lang.String ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED = "android.intent.action.MANAGED_PROFILE_AVAILABILITY_CHANGED";
field public static final java.lang.String ACTION_MANAGED_PROFILE_REMOVED = "android.intent.action.MANAGED_PROFILE_REMOVED";
+ field public static final java.lang.String ACTION_MANAGED_PROFILE_UNLOCKED = "android.intent.action.MANAGED_PROFILE_UNLOCKED";
field public static final java.lang.String ACTION_MANAGE_NETWORK_USAGE = "android.intent.action.MANAGE_NETWORK_USAGE";
field public static final java.lang.String ACTION_MANAGE_PACKAGE_STORAGE = "android.intent.action.MANAGE_PACKAGE_STORAGE";
field public static final java.lang.String ACTION_MEDIA_BAD_REMOVAL = "android.intent.action.MEDIA_BAD_REMOVAL";
@@ -20401,7 +20402,6 @@
method public double getElevationUncertaintyInDeg();
method public byte getLossOfLock();
method public byte getMultipathIndicator();
- method public byte getPrn();
method public double getPseudorangeInMeters();
method public double getPseudorangeRateCarrierInMetersPerSec();
method public double getPseudorangeRateCarrierUncertaintyInMetersPerSec();
@@ -20412,6 +20412,7 @@
method public long getReceivedGpsTowUncertaintyInNs();
method public double getSnrInDb();
method public short getState();
+ method public short getSvid();
method public short getTimeFromLastBitInMs();
method public double getTimeOffsetInNs();
method public boolean hasAzimuthInDeg();
@@ -20471,7 +20472,6 @@
method public void setElevationUncertaintyInDeg(double);
method public void setLossOfLock(byte);
method public void setMultipathIndicator(byte);
- method public void setPrn(byte);
method public void setPseudorangeInMeters(double);
method public void setPseudorangeRateCarrierInMetersPerSec(double);
method public void setPseudorangeRateCarrierUncertaintyInMetersPerSec(double);
@@ -20482,6 +20482,7 @@
method public void setReceivedGpsTowUncertaintyInNs(long);
method public void setSnrInDb(double);
method public void setState(short);
+ method public void setSvid(short);
method public void setTimeFromLastBitInMs(short);
method public void setTimeOffsetInNs(double);
method public void setUsedInFix(boolean);
@@ -20536,17 +20537,17 @@
method public int describeContents();
method public byte[] getData();
method public short getMessageId();
- method public byte getPrn();
method public short getStatus();
method public short getSubmessageId();
+ method public short getSvid();
method public byte getType();
method public void reset();
method public void set(android.location.GnssNavigationMessage);
method public void setData(byte[]);
method public void setMessageId(short);
- method public void setPrn(byte);
method public void setStatus(short);
method public void setSubmessageId(short);
+ method public void setSvid(short);
method public void setType(byte);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.location.GnssNavigationMessage> CREATOR;
@@ -20592,8 +20593,8 @@
method public int getConstellationType(int);
method public float getElevation(int);
method public int getNumSatellites();
- method public int getPrn(int);
method public float getSnr(int);
+ method public int getSvid(int);
method public boolean hasAlmanac(int);
method public boolean hasEphemeris(int);
method public boolean usedInFix(int);
@@ -24649,10 +24650,8 @@
field public static final int INPUT_STATE_CONNECTED_STANDBY = 1; // 0x1
field public static final int INPUT_STATE_DISCONNECTED = 2; // 0x2
field public static final java.lang.String META_DATA_CONTENT_RATING_SYSTEMS = "android.media.tv.metadata.CONTENT_RATING_SYSTEMS";
- field public static final int RECORDING_ERROR_CONNECTION_FAILED = 1; // 0x1
- field public static final int RECORDING_ERROR_DISCONNECTED = 2; // 0x2
- field public static final int RECORDING_ERROR_INSUFFICIENT_SPACE = 3; // 0x3
- field public static final int RECORDING_ERROR_RESOURCE_BUSY = 4; // 0x4
+ field public static final int RECORDING_ERROR_INSUFFICIENT_SPACE = 1; // 0x1
+ field public static final int RECORDING_ERROR_RESOURCE_BUSY = 2; // 0x2
field public static final int RECORDING_ERROR_UNKNOWN = 0; // 0x0
field public static final long TIME_SHIFT_INVALID_TIME = -9223372036854775808L; // 0x8000000000000000L
field public static final int TIME_SHIFT_STATUS_AVAILABLE = 3; // 0x3
@@ -24811,6 +24810,8 @@
public static abstract class TvRecordingClient.RecordingCallback {
ctor public TvRecordingClient.RecordingCallback();
+ method public void onConnectionFailed(java.lang.String);
+ method public void onDisconnected(java.lang.String);
method public void onError(int);
method public void onEvent(java.lang.String, java.lang.String, android.os.Bundle);
method public void onRecordingStopped(android.net.Uri);
@@ -24852,6 +24853,7 @@
method public final java.lang.String getId();
method public final java.lang.String getLanguage();
method public final int getType();
+ method public final byte getVideoActiveFormatDescription();
method public final float getVideoFrameRate();
method public final int getVideoHeight();
method public final float getVideoPixelAspectRatio();
@@ -24871,6 +24873,7 @@
method public final android.media.tv.TvTrackInfo.Builder setDescription(java.lang.CharSequence);
method public final android.media.tv.TvTrackInfo.Builder setExtra(android.os.Bundle);
method public final android.media.tv.TvTrackInfo.Builder setLanguage(java.lang.String);
+ method public final android.media.tv.TvTrackInfo.Builder setVideoActiveFormatDescription(byte);
method public final android.media.tv.TvTrackInfo.Builder setVideoFrameRate(float);
method public final android.media.tv.TvTrackInfo.Builder setVideoHeight(int);
method public final android.media.tv.TvTrackInfo.Builder setVideoPixelAspectRatio(float);
@@ -26680,6 +26683,7 @@
method public boolean reconnect();
method public boolean removeNetwork(int);
method public boolean saveConfiguration();
+ method public boolean setMetered(int, boolean);
method public void setTdlsEnabled(java.net.InetAddress, boolean);
method public void setTdlsEnabledWithMacAddress(java.lang.String, boolean);
method public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration);
@@ -31355,6 +31359,7 @@
method public static final int getThreadPriority(int) throws java.lang.IllegalArgumentException;
method public static final int getUidForName(java.lang.String);
method public static final boolean is64Bit();
+ method public static boolean isApplicationUid(int);
method public static final void killProcess(int);
method public static final int myPid();
method public static final int myTid();
@@ -31581,6 +31586,7 @@
ctor public UserHandle(android.os.Parcel);
method public int describeContents();
method public int getIdentifier();
+ method public static android.os.UserHandle getUserHandleForUid(int);
method public deprecated boolean isOwner();
method public boolean isSystem();
method public static int myUserId();
@@ -54562,7 +54568,6 @@
method public static int getLength(java.lang.Object);
method public static long getLong(java.lang.Object, int) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
method public static short getShort(java.lang.Object, int) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
- method public static java.lang.Object newArray(java.lang.Class<?>, int) throws java.lang.NegativeArraySizeException;
method public static java.lang.Object newInstance(java.lang.Class<?>, int) throws java.lang.NegativeArraySizeException;
method public static java.lang.Object newInstance(java.lang.Class<?>, int...) throws java.lang.IllegalArgumentException, java.lang.NegativeArraySizeException;
method public static void set(java.lang.Object, int, java.lang.Object) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
@@ -55238,7 +55243,6 @@
public class InetAddress implements java.io.Serializable {
method public byte[] getAddress();
- method public byte[] getAddressInternal();
method public static java.net.InetAddress[] getAllByName(java.lang.String) throws java.net.UnknownHostException;
method public static java.net.InetAddress getByAddress(java.lang.String, byte[]) throws java.net.UnknownHostException;
method public static java.net.InetAddress getByAddress(byte[]) throws java.net.UnknownHostException;
@@ -55512,7 +55516,7 @@
method protected abstract void connect(java.net.InetAddress, int) throws java.io.IOException;
method protected abstract void connect(java.net.SocketAddress, int) throws java.io.IOException;
method protected abstract void create(boolean) throws java.io.IOException;
- method public java.io.FileDescriptor getFileDescriptor();
+ method protected java.io.FileDescriptor getFileDescriptor();
method protected java.net.InetAddress getInetAddress();
method protected abstract java.io.InputStream getInputStream() throws java.io.IOException;
method protected int getLocalPort();
@@ -56054,10 +56058,6 @@
package java.nio.channels {
- public class AcceptPendingException extends java.lang.IllegalStateException {
- ctor public AcceptPendingException();
- }
-
public class AlreadyBoundException extends java.lang.IllegalStateException {
ctor public AlreadyBoundException();
}
@@ -56066,68 +56066,10 @@
ctor public AlreadyConnectedException();
}
- public abstract interface AsynchronousByteChannel implements java.nio.channels.AsynchronousChannel {
- method public abstract void read(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
- method public abstract java.util.concurrent.Future<java.lang.Integer> read(java.nio.ByteBuffer);
- method public abstract void write(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
- method public abstract java.util.concurrent.Future<java.lang.Integer> write(java.nio.ByteBuffer);
- }
-
- public abstract interface AsynchronousChannel implements java.nio.channels.Channel {
- method public abstract void close() throws java.io.IOException;
- }
-
- public abstract class AsynchronousChannelGroup {
- ctor protected AsynchronousChannelGroup(java.nio.channels.spi.AsynchronousChannelProvider);
- method public abstract boolean awaitTermination(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
- method public abstract boolean isShutdown();
- method public abstract boolean isTerminated();
- method public final java.nio.channels.spi.AsynchronousChannelProvider provider();
- method public abstract void shutdown();
- method public abstract void shutdownNow() throws java.io.IOException;
- method public static java.nio.channels.AsynchronousChannelGroup withCachedThreadPool(java.util.concurrent.ExecutorService, int) throws java.io.IOException;
- method public static java.nio.channels.AsynchronousChannelGroup withFixedThreadPool(int, java.util.concurrent.ThreadFactory) throws java.io.IOException;
- method public static java.nio.channels.AsynchronousChannelGroup withThreadPool(java.util.concurrent.ExecutorService) throws java.io.IOException;
- }
-
public class AsynchronousCloseException extends java.nio.channels.ClosedChannelException {
ctor public AsynchronousCloseException();
}
- public abstract class AsynchronousServerSocketChannel implements java.nio.channels.AsynchronousChannel java.nio.channels.NetworkChannel {
- ctor protected AsynchronousServerSocketChannel(java.nio.channels.spi.AsynchronousChannelProvider);
- method public abstract void accept(A, java.nio.channels.CompletionHandler<java.nio.channels.AsynchronousSocketChannel, ? super A>);
- method public abstract java.util.concurrent.Future<java.nio.channels.AsynchronousSocketChannel> accept();
- method public final java.nio.channels.AsynchronousServerSocketChannel bind(java.net.SocketAddress) throws java.io.IOException;
- method public abstract java.nio.channels.AsynchronousServerSocketChannel bind(java.net.SocketAddress, int) throws java.io.IOException;
- method public static java.nio.channels.AsynchronousServerSocketChannel open(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
- method public static java.nio.channels.AsynchronousServerSocketChannel open() throws java.io.IOException;
- method public final java.nio.channels.spi.AsynchronousChannelProvider provider();
- method public abstract java.nio.channels.AsynchronousServerSocketChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
- }
-
- public abstract class AsynchronousSocketChannel implements java.nio.channels.AsynchronousByteChannel java.nio.channels.NetworkChannel {
- ctor protected AsynchronousSocketChannel(java.nio.channels.spi.AsynchronousChannelProvider);
- method public abstract java.nio.channels.AsynchronousSocketChannel bind(java.net.SocketAddress) throws java.io.IOException;
- method public abstract void connect(java.net.SocketAddress, A, java.nio.channels.CompletionHandler<java.lang.Void, ? super A>);
- method public abstract java.util.concurrent.Future<java.lang.Void> connect(java.net.SocketAddress);
- method public abstract java.net.SocketAddress getRemoteAddress() throws java.io.IOException;
- method public static java.nio.channels.AsynchronousSocketChannel open(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
- method public static java.nio.channels.AsynchronousSocketChannel open() throws java.io.IOException;
- method public final java.nio.channels.spi.AsynchronousChannelProvider provider();
- method public abstract void read(java.nio.ByteBuffer, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
- method public final void read(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
- method public abstract java.util.concurrent.Future<java.lang.Integer> read(java.nio.ByteBuffer);
- method public abstract void read(java.nio.ByteBuffer[], int, int, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Long, ? super A>);
- method public abstract java.nio.channels.AsynchronousSocketChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
- method public abstract java.nio.channels.AsynchronousSocketChannel shutdownInput() throws java.io.IOException;
- method public abstract java.nio.channels.AsynchronousSocketChannel shutdownOutput() throws java.io.IOException;
- method public abstract void write(java.nio.ByteBuffer, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
- method public final void write(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
- method public abstract java.util.concurrent.Future<java.lang.Integer> write(java.nio.ByteBuffer);
- method public abstract void write(java.nio.ByteBuffer[], int, int, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Long, ? super A>);
- }
-
public abstract interface ByteChannel implements java.nio.channels.ReadableByteChannel java.nio.channels.WritableByteChannel {
}
@@ -56144,9 +56086,7 @@
method public static java.nio.channels.ReadableByteChannel newChannel(java.io.InputStream);
method public static java.nio.channels.WritableByteChannel newChannel(java.io.OutputStream);
method public static java.io.InputStream newInputStream(java.nio.channels.ReadableByteChannel);
- method public static java.io.InputStream newInputStream(java.nio.channels.AsynchronousByteChannel);
method public static java.io.OutputStream newOutputStream(java.nio.channels.WritableByteChannel);
- method public static java.io.OutputStream newOutputStream(java.nio.channels.AsynchronousByteChannel);
method public static java.io.Reader newReader(java.nio.channels.ReadableByteChannel, java.nio.charset.CharsetDecoder, int);
method public static java.io.Reader newReader(java.nio.channels.ReadableByteChannel, java.lang.String);
method public static java.io.Writer newWriter(java.nio.channels.WritableByteChannel, java.nio.charset.CharsetEncoder, int);
@@ -56165,16 +56105,11 @@
ctor public ClosedSelectorException();
}
- public abstract interface CompletionHandler {
- method public abstract void completed(V, A);
- method public abstract void failed(java.lang.Throwable, A);
- }
-
public class ConnectionPendingException extends java.lang.IllegalStateException {
ctor public ConnectionPendingException();
}
- public abstract class DatagramChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.MulticastChannel java.nio.channels.ScatteringByteChannel {
+ public abstract class DatagramChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.ScatteringByteChannel {
ctor protected DatagramChannel(java.nio.channels.spi.SelectorProvider);
method public abstract java.nio.channels.DatagramChannel bind(java.net.SocketAddress) throws java.io.IOException;
method public abstract java.nio.channels.DatagramChannel connect(java.net.SocketAddress) throws java.io.IOException;
@@ -56253,40 +56188,14 @@
ctor public IllegalBlockingModeException();
}
- public class IllegalChannelGroupException extends java.lang.IllegalArgumentException {
- ctor public IllegalChannelGroupException();
- }
-
public class IllegalSelectorException extends java.lang.IllegalArgumentException {
ctor public IllegalSelectorException();
}
- public class InterruptedByTimeoutException extends java.io.IOException {
- ctor public InterruptedByTimeoutException();
- }
-
public abstract interface InterruptibleChannel implements java.nio.channels.Channel {
method public abstract void close() throws java.io.IOException;
}
- public abstract class MembershipKey {
- ctor protected MembershipKey();
- method public abstract java.nio.channels.MembershipKey block(java.net.InetAddress) throws java.io.IOException;
- method public abstract java.nio.channels.MulticastChannel channel();
- method public abstract void drop();
- method public abstract java.net.InetAddress group();
- method public abstract boolean isValid();
- method public abstract java.net.NetworkInterface networkInterface();
- method public abstract java.net.InetAddress sourceAddress();
- method public abstract java.nio.channels.MembershipKey unblock(java.net.InetAddress);
- }
-
- public abstract interface MulticastChannel implements java.nio.channels.NetworkChannel {
- method public abstract void close() throws java.io.IOException;
- method public abstract java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface) throws java.io.IOException;
- method public abstract java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface, java.net.InetAddress) throws java.io.IOException;
- }
-
public abstract interface NetworkChannel implements java.nio.channels.Channel {
method public abstract java.nio.channels.NetworkChannel bind(java.net.SocketAddress) throws java.io.IOException;
method public abstract java.net.SocketAddress getLocalAddress() throws java.io.IOException;
@@ -56336,10 +56245,6 @@
method public final int validOps();
}
- public class ReadPendingException extends java.lang.IllegalStateException {
- ctor public ReadPendingException();
- }
-
public abstract interface ReadableByteChannel implements java.nio.channels.Channel {
method public abstract int read(java.nio.ByteBuffer) throws java.io.IOException;
}
@@ -56417,10 +56322,6 @@
method public final int validOps();
}
- public class ShutdownChannelGroupException extends java.lang.IllegalStateException {
- ctor public ShutdownChannelGroupException();
- }
-
public abstract class SocketChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.NetworkChannel java.nio.channels.ScatteringByteChannel {
ctor protected SocketChannel(java.nio.channels.spi.SelectorProvider);
method public abstract java.nio.channels.SocketChannel bind(java.net.SocketAddress) throws java.io.IOException;
@@ -56456,10 +56357,6 @@
method public abstract int write(java.nio.ByteBuffer) throws java.io.IOException;
}
- public class WritePendingException extends java.lang.IllegalStateException {
- ctor public WritePendingException();
- }
-
}
package java.nio.channels.spi {
@@ -56506,15 +56403,6 @@
method protected abstract java.nio.channels.SelectionKey register(java.nio.channels.spi.AbstractSelectableChannel, int, java.lang.Object);
}
- public abstract class AsynchronousChannelProvider {
- ctor protected AsynchronousChannelProvider();
- method public abstract java.nio.channels.AsynchronousChannelGroup openAsynchronousChannelGroup(int, java.util.concurrent.ThreadFactory) throws java.io.IOException;
- method public abstract java.nio.channels.AsynchronousChannelGroup openAsynchronousChannelGroup(java.util.concurrent.ExecutorService, int) throws java.io.IOException;
- method public abstract java.nio.channels.AsynchronousServerSocketChannel openAsynchronousServerSocketChannel(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
- method public abstract java.nio.channels.AsynchronousSocketChannel openAsynchronousSocketChannel(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
- method public static java.nio.channels.spi.AsynchronousChannelProvider provider();
- }
-
public abstract class SelectorProvider {
ctor protected SelectorProvider();
method public java.nio.channels.Channel inheritedChannel() throws java.io.IOException;
@@ -59869,13 +59757,11 @@
method public static final java.text.DecimalFormatSymbols getInstance(java.util.Locale);
method public java.lang.String getInternationalCurrencySymbol();
method public char getMinusSign();
- method public java.lang.String getMinusSignString();
method public char getMonetaryDecimalSeparator();
method public java.lang.String getNaN();
method public char getPatternSeparator();
method public char getPerMill();
method public char getPercent();
- method public java.lang.String getPercentString();
method public char getZeroDigit();
method public void setCurrency(java.util.Currency);
method public void setCurrencySymbol(java.lang.String);
diff --git a/api/test-current.txt b/api/test-current.txt
index 675b993..d202108 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -8517,6 +8517,7 @@
field public static final java.lang.String ACTION_MANAGED_PROFILE_ADDED = "android.intent.action.MANAGED_PROFILE_ADDED";
field public static final java.lang.String ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED = "android.intent.action.MANAGED_PROFILE_AVAILABILITY_CHANGED";
field public static final java.lang.String ACTION_MANAGED_PROFILE_REMOVED = "android.intent.action.MANAGED_PROFILE_REMOVED";
+ field public static final java.lang.String ACTION_MANAGED_PROFILE_UNLOCKED = "android.intent.action.MANAGED_PROFILE_UNLOCKED";
field public static final java.lang.String ACTION_MANAGE_NETWORK_USAGE = "android.intent.action.MANAGE_NETWORK_USAGE";
field public static final java.lang.String ACTION_MANAGE_PACKAGE_STORAGE = "android.intent.action.MANAGE_PACKAGE_STORAGE";
field public static final java.lang.String ACTION_MEDIA_BAD_REMOVAL = "android.intent.action.MEDIA_BAD_REMOVAL";
@@ -19229,7 +19230,6 @@
method public double getElevationUncertaintyInDeg();
method public byte getLossOfLock();
method public byte getMultipathIndicator();
- method public byte getPrn();
method public double getPseudorangeInMeters();
method public double getPseudorangeRateCarrierInMetersPerSec();
method public double getPseudorangeRateCarrierUncertaintyInMetersPerSec();
@@ -19240,6 +19240,7 @@
method public long getReceivedGpsTowUncertaintyInNs();
method public double getSnrInDb();
method public short getState();
+ method public short getSvid();
method public short getTimeFromLastBitInMs();
method public double getTimeOffsetInNs();
method public boolean hasAzimuthInDeg();
@@ -19299,7 +19300,6 @@
method public void setElevationUncertaintyInDeg(double);
method public void setLossOfLock(byte);
method public void setMultipathIndicator(byte);
- method public void setPrn(byte);
method public void setPseudorangeInMeters(double);
method public void setPseudorangeRateCarrierInMetersPerSec(double);
method public void setPseudorangeRateCarrierUncertaintyInMetersPerSec(double);
@@ -19310,6 +19310,7 @@
method public void setReceivedGpsTowUncertaintyInNs(long);
method public void setSnrInDb(double);
method public void setState(short);
+ method public void setSvid(short);
method public void setTimeFromLastBitInMs(short);
method public void setTimeOffsetInNs(double);
method public void setUsedInFix(boolean);
@@ -19364,17 +19365,17 @@
method public int describeContents();
method public byte[] getData();
method public short getMessageId();
- method public byte getPrn();
method public short getStatus();
method public short getSubmessageId();
+ method public short getSvid();
method public byte getType();
method public void reset();
method public void set(android.location.GnssNavigationMessage);
method public void setData(byte[]);
method public void setMessageId(short);
- method public void setPrn(byte);
method public void setStatus(short);
method public void setSubmessageId(short);
+ method public void setSvid(short);
method public void setType(byte);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.location.GnssNavigationMessage> CREATOR;
@@ -19420,8 +19421,8 @@
method public int getConstellationType(int);
method public float getElevation(int);
method public int getNumSatellites();
- method public int getPrn(int);
method public float getSnr(int);
+ method public int getSvid(int);
method public boolean hasAlmanac(int);
method public boolean hasEphemeris(int);
method public boolean usedInFix(int);
@@ -22959,10 +22960,8 @@
field public static final int INPUT_STATE_CONNECTED_STANDBY = 1; // 0x1
field public static final int INPUT_STATE_DISCONNECTED = 2; // 0x2
field public static final java.lang.String META_DATA_CONTENT_RATING_SYSTEMS = "android.media.tv.metadata.CONTENT_RATING_SYSTEMS";
- field public static final int RECORDING_ERROR_CONNECTION_FAILED = 1; // 0x1
- field public static final int RECORDING_ERROR_DISCONNECTED = 2; // 0x2
- field public static final int RECORDING_ERROR_INSUFFICIENT_SPACE = 3; // 0x3
- field public static final int RECORDING_ERROR_RESOURCE_BUSY = 4; // 0x4
+ field public static final int RECORDING_ERROR_INSUFFICIENT_SPACE = 1; // 0x1
+ field public static final int RECORDING_ERROR_RESOURCE_BUSY = 2; // 0x2
field public static final int RECORDING_ERROR_UNKNOWN = 0; // 0x0
field public static final long TIME_SHIFT_INVALID_TIME = -9223372036854775808L; // 0x8000000000000000L
field public static final int TIME_SHIFT_STATUS_AVAILABLE = 3; // 0x3
@@ -23062,6 +23061,8 @@
public static abstract class TvRecordingClient.RecordingCallback {
ctor public TvRecordingClient.RecordingCallback();
+ method public void onConnectionFailed(java.lang.String);
+ method public void onDisconnected(java.lang.String);
method public void onError(int);
method public void onRecordingStopped(android.net.Uri);
method public void onTuned();
@@ -23076,6 +23077,7 @@
method public final java.lang.String getId();
method public final java.lang.String getLanguage();
method public final int getType();
+ method public final byte getVideoActiveFormatDescription();
method public final float getVideoFrameRate();
method public final int getVideoHeight();
method public final float getVideoPixelAspectRatio();
@@ -23095,6 +23097,7 @@
method public final android.media.tv.TvTrackInfo.Builder setDescription(java.lang.CharSequence);
method public final android.media.tv.TvTrackInfo.Builder setExtra(android.os.Bundle);
method public final android.media.tv.TvTrackInfo.Builder setLanguage(java.lang.String);
+ method public final android.media.tv.TvTrackInfo.Builder setVideoActiveFormatDescription(byte);
method public final android.media.tv.TvTrackInfo.Builder setVideoFrameRate(float);
method public final android.media.tv.TvTrackInfo.Builder setVideoHeight(int);
method public final android.media.tv.TvTrackInfo.Builder setVideoPixelAspectRatio(float);
@@ -29071,6 +29074,7 @@
method public static final int getThreadPriority(int) throws java.lang.IllegalArgumentException;
method public static final int getUidForName(java.lang.String);
method public static final boolean is64Bit();
+ method public static boolean isApplicationUid(int);
method public static final void killProcess(int);
method public static final int myPid();
method public static final int myTid();
@@ -29252,6 +29256,7 @@
ctor public UserHandle(android.os.Parcel);
method public int describeContents();
method public static int getAppId(int);
+ method public static android.os.UserHandle getUserHandleForUid(int);
method public static android.os.UserHandle readFromParcel(android.os.Parcel);
method public void writeToParcel(android.os.Parcel, int);
method public static void writeToParcel(android.os.UserHandle, android.os.Parcel);
@@ -51491,7 +51496,6 @@
method public static int getLength(java.lang.Object);
method public static long getLong(java.lang.Object, int) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
method public static short getShort(java.lang.Object, int) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
- method public static java.lang.Object newArray(java.lang.Class<?>, int) throws java.lang.NegativeArraySizeException;
method public static java.lang.Object newInstance(java.lang.Class<?>, int) throws java.lang.NegativeArraySizeException;
method public static java.lang.Object newInstance(java.lang.Class<?>, int...) throws java.lang.IllegalArgumentException, java.lang.NegativeArraySizeException;
method public static void set(java.lang.Object, int, java.lang.Object) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
@@ -52167,7 +52171,6 @@
public class InetAddress implements java.io.Serializable {
method public byte[] getAddress();
- method public byte[] getAddressInternal();
method public static java.net.InetAddress[] getAllByName(java.lang.String) throws java.net.UnknownHostException;
method public static java.net.InetAddress getByAddress(java.lang.String, byte[]) throws java.net.UnknownHostException;
method public static java.net.InetAddress getByAddress(byte[]) throws java.net.UnknownHostException;
@@ -52441,7 +52444,7 @@
method protected abstract void connect(java.net.InetAddress, int) throws java.io.IOException;
method protected abstract void connect(java.net.SocketAddress, int) throws java.io.IOException;
method protected abstract void create(boolean) throws java.io.IOException;
- method public java.io.FileDescriptor getFileDescriptor();
+ method protected java.io.FileDescriptor getFileDescriptor();
method protected java.net.InetAddress getInetAddress();
method protected abstract java.io.InputStream getInputStream() throws java.io.IOException;
method protected int getLocalPort();
@@ -52983,10 +52986,6 @@
package java.nio.channels {
- public class AcceptPendingException extends java.lang.IllegalStateException {
- ctor public AcceptPendingException();
- }
-
public class AlreadyBoundException extends java.lang.IllegalStateException {
ctor public AlreadyBoundException();
}
@@ -52995,68 +52994,10 @@
ctor public AlreadyConnectedException();
}
- public abstract interface AsynchronousByteChannel implements java.nio.channels.AsynchronousChannel {
- method public abstract void read(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
- method public abstract java.util.concurrent.Future<java.lang.Integer> read(java.nio.ByteBuffer);
- method public abstract void write(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
- method public abstract java.util.concurrent.Future<java.lang.Integer> write(java.nio.ByteBuffer);
- }
-
- public abstract interface AsynchronousChannel implements java.nio.channels.Channel {
- method public abstract void close() throws java.io.IOException;
- }
-
- public abstract class AsynchronousChannelGroup {
- ctor protected AsynchronousChannelGroup(java.nio.channels.spi.AsynchronousChannelProvider);
- method public abstract boolean awaitTermination(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
- method public abstract boolean isShutdown();
- method public abstract boolean isTerminated();
- method public final java.nio.channels.spi.AsynchronousChannelProvider provider();
- method public abstract void shutdown();
- method public abstract void shutdownNow() throws java.io.IOException;
- method public static java.nio.channels.AsynchronousChannelGroup withCachedThreadPool(java.util.concurrent.ExecutorService, int) throws java.io.IOException;
- method public static java.nio.channels.AsynchronousChannelGroup withFixedThreadPool(int, java.util.concurrent.ThreadFactory) throws java.io.IOException;
- method public static java.nio.channels.AsynchronousChannelGroup withThreadPool(java.util.concurrent.ExecutorService) throws java.io.IOException;
- }
-
public class AsynchronousCloseException extends java.nio.channels.ClosedChannelException {
ctor public AsynchronousCloseException();
}
- public abstract class AsynchronousServerSocketChannel implements java.nio.channels.AsynchronousChannel java.nio.channels.NetworkChannel {
- ctor protected AsynchronousServerSocketChannel(java.nio.channels.spi.AsynchronousChannelProvider);
- method public abstract void accept(A, java.nio.channels.CompletionHandler<java.nio.channels.AsynchronousSocketChannel, ? super A>);
- method public abstract java.util.concurrent.Future<java.nio.channels.AsynchronousSocketChannel> accept();
- method public final java.nio.channels.AsynchronousServerSocketChannel bind(java.net.SocketAddress) throws java.io.IOException;
- method public abstract java.nio.channels.AsynchronousServerSocketChannel bind(java.net.SocketAddress, int) throws java.io.IOException;
- method public static java.nio.channels.AsynchronousServerSocketChannel open(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
- method public static java.nio.channels.AsynchronousServerSocketChannel open() throws java.io.IOException;
- method public final java.nio.channels.spi.AsynchronousChannelProvider provider();
- method public abstract java.nio.channels.AsynchronousServerSocketChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
- }
-
- public abstract class AsynchronousSocketChannel implements java.nio.channels.AsynchronousByteChannel java.nio.channels.NetworkChannel {
- ctor protected AsynchronousSocketChannel(java.nio.channels.spi.AsynchronousChannelProvider);
- method public abstract java.nio.channels.AsynchronousSocketChannel bind(java.net.SocketAddress) throws java.io.IOException;
- method public abstract void connect(java.net.SocketAddress, A, java.nio.channels.CompletionHandler<java.lang.Void, ? super A>);
- method public abstract java.util.concurrent.Future<java.lang.Void> connect(java.net.SocketAddress);
- method public abstract java.net.SocketAddress getRemoteAddress() throws java.io.IOException;
- method public static java.nio.channels.AsynchronousSocketChannel open(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
- method public static java.nio.channels.AsynchronousSocketChannel open() throws java.io.IOException;
- method public final java.nio.channels.spi.AsynchronousChannelProvider provider();
- method public abstract void read(java.nio.ByteBuffer, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
- method public final void read(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
- method public abstract java.util.concurrent.Future<java.lang.Integer> read(java.nio.ByteBuffer);
- method public abstract void read(java.nio.ByteBuffer[], int, int, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Long, ? super A>);
- method public abstract java.nio.channels.AsynchronousSocketChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
- method public abstract java.nio.channels.AsynchronousSocketChannel shutdownInput() throws java.io.IOException;
- method public abstract java.nio.channels.AsynchronousSocketChannel shutdownOutput() throws java.io.IOException;
- method public abstract void write(java.nio.ByteBuffer, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
- method public final void write(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
- method public abstract java.util.concurrent.Future<java.lang.Integer> write(java.nio.ByteBuffer);
- method public abstract void write(java.nio.ByteBuffer[], int, int, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Long, ? super A>);
- }
-
public abstract interface ByteChannel implements java.nio.channels.ReadableByteChannel java.nio.channels.WritableByteChannel {
}
@@ -53073,9 +53014,7 @@
method public static java.nio.channels.ReadableByteChannel newChannel(java.io.InputStream);
method public static java.nio.channels.WritableByteChannel newChannel(java.io.OutputStream);
method public static java.io.InputStream newInputStream(java.nio.channels.ReadableByteChannel);
- method public static java.io.InputStream newInputStream(java.nio.channels.AsynchronousByteChannel);
method public static java.io.OutputStream newOutputStream(java.nio.channels.WritableByteChannel);
- method public static java.io.OutputStream newOutputStream(java.nio.channels.AsynchronousByteChannel);
method public static java.io.Reader newReader(java.nio.channels.ReadableByteChannel, java.nio.charset.CharsetDecoder, int);
method public static java.io.Reader newReader(java.nio.channels.ReadableByteChannel, java.lang.String);
method public static java.io.Writer newWriter(java.nio.channels.WritableByteChannel, java.nio.charset.CharsetEncoder, int);
@@ -53094,16 +53033,11 @@
ctor public ClosedSelectorException();
}
- public abstract interface CompletionHandler {
- method public abstract void completed(V, A);
- method public abstract void failed(java.lang.Throwable, A);
- }
-
public class ConnectionPendingException extends java.lang.IllegalStateException {
ctor public ConnectionPendingException();
}
- public abstract class DatagramChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.MulticastChannel java.nio.channels.ScatteringByteChannel {
+ public abstract class DatagramChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.ScatteringByteChannel {
ctor protected DatagramChannel(java.nio.channels.spi.SelectorProvider);
method public abstract java.nio.channels.DatagramChannel bind(java.net.SocketAddress) throws java.io.IOException;
method public abstract java.nio.channels.DatagramChannel connect(java.net.SocketAddress) throws java.io.IOException;
@@ -53182,40 +53116,14 @@
ctor public IllegalBlockingModeException();
}
- public class IllegalChannelGroupException extends java.lang.IllegalArgumentException {
- ctor public IllegalChannelGroupException();
- }
-
public class IllegalSelectorException extends java.lang.IllegalArgumentException {
ctor public IllegalSelectorException();
}
- public class InterruptedByTimeoutException extends java.io.IOException {
- ctor public InterruptedByTimeoutException();
- }
-
public abstract interface InterruptibleChannel implements java.nio.channels.Channel {
method public abstract void close() throws java.io.IOException;
}
- public abstract class MembershipKey {
- ctor protected MembershipKey();
- method public abstract java.nio.channels.MembershipKey block(java.net.InetAddress) throws java.io.IOException;
- method public abstract java.nio.channels.MulticastChannel channel();
- method public abstract void drop();
- method public abstract java.net.InetAddress group();
- method public abstract boolean isValid();
- method public abstract java.net.NetworkInterface networkInterface();
- method public abstract java.net.InetAddress sourceAddress();
- method public abstract java.nio.channels.MembershipKey unblock(java.net.InetAddress);
- }
-
- public abstract interface MulticastChannel implements java.nio.channels.NetworkChannel {
- method public abstract void close() throws java.io.IOException;
- method public abstract java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface) throws java.io.IOException;
- method public abstract java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface, java.net.InetAddress) throws java.io.IOException;
- }
-
public abstract interface NetworkChannel implements java.nio.channels.Channel {
method public abstract java.nio.channels.NetworkChannel bind(java.net.SocketAddress) throws java.io.IOException;
method public abstract java.net.SocketAddress getLocalAddress() throws java.io.IOException;
@@ -53265,10 +53173,6 @@
method public final int validOps();
}
- public class ReadPendingException extends java.lang.IllegalStateException {
- ctor public ReadPendingException();
- }
-
public abstract interface ReadableByteChannel implements java.nio.channels.Channel {
method public abstract int read(java.nio.ByteBuffer) throws java.io.IOException;
}
@@ -53346,10 +53250,6 @@
method public final int validOps();
}
- public class ShutdownChannelGroupException extends java.lang.IllegalStateException {
- ctor public ShutdownChannelGroupException();
- }
-
public abstract class SocketChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.NetworkChannel java.nio.channels.ScatteringByteChannel {
ctor protected SocketChannel(java.nio.channels.spi.SelectorProvider);
method public abstract java.nio.channels.SocketChannel bind(java.net.SocketAddress) throws java.io.IOException;
@@ -53385,10 +53285,6 @@
method public abstract int write(java.nio.ByteBuffer) throws java.io.IOException;
}
- public class WritePendingException extends java.lang.IllegalStateException {
- ctor public WritePendingException();
- }
-
}
package java.nio.channels.spi {
@@ -53435,15 +53331,6 @@
method protected abstract java.nio.channels.SelectionKey register(java.nio.channels.spi.AbstractSelectableChannel, int, java.lang.Object);
}
- public abstract class AsynchronousChannelProvider {
- ctor protected AsynchronousChannelProvider();
- method public abstract java.nio.channels.AsynchronousChannelGroup openAsynchronousChannelGroup(int, java.util.concurrent.ThreadFactory) throws java.io.IOException;
- method public abstract java.nio.channels.AsynchronousChannelGroup openAsynchronousChannelGroup(java.util.concurrent.ExecutorService, int) throws java.io.IOException;
- method public abstract java.nio.channels.AsynchronousServerSocketChannel openAsynchronousServerSocketChannel(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
- method public abstract java.nio.channels.AsynchronousSocketChannel openAsynchronousSocketChannel(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
- method public static java.nio.channels.spi.AsynchronousChannelProvider provider();
- }
-
public abstract class SelectorProvider {
ctor protected SelectorProvider();
method public java.nio.channels.Channel inheritedChannel() throws java.io.IOException;
@@ -56798,13 +56685,11 @@
method public static final java.text.DecimalFormatSymbols getInstance(java.util.Locale);
method public java.lang.String getInternationalCurrencySymbol();
method public char getMinusSign();
- method public java.lang.String getMinusSignString();
method public char getMonetaryDecimalSeparator();
method public java.lang.String getNaN();
method public char getPatternSeparator();
method public char getPerMill();
method public char getPercent();
- method public java.lang.String getPercentString();
method public char getZeroDigit();
method public void setCurrency(java.util.Currency);
method public void setCurrencySymbol(java.lang.String);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 622012e..ea58e29 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -16,6 +16,8 @@
package android.app;
+import static java.lang.Character.MIN_VALUE;
+
import android.annotation.CallSuper;
import android.annotation.DrawableRes;
import android.annotation.IdRes;
@@ -26,20 +28,6 @@
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.StyleRes;
-import android.os.PersistableBundle;
-import android.transition.Scene;
-import android.transition.TransitionManager;
-import android.util.ArrayMap;
-import android.util.SuperNotCalledException;
-import android.view.DragEvent;
-import android.view.DropPermissions;
-import android.view.Window.WindowControllerCallback;
-import android.widget.Toolbar;
-
-import com.android.internal.app.IVoiceInteractor;
-import com.android.internal.app.WindowDecorActionBar;
-import com.android.internal.app.ToolbarActionBar;
-
import android.annotation.SystemApi;
import android.app.admin.DevicePolicyManager;
import android.app.assist.AssistContent;
@@ -61,7 +49,12 @@
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.InsetDrawable;
+import android.graphics.drawable.LayerDrawable;
+import android.graphics.drawable.ShapeDrawable;
import android.media.AudioManager;
import android.media.session.MediaController;
import android.net.Uri;
@@ -71,6 +64,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.Parcelable;
+import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.StrictMode;
import android.os.UserHandle;
@@ -78,16 +72,22 @@
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.method.TextKeyListener;
+import android.transition.Scene;
+import android.transition.TransitionManager;
+import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.EventLog;
import android.util.Log;
import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SuperNotCalledException;
import android.view.ActionMode;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.ContextThemeWrapper;
+import android.view.DragEvent;
+import android.view.DropPermissions;
import android.view.KeyEvent;
import android.view.KeyboardShortcutGroup;
import android.view.KeyboardShortcutInfo;
@@ -104,11 +104,17 @@
import android.view.ViewManager;
import android.view.ViewRootImpl;
import android.view.Window;
+import android.view.Window.WindowControllerCallback;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityEvent;
import android.widget.AdapterView;
+import android.widget.Toolbar;
+import com.android.internal.app.IVoiceInteractor;
+import com.android.internal.app.ToolbarActionBar;
+import com.android.internal.app.WindowDecorActionBar;
+import com.android.internal.policy.DecorView;
import com.android.internal.policy.PhoneWindow;
import java.io.FileDescriptor;
@@ -119,8 +125,6 @@
import java.util.HashMap;
import java.util.List;
-import static java.lang.Character.MIN_VALUE;
-
/**
* An activity is a single, focused thing that the user can do. Almost all
* activities interact with the user, so the Activity class takes care of
@@ -3974,17 +3978,52 @@
// Get the primary color and update the TaskDescription for this activity
if (theme != null) {
TypedArray a = theme.obtainStyledAttributes(com.android.internal.R.styleable.Theme);
+ int windowBgResourceId = a.getResourceId(
+ com.android.internal.R.styleable.Window_windowBackground, 0);
+ int windowBgFallbackResourceId = a.getResourceId(
+ com.android.internal.R.styleable.Window_windowBackgroundFallback, 0);
int colorPrimary = a.getColor(com.android.internal.R.styleable.Theme_colorPrimary, 0);
+ int colorBg = tryExtractColorFromDrawable(DecorView.getResizingBackgroundDrawable(this,
+ windowBgResourceId, windowBgFallbackResourceId));
a.recycle();
if (colorPrimary != 0) {
- ActivityManager.TaskDescription v = new ActivityManager.TaskDescription(null, null,
- colorPrimary);
- setTaskDescription(v);
+ ActivityManager.TaskDescription td = new ActivityManager.TaskDescription();
+ td.setPrimaryColor(colorPrimary);
+ td.setBackgroundColor(colorBg);
+ setTaskDescription(td);
}
}
}
/**
+ * Attempts to extract the color from a given drawable.
+ *
+ * @return the extracted color or 0 if no color could be extracted.
+ */
+ private int tryExtractColorFromDrawable(Drawable drawable) {
+ if (drawable instanceof ColorDrawable) {
+ return ((ColorDrawable) drawable).getColor();
+ } else if (drawable instanceof InsetDrawable) {
+ return tryExtractColorFromDrawable(((InsetDrawable) drawable).getDrawable());
+ } else if (drawable instanceof ShapeDrawable) {
+ Paint p = ((ShapeDrawable) drawable).getPaint();
+ if (p != null) {
+ return p.getColor();
+ }
+ } else if (drawable instanceof LayerDrawable) {
+ LayerDrawable ld = (LayerDrawable) drawable;
+ int numLayers = ld.getNumberOfLayers();
+ for (int i = 0; i < numLayers; i++) {
+ int color = tryExtractColorFromDrawable(ld.getDrawable(i));
+ if (color != 0) {
+ return color;
+ }
+ }
+ }
+ return 0;
+ }
+
+ /**
* Requests permissions to be granted to this application. These permissions
* must be requested in your manifest, they should not be granted to your app,
* and they should have protection level {@link android.content.pm.PermissionInfo
@@ -5612,8 +5651,8 @@
if (taskDescription.getIconFilename() == null && taskDescription.getIcon() != null) {
final int size = ActivityManager.getLauncherLargeIconSizeInner(this);
final Bitmap icon = Bitmap.createScaledBitmap(taskDescription.getIcon(), size, size, true);
- td = new ActivityManager.TaskDescription(taskDescription.getLabel(), icon,
- taskDescription.getPrimaryColor());
+ td = new ActivityManager.TaskDescription(taskDescription);
+ td.setIcon(icon);
} else {
td = taskDescription;
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 1eb2fe2..dd73261 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -632,6 +632,17 @@
public static boolean useWindowFrameForBackdrop(int stackId) {
return stackId == FREEFORM_WORKSPACE_STACK_ID || stackId == PINNED_STACK_ID;
}
+
+ /**
+ * Returns true if a window from the specified stack with {@param stackId} are normally
+ * fullscreen, i. e. they can become the top opaque fullscreen window, meaning that it
+ * controls system bars, lockscreen occluded/dismissing state, screen rotation animation,
+ * etc.
+ */
+ public static boolean normallyFullscreenWindows(int stackId) {
+ return stackId != PINNED_STACK_ID && stackId != FREEFORM_WORKSPACE_STACK_ID
+ && stackId != DOCKED_STACK_ID;
+ }
}
/**
@@ -863,8 +874,10 @@
public static final String ATTR_TASKDESCRIPTION_PREFIX = "task_description_";
private static final String ATTR_TASKDESCRIPTIONLABEL =
ATTR_TASKDESCRIPTION_PREFIX + "label";
- private static final String ATTR_TASKDESCRIPTIONCOLOR =
+ private static final String ATTR_TASKDESCRIPTIONCOLOR_PRIMARY =
ATTR_TASKDESCRIPTION_PREFIX + "color";
+ private static final String ATTR_TASKDESCRIPTIONCOLOR_BACKGROUND =
+ ATTR_TASKDESCRIPTION_PREFIX + "colorBackground";
private static final String ATTR_TASKDESCRIPTIONICONFILENAME =
ATTR_TASKDESCRIPTION_PREFIX + "icon_filename";
@@ -872,28 +885,21 @@
private Bitmap mIcon;
private String mIconFilename;
private int mColorPrimary;
+ private int mColorBackground;
/**
* Creates the TaskDescription to the specified values.
*
* @param label A label and description of the current state of this task.
* @param icon An icon that represents the current state of this task.
- * @param colorPrimary A color to override the theme's primary color. This color must be opaque.
+ * @param colorPrimary A color to override the theme's primary color. This color must be
+ * opaque.
*/
public TaskDescription(String label, Bitmap icon, int colorPrimary) {
+ this(label, icon, null, colorPrimary, 0);
if ((colorPrimary != 0) && (Color.alpha(colorPrimary) != 255)) {
throw new RuntimeException("A TaskDescription's primary color should be opaque");
}
-
- mLabel = label;
- mIcon = icon;
- mColorPrimary = colorPrimary;
- }
-
- /** @hide */
- public TaskDescription(String label, int colorPrimary, String iconFilename) {
- this(label, null, colorPrimary);
- mIconFilename = iconFilename;
}
/**
@@ -903,7 +909,7 @@
* @param icon An icon that represents the current state of this activity.
*/
public TaskDescription(String label, Bitmap icon) {
- this(label, icon, 0);
+ this(label, icon, null, 0, 0);
}
/**
@@ -912,14 +918,24 @@
* @param label A label and description of the current state of this activity.
*/
public TaskDescription(String label) {
- this(label, null, 0);
+ this(label, null, null, 0, 0);
}
/**
* Creates an empty TaskDescription.
*/
public TaskDescription() {
- this(null, null, 0);
+ this(null, null, null, 0, 0);
+ }
+
+ /** @hide */
+ public TaskDescription(String label, Bitmap icon, String iconFilename, int colorPrimary,
+ int colorBackground) {
+ mLabel = label;
+ mIcon = icon;
+ mIconFilename = iconFilename;
+ mColorPrimary = colorPrimary;
+ mColorBackground = colorBackground;
}
/**
@@ -928,8 +944,9 @@
public TaskDescription(TaskDescription td) {
mLabel = td.mLabel;
mIcon = td.mIcon;
- mColorPrimary = td.mColorPrimary;
mIconFilename = td.mIconFilename;
+ mColorPrimary = td.mColorPrimary;
+ mColorBackground = td.mColorBackground;
}
private TaskDescription(Parcel source) {
@@ -957,6 +974,18 @@
}
/**
+ * Sets the background color for this task description.
+ * @hide
+ */
+ public void setBackgroundColor(int backgroundColor) {
+ // Ensure that the given color is valid
+ if ((backgroundColor != 0) && (Color.alpha(backgroundColor) != 255)) {
+ throw new RuntimeException("A TaskDescription's background color should be opaque");
+ }
+ mColorBackground = backgroundColor;
+ }
+
+ /**
* Sets the icon for this task description.
* @hide
*/
@@ -1005,8 +1034,8 @@
public static Bitmap loadTaskDescriptionIcon(String iconFilename, int userId) {
if (iconFilename != null) {
try {
- return ActivityManagerNative.getDefault().
- getTaskDescriptionIcon(iconFilename, userId);
+ return ActivityManagerNative.getDefault().getTaskDescriptionIcon(iconFilename,
+ userId);
} catch (RemoteException e) {
}
}
@@ -1020,13 +1049,26 @@
return mColorPrimary;
}
+ /**
+ * @return The background color.
+ * @hide
+ */
+ public int getBackgroundColor() {
+ return mColorBackground;
+ }
+
/** @hide */
public void saveToXml(XmlSerializer out) throws IOException {
if (mLabel != null) {
out.attribute(null, ATTR_TASKDESCRIPTIONLABEL, mLabel);
}
if (mColorPrimary != 0) {
- out.attribute(null, ATTR_TASKDESCRIPTIONCOLOR, Integer.toHexString(mColorPrimary));
+ out.attribute(null, ATTR_TASKDESCRIPTIONCOLOR_PRIMARY,
+ Integer.toHexString(mColorPrimary));
+ }
+ if (mColorBackground != 0) {
+ out.attribute(null, ATTR_TASKDESCRIPTIONCOLOR_BACKGROUND,
+ Integer.toHexString(mColorBackground));
}
if (mIconFilename != null) {
out.attribute(null, ATTR_TASKDESCRIPTIONICONFILENAME, mIconFilename);
@@ -1037,8 +1079,10 @@
public void restoreFromXml(String attrName, String attrValue) {
if (ATTR_TASKDESCRIPTIONLABEL.equals(attrName)) {
setLabel(attrValue);
- } else if (ATTR_TASKDESCRIPTIONCOLOR.equals(attrName)) {
+ } else if (ATTR_TASKDESCRIPTIONCOLOR_PRIMARY.equals(attrName)) {
setPrimaryColor((int) Long.parseLong(attrValue, 16));
+ } else if (ATTR_TASKDESCRIPTIONCOLOR_BACKGROUND.equals(attrName)) {
+ setBackgroundColor((int) Long.parseLong(attrValue, 16));
} else if (ATTR_TASKDESCRIPTIONICONFILENAME.equals(attrName)) {
setIconFilename(attrValue);
}
@@ -1064,6 +1108,7 @@
mIcon.writeToParcel(dest, 0);
}
dest.writeInt(mColorPrimary);
+ dest.writeInt(mColorBackground);
if (mIconFilename == null) {
dest.writeInt(0);
} else {
@@ -1076,6 +1121,7 @@
mLabel = source.readInt() > 0 ? source.readString() : null;
mIcon = source.readInt() > 0 ? Bitmap.CREATOR.createFromParcel(source) : null;
mColorPrimary = source.readInt();
+ mColorBackground = source.readInt();
mIconFilename = source.readInt() > 0 ? source.readString() : null;
}
@@ -1092,7 +1138,8 @@
@Override
public String toString() {
return "TaskDescription Label: " + mLabel + " Icon: " + mIcon +
- " colorPrimary: " + mColorPrimary;
+ " IconFilename: " + mIconFilename + " colorPrimary: " + mColorPrimary +
+ " colorBackground: " + mColorBackground;
}
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 55c6353..35b7c39 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -3358,13 +3358,9 @@
return mN.bigContentView;
} else if (mStyle != null) {
result = mStyle.makeBigContentView();
- } else if (mActions.size() == 0) {
- return null;
- }
- if (result == null) {
- result = applyStandardTemplateWithActions(getBigBaseLayoutResource());
- } else {
hideLine1Text(result);
+ } else if (mActions.size() != 0) {
+ result = applyStandardTemplateWithActions(getBigBaseLayoutResource());
}
adaptNotificationHeaderForBigContentView(result);
return result;
@@ -3384,11 +3380,15 @@
}
private void hideLine1Text(RemoteViews result) {
- result.setViewVisibility(R.id.text_line_1, View.GONE);
+ if (result != null) {
+ result.setViewVisibility(R.id.text_line_1, View.GONE);
+ }
}
private void adaptNotificationHeaderForBigContentView(RemoteViews result) {
- result.setBoolean(R.id.notification_header, "setExpanded", true);
+ if (result != null) {
+ result.setBoolean(R.id.notification_header, "setExpanded", true);
+ }
}
/**
@@ -4326,6 +4326,15 @@
return makeMediaBigContentView();
}
+ /**
+ * @hide
+ */
+ @Override
+ public RemoteViews makeHeadsUpContentView() {
+ RemoteViews expanded = makeMediaBigContentView();
+ return expanded != null ? expanded : makeMediaContentView();
+ }
+
/** @hide */
@Override
public void addExtras(Bundle extras) {
@@ -4407,6 +4416,13 @@
private RemoteViews makeMediaBigContentView() {
final int actionCount = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS);
+ // Dont add an expanded view if there is no more content to be revealed
+ int actionsInCompact = mActionsToShowInCompact == null
+ ? 0
+ : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
+ if (mBuilder.mN.mLargeIcon == null && actionCount <= actionsInCompact) {
+ return null;
+ }
RemoteViews big = mBuilder.applyStandardTemplate(
R.layout.notification_template_material_big_media,
false);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 83f9357..f53170a 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1826,9 +1826,23 @@
* this method; if it has not, a security exception will be thrown.
*/
public int getCurrentFailedPasswordAttempts() {
+ return getCurrentFailedPasswordAttempts(myUserId());
+ }
+
+ /**
+ * Retrieve the number of times the given user has failed at entering a
+ * password since that last successful password entry.
+ *
+ * <p>The calling device admin must have requested
+ * {@link DeviceAdminInfo#USES_POLICY_WATCH_LOGIN} to be able to call this method; if it has
+ * not and it is not the system uid, a security exception will be thrown.
+ *
+ * @hide
+ */
+ public int getCurrentFailedPasswordAttempts(int userHandle) {
if (mService != null) {
try {
- return mService.getCurrentFailedPasswordAttempts(myUserId(), mParentInstance);
+ return mService.getCurrentFailedPasswordAttempts(userHandle, mParentInstance);
} catch (RemoteException e) {
Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
}
@@ -3023,13 +3037,39 @@
}
/**
+ * @hide
+ */
+ public void reportFailedFingerprintAttempt(int userHandle) {
+ if (mService != null) {
+ try {
+ mService.reportFailedFingerprintAttempt(userHandle);
+ } catch (RemoteException e) {
+ Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
+ }
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public void reportSuccessfulFingerprintAttempt(int userHandle) {
+ if (mService != null) {
+ try {
+ mService.reportSuccessfulFingerprintAttempt(userHandle);
+ } catch (RemoteException e) {
+ Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
+ }
+ }
+ }
+
+ /**
* Should be called when keyguard has been dismissed.
* @hide
*/
- public void reportKeyguardDismissed() {
+ public void reportKeyguardDismissed(int userHandle) {
if (mService != null) {
try {
- mService.reportKeyguardDismissed();
+ mService.reportKeyguardDismissed(userHandle);
} catch (RemoteException e) {
Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
}
@@ -3040,10 +3080,10 @@
* Should be called when keyguard view has been shown to the user.
* @hide
*/
- public void reportKeyguardSecured() {
+ public void reportKeyguardSecured(int userHandle) {
if (mService != null) {
try {
- mService.reportKeyguardSecured();
+ mService.reportKeyguardSecured(userHandle);
} catch (RemoteException e) {
Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
}
@@ -5730,4 +5770,32 @@
return false;
}
}
+
+ /**
+ * @hide
+ * Returns whether the uninstall for {@code packageName} for the current user is in queue
+ * to be started
+ * @param packageName the package to check for
+ * @return whether the uninstall intent for {@code packageName} is pending
+ */
+ public boolean isUninstallInQueue(String packageName) {
+ try {
+ return mService.isUninstallInQueue(packageName);
+ } catch (RemoteException re) {
+ Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
+ return false;
+ }
+ }
+
+ /**
+ * @hide
+ * @param packageName the package containing active DAs to be uninstalled
+ */
+ public void uninstallPackageWithActiveAdmins(String packageName) {
+ try {
+ mService.uninstallPackageWithActiveAdmins(packageName);
+ } catch (RemoteException re) {
+ Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
+ }
+ }
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index c6a5344..bd68182 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -116,9 +116,10 @@
int numbers, int symbols, int nonletter, int userHandle);
void reportFailedPasswordAttempt(int userHandle);
void reportSuccessfulPasswordAttempt(int userHandle);
-
- void reportKeyguardDismissed();
- void reportKeyguardSecured();
+ void reportFailedFingerprintAttempt(int userHandle);
+ void reportSuccessfulFingerprintAttempt(int userHandle);
+ void reportKeyguardDismissed(int userHandle);
+ void reportKeyguardSecured(int userHandle);
boolean setDeviceOwner(in ComponentName who, String ownerName, int userId);
ComponentName getDeviceOwnerComponent(boolean callingUserOnly);
@@ -293,4 +294,7 @@
boolean getDeviceLoggingEnabled(in ComponentName admin);
ParceledListSlice retrieveDeviceLogs(in ComponentName admin);
ParceledListSlice retrievePreviousDeviceLogs(in ComponentName admin);
+
+ boolean isUninstallInQueue(String packageName);
+ void uninstallPackageWithActiveAdmins(String packageName);
}
diff --git a/core/java/android/auditing/SecurityLog.java b/core/java/android/auditing/SecurityLog.java
index 8d8d2f5..f1703d6 100644
--- a/core/java/android/auditing/SecurityLog.java
+++ b/core/java/android/auditing/SecurityLog.java
@@ -77,8 +77,10 @@
SecurityLogTags.SECURITY_KEYGUARD_DISMISSED;
/**
* Indicate that there has been an authentication attempt to dismiss the keyguard. The log entry
- * contains the attempt result (integer, 1 for successful, 0 for unsuccessful), accessible via
- * {@link SecurityEvent#getData()}}
+ * contains the following information about the attempt in order, accessible via
+ * {@link SecurityEvent#getData()}}: attempt result (integer, 1 for successful, 0 for
+ * unsuccessful), strength of auth method (integer, 1 if strong auth method was used,
+ * 0 otherwise)
*/
public static final int TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT =
SecurityLogTags.SECURITY_KEYGUARD_DISMISS_AUTH_ATTEMPT;
diff --git a/core/java/android/auditing/SecurityLogTags.logtags b/core/java/android/auditing/SecurityLogTags.logtags
index cf85894..ccc3799 100644
--- a/core/java/android/auditing/SecurityLogTags.logtags
+++ b/core/java/android/auditing/SecurityLogTags.logtags
@@ -8,5 +8,5 @@
210004 security_adb_sync_send (path|3)
210005 security_app_process_start (process|3),(start_time|2|3),(uid|1),(pid|1),(seinfo|3),(sha256|3)
210006 security_keyguard_dismissed
-210007 security_keyguard_dismiss_auth_attempt (success|1)
+210007 security_keyguard_dismiss_auth_attempt (success|1),(method_strength|1)
210008 security_keyguard_secured
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 0f6f856..b476a25 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3028,6 +3028,17 @@
"android.intent.action.MANAGED_PROFILE_REMOVED";
/**
+ * Broadcast sent to the primary user when the credential-encrypted private storage for
+ * an associated managed profile is unlocked. Carries an extra {@link #EXTRA_USER} that
+ * specifies the UserHandle of the profile that was unlocked. Only applications (for example
+ * Launchers) that need to display merged content across both primary and managed profiles
+ * need to worry about this broadcast. This is only sent to registered receivers,
+ * not manifest receivers.
+ */
+ public static final String ACTION_MANAGED_PROFILE_UNLOCKED =
+ "android.intent.action.MANAGED_PROFILE_UNLOCKED";
+
+ /**
* Broadcast sent to the primary user when an associated managed profile's availability has
* changed. This includes when the user toggles the profile's quiet mode. Carries an extra
* {@link #EXTRA_USER} that specifies the UserHandle of the profile. When quiet mode is changed,
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 43a0cc7..f58b16a 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -178,6 +178,11 @@
*/
public static final int RESIZE_MODE_RESIZEABLE_AND_PIPABLE = 3;
/**
+ * Activity is does not support resizing, but we are forcing it to be resizeable.
+ * @hide
+ */
+ public static final int RESIZE_MODE_FORCE_RESIZEABLE = 4;
+ /**
* Value indicating if the resizing mode the activity supports.
* See {@link android.R.attr#resizeableActivity}.
* @hide
@@ -786,7 +791,9 @@
/** @hide */
public static boolean isResizeableMode(int mode) {
- return mode == RESIZE_MODE_RESIZEABLE || mode == RESIZE_MODE_RESIZEABLE_AND_PIPABLE;
+ return mode == RESIZE_MODE_RESIZEABLE
+ || mode == RESIZE_MODE_RESIZEABLE_AND_PIPABLE
+ || mode == RESIZE_MODE_FORCE_RESIZEABLE;
}
/** @hide */
@@ -800,6 +807,8 @@
return "RESIZE_MODE_RESIZEABLE";
case RESIZE_MODE_RESIZEABLE_AND_PIPABLE:
return "RESIZE_MODE_RESIZEABLE_AND_PIPABLE";
+ case RESIZE_MODE_FORCE_RESIZEABLE:
+ return "RESIZE_MODE_FORCE_RESIZEABLE";
default:
return "unknown=" + mode;
}
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index c79dae5..9082482 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -1038,10 +1038,10 @@
}
deviceEncryptedDataDir = Environment
- .getDataUserDeviceEncryptedPackageDirectory(volumeUuid, userId, packageName)
+ .getDataUserDePackageDirectory(volumeUuid, userId, packageName)
.getAbsolutePath();
credentialEncryptedDataDir = Environment
- .getDataUserCredentialEncryptedPackageDirectory(volumeUuid, userId, packageName)
+ .getDataUserCePackageDirectory(volumeUuid, userId, packageName)
.getAbsolutePath();
if ((privateFlags & PRIVATE_FLAG_FORCE_DEVICE_ENCRYPTED) != 0
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 5dddebd..5ae8d4c 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -80,6 +80,7 @@
import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
import static android.content.pm.ActivityInfo.FLAG_IMMERSIVE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_CROP_WINDOWS;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
@@ -3448,7 +3449,7 @@
a.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
} else if (a.info.screenOrientation == SCREEN_ORIENTATION_UNSPECIFIED
&& (a.info.flags & FLAG_IMMERSIVE) == 0) {
- a.info.resizeMode = RESIZE_MODE_CROP_WINDOWS;
+ a.info.resizeMode = RESIZE_MODE_FORCE_RESIZEABLE;
}
if (sa.getBoolean(R.styleable.AndroidManifestActivity_alwaysFocusable, false)) {
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index 766868d..8724a96 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -847,6 +847,9 @@
* to make forward progress from the partial results and avoid waiting for the completed
* result.</p>
*
+ * <p>For a particular request, {@link #onCaptureProgressed} may happen before or after
+ * {@link #onCaptureStarted}.</p>
+ *
* <p>Each request will generate at least {@code 1} partial results, and at most
* {@link CameraCharacteristics#REQUEST_PARTIAL_RESULT_COUNT} partial results.</p>
*
diff --git a/core/java/android/net/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java
index 3f36d65..b6fe68a 100644
--- a/core/java/android/net/NetworkScoreManager.java
+++ b/core/java/android/net/NetworkScoreManager.java
@@ -105,7 +105,8 @@
/**
* Broadcast action: the active scorer has been changed. Scorer apps may listen to this to
* perform initialization once selected as the active scorer, or clean up unneeded resources
- * if another scorer has been selected. Note that it is unnecessary to clear existing scores as
+ * if another scorer has been selected. This is an explicit broadcast only sent to the
+ * previous scorer and new scorer. Note that it is unnecessary to clear existing scores as
* this is handled by the system.
*
* <p>The new scorer will be specified in {@link #EXTRA_NEW_SCORER}.
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index e841dfe..59bf293 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -176,35 +176,37 @@
return DIR_VENDOR_ROOT;
}
- /** {@hide} */
- @Deprecated
- public static File getSystemSecureDirectory() {
- return getDataSystemDirectory();
- }
-
- /** {@hide} */
- @Deprecated
- public static File getSecureDataDirectory() {
- return getDataDirectory();
- }
-
/**
- * Return the system directory for a user. This is for use by system services to store
- * files relating to the user. This directory will be automatically deleted when the user
- * is removed.
+ * Return the system directory for a user. This is for use by system
+ * services to store files relating to the user. This directory will be
+ * automatically deleted when the user is removed.
*
+ * @deprecated This directory is valid and still exists, but callers should
+ * <em>strongly</em> consider switching to
+ * {@link #getDataSystemCeDirectory(int)} which is protected
+ * with user credentials or
+ * {@link #getDataSystemDeDirectory(int)} which supports fast
+ * user wipe.
* @hide
*/
+ @Deprecated
public static File getUserSystemDirectory(int userId) {
return new File(new File(getDataSystemDirectory(), "users"), Integer.toString(userId));
}
/**
- * Returns the config directory for a user. This is for use by system services to store files
- * relating to the user which should be readable by any app running as that user.
+ * Returns the config directory for a user. This is for use by system
+ * services to store files relating to the user which should be readable by
+ * any app running as that user.
*
+ * @deprecated This directory is valid and still exists, but callers should
+ * <em>strongly</em> consider switching to
+ * {@link #getDataMiscCeDirectory(int)} which is protected with
+ * user credentials or {@link #getDataMiscDeDirectory(int)}
+ * which supports fast user wipe.
* @hide
*/
+ @Deprecated
public static File getUserConfigDirectory(int userId) {
return new File(new File(new File(
getDataDirectory(), "misc"), "user"), Integer.toString(userId));
@@ -232,13 +234,28 @@
}
/** {@hide} */
- public static File getDataSystemCredentialEncryptedDirectory() {
- return new File(getDataDirectory(), "system_ce");
+ public static File getDataSystemCeDirectory(int userId) {
+ return buildPath(getDataDirectory(), "system_ce", String.valueOf(userId));
}
/** {@hide} */
- public static File getDataSystemCredentialEncryptedDirectory(int userId) {
- return new File(getDataSystemCredentialEncryptedDirectory(), String.valueOf(userId));
+ public static File getDataSystemDeDirectory(int userId) {
+ return buildPath(getDataDirectory(), "system_de", String.valueOf(userId));
+ }
+
+ /** {@hide} */
+ public static File getDataMiscDirectory() {
+ return new File(getDataDirectory(), "misc");
+ }
+
+ /** {@hide} */
+ public static File getDataMiscCeDirectory(int userId) {
+ return buildPath(getDataDirectory(), "misc_ce", String.valueOf(userId));
+ }
+
+ /** {@hide} */
+ public static File getDataMiscDeDirectory(int userId) {
+ return buildPath(getDataDirectory(), "misc_de", String.valueOf(userId));
}
/** {@hide} */
@@ -252,57 +269,37 @@
}
/** {@hide} */
- @Deprecated
- public static File getDataUserDirectory(String volumeUuid) {
- return getDataUserCredentialEncryptedDirectory(volumeUuid);
- }
-
- /** {@hide} */
- @Deprecated
- public static File getDataUserDirectory(String volumeUuid, int userId) {
- return getDataUserCredentialEncryptedDirectory(volumeUuid, userId);
- }
-
- /** {@hide} */
- @Deprecated
- public static File getDataUserPackageDirectory(String volumeUuid, int userId,
- String packageName) {
- return getDataUserCredentialEncryptedPackageDirectory(volumeUuid, userId, packageName);
- }
-
- /** {@hide} */
- public static File getDataUserCredentialEncryptedDirectory(String volumeUuid) {
+ public static File getDataUserCeDirectory(String volumeUuid) {
return new File(getDataDirectory(volumeUuid), "user");
}
/** {@hide} */
- public static File getDataUserCredentialEncryptedDirectory(String volumeUuid, int userId) {
- return new File(getDataUserCredentialEncryptedDirectory(volumeUuid),
- String.valueOf(userId));
+ public static File getDataUserCeDirectory(String volumeUuid, int userId) {
+ return new File(getDataUserCeDirectory(volumeUuid), String.valueOf(userId));
}
/** {@hide} */
- public static File getDataUserCredentialEncryptedPackageDirectory(String volumeUuid, int userId,
+ public static File getDataUserCePackageDirectory(String volumeUuid, int userId,
String packageName) {
// TODO: keep consistent with installd
- return new File(getDataUserCredentialEncryptedDirectory(volumeUuid, userId), packageName);
+ return new File(getDataUserCeDirectory(volumeUuid, userId), packageName);
}
/** {@hide} */
- public static File getDataUserDeviceEncryptedDirectory(String volumeUuid) {
+ public static File getDataUserDeDirectory(String volumeUuid) {
return new File(getDataDirectory(volumeUuid), "user_de");
}
/** {@hide} */
- public static File getDataUserDeviceEncryptedDirectory(String volumeUuid, int userId) {
- return new File(getDataUserDeviceEncryptedDirectory(volumeUuid), String.valueOf(userId));
+ public static File getDataUserDeDirectory(String volumeUuid, int userId) {
+ return new File(getDataUserDeDirectory(volumeUuid), String.valueOf(userId));
}
/** {@hide} */
- public static File getDataUserDeviceEncryptedPackageDirectory(String volumeUuid, int userId,
+ public static File getDataUserDePackageDirectory(String volumeUuid, int userId,
String packageName) {
// TODO: keep consistent with installd
- return new File(getDataUserDeviceEncryptedDirectory(volumeUuid, userId), packageName);
+ return new File(getDataUserDeDirectory(volumeUuid, userId), packageName);
}
/**
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index b51d2dfb..9984755 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -823,6 +823,16 @@
}
/**
+ * Returns whether the given uid belongs to an application.
+ * @param uid A kernel uid.
+ * @return Whether the uid corresponds to an application sandbox running in
+ * a specific user.
+ */
+ public static boolean isApplicationUid(int uid) {
+ return UserHandle.isApp(uid);
+ }
+
+ /**
* Returns whether the current process is in an isolated sandbox.
* @hide
*/
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index 24666fe..b3f4453 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -130,6 +130,15 @@
}
/**
+ * Returns the user for a given uid.
+ * @param uid A uid for an application running in a particular user.
+ * @return A {@link UserHandle} for that user.
+ */
+ public static UserHandle getUserHandleForUid(int uid) {
+ return of(getUserId(uid));
+ }
+
+ /**
* Returns the user id for a given uid.
* @hide
*/
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index dc0e249..69d564f 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1141,6 +1141,8 @@
UserInfo user = null;
try {
user = mService.createUser(name, flags);
+ // TODO: Keep this in sync with
+ // UserManagerService.LocalService.createUserEvenWhenDisallowed
if (user != null && !user.isAdmin()) {
mService.setUserRestriction(DISALLOW_SMS, true, user.id);
mService.setUserRestriction(DISALLOW_OUTGOING_CALLS, true, user.id);
diff --git a/core/java/android/os/UserManagerInternal.java b/core/java/android/os/UserManagerInternal.java
index 58a0269..d2ece8b 100644
--- a/core/java/android/os/UserManagerInternal.java
+++ b/core/java/android/os/UserManagerInternal.java
@@ -17,6 +17,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.pm.UserInfo;
import android.graphics.Bitmap;
/**
@@ -106,4 +107,12 @@
* non-ephemeral users left.
*/
public abstract void removeAllUsers();
+
+ /**
+ * Same as UserManager.createUser(), but bypasses the check for DISALLOW_ADD_USER.
+ *
+ * <p>Called by the {@link com.android.server.devicepolicy.DevicePolicyManagerService} when
+ * createAndManageUser is called by the device owner.
+ */
+ public abstract UserInfo createUserEvenWhenDisallowed(String name, int flags);
}
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 8468040..2ca7589 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -1201,7 +1201,7 @@
final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
sourceDocumentUri.getAuthority());
try {
- return moveDocument(client, sourceParentDocumentUri, sourceDocumentUri,
+ return moveDocument(client, sourceDocumentUri, sourceParentDocumentUri,
targetParentDocumentUri);
} catch (Exception e) {
Log.w(TAG, "Failed to move document", e);
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 7830142..0658bd4 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5139,14 +5139,6 @@
public static final String TTS_DEFAULT_SYNTH = "tts_default_synth";
/**
- * Whether text-to-speech higher speech rate is enabled.
- * 0 = disabled.
- * 1 = enabled.
- * @hide
- */
- public static final String TTS_DEFAULT_HIGHER_SPEECH_RATE_ENABLED =
- "tts_default_higher_speech_rate_enabled";
- /**
* Default text-to-speech language.
*
* @deprecated this setting is no longer in use, as of the Ice Cream
@@ -5907,13 +5899,6 @@
"camera_double_tap_power_gesture_disabled";
/**
- * Name of the package used as WebView provider (if unset the provider is instead determined
- * by the system).
- * @hide
- */
- public static final String WEBVIEW_PROVIDER = "webview_provider";
-
- /**
* This are the settings to be backed up.
*
* NOTE: Settings are backed up and restored in the order they appear
@@ -5958,7 +5943,6 @@
ACCESSIBILITY_CAPTIONING_WINDOW_COLOR,
TTS_USE_DEFAULTS,
TTS_DEFAULT_RATE,
- TTS_DEFAULT_HIGHER_SPEECH_RATE_ENABLED,
TTS_DEFAULT_PITCH,
TTS_DEFAULT_SYNTH,
TTS_DEFAULT_LANG,
@@ -6940,6 +6924,13 @@
public static final String WEBVIEW_DATA_REDUCTION_PROXY_KEY =
"webview_data_reduction_proxy_key";
+ /**
+ * Name of the package used as WebView provider (if unset the provider is instead determined
+ * by the system).
+ * @hide
+ */
+ public static final String WEBVIEW_PROVIDER = "webview_provider";
+
/**
* Whether Wifi display is enabled/disabled
* 0=disabled. 1=enabled.
diff --git a/core/java/android/text/BidiFormatter.java b/core/java/android/text/BidiFormatter.java
index 675803c..707c0fc 100644
--- a/core/java/android/text/BidiFormatter.java
+++ b/core/java/android/text/BidiFormatter.java
@@ -16,6 +16,7 @@
package android.text;
+import android.annotation.Nullable;
import android.view.View;
import static android.text.TextDirectionHeuristics.FIRSTSTRONG_LTR;
@@ -390,14 +391,17 @@
* @return Input string after applying the above processing. {@code null} if {@code str} is
* {@code null}.
*/
- public String unicodeWrap(String str, TextDirectionHeuristic heuristic, boolean isolate) {
+ public @Nullable String unicodeWrap(@Nullable String str, TextDirectionHeuristic heuristic,
+ boolean isolate) {
+ if (str == null) return null;
return unicodeWrap((CharSequence) str, heuristic, isolate).toString();
}
/**
* @hide
*/
- public CharSequence unicodeWrap(CharSequence str, TextDirectionHeuristic heuristic, boolean isolate) {
+ public @Nullable CharSequence unicodeWrap(@Nullable CharSequence str,
+ TextDirectionHeuristic heuristic, boolean isolate) {
if (str == null) return null;
final boolean isRtl = heuristic.isRtl(str, 0, str.length());
SpannableStringBuilder result = new SpannableStringBuilder();
diff --git a/core/java/android/text/Html.java b/core/java/android/text/Html.java
index 82f69ef..409994d 100644
--- a/core/java/android/text/Html.java
+++ b/core/java/android/text/Html.java
@@ -54,6 +54,9 @@
import java.io.IOException;
import java.io.StringReader;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -356,24 +359,48 @@
}
}
- private static String getTextStyles(Spanned text, int start, int end) {
- final StringBuilder style = new StringBuilder(" style=\"margin-top:0; margin-bottom:0;");
+ private static String getTextStyles(Spanned text, int start, int end,
+ boolean forceNoVerticalMargin, boolean includeTextAlign) {
+ String margin = null;
+ String textAlign = null;
- final AlignmentSpan[] alignmentSpans = text.getSpans(start, end, AlignmentSpan.class);
- final int len = alignmentSpans.length;
- if (len > 0) {
- final Layout.Alignment alignment = alignmentSpans[len - 1].getAlignment();
- if (alignment == Layout.Alignment.ALIGN_NORMAL) {
- style.append(" text-align:start;");
- } else if (alignment == Layout.Alignment.ALIGN_CENTER) {
- style.append(" text-align:center;");
- } else if (alignment == Layout.Alignment.ALIGN_OPPOSITE) {
- style.append(" text-align:end;");
+ if (forceNoVerticalMargin) {
+ margin = "margin-top:0; margin-bottom:0;";
+ }
+ if (includeTextAlign) {
+ final AlignmentSpan[] alignmentSpans = text.getSpans(start, end, AlignmentSpan.class);
+
+ // Only use the last AlignmentSpan with flag SPAN_PARAGRAPH
+ for (int i = alignmentSpans.length - 1; i >= 0; i--) {
+ AlignmentSpan s = alignmentSpans[i];
+ if ((text.getSpanFlags(s) & Spanned.SPAN_PARAGRAPH) == Spanned.SPAN_PARAGRAPH) {
+ final Layout.Alignment alignment = s.getAlignment();
+ if (alignment == Layout.Alignment.ALIGN_NORMAL) {
+ textAlign = "text-align:start;";
+ } else if (alignment == Layout.Alignment.ALIGN_CENTER) {
+ textAlign = "text-align:center;";
+ } else if (alignment == Layout.Alignment.ALIGN_OPPOSITE) {
+ textAlign = "text-align:end;";
+ }
+ break;
+ }
}
}
- style.append("\"");
- return style.toString();
+ if (margin == null && textAlign == null) {
+ return "";
+ }
+
+ final StringBuilder style = new StringBuilder(" style=\"");
+ if (margin != null && textAlign != null) {
+ style.append(margin).append(" ").append(textAlign);
+ } else if (margin != null) {
+ style.append(margin);
+ } else if (textAlign != null) {
+ style.append(textAlign);
+ }
+
+ return style.append("\"").toString();
}
private static void withinBlockquote(StringBuilder out, Spanned text, int start, int end,
@@ -395,46 +422,55 @@
next = end;
}
- boolean isListItem = false;
- ParagraphStyle[] paragraphStyles = text.getSpans(i, next, ParagraphStyle.class);
- for (ParagraphStyle paragraphStyle : paragraphStyles) {
- final int spanFlags = text.getSpanFlags(paragraphStyle);
- if ((spanFlags & Spanned.SPAN_PARAGRAPH) == Spanned.SPAN_PARAGRAPH
- && paragraphStyle instanceof BulletSpan) {
- isListItem = true;
- break;
+ if (next == i) {
+ if (isInList) {
+ // Current paragraph is no longer a list item; close the previously opened list
+ isInList = false;
+ out.append("</ul>\n");
}
- }
-
- if (isListItem && !isInList) {
- // Current paragraph is the first item in a list
- isInList = true;
- out.append("<ul>\n");
- }
-
- if (isInList && !isListItem) {
- // Current paragraph is no longer a list item; close the previously opened list
- isInList = false;
- out.append("</ul>\n");
- }
-
- String tagType = isListItem ? "li" : "p";
- out.append("<").append(tagType).append(getTextDirection(text, start, next))
- .append(getTextStyles(text, start, next)).append(">");
-
- if (next - i == 0) {
- out.append("<br>");
+ out.append("<br>\n");
} else {
+ boolean isListItem = false;
+ ParagraphStyle[] paragraphStyles = text.getSpans(i, next, ParagraphStyle.class);
+ for (ParagraphStyle paragraphStyle : paragraphStyles) {
+ final int spanFlags = text.getSpanFlags(paragraphStyle);
+ if ((spanFlags & Spanned.SPAN_PARAGRAPH) == Spanned.SPAN_PARAGRAPH
+ && paragraphStyle instanceof BulletSpan) {
+ isListItem = true;
+ break;
+ }
+ }
+
+ if (isListItem && !isInList) {
+ // Current paragraph is the first item in a list
+ isInList = true;
+ out.append("<ul")
+ .append(getTextStyles(text, i, next, true, false))
+ .append(">\n");
+ }
+
+ if (isInList && !isListItem) {
+ // Current paragraph is no longer a list item; close the previously opened list
+ isInList = false;
+ out.append("</ul>\n");
+ }
+
+ String tagType = isListItem ? "li" : "p";
+ out.append("<").append(tagType)
+ .append(getTextDirection(text, i, next))
+ .append(getTextStyles(text, i, next, !isListItem, true))
+ .append(">");
+
withinParagraph(out, text, i, next);
- }
- out.append("</");
- out.append(tagType);
- out.append(">\n");
+ out.append("</");
+ out.append(tagType);
+ out.append(">\n");
- if (next == end && isInList) {
- isInList = false;
- out.append("</ul>\n");
+ if (next == end && isInList) {
+ isInList = false;
+ out.append("</ul>\n");
+ }
}
next++;
@@ -654,6 +690,25 @@
private int mFlags;
private static Pattern sTextAlignPattern;
+ private static Pattern sForegroundColorPattern;
+ private static Pattern sBackgroundColorPattern;
+ private static Pattern sTextDecorationPattern;
+
+ /**
+ * Name-value mapping of HTML/CSS colors which have different values in {@link Color}.
+ */
+ private static final Map<String, Integer> sColorMap;
+
+ static {
+ sColorMap = new HashMap<>();
+ sColorMap.put("darkgray", 0xFFA9A9A9);
+ sColorMap.put("gray", 0xFF808080);
+ sColorMap.put("lightgray", 0xFFD3D3D3);
+ sColorMap.put("darkgrey", 0xFFA9A9A9);
+ sColorMap.put("grey", 0xFF808080);
+ sColorMap.put("lightgrey", 0xFFD3D3D3);
+ sColorMap.put("green", 0xFF008000);
+ }
private static Pattern getTextAlignPattern() {
if (sTextAlignPattern == null) {
@@ -662,6 +717,30 @@
return sTextAlignPattern;
}
+ private static Pattern getForegroundColorPattern() {
+ if (sForegroundColorPattern == null) {
+ sForegroundColorPattern = Pattern.compile(
+ "(?:\\s+|\\A)color\\s*:\\s*(\\S*)\\b");
+ }
+ return sForegroundColorPattern;
+ }
+
+ private static Pattern getBackgroundColorPattern() {
+ if (sBackgroundColorPattern == null) {
+ sBackgroundColorPattern = Pattern.compile(
+ "(?:\\s+|\\A)background(?:-color)?\\s*:\\s*(\\S*)\\b");
+ }
+ return sBackgroundColorPattern;
+ }
+
+ private static Pattern getTextDecorationPattern() {
+ if (sTextDecorationPattern == null) {
+ sTextDecorationPattern = Pattern.compile(
+ "(?:\\s+|\\A)text-decoration\\s*:\\s*(\\S*)\\b");
+ }
+ return sTextDecorationPattern;
+ }
+
public HtmlToSpannedConverter( String source, Html.ImageGetter imageGetter,
Html.TagHandler tagHandler, Parser parser, int flags) {
mSource = source;
@@ -715,8 +794,15 @@
// so we can safely emit the linebreaks when we handle the close tag.
} else if (tag.equalsIgnoreCase("p")) {
startBlockElement(mSpannableStringBuilder, attributes, getMarginParagraph());
+ startCssStyle(mSpannableStringBuilder, attributes);
+ } else if (tag.equalsIgnoreCase("ul")) {
+ startBlockElement(mSpannableStringBuilder, attributes, getMarginList());
+ } else if (tag.equalsIgnoreCase("li")) {
+ startLi(mSpannableStringBuilder, attributes);
} else if (tag.equalsIgnoreCase("div")) {
startBlockElement(mSpannableStringBuilder, attributes, getMarginDiv());
+ } else if (tag.equalsIgnoreCase("span")) {
+ startCssStyle(mSpannableStringBuilder, attributes);
} else if (tag.equalsIgnoreCase("strong")) {
start(mSpannableStringBuilder, new Bold());
} else if (tag.equalsIgnoreCase("b")) {
@@ -768,9 +854,16 @@
if (tag.equalsIgnoreCase("br")) {
handleBr(mSpannableStringBuilder);
} else if (tag.equalsIgnoreCase("p")) {
+ endCssStyle(mSpannableStringBuilder);
endBlockElement(mSpannableStringBuilder);
+ } else if (tag.equalsIgnoreCase("ul")) {
+ endBlockElement(mSpannableStringBuilder);
+ } else if (tag.equalsIgnoreCase("li")) {
+ endLi(mSpannableStringBuilder);
} else if (tag.equalsIgnoreCase("div")) {
endBlockElement(mSpannableStringBuilder);
+ } else if (tag.equalsIgnoreCase("span")) {
+ endCssStyle(mSpannableStringBuilder);
} else if (tag.equalsIgnoreCase("strong")) {
end(mSpannableStringBuilder, Bold.class, new StyleSpan(Typeface.BOLD));
} else if (tag.equalsIgnoreCase("b")) {
@@ -824,6 +917,14 @@
return getMargin(Html.FROM_HTML_SEPARATOR_LINE_BREAK_HEADING);
}
+ private int getMarginListItem() {
+ return getMargin(Html.FROM_HTML_SEPARATOR_LINE_BREAK_LIST_ITEM);
+ }
+
+ private int getMarginList() {
+ return getMargin(Html.FROM_HTML_SEPARATOR_LINE_BREAK_LIST);
+ }
+
private int getMarginDiv() {
return getMargin(Html.FROM_HTML_SEPARATOR_LINE_BREAK_DIV);
}
@@ -866,7 +967,7 @@
final int len = text.length();
if (margin > 0) {
appendNewlines(text, margin);
- text.setSpan(new Newline(margin), len, len, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
+ start(text, new Newline(margin));
}
String style = attributes.getValue("", "style");
@@ -875,14 +976,11 @@
if (m.find()) {
String alignment = m.group(1);
if (alignment.equalsIgnoreCase("start")) {
- text.setSpan(new Alignment(Layout.Alignment.ALIGN_NORMAL),
- len, len, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
+ start(text, new Alignment(Layout.Alignment.ALIGN_NORMAL));
} else if (alignment.equalsIgnoreCase("center")) {
- text.setSpan(new Alignment(Layout.Alignment.ALIGN_CENTER),
- len, len, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
+ start(text, new Alignment(Layout.Alignment.ALIGN_CENTER));
} else if (alignment.equalsIgnoreCase("end")) {
- text.setSpan(new Alignment(Layout.Alignment.ALIGN_OPPOSITE),
- len, len, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
+ start(text, new Alignment(Layout.Alignment.ALIGN_OPPOSITE));
}
}
}
@@ -905,6 +1003,18 @@
text.append('\n');
}
+ private void startLi(Editable text, Attributes attributes) {
+ startBlockElement(text, attributes, getMarginListItem());
+ start(text, new Bullet());
+ startCssStyle(text, attributes);
+ }
+
+ private static void endLi(Editable text) {
+ endCssStyle(text);
+ endBlockElement(text);
+ end(text, Bullet.class, new BulletSpan());
+ }
+
private void startBlockquote(Editable text, Attributes attributes) {
startBlockElement(text, attributes, getMarginBlockquote());
start(text, new Blockquote());
@@ -959,7 +1069,7 @@
private static void start(Editable text, Object mark) {
int len = text.length();
- text.setSpan(mark, len, len, Spannable.SPAN_MARK_MARK);
+ text.setSpan(mark, len, len, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
}
private static void end(Editable text, Class kind, Object repl) {
@@ -970,6 +1080,52 @@
}
}
+ private void startCssStyle(Editable text, Attributes attributes) {
+ String style = attributes.getValue("", "style");
+ if (style != null) {
+ Matcher m = getForegroundColorPattern().matcher(style);
+ if (m.find()) {
+ int c = getHtmlColor(m.group(1));
+ if (c != -1) {
+ start(text, new Foreground(c | 0xFF000000));
+ }
+ }
+
+ m = getBackgroundColorPattern().matcher(style);
+ if (m.find()) {
+ int c = getHtmlColor(m.group(1));
+ if (c != -1) {
+ start(text, new Background(c | 0xFF000000));
+ }
+ }
+
+ m = getTextDecorationPattern().matcher(style);
+ if (m.find()) {
+ String textDecoration = m.group(1);
+ if (textDecoration.equalsIgnoreCase("line-through")) {
+ start(text, new Strikethrough());
+ }
+ }
+ }
+ }
+
+ private static void endCssStyle(Editable text) {
+ Strikethrough s = getLast(text, Strikethrough.class);
+ if (s != null) {
+ setSpanFromMark(text, s, new StrikethroughSpan());
+ }
+
+ Background b = getLast(text, Background.class);
+ if (b != null) {
+ setSpanFromMark(text, b, new BackgroundColorSpan(b.mBackgroundColor));
+ }
+
+ Foreground f = getLast(text, Foreground.class);
+ if (f != null) {
+ setSpanFromMark(text, f, new ForegroundColorSpan(f.mForegroundColor));
+ }
+ }
+
private static void startImg(Editable text, Attributes attributes, Html.ImageGetter img) {
String src = attributes.getValue("", "src");
Drawable d = null;
@@ -991,58 +1147,41 @@
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
- private static void startFont(Editable text, Attributes attributes) {
+ private void startFont(Editable text, Attributes attributes) {
String color = attributes.getValue("", "color");
String face = attributes.getValue("", "face");
- int len = text.length();
- text.setSpan(new Font(color, face), len, len, Spannable.SPAN_MARK_MARK);
+ if (!TextUtils.isEmpty(color)) {
+ int c = getHtmlColor(color);
+ if (c != -1) {
+ start(text, new Foreground(c | 0xFF000000));
+ }
+ }
+
+ if (!TextUtils.isEmpty(face)) {
+ start(text, new Font(face));
+ }
}
private static void endFont(Editable text) {
- int len = text.length();
- Font f = getLast(text, Font.class);
- int where = text.getSpanStart(f);
- text.removeSpan(f);
+ Font font = getLast(text, Font.class);
+ if (font != null) {
+ setSpanFromMark(text, font, new TypefaceSpan(font.mFace));
+ }
- if (where != len) {
- if (!TextUtils.isEmpty(f.mColor)) {
- if (f.mColor.startsWith("@")) {
- Resources res = Resources.getSystem();
- String name = f.mColor.substring(1);
- int colorRes = res.getIdentifier(name, "color", "android");
- if (colorRes != 0) {
- ColorStateList colors = res.getColorStateList(colorRes, null);
- text.setSpan(new TextAppearanceSpan(null, 0, 0, colors, null),
- where, len,
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- } else {
- int c = Color.getHtmlColor(f.mColor);
- if (c != -1) {
- text.setSpan(new ForegroundColorSpan(c | 0xFF000000),
- where, len,
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- }
- }
-
- if (f.mFace != null) {
- text.setSpan(new TypefaceSpan(f.mFace), where, len,
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
+ Foreground foreground = getLast(text, Foreground.class);
+ if (foreground != null) {
+ setSpanFromMark(text, foreground,
+ new ForegroundColorSpan(foreground.mForegroundColor));
}
}
private static void startA(Editable text, Attributes attributes) {
String href = attributes.getValue("", "href");
-
- int len = text.length();
- text.setSpan(new Href(href), len, len, Spannable.SPAN_MARK_MARK);
+ start(text, new Href(href));
}
private static void endA(Editable text) {
- int len = text.length();
Href h = getLast(text, Href.class);
if (h != null) {
if (h.mHref != null) {
@@ -1051,6 +1190,17 @@
}
}
+ private int getHtmlColor(String color) {
+ if ((mFlags & Html.FROM_HTML_OPTION_USE_CSS_COLORS)
+ == Html.FROM_HTML_OPTION_USE_CSS_COLORS) {
+ Integer i = sColorMap.get(color.toLowerCase(Locale.US));
+ if (i != null) {
+ return i;
+ }
+ }
+ return Color.getHtmlColor(color);
+ }
+
public void setDocumentLocator(Locator locator) {
}
@@ -1132,13 +1282,12 @@
private static class Blockquote { }
private static class Super { }
private static class Sub { }
+ private static class Bullet { }
private static class Font {
- public String mColor;
public String mFace;
- public Font(String color, String face) {
- mColor = color;
+ public Font(String face) {
mFace = face;
}
}
@@ -1151,6 +1300,22 @@
}
}
+ private static class Foreground {
+ private int mForegroundColor;
+
+ public Foreground(int foregroundColor) {
+ mForegroundColor = foregroundColor;
+ }
+ }
+
+ private static class Background {
+ private int mBackgroundColor;
+
+ public Background(int backgroundColor) {
+ mBackgroundColor = backgroundColor;
+ }
+ }
+
private static class Heading {
private int mLevel;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 2612ab2..bba5a17 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -6874,6 +6874,15 @@
* @param info The info whose drawing order should be populated
*/
private void populateAccessibilityNodeInfoDrawingOrderInParent(AccessibilityNodeInfo info) {
+ /*
+ * If the view's bounds haven't been set yet, layout has not completed. In that situation,
+ * drawing order may not be well-defined, and some Views with custom drawing order may
+ * not be initialized sufficiently to respond properly getChildDrawingOrder.
+ */
+ if ((mPrivateFlags & PFLAG_HAS_BOUNDS) == 0) {
+ info.setDrawingOrder(0);
+ return;
+ }
int drawingOrderInParent = 1;
// Iterate up the hierarchy if parents are not important for a11y
View viewAtDrawingLevel = this;
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 947906b..609c471 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -18,6 +18,7 @@
import android.annotation.IntDef;
import android.annotation.SystemApi;
+import android.app.ActivityManager.StackId;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.CompatibilityInfo;
@@ -388,6 +389,12 @@
* Check whether the window is currently dimming.
*/
public boolean isDimming();
+
+ /**
+ * @return the stack id this windows belongs to, or {@link StackId#INVALID_STACK_ID} if
+ * not attached to any stack.
+ */
+ int getStackId();
}
/**
@@ -465,6 +472,11 @@
* @return The content insets of the docked divider window.
*/
int getDockedDividerInsetsLw();
+
+ /**
+ * Retrieves the {@param outBounds} from the stack with id {@param stackId}.
+ */
+ void getStackBounds(int stackId, Rect outBounds);
}
public interface PointerEventListener {
diff --git a/core/java/android/webkit/WebViewProviderInfo.java b/core/java/android/webkit/WebViewProviderInfo.java
index 3f50fe2..94e8b70 100644
--- a/core/java/android/webkit/WebViewProviderInfo.java
+++ b/core/java/android/webkit/WebViewProviderInfo.java
@@ -97,22 +97,12 @@
*/
public boolean isEnabled() {
try {
- PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
- int enabled_state = pm.getApplicationEnabledSetting(packageName);
- switch (enabled_state) {
- case PackageManager.COMPONENT_ENABLED_STATE_ENABLED:
- return true;
- case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT:
- ApplicationInfo applicationInfo = getPackageInfo().applicationInfo;
- return applicationInfo.enabled;
- default:
- return false;
- }
+ // Explicitly fetch up-to-date package info here since the enabled-state of the package
+ // might have changed since we last fetched its package info.
+ updatePackageInfo();
+ return getPackageInfo().applicationInfo.enabled;
} catch (WebViewPackageNotFoundException e) {
return false;
- } catch (IllegalArgumentException e) {
- // Thrown by PackageManager.getApplicationEnabledSetting if the package does not exist
- return false;
}
}
@@ -124,14 +114,18 @@
return availableByDefault;
}
+ private void updatePackageInfo() {
+ try {
+ PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
+ packageInfo = pm.getPackageInfo(packageName, PACKAGE_FLAGS);
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new WebViewPackageNotFoundException(e);
+ }
+ }
+
public PackageInfo getPackageInfo() {
if (packageInfo == null) {
- try {
- PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
- packageInfo = pm.getPackageInfo(packageName, PACKAGE_FLAGS);
- } catch (PackageManager.NameNotFoundException e) {
- throw new WebViewPackageNotFoundException(e);
- }
+ updatePackageInfo();
}
return packageInfo;
}
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index b0fb93b..3402989 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -45,6 +45,7 @@
import android.os.ResultReceiver;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.storage.StorageManager;
import android.provider.DocumentsContract;
import android.service.chooser.ChooserTarget;
import android.service.chooser.ChooserTargetService;
@@ -232,7 +233,7 @@
// the case where we don't have access to credential encrypted storage we just won't
// have our pinned target info.
final File prefsFile = new File(new File(
- Environment.getDataUserCredentialEncryptedPackageDirectory(null,
+ Environment.getDataUserCePackageDirectory(StorageManager.UUID_PRIVATE_INTERNAL,
context.getUserId(), context.getPackageName()),
"shared_prefs"),
PINNED_SHARED_PREFS_NAME + ".xml");
diff --git a/core/java/com/android/internal/app/LocaleStore.java b/core/java/com/android/internal/app/LocaleStore.java
index 210adce..465c4d8 100644
--- a/core/java/com/android/internal/app/LocaleStore.java
+++ b/core/java/com/android/internal/app/LocaleStore.java
@@ -104,6 +104,9 @@
}
private boolean isSuggestionOfType(int suggestionMask) {
+ if (!mIsTranslated) { // Never suggest an untranslated locale
+ return false;
+ }
return (mSuggestionFlags & suggestionMask) == suggestionMask;
}
@@ -207,6 +210,27 @@
}
}
+ /*
+ * Show all the languages supported for a country in the suggested list.
+ * This is also handy for devices without SIM (tablets).
+ */
+ private static void addSuggestedLocalesForRegion(Locale locale) {
+ if (locale == null) {
+ return;
+ }
+ final String country = locale.getCountry();
+ if (country.isEmpty()) {
+ return;
+ }
+
+ for (LocaleInfo li : sLocaleCache.values()) {
+ if (country.equals(li.getLocale().getCountry())) {
+ // We don't need to differentiate between manual and SIM suggestions
+ li.mSuggestionFlags |= LocaleInfo.SUGGESTION_TYPE_SIM;
+ }
+ }
+ }
+
public static void fillCache(Context context) {
if (sFullyInitialized) {
return;
@@ -256,6 +280,8 @@
li.setTranslated(localizedLocales.contains(li.getLangScriptKey()));
}
+ addSuggestedLocalesForRegion(Locale.getDefault());
+
sFullyInitialized = true;
}
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 88af920..f1c79fa 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -1718,8 +1718,13 @@
private void loadBackgroundDrawablesIfNeeded() {
if (mResizingBackgroundDrawable == null) {
- mResizingBackgroundDrawable = getResizingBackgroundDrawable(
+ mResizingBackgroundDrawable = getResizingBackgroundDrawable(getContext(),
mWindow.mBackgroundResource, mWindow.mBackgroundFallbackResource);
+ if (mResizingBackgroundDrawable == null) {
+ // We shouldn't really get here as the background fallback should be always
+ // available since it is defaulted by the system.
+ Log.w(mLogTag, "Failed to find background drawable for PhoneWindow=" + mWindow);
+ }
}
if (mCaptionBackgroundDrawable == null) {
mCaptionBackgroundDrawable = getContext().getDrawable(
@@ -1817,9 +1822,8 @@
* Returns the color used to fill areas the app has not rendered content to yet when the
* user is resizing the window of an activity in multi-window mode.
*/
- private Drawable getResizingBackgroundDrawable(int backgroundRes, int backgroundFallbackRes) {
- final Context context = getContext();
-
+ public static Drawable getResizingBackgroundDrawable(Context context, int backgroundRes,
+ int backgroundFallbackRes) {
if (backgroundRes != 0) {
final Drawable drawable = context.getDrawable(backgroundRes);
if (drawable != null) {
@@ -1833,10 +1837,6 @@
return fallbackDrawable;
}
}
-
- // We shouldn't really get here as the background fallback should be always available since
- // it is defaulted by the system.
- Log.w(mLogTag, "Failed to find background drawable for PhoneWindow=" + mWindow);
return null;
}
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 6e374e2..08d4fba 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -17,6 +17,7 @@
package com.android.internal.statusbar;
import android.content.ComponentName;
+import android.graphics.Rect;
import android.os.Bundle;
import android.service.notification.StatusBarNotification;
@@ -31,7 +32,23 @@
void animateExpandNotificationsPanel();
void animateExpandSettingsPanel(String subPanel);
void animateCollapsePanels();
- void setSystemUiVisibility(int vis, int mask);
+
+ /**
+ * Notifies the status bar of a System UI visibility flag change.
+ *
+ * @param vis the visibility flags except SYSTEM_UI_FLAG_LIGHT_STATUS_BAR which will be reported
+ * separately in fullscreenStackVis and dockedStackVis
+ * @param fullscreenStackVis the flags which only apply in the region of the fullscreen stack,
+ * which is currently only SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
+ * @param dockedStackVis the flags that only apply in the region of the docked stack, which is
+ * currently only SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
+ * @param mask which flags to change
+ * @param fullscreenBounds the current bounds of the fullscreen stack, in screen coordinates
+ * @param dockedBounds the current bounds of the docked stack, in screen coordinates
+ */
+ void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis, int mask,
+ in Rect fullscreenBounds, in Rect dockedBounds);
+
void topAppWindowChanged(boolean menuVisible);
void setImeWindowStatus(in IBinder token, int vis, int backDisposition,
boolean showImeSwitcher);
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index bec18ec..8acf5d3 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -17,6 +17,7 @@
package com.android.internal.statusbar;
import android.content.ComponentName;
+import android.graphics.Rect;
import android.os.Bundle;
import android.service.notification.StatusBarNotification;
@@ -37,7 +38,6 @@
void setIcon(String slot, String iconPackage, int iconId, int iconLevel, String contentDescription);
void setIconVisibility(String slot, boolean visible);
void removeIcon(String slot);
- void topAppWindowChanged(boolean menuVisible);
void setImeWindowStatus(in IBinder token, int vis, int backDisposition,
boolean showImeSwitcher);
void expandSettingsPanel(String subPanel);
@@ -47,7 +47,8 @@
// You need the STATUS_BAR_SERVICE permission
void registerStatusBar(IStatusBar callbacks, out List<String> iconSlots,
out List<StatusBarIcon> iconList,
- out int[] switches, out List<IBinder> binders);
+ out int[] switches, out List<IBinder> binders, out Rect fullscreenStackBounds,
+ out Rect dockedStackBounds);
void onPanelRevealed(boolean clearNotificationEffects, int numItems);
void onPanelHidden();
// Mark current notifications as "seen" and stop ringing, vibrating, blinking.
diff --git a/core/java/com/android/internal/util/LineBreakBufferedWriter.java b/core/java/com/android/internal/util/LineBreakBufferedWriter.java
index f831e7a..552a93f 100644
--- a/core/java/com/android/internal/util/LineBreakBufferedWriter.java
+++ b/core/java/com/android/internal/util/LineBreakBufferedWriter.java
@@ -96,7 +96,7 @@
@Override
public void write(int c) {
- if (bufferIndex < bufferSize) {
+ if (bufferIndex < buffer.length) {
buffer[bufferIndex] = (char)c;
bufferIndex++;
if ((char)c == '\n') {
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index e239852..cbc735f 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -283,6 +283,15 @@
getTrustManager().reportUnlockAttempt(true /* authenticated */, userId);
}
+ public int getCurrentFailedPasswordAttempts(int userId) {
+ return getDevicePolicyManager().getCurrentFailedPasswordAttempts(userId);
+ }
+
+ public int getMaximumFailedPasswordsForWipe(int userId) {
+ return getDevicePolicyManager().getMaximumFailedPasswordsForWipe(
+ null /* componentName */, userId);
+ }
+
/**
* Check to see if a pattern matches the saved pattern.
* If pattern matches, return an opaque attestation that the challenge
diff --git a/core/java/com/android/server/backup/AccountSyncSettingsBackupHelper.java b/core/java/com/android/server/backup/AccountSyncSettingsBackupHelper.java
index 0449340..1b40492 100644
--- a/core/java/com/android/server/backup/AccountSyncSettingsBackupHelper.java
+++ b/core/java/com/android/server/backup/AccountSyncSettingsBackupHelper.java
@@ -16,10 +16,6 @@
package com.android.server.backup;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.backup.BackupDataInputStream;
@@ -28,14 +24,21 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.SyncAdapterType;
+import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.util.Log;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
+import java.io.File;
import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.MessageDigest;
@@ -73,6 +76,8 @@
private static final String KEY_AUTHORITY_NAME = "name";
private static final String KEY_AUTHORITY_SYNC_STATE = "syncState";
private static final String KEY_AUTHORITY_SYNC_ENABLED = "syncEnabled";
+ private static final String STASH_FILE = Environment.getDataDirectory()
+ + "/backup/unadded_account_syncsettings.json";
private Context mContext;
private AccountManager mAccountManager;
@@ -256,41 +261,99 @@
}
try {
- HashSet<Account> currentAccounts = getAccountsHashSet();
- for (int i = 0; i < accountJSONArray.length(); i++) {
- JSONObject accountJSON = (JSONObject) accountJSONArray.get(i);
- String accountName = accountJSON.getString(KEY_ACCOUNT_NAME);
- String accountType = accountJSON.getString(KEY_ACCOUNT_TYPE);
-
- Account account = new Account(accountName, accountType);
-
- // Check if the account already exists. Accounts that don't exist on the device
- // yet won't be restored.
- if (currentAccounts.contains(account)) {
- restoreExistingAccountSyncSettingsFromJSON(accountJSON);
- } else {
- // TODO:
- // Stash the data to a file that the SyncManager can read from to restore
- // settings at a later date.
- }
- }
+ restoreFromJsonArray(accountJSONArray);
} finally {
// Set the master sync preference to the value from the backup set.
ContentResolver.setMasterSyncAutomatically(masterSyncEnabled);
}
-
Log.i(TAG, "Restore successful.");
} catch (IOException | JSONException e) {
Log.e(TAG, "Couldn't restore account sync settings\n" + e);
}
}
+ private void restoreFromJsonArray(JSONArray accountJSONArray)
+ throws JSONException {
+ HashSet<Account> currentAccounts = getAccounts();
+ JSONArray unaddedAccountsJSONArray = new JSONArray();
+ for (int i = 0; i < accountJSONArray.length(); i++) {
+ JSONObject accountJSON = (JSONObject) accountJSONArray.get(i);
+ String accountName = accountJSON.getString(KEY_ACCOUNT_NAME);
+ String accountType = accountJSON.getString(KEY_ACCOUNT_TYPE);
+
+ Account account = null;
+ try {
+ account = new Account(accountName, accountType);
+ } catch (IllegalArgumentException iae) {
+ continue;
+ }
+
+ // Check if the account already exists. Accounts that don't exist on the device
+ // yet won't be restored.
+ if (currentAccounts.contains(account)) {
+ if (DEBUG) Log.i(TAG, "Restoring Sync Settings for" + accountName);
+ restoreExistingAccountSyncSettingsFromJSON(accountJSON);
+ } else {
+ unaddedAccountsJSONArray.put(accountJSON);
+ }
+ }
+
+ if (unaddedAccountsJSONArray.length() > 0) {
+ try (FileOutputStream fOutput = new FileOutputStream(STASH_FILE)) {
+ String jsonString = unaddedAccountsJSONArray.toString();
+ DataOutputStream out = new DataOutputStream(fOutput);
+ out.writeUTF(jsonString);
+ } catch (IOException ioe) {
+ // Error in writing to stash file
+ Log.e(TAG, "unable to write the sync settings to the stash file", ioe);
+ }
+ } else {
+ File stashFile = new File(STASH_FILE);
+ if (stashFile.exists()) stashFile.delete();
+ }
+ }
+
+ /**
+ * Restore SyncSettings for all existing accounts from a stashed backup-set
+ */
+ private void accountAddedInternal() {
+ String jsonString;
+
+ try (FileInputStream fIn = new FileInputStream(new File(STASH_FILE))) {
+ DataInputStream in = new DataInputStream(fIn);
+ jsonString = in.readUTF();
+ } catch (FileNotFoundException fnfe) {
+ // This is expected to happen when there is no accounts info stashed
+ if (DEBUG) Log.d(TAG, "unable to find the stash file", fnfe);
+ return;
+ } catch (IOException ioe) {
+ if (DEBUG) Log.d(TAG, "could not read sync settings from stash file", ioe);
+ return;
+ }
+
+ try {
+ JSONArray unaddedAccountsJSONArray = new JSONArray(jsonString);
+ restoreFromJsonArray(unaddedAccountsJSONArray);
+ } catch (JSONException jse) {
+ // Malformed jsonString
+ Log.e(TAG, "there was an error with the stashed sync settings", jse);
+ }
+ }
+
+ /**
+ * Restore SyncSettings for all existing accounts from a stashed backup-set
+ */
+ public static void accountAdded(Context context) {
+ AccountSyncSettingsBackupHelper helper = new AccountSyncSettingsBackupHelper(context);
+ helper.accountAddedInternal();
+ }
+
/**
* Helper method - fetch accounts and return them as a HashSet.
*
* @return Accounts in a HashSet.
*/
- private HashSet<Account> getAccountsHashSet() {
+ private HashSet<Account> getAccounts() {
Account[] accounts = mAccountManager.getAccounts();
HashSet<Account> accountHashSet = new HashSet<Account>();
for (Account account : accounts) {
@@ -359,4 +422,4 @@
public void writeNewStateDescription(ParcelFileDescriptor newState) {
}
-}
+}
\ No newline at end of file
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index dd0e456..ac77007 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -322,7 +322,11 @@
void NotifyHandler::handleMessage(const Message& message) {
JNIEnv* env = getenv(mVm);
- jobject target = env->NewLocalRef(mObserver->getObserverReference());
+ ObserverProxy* observer = mObserver.get();
+ LOG_ALWAYS_FATAL_IF(observer == nullptr, "received message with no observer configured");
+ LOG_ALWAYS_FATAL_IF(mBuffer == nullptr, "received message with no data to report");
+
+ jobject target = env->NewLocalRef(observer->getObserverReference());
if (target != nullptr) {
jlongArray javaBuffer = get_metrics_buffer(env, target);
diff --git a/core/res/res/layout/floating_popup_container.xml b/core/res/res/layout/floating_popup_container.xml
index dd161e3..ca03737 100644
--- a/core/res/res/layout/floating_popup_container.xml
+++ b/core/res/res/layout/floating_popup_container.xml
@@ -19,8 +19,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="0dp"
- android:layout_margin="20dp"
- android:elevation="2dp"
+ android:layout_margin="@android:dimen/text_edit_floating_toolbar_margin"
+ android:elevation="@android:dimen/text_edit_floating_toolbar_elevation"
android:focusable="true"
android:focusableInTouchMode="true"
android:background="?attr/floatingToolbarPopupBackgroundDrawable"/>
diff --git a/core/res/res/layout/text_edit_suggestion_container.xml b/core/res/res/layout/text_edit_suggestion_container.xml
index 17e93d0..b2589da 100644
--- a/core/res/res/layout/text_edit_suggestion_container.xml
+++ b/core/res/res/layout/text_edit_suggestion_container.xml
@@ -22,8 +22,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
- android:elevation="2dp"
- android:layout_margin="20dp"
+ android:elevation="@android:dimen/text_edit_floating_toolbar_elevation"
+ android:layout_margin="@android:dimen/text_edit_floating_toolbar_margin"
android:background="@drawable/text_edit_suggestions_window"
android:dropDownSelector="@drawable/list_selector_background"
android:divider="@null">
diff --git a/core/res/res/layout/text_edit_suggestion_container_material.xml b/core/res/res/layout/text_edit_suggestion_container_material.xml
index 7826803..20a80489 100644
--- a/core/res/res/layout/text_edit_suggestion_container_material.xml
+++ b/core/res/res/layout/text_edit_suggestion_container_material.xml
@@ -24,8 +24,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?android:attr/floatingToolbarPopupBackgroundDrawable"
- android:elevation="2dp"
- android:layout_margin="20dp"
+ android:elevation="@android:dimen/text_edit_floating_toolbar_elevation"
+ android:layout_margin="@android:dimen/text_edit_floating_toolbar_margin"
android:orientation="vertical"
android:divider="?android:attr/listDivider"
android:showDividers="middle">
diff --git a/core/res/res/values-watch/themes_device_defaults.xml b/core/res/res/values-watch/themes_device_defaults.xml
index 63df5be..61753b1 100644
--- a/core/res/res/values-watch/themes_device_defaults.xml
+++ b/core/res/res/values-watch/themes_device_defaults.xml
@@ -25,6 +25,7 @@
<style name="Theme.DeviceDefault.Light.Dialog" parent="Theme.Micro.Dialog" />
<style name="Theme.DeviceDefault.Light.DialogWhenLarge" parent="Theme.Micro.Dialog" />
<style name="Theme.DeviceDefault.Light.Dialog.Alert" parent="Theme.Micro.Dialog.Alert" />
+ <style name="Theme.DeviceDefault.Settings" parent="Theme.Micro" />
<style name="Theme.DeviceDefault.Wallpaper" parent="Theme.Micro" />
</resources>
diff --git a/core/res/res/values/dimens_material.xml b/core/res/res/values/dimens_material.xml
index 96a81d1..2fe4f66 100644
--- a/core/res/res/values/dimens_material.xml
+++ b/core/res/res/values/dimens_material.xml
@@ -82,6 +82,9 @@
<dimen name="text_size_medium_material">18sp</dimen>
<dimen name="text_size_small_material">14sp</dimen>
+ <dimen name="text_edit_floating_toolbar_elevation">2dp</dimen>
+ <dimen name="text_edit_floating_toolbar_margin">20dp</dimen>
+
<dimen name="floating_window_z">16dp</dimen>
<dimen name="floating_window_margin_left">16dp</dimen>
<dimen name="floating_window_margin_top">8dp</dimen>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 4e10d39..daa8202 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4204,11 +4204,16 @@
<string name="new_sms_notification_content">Open SMS app to view</string>
<!-- Notification title shown when user profile is credential encrypted and requires the user to unlock before some features are usable [CHAR LIMIT=30] -->
- <string name="user_encrypted_title">Some functions might not be available</string>
+ <string name="user_encrypted_title">Some functionality may be limited</string>
<!-- Notification message shown when user profile is credential encrypted and requires the user to unlock before some features are usable [CHAR LIMIT=30] -->
- <string name="user_encrypted_message">Touch to continue</string>
+ <string name="user_encrypted_message">Tap to unlock</string>
<!-- Notification detail shown when user profile is credential encrypted and requires the user to unlock before some features are usable [CHAR LIMIT=30] -->
- <string name="user_encrypted_detail">User profile locked</string>
+ <string name="user_encrypted_detail">User data locked</string>
+
+ <!-- Notification detail shown when work profile is credential encrypted and requires the user to unlock before some features are usable [CHAR LIMIT=30] -->
+ <string name="profile_encrypted_detail">Work profile locked</string>
+ <!-- Notification message shown when work profile is credential encrypted and requires the user to unlock before some features are usable [CHAR LIMIT=30] -->
+ <string name="profile_encrypted_message">Tap to unlock work profile</string>
<!-- Title of notification shown after a MTP device is connected to Android. -->
<string name="usb_mtp_launch_notification_title">Connected to <xliff:g id="product_name">%1$s</xliff:g></string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index f75f023..8df6c2e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2522,6 +2522,8 @@
<java-symbol type="string" name="user_encrypted_title" />
<java-symbol type="string" name="user_encrypted_message" />
<java-symbol type="string" name="user_encrypted_detail" />
+ <java-symbol type="string" name="profile_encrypted_detail" />
+ <java-symbol type="string" name="profile_encrypted_message" />
<java-symbol type="drawable" name="ic_user_secure" />
<java-symbol type="string" name="usb_mtp_launch_notification_title" />
diff --git a/core/tests/coretests/src/com/android/internal/util/LineBreakBufferedWriterTest.java b/core/tests/coretests/src/com/android/internal/util/LineBreakBufferedWriterTest.java
index 49ae104..4845c4e 100644
--- a/core/tests/coretests/src/com/android/internal/util/LineBreakBufferedWriterTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/LineBreakBufferedWriterTest.java
@@ -180,6 +180,22 @@
assertOutput("aaaaaaaaaabbbbbc\nd", "ddddddddd");
}
+ public void testMoreThenInitialCapacitySimpleWrites() {
+ // This check is different from testMoreThanBufferSizeChar. The initial capacity is lower
+ // than the maximum buffer size here.
+ final LineBreakBufferedWriter lw = new LineBreakBufferedWriter(mWriter, 1024, 3);
+
+ for(int i = 0; i < 10; i++) {
+ lw.print('$');
+ }
+ for(int i = 0; i < 10; i++) {
+ lw.print('%');
+ }
+ lw.flush();
+
+ assertOutput("$$$$$$$$$$%%%%%%%%%%");
+ }
+
private void assertOutput(String... golden) {
List<String> goldList = createTestGolden(golden);
assertEquals(goldList, mWriter.getStrings());
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 1d9fe35..3277c36 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -2236,7 +2236,7 @@
// See if any of the regions is better than the other
const int region_comparison = localeDataCompareRegions(
country, o.country,
- language, localeScript, requested->country);
+ language, requested->localeScript, requested->country);
if (region_comparison != 0) {
return (region_comparison > 0);
}
@@ -2526,17 +2526,34 @@
// For backward compatibility and supporting private-use locales, we
// fall back to old behavior if we couldn't determine the script for
- // either of the desired locale or the provided locale.
- if (localeScript[0] == '\0' || localeScript[1] == '\0') {
+ // either of the desired locale or the provided locale. But if we could determine
+ // the scripts, they should be the same for the locales to match.
+ bool countriesMustMatch = false;
+ char computed_script[4];
+ const char* script;
+ if (settings.localeScript[0] == '\0') { // could not determine the request's script
+ countriesMustMatch = true;
+ } else {
+ if (localeScript[0] == '\0') { // script was not provided, so we try to compute it
+ localeDataComputeScript(computed_script, language, country);
+ if (computed_script[0] == '\0') { // we could not compute the script
+ countriesMustMatch = true;
+ } else {
+ script = computed_script;
+ }
+ } else { // script was provided, so just use it
+ script = localeScript;
+ }
+ }
+
+ if (countriesMustMatch) {
if (country[0] != '\0'
&& (country[0] != settings.country[0]
|| country[1] != settings.country[1])) {
return false;
}
} else {
- // But if we could determine the scripts, they should be the same
- // for the locales to match.
- if (memcmp(localeScript, settings.localeScript, sizeof(localeScript)) != 0) {
+ if (memcmp(script, settings.localeScript, sizeof(settings.localeScript)) != 0) {
return false;
}
}
diff --git a/libs/androidfw/tests/ConfigLocale_test.cpp b/libs/androidfw/tests/ConfigLocale_test.cpp
index 7b38640..4b8d65c 100644
--- a/libs/androidfw/tests/ConfigLocale_test.cpp
+++ b/libs/androidfw/tests/ConfigLocale_test.cpp
@@ -371,6 +371,19 @@
EXPECT_TRUE(supported.match(requested));
}
+TEST(ConfigLocaleTest, match_emptyScript) {
+ ResTable_config supported, requested;
+
+ fillIn("fr", "FR", NULL, NULL, &supported);
+ fillIn("fr", "CA", NULL, NULL, &requested);
+
+ // emulate packages built with older AAPT
+ memset(supported.localeScript, '\0', 4);
+ supported.localeScriptWasProvided = false;
+
+ EXPECT_TRUE(supported.match(requested));
+}
+
TEST(ConfigLocaleTest, isLocaleBetterThan_basics) {
ResTable_config config1, config2, request;
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 1f242a3..6a565033 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -144,20 +144,8 @@
external/skia/include/private \
external/skia/src/core
-hwui_shared_libraries := \
- liblog \
- libcutils \
- libutils \
- libEGL \
- libGLESv2 \
- libskia \
- libui \
- libgui \
- libprotobuf-cpp-lite \
-
ifneq (false,$(ANDROID_ENABLE_RENDERSCRIPT))
hwui_cflags += -DANDROID_ENABLE_RENDERSCRIPT
- hwui_shared_libraries += libRS libRScpp
hwui_c_includes += \
$(call intermediates-dir-for,STATIC_LIBRARIES,libRS,TARGET,) \
frameworks/rs/cpp \
@@ -180,12 +168,15 @@
LOCAL_MODULE_CLASS := STATIC_LIBRARIES
LOCAL_MODULE := libhwui_static
-LOCAL_SHARED_LIBRARIES := $(hwui_shared_libraries)
LOCAL_CFLAGS := $(hwui_cflags)
LOCAL_SRC_FILES := $(hwui_src_files)
LOCAL_C_INCLUDES := $(hwui_c_includes) $(call hwui_proto_include)
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(hwui_c_includes) $(call hwui_proto_include)
+LOCAL_EXPORT_C_INCLUDE_DIRS := \
+ $(LOCAL_PATH) \
+ $(hwui_c_includes) \
+ $(call hwui_proto_include)
+include $(LOCAL_PATH)/hwui_static_deps.mk
include $(BUILD_STATIC_LIBRARY)
# ------------------------
@@ -196,7 +187,6 @@
LOCAL_MODULE_CLASS := STATIC_LIBRARIES
LOCAL_MODULE := libhwui_static_null_gpu
-LOCAL_SHARED_LIBRARIES := $(hwui_shared_libraries)
LOCAL_CFLAGS := \
$(hwui_cflags) \
-DHWUI_NULL_GPU
@@ -205,8 +195,12 @@
debug/nullegl.cpp \
debug/nullgles.cpp
LOCAL_C_INCLUDES := $(hwui_c_includes) $(call hwui_proto_include)
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(hwui_c_includes) $(call hwui_proto_include)
+LOCAL_EXPORT_C_INCLUDE_DIRS := \
+ $(LOCAL_PATH) \
+ $(hwui_c_includes) \
+ $(call hwui_proto_include)
+include $(LOCAL_PATH)/hwui_static_deps.mk
include $(BUILD_STATIC_LIBRARY)
# ------------------------
@@ -218,8 +212,9 @@
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
LOCAL_MODULE := libhwui
LOCAL_WHOLE_STATIC_LIBRARIES := libhwui_static
-LOCAL_SHARED_LIBRARIES := $(hwui_shared_libraries)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
+include $(LOCAL_PATH)/hwui_static_deps.mk
include $(BUILD_SHARED_LIBRARY)
# ------------------------
@@ -230,7 +225,6 @@
LOCAL_MODULE := hwui_unit_tests
LOCAL_MODULE_TAGS := tests
-LOCAL_SHARED_LIBRARIES := $(hwui_shared_libraries)
LOCAL_STATIC_LIBRARIES := libhwui_static_null_gpu
LOCAL_CFLAGS := \
$(hwui_cflags) \
@@ -263,6 +257,7 @@
tests/unit/RecordingCanvasTests.cpp
endif
+include $(LOCAL_PATH)/hwui_static_deps.mk
include $(BUILD_NATIVE_TEST)
# ------------------------
@@ -278,7 +273,6 @@
LOCAL_MULTILIB := both
LOCAL_MODULE_STEM_32 := hwuitest
LOCAL_MODULE_STEM_64 := hwuitest64
-LOCAL_SHARED_LIBRARIES := $(hwui_shared_libraries)
LOCAL_CFLAGS := $(hwui_cflags)
# set to libhwui_static_null_gpu to skip actual GL commands
@@ -289,6 +283,7 @@
tests/macrobench/TestSceneRunner.cpp \
tests/macrobench/main.cpp
+include $(LOCAL_PATH)/hwui_static_deps.mk
include $(BUILD_EXECUTABLE)
# ------------------------
@@ -303,7 +298,6 @@
LOCAL_MULTILIB := both
LOCAL_MODULE_STEM_32 := hwuimicro
LOCAL_MODULE_STEM_64 := hwuimicro64
-LOCAL_SHARED_LIBRARIES := $(hwui_shared_libraries)
LOCAL_CFLAGS := \
$(hwui_cflags) \
-DHWUI_NULL_GPU
@@ -325,6 +319,5 @@
tests/microbench/FrameBuilderBench.cpp
endif
-LOCAL_CLANG := true # workaround gcc bug
-
+include $(LOCAL_PATH)/hwui_static_deps.mk
include $(BUILD_EXECUTABLE)
diff --git a/libs/hwui/BakedOpState.h b/libs/hwui/BakedOpState.h
index 3db28c9..5a5845a 100644
--- a/libs/hwui/BakedOpState.h
+++ b/libs/hwui/BakedOpState.h
@@ -100,7 +100,7 @@
static BakedOpState* tryConstruct(LinearAllocator& allocator,
Snapshot& snapshot, const RecordedOp& recordedOp) {
if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr;
- BakedOpState* bakedState = new (allocator) BakedOpState(
+ BakedOpState* bakedState = allocator.create_trivial<BakedOpState>(
allocator, snapshot, recordedOp, false);
if (bakedState->computedState.clippedBounds.isEmpty()) {
// bounds are empty, so op is rejected
@@ -124,7 +124,7 @@
? (recordedOp.paint && recordedOp.paint->getStyle() != SkPaint::kFill_Style)
: true;
- BakedOpState* bakedState = new (allocator) BakedOpState(
+ BakedOpState* bakedState = allocator.create_trivial<BakedOpState>(
allocator, snapshot, recordedOp, expandForStroke);
if (bakedState->computedState.clippedBounds.isEmpty()) {
// bounds are empty, so op is rejected
@@ -140,16 +140,12 @@
if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr;
// clip isn't empty, so construct the op
- return new (allocator) BakedOpState(allocator, snapshot, shadowOpPtr);
+ return allocator.create_trivial<BakedOpState>(allocator, snapshot, shadowOpPtr);
}
static BakedOpState* directConstruct(LinearAllocator& allocator,
const ClipRect* clip, const Rect& dstRect, const RecordedOp& recordedOp) {
- return new (allocator) BakedOpState(clip, dstRect, recordedOp);
- }
-
- static void* operator new(size_t size, LinearAllocator& allocator) {
- return allocator.alloc(size);
+ return allocator.create_trivial<BakedOpState>(clip, dstRect, recordedOp);
}
// computed state:
@@ -162,6 +158,8 @@
const RecordedOp* op;
private:
+ friend class LinearAllocator;
+
BakedOpState(LinearAllocator& allocator, Snapshot& snapshot,
const RecordedOp& recordedOp, bool expandForStroke)
: computedState(allocator, snapshot, recordedOp, expandForStroke)
diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp
index c2e14a2..6d5833b 100644
--- a/libs/hwui/DamageAccumulator.cpp
+++ b/libs/hwui/DamageAccumulator.cpp
@@ -45,7 +45,7 @@
};
DamageAccumulator::DamageAccumulator() {
- mHead = (DirtyStack*) mAllocator.alloc(sizeof(DirtyStack));
+ mHead = mAllocator.create_trivial<DirtyStack>();
memset(mHead, 0, sizeof(DirtyStack));
// Create a root that we will not pop off
mHead->prev = mHead;
@@ -78,7 +78,7 @@
void DamageAccumulator::pushCommon() {
if (!mHead->next) {
- DirtyStack* nextFrame = (DirtyStack*) mAllocator.alloc(sizeof(DirtyStack));
+ DirtyStack* nextFrame = mAllocator.create_trivial<DirtyStack>();
nextFrame->next = nullptr;
nextFrame->prev = mHead;
mHead->next = nextFrame;
diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h
index 2d5979f..98ccf11 100644
--- a/libs/hwui/DeferredDisplayList.h
+++ b/libs/hwui/DeferredDisplayList.h
@@ -49,11 +49,6 @@
class DeferredDisplayState {
public:
- static void* operator new(size_t size) = delete;
- static void* operator new(size_t size, LinearAllocator& allocator) {
- return allocator.alloc(size);
- }
-
// global op bounds, mapped by mMatrix to be in screen space coordinates, clipped
Rect mBounds;
@@ -124,7 +119,7 @@
DeferredDisplayList(const DeferredDisplayList& other); // disallow copy
DeferredDisplayState* createState() {
- return new (mAllocator) DeferredDisplayState();
+ return mAllocator.create_trivial<DeferredDisplayState>();
}
void tryRecycleState(DeferredDisplayState* state) {
diff --git a/libs/hwui/DisplayListCanvas.h b/libs/hwui/DisplayListCanvas.h
index e5711e3..a703e22 100644
--- a/libs/hwui/DisplayListCanvas.h
+++ b/libs/hwui/DisplayListCanvas.h
@@ -251,7 +251,7 @@
inline const T* refBuffer(const T* srcBuffer, int32_t count) {
if (!srcBuffer) return nullptr;
- T* dstBuffer = (T*) mDisplayList->allocator.alloc(count * sizeof(T));
+ T* dstBuffer = (T*) mDisplayList->allocator.alloc<T>(count * sizeof(T));
memcpy(dstBuffer, srcBuffer, count * sizeof(T));
return dstBuffer;
}
@@ -320,8 +320,7 @@
// correctly, such as creating the bitmap from scratch, drawing with it, changing its
// contents, and drawing again. The only fix would be to always copy it the first time,
// which doesn't seem worth the extra cycles for this unlikely case.
- SkBitmap* localBitmap = new (alloc()) SkBitmap(bitmap);
- alloc().autoDestroy(localBitmap);
+ SkBitmap* localBitmap = alloc().create<SkBitmap>(bitmap);
mDisplayList->bitmapResources.push_back(localBitmap);
return localBitmap;
}
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 20501ba..98315d0 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -64,7 +64,9 @@
static void operator delete(void* ptr) { LOG_ALWAYS_FATAL("delete not supported"); }
static void* operator new(size_t size) = delete; /** PURPOSELY OMITTED **/
static void* operator new(size_t size, LinearAllocator& allocator) {
- return allocator.alloc(size);
+ // FIXME: Quick hack to keep old pipeline working, delete this when
+ // we no longer need to support HWUI_NEWOPS := false
+ return allocator.alloc<char>(size);
}
enum OpLogFlag {
diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp
index 185acce..4f51036 100644
--- a/libs/hwui/FrameBuilder.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -209,7 +209,7 @@
// not rejected, so defer render as either Layer, or direct (possibly wrapped in saveLayer)
if (node.getLayer()) {
// HW layer
- LayerOp* drawLayerOp = new (mAllocator) LayerOp(node);
+ LayerOp* drawLayerOp = mAllocator.create_trivial<LayerOp>(node);
BakedOpState* bakedOpState = tryBakeOpState(*drawLayerOp);
if (bakedOpState) {
// Node's layer already deferred, schedule it to render into parent layer
@@ -220,13 +220,13 @@
// (temp layers are clipped to viewport, since they don't persist offscreen content)
SkPaint saveLayerPaint;
saveLayerPaint.setAlpha(properties.getAlpha());
- deferBeginLayerOp(*new (mAllocator) BeginLayerOp(
+ deferBeginLayerOp(*mAllocator.create_trivial<BeginLayerOp>(
saveLayerBounds,
Matrix4::identity(),
nullptr, // no record-time clip - need only respect defer-time one
&saveLayerPaint));
deferNodeOps(node);
- deferEndLayerOp(*new (mAllocator) EndLayerOp());
+ deferEndLayerOp(*mAllocator.create_trivial<EndLayerOp>());
} else {
deferNodeOps(node);
}
@@ -549,7 +549,7 @@
void FrameBuilder::deferVectorDrawableOp(const VectorDrawableOp& op) {
const SkBitmap& bitmap = op.vectorDrawable->getBitmapUpdateIfDirty();
SkPaint* paint = op.vectorDrawable->getPaint();
- const BitmapRectOp* resolvedOp = new (mAllocator) BitmapRectOp(op.unmappedBounds,
+ const BitmapRectOp* resolvedOp = mAllocator.create_trivial<BitmapRectOp>(op.unmappedBounds,
op.localMatrix,
op.localClip,
paint,
@@ -565,7 +565,7 @@
float y = *(op.y);
float radius = *(op.radius);
Rect unmappedBounds(x - radius, y - radius, x + radius, y + radius);
- const OvalOp* resolvedOp = new (mAllocator) OvalOp(
+ const OvalOp* resolvedOp = mAllocator.create_trivial<OvalOp>(
unmappedBounds,
op.localMatrix,
op.localClip,
@@ -626,7 +626,7 @@
void FrameBuilder::deferRoundRectPropsOp(const RoundRectPropsOp& op) {
// allocate a temporary round rect op (with mAllocator, so it persists until render), so the
// renderer doesn't have to handle the RoundRectPropsOp type, and so state baking is simple.
- const RoundRectOp* resolvedOp = new (mAllocator) RoundRectOp(
+ const RoundRectOp* resolvedOp = mAllocator.create_trivial<RoundRectOp>(
Rect(*(op.left), *(op.top), *(op.right), *(op.bottom)),
op.localMatrix,
op.localClip,
@@ -754,7 +754,7 @@
// record the draw operation into the previous layer's list of draw commands
// uses state from the associated beginLayerOp, since it has all the state needed for drawing
- LayerOp* drawLayerOp = new (mAllocator) LayerOp(
+ LayerOp* drawLayerOp = mAllocator.create_trivial<LayerOp>(
beginLayerOp.unmappedBounds,
beginLayerOp.localMatrix,
beginLayerOp.localClip,
@@ -788,7 +788,7 @@
/**
* First, defer an operation to copy out the content from the rendertarget into a layer.
*/
- auto copyToOp = new (mAllocator) CopyToLayerOp(op, layerHandle);
+ auto copyToOp = mAllocator.create_trivial<CopyToLayerOp>(op, layerHandle);
BakedOpState* bakedState = BakedOpState::directConstruct(mAllocator,
&(currentLayer().viewportClip), dstRect, *copyToOp);
currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::CopyToLayer);
@@ -803,7 +803,7 @@
* And stash an operation to copy that layer back under the rendertarget until
* a balanced EndUnclippedLayerOp is seen
*/
- auto copyFromOp = new (mAllocator) CopyFromLayerOp(op, layerHandle);
+ auto copyFromOp = mAllocator.create_trivial<CopyFromLayerOp>(op, layerHandle);
bakedState = BakedOpState::directConstruct(mAllocator,
&(currentLayer().viewportClip), dstRect, *copyFromOp);
currentLayer().activeUnclippedSaveLayers.push_back(bakedState);
diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp
index 11293d6..c8f5e94 100644
--- a/libs/hwui/GradientCache.cpp
+++ b/libs/hwui/GradientCache.cpp
@@ -165,6 +165,10 @@
generateTexture(colors, positions, info.width, 2, texture);
mSize += size;
+ LOG_ALWAYS_FATAL_IF((int)size != texture->objectSize(),
+ "size != texture->objectSize(), size %" PRIu32 ", objectSize %d"
+ " width = %" PRIu32 " bytesPerPixel() = %zu",
+ size, texture->objectSize(), info.width, bytesPerPixel());
mCache.put(gradient, texture);
return texture;
diff --git a/libs/hwui/LayerBuilder.cpp b/libs/hwui/LayerBuilder.cpp
index 7170d4f..1ba3bf2 100644
--- a/libs/hwui/LayerBuilder.cpp
+++ b/libs/hwui/LayerBuilder.cpp
@@ -64,10 +64,6 @@
class OpBatch : public BatchBase {
public:
- static void* operator new(size_t size, LinearAllocator& allocator) {
- return allocator.alloc(size);
- }
-
OpBatch(batchid_t batchId, BakedOpState* op)
: BatchBase(batchId, op, false) {
}
@@ -80,10 +76,6 @@
class MergingOpBatch : public BatchBase {
public:
- static void* operator new(size_t size, LinearAllocator& allocator) {
- return allocator.alloc(size);
- }
-
MergingOpBatch(batchid_t batchId, BakedOpState* op)
: BatchBase(batchId, op, true)
, mClipSideFlags(op->computedState.clipSideFlags) {
@@ -247,7 +239,7 @@
// put the verts in the frame allocator, since
// 1) SimpleRectsOps needs verts, not rects
// 2) even if mClearRects stored verts, std::vectors will move their contents
- Vertex* const verts = (Vertex*) allocator.alloc(vertCount * sizeof(Vertex));
+ Vertex* const verts = (Vertex*) allocator.alloc<Vertex>(vertCount * sizeof(Vertex));
Vertex* currentVert = verts;
Rect bounds = mClearRects[0];
@@ -264,7 +256,7 @@
// Flush all of these clears with a single draw
SkPaint* paint = allocator.create<SkPaint>();
paint->setXfermodeMode(SkXfermode::kClear_Mode);
- SimpleRectsOp* op = new (allocator) SimpleRectsOp(bounds,
+ SimpleRectsOp* op = allocator.create_trivial<SimpleRectsOp>(bounds,
Matrix4::identity(), nullptr, paint,
verts, vertCount);
BakedOpState* bakedState = BakedOpState::directConstruct(allocator,
@@ -292,7 +284,7 @@
targetBatch->batchOp(op);
} else {
// new non-merging batch
- targetBatch = new (allocator) OpBatch(batchId, op);
+ targetBatch = allocator.create<OpBatch>(batchId, op);
mBatchLookup[batchId] = targetBatch;
mBatches.insert(mBatches.begin() + insertBatchIndex, targetBatch);
}
@@ -323,7 +315,7 @@
targetBatch->mergeOp(op);
} else {
// new merging batch
- targetBatch = new (allocator) MergingOpBatch(batchId, op);
+ targetBatch = allocator.create<MergingOpBatch>(batchId, op);
mMergingBatchLookup[batchId].insert(std::make_pair(mergeId, targetBatch));
mBatches.insert(mBatches.begin() + insertBatchIndex, targetBatch);
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 16929b8..269e590 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -83,9 +83,9 @@
void RecordingCanvas::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {
if (removed.flags & Snapshot::kFlagIsFboLayer) {
- addOp(new (alloc()) EndLayerOp());
+ addOp(alloc().create_trivial<EndLayerOp>());
} else if (removed.flags & Snapshot::kFlagIsLayer) {
- addOp(new (alloc()) EndUnclippedLayerOp());
+ addOp(alloc().create_trivial<EndUnclippedLayerOp>());
}
}
@@ -167,7 +167,7 @@
snapshot.resetClip(clip.left, clip.top, clip.right, clip.bottom);
snapshot.roundRectClipState = nullptr;
- addOp(new (alloc()) BeginLayerOp(
+ addOp(alloc().create_trivial<BeginLayerOp>(
unmappedBounds,
*previous.transform, // transform to *draw* with
previousClip, // clip to *draw* with
@@ -175,7 +175,7 @@
} else {
snapshot.flags |= Snapshot::kFlagIsLayer;
- addOp(new (alloc()) BeginUnclippedLayerOp(
+ addOp(alloc().create_trivial<BeginUnclippedLayerOp>(
unmappedBounds,
*mState.currentSnapshot()->transform,
getRecordedClip(),
@@ -241,7 +241,7 @@
}
void RecordingCanvas::drawPaint(const SkPaint& paint) {
- addOp(new (alloc()) RectOp(
+ addOp(alloc().create_trivial<RectOp>(
mState.getRenderTargetClipBounds(), // OK, since we've not passed transform
Matrix4::identity(),
getRecordedClip(),
@@ -261,7 +261,7 @@
if (floatCount < 2) return;
floatCount &= ~0x1; // round down to nearest two
- addOp(new (alloc()) PointsOp(
+ addOp(alloc().create_trivial<PointsOp>(
calcBoundsOfPoints(points, floatCount),
*mState.currentSnapshot()->transform,
getRecordedClip(),
@@ -272,7 +272,7 @@
if (floatCount < 4) return;
floatCount &= ~0x3; // round down to nearest four
- addOp(new (alloc()) LinesOp(
+ addOp(alloc().create_trivial<LinesOp>(
calcBoundsOfPoints(points, floatCount),
*mState.currentSnapshot()->transform,
getRecordedClip(),
@@ -280,7 +280,7 @@
}
void RecordingCanvas::drawRect(float left, float top, float right, float bottom, const SkPaint& paint) {
- addOp(new (alloc()) RectOp(
+ addOp(alloc().create_trivial<RectOp>(
Rect(left, top, right, bottom),
*(mState.currentSnapshot()->transform),
getRecordedClip(),
@@ -290,7 +290,7 @@
void RecordingCanvas::drawSimpleRects(const float* rects, int vertexCount, const SkPaint* paint) {
if (rects == nullptr) return;
- Vertex* rectData = (Vertex*) mDisplayList->allocator.alloc(vertexCount * sizeof(Vertex));
+ Vertex* rectData = (Vertex*) mDisplayList->allocator.alloc<Vertex>(vertexCount * sizeof(Vertex));
Vertex* vertex = rectData;
float left = FLT_MAX;
@@ -313,7 +313,7 @@
right = std::max(right, r);
bottom = std::max(bottom, b);
}
- addOp(new (alloc()) SimpleRectsOp(
+ addOp(alloc().create_trivial<SimpleRectsOp>(
Rect(left, top, right, bottom),
*(mState.currentSnapshot()->transform),
getRecordedClip(),
@@ -347,7 +347,7 @@
}
void RecordingCanvas::drawRoundRect(float left, float top, float right, float bottom,
float rx, float ry, const SkPaint& paint) {
- addOp(new (alloc()) RoundRectOp(
+ addOp(alloc().create_trivial<RoundRectOp>(
Rect(left, top, right, bottom),
*(mState.currentSnapshot()->transform),
getRecordedClip(),
@@ -367,7 +367,7 @@
mDisplayList->ref(ry);
mDisplayList->ref(paint);
refBitmapsInShader(paint->value.getShader());
- addOp(new (alloc()) RoundRectPropsOp(
+ addOp(alloc().create_trivial<RoundRectPropsOp>(
*(mState.currentSnapshot()->transform),
getRecordedClip(),
&paint->value,
@@ -389,7 +389,7 @@
mDisplayList->ref(radius);
mDisplayList->ref(paint);
refBitmapsInShader(paint->value.getShader());
- addOp(new (alloc()) CirclePropsOp(
+ addOp(alloc().create_trivial<CirclePropsOp>(
*(mState.currentSnapshot()->transform),
getRecordedClip(),
&paint->value,
@@ -397,7 +397,7 @@
}
void RecordingCanvas::drawOval(float left, float top, float right, float bottom, const SkPaint& paint) {
- addOp(new (alloc()) OvalOp(
+ addOp(alloc().create_trivial<OvalOp>(
Rect(left, top, right, bottom),
*(mState.currentSnapshot()->transform),
getRecordedClip(),
@@ -406,7 +406,7 @@
void RecordingCanvas::drawArc(float left, float top, float right, float bottom,
float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) {
- addOp(new (alloc()) ArcOp(
+ addOp(alloc().create_trivial<ArcOp>(
Rect(left, top, right, bottom),
*(mState.currentSnapshot()->transform),
getRecordedClip(),
@@ -415,7 +415,7 @@
}
void RecordingCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
- addOp(new (alloc()) PathOp(
+ addOp(alloc().create_trivial<PathOp>(
Rect(path.getBounds()),
*(mState.currentSnapshot()->transform),
getRecordedClip(),
@@ -424,7 +424,7 @@
void RecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) {
mDisplayList->ref(tree);
- addOp(new (alloc()) VectorDrawableOp(
+ addOp(alloc().create_trivial<VectorDrawableOp>(
tree,
Rect(tree->getBounds()),
*(mState.currentSnapshot()->transform),
@@ -475,7 +475,7 @@
drawBitmap(&bitmap, paint);
restore();
} else {
- addOp(new (alloc()) BitmapRectOp(
+ addOp(alloc().create_trivial<BitmapRectOp>(
Rect(dstLeft, dstTop, dstRight, dstBottom),
*(mState.currentSnapshot()->transform),
getRecordedClip(),
@@ -487,7 +487,7 @@
void RecordingCanvas::drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
const float* vertices, const int* colors, const SkPaint* paint) {
int vertexCount = (meshWidth + 1) * (meshHeight + 1);
- addOp(new (alloc()) BitmapMeshOp(
+ addOp(alloc().create_trivial<BitmapMeshOp>(
calcBoundsOfPoints(vertices, vertexCount * 2),
*(mState.currentSnapshot()->transform),
getRecordedClip(),
@@ -499,7 +499,7 @@
void RecordingCanvas::drawNinePatch(const SkBitmap& bitmap, const android::Res_png_9patch& patch,
float dstLeft, float dstTop, float dstRight, float dstBottom,
const SkPaint* paint) {
- addOp(new (alloc()) PatchOp(
+ addOp(alloc().create_trivial<PatchOp>(
Rect(dstLeft, dstTop, dstRight, dstBottom),
*(mState.currentSnapshot()->transform),
getRecordedClip(),
@@ -515,7 +515,7 @@
positions = refBuffer<float>(positions, glyphCount * 2);
// TODO: either must account for text shadow in bounds, or record separate ops for text shadows
- addOp(new (alloc()) TextOp(
+ addOp(alloc().create_trivial<TextOp>(
Rect(boundsLeft, boundsTop, boundsRight, boundsBottom),
*(mState.currentSnapshot()->transform),
getRecordedClip(),
@@ -527,7 +527,7 @@
float hOffset, float vOffset, const SkPaint& paint) {
if (!glyphs || glyphCount <= 0 || PaintUtils::paintWillNotDrawText(paint)) return;
glyphs = refBuffer<glyph_t>(glyphs, glyphCount);
- addOp(new (alloc()) TextOnPathOp(
+ addOp(alloc().create_trivial<TextOnPathOp>(
mState.getLocalClipBounds(), // TODO: explicitly define bounds
*(mState.currentSnapshot()->transform),
getRecordedClip(),
@@ -535,7 +535,7 @@
}
void RecordingCanvas::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) {
- addOp(new (alloc()) BitmapOp(
+ addOp(alloc().create_trivial<BitmapOp>(
Rect(bitmap->width(), bitmap->height()),
*(mState.currentSnapshot()->transform),
getRecordedClip(),
@@ -544,7 +544,7 @@
void RecordingCanvas::drawRenderNode(RenderNode* renderNode) {
auto&& stagingProps = renderNode->stagingProperties();
- RenderNodeOp* op = new (alloc()) RenderNodeOp(
+ RenderNodeOp* op = alloc().create_trivial<RenderNodeOp>(
Rect(stagingProps.getWidth(), stagingProps.getHeight()),
*(mState.currentSnapshot()->transform),
getRecordedClip(),
@@ -570,7 +570,7 @@
Matrix4 totalTransform(*(mState.currentSnapshot()->transform));
totalTransform.multiply(layer->getTransform());
- addOp(new (alloc()) TextureLayerOp(
+ addOp(alloc().create_trivial<TextureLayerOp>(
Rect(layer->getWidth(), layer->getHeight()),
totalTransform,
getRecordedClip(),
@@ -579,7 +579,7 @@
void RecordingCanvas::callDrawGLFunction(Functor* functor) {
mDisplayList->functors.push_back(functor);
- addOp(new (alloc()) FunctorOp(
+ addOp(alloc().create_trivial<FunctorOp>(
mState.getLocalClipBounds(), // TODO: explicitly define bounds
*(mState.currentSnapshot()->transform),
getRecordedClip(),
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index cc14e61..719872d 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -219,7 +219,7 @@
inline const T* refBuffer(const T* srcBuffer, int32_t count) {
if (!srcBuffer) return nullptr;
- T* dstBuffer = (T*) mDisplayList->allocator.alloc(count * sizeof(T));
+ T* dstBuffer = (T*) mDisplayList->allocator.alloc<T>(count * sizeof(T));
memcpy(dstBuffer, srcBuffer, count * sizeof(T));
return dstBuffer;
}
@@ -290,8 +290,7 @@
// correctly, such as creating the bitmap from scratch, drawing with it, changing its
// contents, and drawing again. The only fix would be to always copy it the first time,
// which doesn't seem worth the extra cycles for this unlikely case.
- SkBitmap* localBitmap = new (alloc()) SkBitmap(bitmap);
- alloc().autoDestroy(localBitmap);
+ SkBitmap* localBitmap = alloc().create<SkBitmap>(bitmap);
mDisplayList->bitmapResources.push_back(localBitmap);
return localBitmap;
}
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index dbaa905..0ac2f14 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -46,7 +46,7 @@
public:
/** static void* operator new(size_t size); PURPOSELY OMITTED, allocator only **/
static void* operator new(size_t size, LinearAllocator& allocator) {
- return allocator.alloc(size);
+ return allocator.alloc<RoundRectClipState>(size);
}
bool areaRequiresRoundRectClip(const Rect& rect) const {
@@ -67,7 +67,7 @@
public:
/** static void* operator new(size_t size); PURPOSELY OMITTED, allocator only **/
static void* operator new(size_t size, LinearAllocator& allocator) {
- return allocator.alloc(size);
+ return allocator.alloc<ProjectionPathMask>(size);
}
const SkPath* projectionMask;
diff --git a/libs/hwui/Texture.cpp b/libs/hwui/Texture.cpp
index c09b6dd..4f49a35 100644
--- a/libs/hwui/Texture.cpp
+++ b/libs/hwui/Texture.cpp
@@ -28,13 +28,19 @@
static int bytesPerPixel(GLint glFormat) {
switch (glFormat) {
+ // The wrapped-texture case, usually means a SurfaceTexture
+ case 0:
+ return 0;
case GL_ALPHA:
return 1;
case GL_RGB:
return 3;
case GL_RGBA:
- default:
return 4;
+ case GL_RGBA16F:
+ return 16;
+ default:
+ LOG_ALWAYS_FATAL("UNKNOWN FORMAT %d", glFormat);
}
}
diff --git a/libs/hwui/hwui_static_deps.mk b/libs/hwui/hwui_static_deps.mk
new file mode 100644
index 0000000..7d4ef0f
--- /dev/null
+++ b/libs/hwui/hwui_static_deps.mk
@@ -0,0 +1,28 @@
+###############################################################################
+#
+#
+# This file contains the shared and static dependencies needed by any target
+# that attempts to statically link HWUI (i.e. libhwui_static build target). This
+# file should be included by any target that lists libhwui_static as a
+# dependency.
+#
+# This is a workaround for the fact that the build system does not add these
+# transitive dependencies when it attempts to link libhwui_static into another
+# library.
+#
+###############################################################################
+
+LOCAL_SHARED_LIBRARIES += \
+ liblog \
+ libcutils \
+ libutils \
+ libEGL \
+ libGLESv2 \
+ libskia \
+ libui \
+ libgui \
+ libprotobuf-cpp-lite
+
+ifneq (false,$(ANDROID_ENABLE_RENDERSCRIPT))
+ LOCAL_SHARED_LIBRARIES += libRS libRScpp
+endif
\ No newline at end of file
diff --git a/libs/hwui/tests/unit/LinearAllocatorTests.cpp b/libs/hwui/tests/unit/LinearAllocatorTests.cpp
index 5c44290..402a09c 100644
--- a/libs/hwui/tests/unit/LinearAllocatorTests.cpp
+++ b/libs/hwui/tests/unit/LinearAllocatorTests.cpp
@@ -30,7 +30,7 @@
TEST(LinearAllocator, create) {
LinearAllocator la;
EXPECT_EQ(0u, la.usedSize());
- la.alloc(64);
+ la.alloc<char>(64);
// There's some internal tracking as well as padding
// so the usedSize isn't strictly defined
EXPECT_LE(64u, la.usedSize());
@@ -50,13 +50,12 @@
la.create<TestUtils::SignalingDtor>()->setSignal(destroyed + i);
la.create<SimplePair>();
}
- la.alloc(100);
+ la.alloc<char>(100);
for (int i = 0; i < 5; i++) {
- auto sd = new (la) TestUtils::SignalingDtor(destroyed + 5 + i);
- la.autoDestroy(sd);
- new (la) SimplePair();
+ la.create<TestUtils::SignalingDtor>(destroyed + 5 + i);
+ la.create_trivial<SimplePair>();
}
- la.alloc(100);
+ la.alloc<char>(100);
for (int i = 0; i < 10; i++) {
EXPECT_EQ(0, destroyed[i]);
}
@@ -70,7 +69,7 @@
int destroyed = 0;
{
LinearAllocator la;
- auto addr = la.alloc(100);
+ auto addr = la.alloc<char>(100);
EXPECT_LE(100u, la.usedSize());
la.rewindIfLastAlloc(addr, 100);
EXPECT_GT(16u, la.usedSize());
diff --git a/libs/hwui/utils/LinearAllocator.cpp b/libs/hwui/utils/LinearAllocator.cpp
index e6a4c03..5bba420 100644
--- a/libs/hwui/utils/LinearAllocator.cpp
+++ b/libs/hwui/utils/LinearAllocator.cpp
@@ -81,10 +81,6 @@
#define min(x,y) (((x) < (y)) ? (x) : (y))
-void* operator new(std::size_t size, android::uirenderer::LinearAllocator& la) {
- return la.alloc(size);
-}
-
namespace android {
namespace uirenderer {
@@ -171,7 +167,7 @@
mNext = start(mCurrentPage);
}
-void* LinearAllocator::alloc(size_t size) {
+void* LinearAllocator::allocImpl(size_t size) {
size = ALIGN(size);
if (size > mMaxAllocSize && !fitsInCurrentPage(size)) {
ALOGV("Exceeded max size %zu > %zu", size, mMaxAllocSize);
@@ -196,7 +192,7 @@
"DestructorNode must have standard layout");
static_assert(std::is_trivially_destructible<DestructorNode>::value,
"DestructorNode must be trivially destructable");
- auto node = new (*this) DestructorNode();
+ auto node = new (allocImpl(sizeof(DestructorNode))) DestructorNode();
node->dtor = dtor;
node->addr = addr;
node->next = mDtorList;
diff --git a/libs/hwui/utils/LinearAllocator.h b/libs/hwui/utils/LinearAllocator.h
index dcbc0dd..0a0e185 100644
--- a/libs/hwui/utils/LinearAllocator.h
+++ b/libs/hwui/utils/LinearAllocator.h
@@ -52,30 +52,36 @@
* The lifetime of the returned buffers is tied to that of the LinearAllocator. If calling
* delete() on an object stored in a buffer is needed, it should be overridden to use
* rewindIfLastAlloc()
+ *
+ * Note that unlike create, for alloc the type is purely for compile-time error
+ * checking and does not affect size.
*/
- void* alloc(size_t size);
+ template<class T>
+ void* alloc(size_t size) {
+ static_assert(std::is_trivially_destructible<T>::value,
+ "Error, type is non-trivial! did you mean to use create()?");
+ return allocImpl(size);
+ }
/**
* Allocates an instance of the template type with the given construction parameters
* and adds it to the automatic destruction list.
*/
template<class T, typename... Params>
- T* create(Params... params) {
- T* ret = new (*this) T(params...);
- autoDestroy(ret);
+ T* create(Params&&... params) {
+ T* ret = new (allocImpl(sizeof(T))) T(std::forward<Params>(params)...);
+ if (!std::is_trivially_destructible<T>::value) {
+ auto dtor = [](void* ret) { ((T*)ret)->~T(); };
+ addToDestructionList(dtor, ret);
+ }
return ret;
}
- /**
- * Adds the pointer to the tracking list to have its destructor called
- * when the LinearAllocator is destroyed.
- */
- template<class T>
- void autoDestroy(T* addr) {
- if (!std::is_trivially_destructible<T>::value) {
- auto dtor = [](void* addr) { ((T*)addr)->~T(); };
- addToDestructionList(dtor, addr);
- }
+ template<class T, typename... Params>
+ T* create_trivial(Params&&... params) {
+ static_assert(std::is_trivially_destructible<T>::value,
+ "Error, called create_trivial on a non-trivial type");
+ return new (allocImpl(sizeof(T))) T(std::forward<Params>(params)...);
}
/**
@@ -114,6 +120,8 @@
DestructorNode* next = nullptr;
};
+ void* allocImpl(size_t size);
+
void addToDestructionList(Destructor, void* addr);
void runDestructorFor(void* addr);
Page* newPage(size_t pageSize);
@@ -159,7 +167,7 @@
: linearAllocator(other.linearAllocator) {}
T* allocate(size_t num, const void* = 0) {
- return (T*)(linearAllocator.alloc(num * sizeof(T)));
+ return (T*)(linearAllocator.alloc<void*>(num * sizeof(T)));
}
void deallocate(pointer p, size_t num) {
@@ -187,6 +195,4 @@
}; // namespace uirenderer
}; // namespace android
-void* operator new(std::size_t size, android::uirenderer::LinearAllocator& la);
-
#endif // ANDROID_LINEARALLOCATOR_H
diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java
index d8f507c..a490685 100644
--- a/location/java/android/location/GnssMeasurement.java
+++ b/location/java/android/location/GnssMeasurement.java
@@ -28,7 +28,7 @@
*/
public final class GnssMeasurement implements Parcelable {
private int mFlags;
- private byte mPrn;
+ private short mSvid;
private double mTimeOffsetInNs;
private short mState;
private long mReceivedGpsTowInNs;
@@ -198,7 +198,7 @@
*/
public void set(GnssMeasurement measurement) {
mFlags = measurement.mFlags;
- mPrn = measurement.mPrn;
+ mSvid = measurement.mSvid;
mTimeOffsetInNs = measurement.mTimeOffsetInNs;
mState = measurement.mState;
mReceivedGpsTowInNs = measurement.mReceivedGpsTowInNs;
@@ -248,15 +248,15 @@
* Gets the Pseudo-random number (PRN).
* Range: [1, 32]
*/
- public byte getPrn() {
- return mPrn;
+ public short getSvid() {
+ return mSvid;
}
/**
* Sets the Pseud-random number (PRN).
*/
- public void setPrn(byte value) {
- mPrn = value;
+ public void setSvid(short value) {
+ mSvid = value;
}
/**
@@ -1210,7 +1210,7 @@
GnssMeasurement gnssMeasurement = new GnssMeasurement();
gnssMeasurement.mFlags = parcel.readInt();
- gnssMeasurement.mPrn = parcel.readByte();
+ gnssMeasurement.mSvid = (short) parcel.readInt();
gnssMeasurement.mTimeOffsetInNs = parcel.readDouble();
gnssMeasurement.mState = (short) parcel.readInt();
gnssMeasurement.mReceivedGpsTowInNs = parcel.readLong();
@@ -1253,9 +1253,10 @@
}
};
+ @Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeInt(mFlags);
- parcel.writeByte(mPrn);
+ parcel.writeInt(mSvid);
parcel.writeDouble(mTimeOffsetInNs);
parcel.writeInt(mState);
parcel.writeLong(mReceivedGpsTowInNs);
@@ -1301,7 +1302,7 @@
final String formatWithUncertainty = " %-29s = %-25s %-40s = %s\n";
StringBuilder builder = new StringBuilder("GnssMeasurement:\n");
- builder.append(String.format(format, "Prn", mPrn));
+ builder.append(String.format(format, "Svid", mSvid));
builder.append(String.format(format, "TimeOffsetInNs", mTimeOffsetInNs));
@@ -1422,7 +1423,7 @@
private void initialize() {
mFlags = HAS_NO_FLAGS;
- setPrn(Byte.MIN_VALUE);
+ setSvid((short) 0);
setTimeOffsetInNs(Long.MIN_VALUE);
setState(STATE_UNKNOWN);
setReceivedGpsTowInNs(Long.MIN_VALUE);
diff --git a/location/java/android/location/GnssNavigationMessage.java b/location/java/android/location/GnssNavigationMessage.java
index 0e011d5..86328eb 100644
--- a/location/java/android/location/GnssNavigationMessage.java
+++ b/location/java/android/location/GnssNavigationMessage.java
@@ -26,7 +26,7 @@
import java.security.InvalidParameterException;
/**
- * A class containing a GPS satellite Navigation Message.
+ * A class containing a GNSS satellite Navigation Message.
*/
public final class GnssNavigationMessage implements Parcelable {
@@ -84,7 +84,7 @@
// End enumerations in sync with gps.h
private byte mType;
- private byte mPrn;
+ private short mSvid;
private short mMessageId;
private short mSubmessageId;
private byte[] mData;
@@ -99,7 +99,7 @@
*/
public void set(GnssNavigationMessage navigationMessage) {
mType = navigationMessage.mType;
- mPrn = navigationMessage.mPrn;
+ mSvid = navigationMessage.mSvid;
mMessageId = navigationMessage.mMessageId;
mSubmessageId = navigationMessage.mSubmessageId;
mData = navigationMessage.mData;
@@ -153,15 +153,15 @@
* Gets the Pseudo-random number.
* Range: [1, 32].
*/
- public byte getPrn() {
- return mPrn;
+ public short getSvid() {
+ return mSvid;
}
/**
* Sets the Pseud-random number.
*/
- public void setPrn(byte value) {
- mPrn = value;
+ public void setSvid(short value) {
+ mSvid = value;
}
/**
@@ -256,7 +256,7 @@
GnssNavigationMessage navigationMessage = new GnssNavigationMessage();
navigationMessage.setType(parcel.readByte());
- navigationMessage.setPrn(parcel.readByte());
+ navigationMessage.setSvid((short) parcel.readInt());
navigationMessage.setMessageId((short) parcel.readInt());
navigationMessage.setSubmessageId((short) parcel.readInt());
@@ -281,9 +281,10 @@
}
};
+ @Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeByte(mType);
- parcel.writeByte(mPrn);
+ parcel.writeInt(mSvid);
parcel.writeInt(mMessageId);
parcel.writeInt(mSubmessageId);
parcel.writeInt(mData.length);
@@ -302,7 +303,7 @@
StringBuilder builder = new StringBuilder("GnssNavigationMessage:\n");
builder.append(String.format(format, "Type", getTypeString()));
- builder.append(String.format(format, "Prn", mPrn));
+ builder.append(String.format(format, "Svid", mSvid));
builder.append(String.format(format, "Status", getStatusString()));
builder.append(String.format(format, "MessageId", mMessageId));
builder.append(String.format(format, "SubmessageId", mSubmessageId));
@@ -321,7 +322,7 @@
private void initialize() {
mType = MESSAGE_TYPE_UNKNOWN;
- mPrn = 0;
+ mSvid = 0;
mMessageId = -1;
mSubmessageId = -1;
mData = EMPTY_ARRAY;
diff --git a/location/java/android/location/GnssStatus.java b/location/java/android/location/GnssStatus.java
index 77e8a5b..906e944 100644
--- a/location/java/android/location/GnssStatus.java
+++ b/location/java/android/location/GnssStatus.java
@@ -47,24 +47,26 @@
public static final int GNSS_SV_FLAGS_USED_IN_FIX = (1 << 2);
/** @hide */
- public static final int PRN_SHIFT_WIDTH = 3;
+ public static final int SVID_SHIFT_WIDTH = 7;
+ /** @hide */
+ public static final int CONSTELLATION_TYPE_SHIFT_WIDTH = 3;
+ /** @hide */
+ public static final int CONSTELLATION_TYPE_MASK = 0xf;
/* These package private values are modified by the LocationManager class */
- /* package */ int[] mPrnWithFlags;
+ /* package */ int[] mSvidWithFlags;
/* package */ float[] mSnrs;
/* package */ float[] mElevations;
/* package */ float[] mAzimuths;
- /* package */ int[] mConstellationTypes;
/* package */ int mSvCount;
- GnssStatus(int svCount, int[] prnWithFlags, float[] snrs, float[] elevations, float[] azimuths,
- int[] constellationTypes) {
+ GnssStatus(int svCount, int[] svidWithFlags, float[] snrs, float[] elevations,
+ float[] azimuths) {
mSvCount = svCount;
- mPrnWithFlags = prnWithFlags;
+ mSvidWithFlags = svidWithFlags;
mSnrs = snrs;
mElevations = elevations;
mAzimuths = azimuths;
- mConstellationTypes = constellationTypes;
}
/**
@@ -79,15 +81,16 @@
* @param satIndex the index of the satellite in the list.
*/
public int getConstellationType(int satIndex) {
- return mConstellationTypes[satIndex];
+ return (mSvidWithFlags[satIndex] >> CONSTELLATION_TYPE_SHIFT_WIDTH)
+ & CONSTELLATION_TYPE_MASK;
}
/**
* Retrieves the pseudo-random number of the satellite at the specified position.
* @param satIndex the index of the satellite in the list.
*/
- public int getPrn(int satIndex) {
- return mPrnWithFlags[satIndex] >> PRN_SHIFT_WIDTH;
+ public int getSvid(int satIndex) {
+ return mSvidWithFlags[satIndex] >> SVID_SHIFT_WIDTH;
}
/**
@@ -119,7 +122,7 @@
* @param satIndex the index of the satellite in the list.
*/
public boolean hasEphemeris(int satIndex) {
- return (mPrnWithFlags[satIndex] & GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA) != 0;
+ return (mSvidWithFlags[satIndex] & GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA) != 0;
}
/**
@@ -127,7 +130,7 @@
* @param satIndex the index of the satellite in the list.
*/
public boolean hasAlmanac(int satIndex) {
- return (mPrnWithFlags[satIndex] & GNSS_SV_FLAGS_HAS_ALMANAC_DATA) != 0;
+ return (mSvidWithFlags[satIndex] & GNSS_SV_FLAGS_HAS_ALMANAC_DATA) != 0;
}
/**
@@ -135,6 +138,6 @@
* @param satIndex the index of the satellite in the list.
*/
public boolean usedInFix(int satIndex) {
- return (mPrnWithFlags[satIndex] & GNSS_SV_FLAGS_USED_IN_FIX) != 0;
+ return (mSvidWithFlags[satIndex] & GNSS_SV_FLAGS_USED_IN_FIX) != 0;
}
}
diff --git a/location/java/android/location/GpsStatus.java b/location/java/android/location/GpsStatus.java
index 8d2f781..e41e20c 100644
--- a/location/java/android/location/GpsStatus.java
+++ b/location/java/android/location/GpsStatus.java
@@ -138,15 +138,19 @@
// For API-compat a public ctor() is not available
GpsStatus() {}
- private void setStatus(int svCount, int[] prnWithFlags, float[] snrs, float[] elevations,
- float[] azimuths, int[] constellationTypes) {
+ private void setStatus(int svCount, int[] svidWithFlags, float[] snrs, float[] elevations,
+ float[] azimuths) {
clearSatellites();
for (int i = 0; i < svCount; i++) {
+ final int constellationType =
+ (svidWithFlags[i] >> GnssStatus.CONSTELLATION_TYPE_SHIFT_WIDTH)
+ & GnssStatus.CONSTELLATION_TYPE_MASK;
// Skip all non-GPS satellites.
- if (constellationTypes[i] != GnssStatus.CONSTELLATION_GPS) {
+ if (constellationType != GnssStatus.CONSTELLATION_GPS) {
+ // TODO: translate the defacto pre-N use of prn's >32 to new struct
continue;
}
- int prn = prnWithFlags[i] >> GnssStatus.PRN_SHIFT_WIDTH;
+ int prn = svidWithFlags[i] >> GnssStatus.SVID_SHIFT_WIDTH;
if (prn > 0 && prn <= NUM_SATELLITES) {
GpsSatellite satellite = mSatellites.get(prn);
if (satellite == null) {
@@ -159,11 +163,11 @@
satellite.mElevation = elevations[i];
satellite.mAzimuth = azimuths[i];
satellite.mHasEphemeris =
- (prnWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA) != 0;
+ (svidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA) != 0;
satellite.mHasAlmanac =
- (prnWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_ALMANAC_DATA) != 0;
+ (svidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_ALMANAC_DATA) != 0;
satellite.mUsedInFix =
- (prnWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_USED_IN_FIX) != 0;
+ (svidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_USED_IN_FIX) != 0;
}
}
}
@@ -176,8 +180,8 @@
*/
void setStatus(GnssStatus status, int timeToFirstFix) {
mTimeToFirstFix = timeToFirstFix;
- setStatus(status.mSvCount, status.mPrnWithFlags, status.mSnrs, status.mElevations,
- status.mAzimuths, status.mConstellationTypes);
+ setStatus(status.mSvCount, status.mSvidWithFlags, status.mSnrs, status.mElevations,
+ status.mAzimuths);
}
void setTimeToFirstFix(int ttff) {
diff --git a/location/java/android/location/IGnssStatusListener.aidl b/location/java/android/location/IGnssStatusListener.aidl
index d1c6a85..8c7d06e 100644
--- a/location/java/android/location/IGnssStatusListener.aidl
+++ b/location/java/android/location/IGnssStatusListener.aidl
@@ -26,7 +26,7 @@
void onGnssStarted();
void onGnssStopped();
void onFirstFix(int ttff);
- void onSvStatusChanged(int svCount, in int[] prnWithFlags, in float[] snrs,
- in float[] elevations, in float[] azimuths, in int[] constellationTypes);
+ void onSvStatusChanged(int svCount, in int[] svidWithFlags, in float[] snrs,
+ in float[] elevations, in float[] azimuths);
void onNmeaReceived(long timestamp, String nmea);
}
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 30cf101..23f0710 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -1562,10 +1562,9 @@
@Override
public void onSvStatusChanged(int svCount, int[] prnWithFlags,
- float[] snrs, float[] elevations, float[] azimuths, int[] constellationTypes) {
+ float[] snrs, float[] elevations, float[] azimuths) {
if (mGnssCallback != null) {
- mGnssStatus = new GnssStatus(svCount, prnWithFlags, snrs, elevations, azimuths,
- constellationTypes);
+ mGnssStatus = new GnssStatus(svCount, prnWithFlags, snrs, elevations, azimuths);
Message msg = Message.obtain();
msg.what = GpsStatus.GPS_EVENT_SATELLITE_STATUS;
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index bdf6d9f..4d6c451 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -1052,32 +1052,27 @@
}
}
-// TODO Change getBufferCapacityInFrames() reference below to
-// {@link #getBufferCapacityInFrames()} after @hide is removed.
-// TODO Change setBufferSizeInFrames(int) reference below to
-// {@link #setBufferSizeInFrames(int)} after @hide is removed.
+
/**
- * Returns the effective size of the <code>AudioTrack</code> buffer
+ * Returns the effective size of the <code>AudioTrack</code> buffer
* that the application writes to.
- * <p> This will be less than or equal to the result of
- * getBufferCapacityInFrames().
- * It will be equal if setBufferSizeInFrames(int) has never been called.
- * <p> If the track is subsequently routed to a different output sink, the buffer
- * size and capacity may enlarge to accommodate.
- * <p> If the <code>AudioTrack</code> encoding indicates compressed data,
- * e.g. {@link AudioFormat#ENCODING_AC3}, then the frame count returned is
- * the size of the native <code>AudioTrack</code> buffer in bytes.
- * <p> See also {@link AudioManager#getProperty(String)} for key
- * {@link AudioManager#PROPERTY_OUTPUT_FRAMES_PER_BUFFER}.
- * @return current size in frames of the <code>AudioTrack</code> buffer.
- * @throws IllegalStateException
+ * <p> This will be less than or equal to the result of
+ * {@link #getBufferCapacityInFrames()}.
+ * It will be equal if {@link #setBufferSizeInFrames(int)} has never been called.
+ * <p> If the track is subsequently routed to a different output sink, the buffer
+ * size and capacity may enlarge to accommodate.
+ * <p> If the <code>AudioTrack</code> encoding indicates compressed data,
+ * e.g. {@link AudioFormat#ENCODING_AC3}, then the frame count returned is
+ * the size of the native <code>AudioTrack</code> buffer in bytes.
+ * <p> See also {@link AudioManager#getProperty(String)} for key
+ * {@link AudioManager#PROPERTY_OUTPUT_FRAMES_PER_BUFFER}.
+ * @return current size in frames of the <code>AudioTrack</code> buffer.
+ * @throws IllegalStateException
*/
public int getBufferSizeInFrames() {
return native_get_buffer_size_frames();
}
-// TODO Change getBufferCapacityInFrames() reference below to
-// {@link #getBufferCapacityInFrames()} after @hide is removed.
/**
* Limits the effective size of the <code>AudioTrack</code> buffer
* that the application writes to.
@@ -1087,9 +1082,9 @@
* <p>Changing this limit modifies the latency associated with
* the buffer for this track. A smaller size will give lower latency
* but there may be more glitches due to buffer underruns.
- * <p>The actual size used may not be equal to this requested size.
+ * <p>The actual size used may not be equal to this requested size.
* It will be limited to a valid range with a maximum of
- * getBufferCapacityInFrames().
+ * {@link #getBufferCapacityInFrames()}.
* It may also be adjusted slightly for internal reasons.
* If bufferSizeInFrames is less than zero then {@link #ERROR_BAD_VALUE}
* will be returned.
@@ -1097,9 +1092,9 @@
* It is not supported for compressed audio tracks.
*
* @param bufferSizeInFrames requested buffer size
- * @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE},
- * {@link #ERROR_INVALID_OPERATION}
- * @throws IllegalStateException
+ * @return the actual buffer size in frames or an error code,
+ * {@link #ERROR_BAD_VALUE}, {@link #ERROR_INVALID_OPERATION}
+ * @throws IllegalStateException
*/
public int setBufferSizeInFrames(int bufferSizeInFrames) {
if (mDataLoadMode == MODE_STATIC || mState == STATE_UNINITIALIZED) {
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 1320e38..00083e6 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -157,35 +157,22 @@
public static final int RECORDING_ERROR_UNKNOWN = 0;
/**
- * Error for {@link TvRecordingClient.RecordingCallback#onError(int)}: The recording client has
- * failed to establish a connection to a recording session.
- */
- public static final int RECORDING_ERROR_CONNECTION_FAILED = 1;
-
- /**
- * Error for {@link TvRecordingClient.RecordingCallback#onError(int)}: The recording client has
- * been disconnected from the current recording session.
- */
- public static final int RECORDING_ERROR_DISCONNECTED = 2;
-
- /**
* Error for {@link TvInputService.RecordingSession#notifyError(int)} and
* {@link TvRecordingClient.RecordingCallback#onError(int)}: Recording cannot proceed due to
* insufficient storage space.
*/
- public static final int RECORDING_ERROR_INSUFFICIENT_SPACE = 3;
+ public static final int RECORDING_ERROR_INSUFFICIENT_SPACE = 1;
/**
* Error for {@link TvInputService.RecordingSession#notifyError(int)} and
* {@link TvRecordingClient.RecordingCallback#onError(int)}: Recording cannot proceed because
* a required recording resource was not able to be allocated.
*/
- public static final int RECORDING_ERROR_RESOURCE_BUSY = 4;
+ public static final int RECORDING_ERROR_RESOURCE_BUSY = 2;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef({RECORDING_ERROR_UNKNOWN, RECORDING_ERROR_CONNECTION_FAILED,
- RECORDING_ERROR_DISCONNECTED, RECORDING_ERROR_INSUFFICIENT_SPACE,
+ @IntDef({RECORDING_ERROR_UNKNOWN, RECORDING_ERROR_INSUFFICIENT_SPACE,
RECORDING_ERROR_RESOURCE_BUSY})
public @interface RecordingError {}
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 334c84b..4ebd0fc 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -1584,6 +1584,10 @@
* new data entry in the {@link TvContract.RecordedPrograms} table that describes the newly
* recorded program.
*
+ * <p>The recording session must call this method in response to {@link #onStopRecording()}.
+ * The session may call it even before receiving a call to {@link #onStopRecording()} if a
+ * partially recorded program is available when there is an error.
+ *
* @param recordedProgramUri The URI of the newly recorded program.
*/
public void notifyRecordingStopped(final Uri recordedProgramUri) {
@@ -1604,8 +1608,14 @@
}
/**
- * Informs the application that there is an error. It may be called at any time after this
- * recording session is created until {@link #onRelease()} is called.
+ * Informs the application that there is an error and this recording session is no longer
+ * able to start or continue recording. It may be called at any time after the recording
+ * session is created until {@link #onRelease()} is called.
+ *
+ * <p>The application may release the current session upon receiving the error code through
+ * {@link TvRecordingClient.RecordingCallback#onError(int)}. The session may call
+ * {@link #notifyRecordingStopped(Uri)} if a partially recorded but still playable program
+ * is available, before calling this method.
*
* @param error The error code. Should be one of the followings.
* <ul>
diff --git a/media/java/android/media/tv/TvRecordingClient.java b/media/java/android/media/tv/TvRecordingClient.java
index 72606f5..1c920f5 100644
--- a/media/java/android/media/tv/TvRecordingClient.java
+++ b/media/java/android/media/tv/TvRecordingClient.java
@@ -68,7 +68,7 @@
/**
* Tunes to a given channel for TV program recording. The first tune request will create a new
- * recording session for the corresponding TV input and establish the connection between the
+ * recording session for the corresponding TV input and establish a connection between the
* application and the session. If recording has already started in the current recording
* session, this method throws an exception.
*
@@ -88,7 +88,7 @@
/**
* Tunes to a given channel for TV program recording. The first tune request will create a new
- * recording session for the corresponding TV input and establish the connection between the
+ * recording session for the corresponding TV input and establish a connection between the
* application and the session. If recording has already started in the current recording
* session, this method throws an exception.
*
@@ -226,6 +226,23 @@
*/
public abstract static class RecordingCallback {
/**
+ * This is called when an error occurred while establishing a connection to the recording
+ * session for the corresponding TV input.
+ *
+ * @param inputId The ID of the TV input bound to the current TvRecordingClient.
+ */
+ public void onConnectionFailed(String inputId) {
+ }
+
+ /**
+ * This is called when the connection to the current recording session is lost.
+ *
+ * @param inputId The ID of the TV input bound to the current TvRecordingClient.
+ */
+ public void onDisconnected(String inputId) {
+ }
+
+ /**
* This is called when the recording session has been tuned to the given channel and is
* ready to start recording.
*/
@@ -249,8 +266,6 @@
* @param error The error code. Should be one of the followings.
* <ul>
* <li>{@link TvInputManager#RECORDING_ERROR_UNKNOWN}
- * <li>{@link TvInputManager#RECORDING_ERROR_CONNECTION_FAILED}
- * <li>{@link TvInputManager#RECORDING_ERROR_DISCONNECTED}
* <li>{@link TvInputManager#RECORDING_ERROR_INSUFFICIENT_SPACE}
* <li>{@link TvInputManager#RECORDING_ERROR_RESOURCE_BUSY}
* </ul>
@@ -305,7 +320,9 @@
mSession.tune(mChannelUri, mConnectionParams);
} else {
mSessionCallback = null;
- mCallback.onError(TvInputManager.RECORDING_ERROR_CONNECTION_FAILED);
+ if (mCallback != null) {
+ mCallback.onConnectionFailed(mInputId);
+ }
}
}
@@ -331,11 +348,13 @@
Log.w(TAG, "onSessionReleased - session not created");
return;
}
- mSessionCallback = null;
- mSession = null;
mIsTuned = false;
mIsRecordingStarted = false;
- mCallback.onError(TvInputManager.RECORDING_ERROR_DISCONNECTED);
+ mSessionCallback = null;
+ mSession = null;
+ if (mCallback != null) {
+ mCallback.onDisconnected(mInputId);
+ }
}
@Override
diff --git a/media/java/android/media/tv/TvTrackInfo.java b/media/java/android/media/tv/TvTrackInfo.java
index ed432c46..6a44b1e 100644
--- a/media/java/android/media/tv/TvTrackInfo.java
+++ b/media/java/android/media/tv/TvTrackInfo.java
@@ -52,11 +52,14 @@
private final int mVideoHeight;
private final float mVideoFrameRate;
private final float mVideoPixelAspectRatio;
+ private final byte mVideoActiveFormatDescription;
+
private final Bundle mExtra;
private TvTrackInfo(int type, String id, String language, CharSequence description,
int audioChannelCount, int audioSampleRate, int videoWidth, int videoHeight,
- float videoFrameRate, float videoPixelAspectRatio, Bundle extra) {
+ float videoFrameRate, float videoPixelAspectRatio, byte videoActiveFormatDescription,
+ Bundle extra) {
mType = type;
mId = id;
mLanguage = language;
@@ -67,6 +70,7 @@
mVideoHeight = videoHeight;
mVideoFrameRate = videoFrameRate;
mVideoPixelAspectRatio = videoPixelAspectRatio;
+ mVideoActiveFormatDescription = videoActiveFormatDescription;
mExtra = extra;
}
@@ -81,6 +85,7 @@
mVideoHeight = in.readInt();
mVideoFrameRate = in.readFloat();
mVideoPixelAspectRatio = in.readFloat();
+ mVideoActiveFormatDescription = in.readByte();
mExtra = in.readBundle();
}
@@ -179,6 +184,20 @@
}
/**
+ * Returns the Active Format Description (AFD) code of the video.
+ * Valid only for {@link #TYPE_VIDEO} tracks.
+ *
+ * <p>The complete list of values are defined in ETSI TS 101 154 V1.7.1 Annex B, ATSC A/53 Part
+ * 4 and SMPTE 2016-1-2007.
+ */
+ public final byte getVideoActiveFormatDescription() {
+ if (mType != TYPE_VIDEO) {
+ throw new IllegalStateException("Not a video track");
+ }
+ return mVideoActiveFormatDescription;
+ }
+
+ /**
* Returns the extra information about the current track.
*/
public final Bundle getExtra() {
@@ -208,6 +227,7 @@
dest.writeInt(mVideoHeight);
dest.writeFloat(mVideoFrameRate);
dest.writeFloat(mVideoPixelAspectRatio);
+ dest.writeByte(mVideoActiveFormatDescription);
dest.writeBundle(mExtra);
}
@@ -238,6 +258,7 @@
private int mVideoHeight;
private float mVideoFrameRate;
private float mVideoPixelAspectRatio = 1.0f;
+ private byte mVideoActiveFormatDescription;
private Bundle mExtra;
/**
@@ -368,6 +389,25 @@
}
/**
+ * Sets the Active Format Description (AFD) code of the video.
+ * Valid only for {@link #TYPE_VIDEO} tracks.
+ *
+ * <p>This is needed for applications to be able to scale the video properly based on the
+ * information about where in the coded picture the active video is.
+ * The complete list of values are defined in ETSI TS 101 154 V1.7.1 Annex B, ATSC A/53 Part
+ * 4 and SMPTE 2016-1-2007.
+ *
+ * @param videoActiveFormatDescription The AFD code of the video.
+ */
+ public final Builder setVideoActiveFormatDescription(byte videoActiveFormatDescription) {
+ if (mType != TYPE_VIDEO) {
+ throw new IllegalStateException("Not a video track");
+ }
+ mVideoActiveFormatDescription = videoActiveFormatDescription;
+ return this;
+ }
+
+ /**
* Sets the extra information about the current track.
*
* @param extra The extra information.
@@ -385,7 +425,7 @@
public TvTrackInfo build() {
return new TvTrackInfo(mType, mId, mLanguage, mDescription, mAudioChannelCount,
mAudioSampleRate, mVideoWidth, mVideoHeight, mVideoFrameRate,
- mVideoPixelAspectRatio, mExtra);
+ mVideoPixelAspectRatio, mVideoActiveFormatDescription, mExtra);
}
}
}
diff --git a/packages/DocumentsUI/res/layout/fixed_layout.xml b/packages/DocumentsUI/res/layout/fixed_layout.xml
index 8414feb..84a928d 100644
--- a/packages/DocumentsUI/res/layout/fixed_layout.xml
+++ b/packages/DocumentsUI/res/layout/fixed_layout.xml
@@ -16,11 +16,14 @@
<!-- CoordinatorLayout is necessary for various components (e.g. Snackbars, and
floating action buttons) to operate correctly. -->
+<!-- focusableInTouchMode is set in order to force key events to go to the activity's global key
+ callback, which is necessary for proper event routing. See BaseActivity.onKeyDown. -->
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:id="@+id/coordinator_layout">
+ android:id="@+id/coordinator_layout"
+ android:focusableInTouchMode="true">
<LinearLayout
android:layout_width="match_parent"
diff --git a/packages/DocumentsUI/res/layout/fragment_directory.xml b/packages/DocumentsUI/res/layout/fragment_directory.xml
index d0364ff..0fb74e5 100644
--- a/packages/DocumentsUI/res/layout/fragment_directory.xml
+++ b/packages/DocumentsUI/res/layout/fragment_directory.xml
@@ -83,7 +83,7 @@
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
- android:id="@+id/list"
+ android:id="@+id/dir_list"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
diff --git a/packages/DocumentsUI/res/layout/fragment_roots.xml b/packages/DocumentsUI/res/layout/fragment_roots.xml
index f3de3b4..b33b8d0 100644
--- a/packages/DocumentsUI/res/layout/fragment_roots.xml
+++ b/packages/DocumentsUI/res/layout/fragment_roots.xml
@@ -14,8 +14,8 @@
limitations under the License.
-->
-<ListView xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@android:id/list"
+<com.android.documentsui.RootsList xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/roots_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="8dp"
diff --git a/packages/DocumentsUI/res/layout/single_pane_layout.xml b/packages/DocumentsUI/res/layout/single_pane_layout.xml
index f53d698..235d22d 100644
--- a/packages/DocumentsUI/res/layout/single_pane_layout.xml
+++ b/packages/DocumentsUI/res/layout/single_pane_layout.xml
@@ -16,11 +16,14 @@
<!-- CoordinatorLayout is necessary for various components (e.g. Snackbars, and
floating action buttons) to operate correctly. -->
+<!-- focusableInTouchMode is set in order to force key events to go to the activity's global key
+ callback, which is necessary for proper event routing. See BaseActivity.onKeyDown. -->
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:id="@+id/coordinator_layout">
+ android:id="@+id/coordinator_layout"
+ android:focusableInTouchMode="true">
<LinearLayout
android:layout_width="match_parent"
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
index 475387b..4a55906 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
@@ -42,6 +42,7 @@
import android.support.annotation.LayoutRes;
import android.support.annotation.Nullable;
import android.util.Log;
+import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Spinner;
@@ -83,6 +84,8 @@
// We use the time gap to figure out whether to close app or reopen the drawer.
private long mDrawerLastFiddled;
+ private boolean mNavDrawerHasFocus;
+
public abstract void onDocumentPicked(DocumentInfo doc, @Nullable SiblingProvider siblings);
public abstract void onDocumentsPicked(List<DocumentInfo> docs);
@@ -580,6 +583,54 @@
}
}
+ /**
+ * Declare a global key handler to route key events when there isn't a specific focus view. This
+ * covers the scenario where a user opens DocumentsUI and just starts typing.
+ *
+ * @param keyCode
+ * @param event
+ * @return
+ */
+ @CallSuper
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (Events.isNavigationKeyCode(keyCode)) {
+ // Forward all unclaimed navigation keystrokes to the DirectoryFragment. This causes any
+ // stray navigation keystrokes focus the content pane, which is probably what the user
+ // is trying to do.
+ DirectoryFragment df = DirectoryFragment.get(getFragmentManager());
+ if (df != null) {
+ df.requestFocus();
+ return true;
+ }
+ } else if (keyCode == KeyEvent.KEYCODE_TAB) {
+ toggleNavDrawerFocus();
+ return true;
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+
+ /**
+ * Toggles focus between the navigation drawer and the directory listing. If the drawer isn't
+ * locked, open/close it as appropriate.
+ */
+ void toggleNavDrawerFocus() {
+ if (mNavDrawerHasFocus) {
+ mDrawer.setOpen(false);
+ DirectoryFragment df = DirectoryFragment.get(getFragmentManager());
+ if (df != null) {
+ df.requestFocus();
+ }
+ } else {
+ mDrawer.setOpen(true);
+ RootsFragment rf = RootsFragment.get(getFragmentManager());
+ if (rf != null) {
+ rf.requestFocus();
+ }
+ }
+ mNavDrawerHasFocus = !mNavDrawerHasFocus;
+ }
+
DocumentInfo getRootDocumentBlocking(RootInfo root) {
try {
final Uri uri = DocumentsContract.buildDocumentUri(
diff --git a/packages/DocumentsUI/src/com/android/documentsui/NavigationView.java b/packages/DocumentsUI/src/com/android/documentsui/NavigationView.java
index ff1940a..4cba135 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/NavigationView.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/NavigationView.java
@@ -132,7 +132,7 @@
showBreadcrumb(true);
mToolbar.setTitle(null);
mIgnoreNextNavigation = true;
- mBreadcrumb.setSelection(mBreadcrumbAdapter.getCount() - 1);
+ mBreadcrumb.setSelection(mBreadcrumbAdapter.getCount() - 1, false);
}
if (DEBUG) Log.d(TAG, "Final toolbar title is: " + mToolbar.getTitle());
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
index 7dac0c1..0e27622 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
@@ -83,7 +83,7 @@
final View view = inflater.inflate(R.layout.fragment_directory, container, false);
- mRecView = (RecyclerView) view.findViewById(R.id.list);
+ mRecView = (RecyclerView) view.findViewById(R.id.dir_list);
mRecView.setLayoutManager(new LinearLayoutManager(getContext()));
mRecView.addOnItemTouchListener(mItemListener);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
index 26bda31..53f8297 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
@@ -89,7 +89,7 @@
final Context context = inflater.getContext();
final View view = inflater.inflate(R.layout.fragment_roots, container, false);
- mList = (ListView) view.findViewById(android.R.id.list);
+ mList = (ListView) view.findViewById(R.id.roots_list);
mList.setOnItemClickListener(mItemListener);
mList.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
return view;
@@ -167,6 +167,13 @@
}
}
+ /**
+ * Attempts to shift focus back to the navigation drawer.
+ */
+ public void requestFocus() {
+ mList.requestFocus();
+ }
+
private void showAppDetails(ResolveInfo ri) {
final Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.fromParts("package", ri.activityInfo.packageName, null));
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsList.java b/packages/DocumentsUI/src/com/android/documentsui/RootsList.java
new file mode 100644
index 0000000..bf03ffd
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsList.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.documentsui;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.widget.ListView;
+
+/**
+ * The list in the navigation drawer. This class exists for the purpose of overriding the key
+ * handler on ListView. Ignoring keystrokes (e.g. the tab key) cannot be properly done using
+ * View.OnKeyListener.
+ */
+public class RootsList extends ListView {
+
+ // Multiple constructors are needed to handle all the different ways this View could be
+ // constructed by the framework. Don't remove them!
+ public RootsList(Context context) {
+ super(context);
+ }
+
+ public RootsList(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ public RootsList(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public RootsList(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ switch (keyCode) {
+ // Ignore tab key events - this causes them to bubble up to the global key handler where
+ // they are appropriately handled. See BaseActivity.onKeyDown.
+ case KeyEvent.KEYCODE_TAB:
+ return false;
+ // Prevent left/right arrow keystrokes from shifting focus away from the roots list.
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ case KeyEvent.KEYCODE_DPAD_RIGHT:
+ return true;
+ default:
+ return super.onKeyDown(keyCode, event);
+ }
+ }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 174984c..f8735b2 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -101,7 +101,6 @@
import com.android.documentsui.services.FileOperationService;
import com.android.documentsui.services.FileOperationService.OpType;
import com.android.documentsui.services.FileOperations;
-
import com.google.common.collect.Lists;
import java.lang.annotation.Retention;
@@ -183,7 +182,7 @@
mEmptyView = view.findViewById(android.R.id.empty);
- mRecView = (RecyclerView) view.findViewById(R.id.list);
+ mRecView = (RecyclerView) view.findViewById(R.id.dir_list);
mRecView.setRecyclerListener(
new RecyclerListener() {
@Override
@@ -194,6 +193,11 @@
mRecView.setItemAnimator(new DirectoryItemAnimator(getActivity()));
+ // Make the RecyclerView unfocusable. This is needed in order for the focus search code in
+ // FocusManager to work correctly. Setting android:focusable=false in the layout xml doesn't
+ // work, for some reason.
+ mRecView.setFocusable(false);
+
// TODO: Add a divider between views (which might use RecyclerView.ItemDecoration).
if (DEBUG_ENABLE_DND) {
setupDragAndDropOnDirectoryView(mRecView);
@@ -263,7 +267,8 @@
mSelectionManager.addCallback(selectionListener);
- mFocusManager = new FocusManager(mRecView, mSelectionManager);
+ // Make sure this is done after the RecyclerView is set up.
+ mFocusManager = new FocusManager(mRecView);
mModel = new Model();
mModel.addUpdateListener(mAdapter);
@@ -834,6 +839,7 @@
@Override
public void initDocumentHolder(DocumentHolder holder) {
holder.addEventListener(mItemEventListener);
+ holder.itemView.setOnFocusChangeListener(mFocusManager);
}
@Override
@@ -1054,6 +1060,13 @@
}
}
+ /**
+ * Attempts to restore focus on the directory listing.
+ */
+ public void requestFocus() {
+ mFocusManager.restoreLastFocus();
+ }
+
private void setupDragAndDropOnDirectoryView(View view) {
// Listen for drops on non-directory items and empty space.
view.setOnDragListener(mOnDragListener);
@@ -1253,16 +1266,37 @@
}
if (mFocusManager.handleKey(doc, keyCode, event)) {
+ // Handle range selection adjustments. Extending the selection will adjust the
+ // bounds of the in-progress range selection. Each time an unshifted navigation
+ // event is received, the range selection is restarted.
+ if (shouldExtendSelection(event)) {
+ if (!mSelectionManager.isRangeSelectionActive()) {
+ // Start a range selection if one isn't active
+ mSelectionManager.startRangeSelection(doc.getAdapterPosition());
+ }
+ mSelectionManager.snapRangeSelection(mFocusManager.getFocusPosition());
+ } else {
+ mSelectionManager.endRangeSelection();
+ }
return true;
}
// Handle enter key events
if (keyCode == KeyEvent.KEYCODE_ENTER) {
- return onActivate(doc);
+ if (event.isShiftPressed()) {
+ return onSelect(doc);
+ } else {
+ return onActivate(doc);
+ }
}
return false;
}
+
+ private boolean shouldExtendSelection(KeyEvent event) {
+ return Events.isNavigationKeyCode(event.getKeyCode()) &&
+ event.isShiftPressed();
+ }
}
private final class ModelUpdateListener implements Model.UpdateListener {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/FocusManager.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FocusManager.java
index 86b9146..93ec842 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/FocusManager.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FocusManager.java
@@ -16,7 +16,7 @@
package com.android.documentsui.dirlist;
-import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.KeyEvent;
@@ -27,19 +27,19 @@
/**
* A class that handles navigation and focus within the DirectoryFragment.
*/
-class FocusManager {
+class FocusManager implements View.OnFocusChangeListener {
private static final String TAG = "FocusManager";
private RecyclerView mView;
private RecyclerView.Adapter<?> mAdapter;
- private LinearLayoutManager mLayout;
- private MultiSelectManager mSelectionManager;
+ private GridLayoutManager mLayout;
- public FocusManager(RecyclerView view, MultiSelectManager selectionManager) {
+ private int mLastFocusPosition = RecyclerView.NO_POSITION;
+
+ public FocusManager(RecyclerView view) {
mView = view;
mAdapter = view.getAdapter();
- mLayout = (LinearLayoutManager) view.getLayoutManager();
- mSelectionManager = selectionManager;
+ mLayout = (GridLayoutManager) view.getLayoutManager();
}
/**
@@ -52,24 +52,58 @@
* @return Whether the event was handled.
*/
public boolean handleKey(DocumentHolder doc, int keyCode, KeyEvent event) {
- boolean handled = false;
+ boolean extendSelection = false;
+ // Translate space/shift-space into PgDn/PgUp
+ if (keyCode == KeyEvent.KEYCODE_SPACE) {
+ if (event.isShiftPressed()) {
+ keyCode = KeyEvent.KEYCODE_PAGE_UP;
+ } else {
+ keyCode = KeyEvent.KEYCODE_PAGE_DOWN;
+ }
+ } else {
+ extendSelection = event.isShiftPressed();
+ }
+
if (Events.isNavigationKeyCode(keyCode)) {
// Find the target item and focus it.
int endPos = findTargetPosition(doc.itemView, keyCode, event);
if (endPos != RecyclerView.NO_POSITION) {
focusItem(endPos);
-
- // Handle any necessary adjustments to selection.
- boolean extendSelection = event.isShiftPressed();
- if (extendSelection) {
- int startPos = doc.getAdapterPosition();
- mSelectionManager.selectRange(startPos, endPos);
- }
- handled = true;
}
+ // Swallow all navigation keystrokes. Otherwise they go to the app's global
+ // key-handler, which will route them back to the DF and cause focus to be reset.
+ return true;
}
- return handled;
+ return false;
+ }
+
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ // Remember focus events on items.
+ if (hasFocus && v.getParent() == mView) {
+ mLastFocusPosition = mView.getChildAdapterPosition(v);
+ }
+ }
+
+ /**
+ * Requests focus on the item that last had focus. Scrolls to that item if necessary.
+ */
+ public void restoreLastFocus() {
+ if (mLastFocusPosition != RecyclerView.NO_POSITION) {
+ // The system takes care of situations when a view is no longer on screen, etc,
+ focusItem(mLastFocusPosition);
+ } else {
+ // Focus the first visible item
+ focusItem(mLayout.findFirstVisibleItemPosition());
+ }
+ }
+
+ /**
+ * @return The adapter position of the last focused item.
+ */
+ public int getFocusPosition() {
+ return mLastFocusPosition;
}
/**
@@ -100,12 +134,27 @@
case KeyEvent.KEYCODE_DPAD_DOWN:
searchDir = View.FOCUS_DOWN;
break;
- case KeyEvent.KEYCODE_DPAD_LEFT:
- searchDir = View.FOCUS_LEFT;
- break;
- case KeyEvent.KEYCODE_DPAD_RIGHT:
- searchDir = View.FOCUS_RIGHT;
- break;
+ }
+
+ if (inGridMode()) {
+ int currentPosition = mView.getChildAdapterPosition(view);
+ // Left and right arrow keys only work in grid mode.
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ if (currentPosition > 0) {
+ // Stop backward focus search at the first item, otherwise focus will wrap
+ // around to the last visible item.
+ searchDir = View.FOCUS_BACKWARD;
+ }
+ break;
+ case KeyEvent.KEYCODE_DPAD_RIGHT:
+ if (currentPosition < mAdapter.getItemCount() - 1) {
+ // Stop forward focus search at the last item, otherwise focus will wrap
+ // around to the first visible item.
+ searchDir = View.FOCUS_FORWARD;
+ }
+ break;
+ }
}
if (searchDir != -1) {
@@ -204,4 +253,11 @@
});
}
}
+
+ /**
+ * @return Whether the layout manager is currently in a grid-configuration.
+ */
+ private boolean inGridMode() {
+ return mLayout.getSpanCount() > 1;
+ }
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java
index d60825b..c8b6f85 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java
@@ -370,39 +370,41 @@
}
/**
- * Handle a range selection event.
- * <li> If the MSM is currently in single-select mode, only the last item in the range will
- * actually be selected.
- * <li>If a range selection is not already active, one will be started, and the given range of
- * items will be selected. The given startPos becomes the anchor for the range selection.
- * <li>If a range selection is already active, the anchor is not changed. The range is extended
- * from its current anchor to endPos.
+ * Starts a range selection. If a range selection is already active, this will start a new range
+ * selection (which will reset the range anchor).
*
- * @param startPos
- * @param endPos
+ * @param pos The anchor position for the selection range.
*/
- public void selectRange(int startPos, int endPos) {
- // In single-select mode, just select the last item in the range.
- if (mSingleSelect) {
- attemptSelect(mAdapter.getModelId(endPos));
- return;
- }
+ void startRangeSelection(int pos) {
+ attemptSelect(mAdapter.getModelId(pos));
+ setSelectionRangeBegin(pos);
+ }
- // In regular (i.e. multi-select) mode
- if (!isRangeSelectionActive()) {
- // If a range selection isn't active, start one up
- attemptSelect(mAdapter.getModelId(startPos));
- setSelectionRangeBegin(startPos);
- }
- // Extend the range selection
- mRanger.snapSelection(endPos);
+ /**
+ * Sets the end point for the current range selection, started by a call to
+ * {@link #startRangeSelection(int)}. This function should only be called when a range selection
+ * is active (see {@link #isRangeSelectionActive()}. Items in the range [anchor, end] will be
+ * selected.
+ *
+ * @param pos The new end position for the selection range.
+ */
+ void snapRangeSelection(int pos) {
+ checkNotNull(mRanger);
+ mRanger.snapSelection(pos);
notifySelectionChanged();
}
/**
+ * Stops an in-progress range selection.
+ */
+ void endRangeSelection() {
+ mRanger = null;
+ }
+
+ /**
* @return Whether or not there is a current range selection active.
*/
- private boolean isRangeSelectionActive() {
+ boolean isRangeSelectionActive() {
return mRanger != null;
}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
index 77f16d9..609dc0c 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
@@ -21,6 +21,7 @@
import android.os.RemoteException;
import android.test.suitebuilder.annotation.LargeTest;
+import android.view.KeyEvent;
@LargeTest
public class FilesActivityUiTest extends ActivityTest<FilesActivity> {
@@ -115,4 +116,37 @@
bot.waitForDeleteSnackbarGone();
assertFalse(bot.hasDocuments("poodles.text"));
}
+
+ // Tests that pressing tab switches focus between the roots and directory listings.
+ public void testKeyboard_tab() throws Exception {
+ bot.pressKey(KeyEvent.KEYCODE_TAB);
+ bot.assertHasFocus("com.android.documentsui:id/roots_list");
+ bot.pressKey(KeyEvent.KEYCODE_TAB);
+ bot.assertHasFocus("com.android.documentsui:id/dir_list");
+ }
+
+ // Tests that arrow keys do not switch focus away from the dir list.
+ public void testKeyboard_arrowsDirList() throws Exception {
+ for (int i = 0; i < 10; i++) {
+ bot.pressKey(KeyEvent.KEYCODE_DPAD_LEFT);
+ bot.assertHasFocus("com.android.documentsui:id/dir_list");
+ }
+ for (int i = 0; i < 10; i++) {
+ bot.pressKey(KeyEvent.KEYCODE_DPAD_RIGHT);
+ bot.assertHasFocus("com.android.documentsui:id/dir_list");
+ }
+ }
+
+ // Tests that arrow keys do not switch focus away from the roots list.
+ public void testKeyboard_arrowsRootsList() throws Exception {
+ bot.pressKey(KeyEvent.KEYCODE_TAB);
+ for (int i = 0; i < 10; i++) {
+ bot.pressKey(KeyEvent.KEYCODE_DPAD_RIGHT);
+ bot.assertHasFocus("com.android.documentsui:id/roots_list");
+ }
+ for (int i = 0; i < 10; i++) {
+ bot.pressKey(KeyEvent.KEYCODE_DPAD_LEFT);
+ bot.assertHasFocus("com.android.documentsui:id/roots_list");
+ }
+ }
}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/UiBot.java b/packages/DocumentsUI/tests/src/com/android/documentsui/UiBot.java
index 4534c40..d2f8403 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/UiBot.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/UiBot.java
@@ -71,7 +71,7 @@
UiObject findRoot(String label) throws UiObjectNotFoundException {
final UiSelector rootsList = new UiSelector().resourceId(
"com.android.documentsui:id/container_roots").childSelector(
- new UiSelector().resourceId("android:id/list"));
+ new UiSelector().resourceId("com.android.documentsui:id/roots_list"));
// We might need to expand drawer if not visible
if (!new UiObject(rootsList).waitForExists(mTimeout)) {
@@ -195,6 +195,15 @@
assertNotNull(getSnackbar(mContext.getString(id)));
}
+ /**
+ * Asserts that the specified view or one of its descendents has focus.
+ */
+ void assertHasFocus(String resourceName) {
+ UiObject2 candidate = mDevice.findObject(By.res(resourceName));
+ assertNotNull("Expected " + resourceName + " to have focus, but it didn't.",
+ candidate.findObject(By.focused(true)));
+ }
+
void openDocument(String label) throws UiObjectNotFoundException {
int toolType = Configurator.getInstance().getToolType();
Configurator.getInstance().setToolType(MotionEvent.TOOL_TYPE_FINGER);
@@ -309,7 +318,7 @@
UiObject findDocument(String label) throws UiObjectNotFoundException {
final UiSelector docList = new UiSelector().resourceId(
"com.android.documentsui:id/container_directory").childSelector(
- new UiSelector().resourceId("com.android.documentsui:id/list"));
+ new UiSelector().resourceId("com.android.documentsui:id/dir_list"));
// Wait for the first list item to appear
new UiObject(docList.childSelector(new UiSelector())).waitForExists(mTimeout);
@@ -330,7 +339,7 @@
UiObject findDocumentsList() {
return findObject(
"com.android.documentsui:id/container_directory",
- "com.android.documentsui:id/list");
+ "com.android.documentsui:id/dir_list");
}
UiObject findSearchView() {
@@ -416,4 +425,8 @@
mDevice.wait(Until.hasObject(By.pkg(TARGET_PKG).depth(0)), mTimeout);
mDevice.waitForIdle();
}
+
+ void pressKey(int keyCode) {
+ mDevice.pressKeyCode(keyCode);
+ }
}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManagerTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManagerTest.java
index d95fb49..9447d9c1 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManagerTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManagerTest.java
@@ -189,6 +189,54 @@
assertSelection(items.get(20));
}
+ public void testRangeSelection() {
+ mManager.startRangeSelection(15);
+ mManager.snapRangeSelection(19);
+ assertRangeSelection(15, 19);
+ }
+
+ public void testRangeSelection_snapExpand() {
+ mManager.startRangeSelection(15);
+ mManager.snapRangeSelection(19);
+ mManager.snapRangeSelection(27);
+ assertRangeSelection(15, 27);
+ }
+
+ public void testRangeSelection_snapContract() {
+ mManager.startRangeSelection(15);
+ mManager.snapRangeSelection(27);
+ mManager.snapRangeSelection(19);
+ assertRangeSelection(15, 19);
+ }
+
+ public void testRangeSelection_snapInvert() {
+ mManager.startRangeSelection(15);
+ mManager.snapRangeSelection(27);
+ mManager.snapRangeSelection(3);
+ assertRangeSelection(3, 15);
+ }
+
+ public void testRangeSelection_multiple() {
+ mManager.startRangeSelection(15);
+ mManager.snapRangeSelection(27);
+ mManager.endRangeSelection();
+ mManager.startRangeSelection(42);
+ mManager.snapRangeSelection(57);
+ assertSelectionSize(29);
+ assertRangeSelected(15, 27);
+ assertRangeSelected(42, 57);
+
+ }
+
+ public void testRangeSelection_singleSelect() {
+ mManager = new MultiSelectManager(mEnv, mAdapter, MultiSelectManager.MODE_SINGLE, null);
+ mManager.addCallback(mCallback);
+ mManager.startRangeSelection(11);
+ mManager.snapRangeSelection(19);
+ assertSelectionSize(1);
+ assertSelection(items.get(19));
+ }
+
public void testProvisionalSelection() {
Selection s = mManager.getSelection();
assertSelection();
diff --git a/packages/MtpDocumentsProvider/res/values/strings.xml b/packages/MtpDocumentsProvider/res/values/strings.xml
index 43a420c..f3a3fcf 100644
--- a/packages/MtpDocumentsProvider/res/values/strings.xml
+++ b/packages/MtpDocumentsProvider/res/values/strings.xml
@@ -25,4 +25,8 @@
<string name="accessing_notification_title">Accessing files from <xliff:g id="device_model" example="Nexus 9">%1$s</xliff:g></string>
<!-- Description of notification showing Files app is accessing files in a MTP device. [CHAR LIMIT=60]-->
<string name="accessing_notification_description">Don\'t disconnect the device</string>
+ <!-- Error message shown in Files app when the connected MTP device is busy. [CHAR LIMIT=150]-->
+ <string name="error_busy_device">The other device is busy. You can\'t transfer files until it\'s available.</string>
+ <!-- Error message shown in Files app when the connected MTP device may be locked. [CHAR LIMIT=150]-->
+ <string name="error_locked_device">No files found. The other device may be locked. If so, unlock it and try again.</string>
</resources>
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
index c456be9..5a11327 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
@@ -37,6 +37,7 @@
import android.provider.DocumentsContract.Root;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
import java.io.FileNotFoundException;
import java.util.Objects;
@@ -244,15 +245,16 @@
}
/**
- * Returns identifier of single storage if given document points device and it has only one
- * storage. Otherwise null.
+ * Returns document IDs of storages under the given device document.
*
- * @param documentId Document ID that may point a device.
- * @return Identifier for single storage or null.
+ * @param documentId Document ID that points a device.
+ * @return Storage document IDs.
* @throws FileNotFoundException The given document ID is not registered in database.
*/
- @Nullable Identifier getSingleStorageIdentifier(String documentId)
+ String[] getStorageDocumentIds(String documentId)
throws FileNotFoundException {
+ Preconditions.checkArgument(createIdentifier(documentId).mDocumentType ==
+ DOCUMENT_TYPE_DEVICE);
// Check if the parent document is device that has single storage.
try (final Cursor cursor = mDatabase.query(
TABLE_DOCUMENTS,
@@ -267,12 +269,11 @@
null,
null,
null)) {
- if (cursor.getCount() == 1) {
- cursor.moveToNext();
- return createIdentifier(cursor.getString(0));
- } else {
- return null;
+ final String[] ids = new String[cursor.getCount()];
+ for (int i = 0; cursor.moveToNext(); i++) {
+ ids[i] = cursor.getString(0);
}
+ return ids;
}
}
@@ -342,6 +343,26 @@
}
}
+ String getDeviceDocumentId(int deviceId) throws FileNotFoundException {
+ try (final Cursor cursor = mDatabase.query(
+ TABLE_DOCUMENTS,
+ strings(Document.COLUMN_DOCUMENT_ID),
+ COLUMN_DEVICE_ID + " = ? AND " + COLUMN_DOCUMENT_TYPE + " = ? AND " +
+ COLUMN_ROW_STATE + " != ?",
+ strings(deviceId, DOCUMENT_TYPE_DEVICE, ROW_STATE_DISCONNECTED),
+ null,
+ null,
+ null,
+ "1")) {
+ if (cursor.getCount() > 0) {
+ cursor.moveToNext();
+ return cursor.getString(0);
+ } else {
+ throw new FileNotFoundException("The device ID not found: " + deviceId);
+ }
+ }
+ }
+
/**
* Adds new document under the parent.
* The method does not affect invalidated and pending documents because we know the document is
@@ -557,13 +578,9 @@
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- if (oldVersion == 1) {
- db.execSQL("DROP TABLE " + TABLE_DOCUMENTS);
- db.execSQL("DROP TABLE " + TABLE_ROOT_EXTRA);
- onCreate(db);
- return;
- }
- throw new UnsupportedOperationException();
+ db.execSQL("DROP TABLE " + TABLE_DOCUMENTS);
+ db.execSQL("DROP TABLE " + TABLE_ROOT_EXTRA);
+ onCreate(db);
}
}
@@ -680,7 +697,12 @@
if (formatCodeMimeType != null) {
return formatCodeMimeType;
}
- return MediaFile.getMimeTypeForFile(info.getName());
+ final String mediaFileMimeType = MediaFile.getMimeTypeForFile(info.getName());
+ if (mediaFileMimeType != null) {
+ return mediaFileMimeType;
+ }
+ // We don't know the file type.
+ return "application/octet-stream";
}
static String[] strings(Object... args) {
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java
index cb076af..ab356ce 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java
@@ -30,7 +30,7 @@
* Class containing MtpDatabase constants.
*/
class MtpDatabaseConstants {
- static final int DATABASE_VERSION = 2;
+ static final int DATABASE_VERSION = 3;
static final String DATABASE_NAME = "database";
static final int FLAG_DATABASE_IN_MEMORY = 1;
@@ -125,7 +125,7 @@
COLUMN_PARENT_DOCUMENT_ID + " INTEGER," +
COLUMN_ROW_STATE + " INTEGER NOT NULL," +
COLUMN_DOCUMENT_TYPE + " INTEGER NOT NULL," +
- Document.COLUMN_MIME_TYPE + " TEXT," +
+ Document.COLUMN_MIME_TYPE + " TEXT NOT NULL," +
Document.COLUMN_DISPLAY_NAME + " TEXT NOT NULL," +
Document.COLUMN_SUMMARY + " TEXT," +
Document.COLUMN_LAST_MODIFIED + " INTEGER," +
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
index 0338454..a512509 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
@@ -20,10 +20,12 @@
import android.content.res.AssetFileDescriptor;
import android.content.res.Resources;
import android.database.Cursor;
+import android.database.MatrixCursor;
import android.graphics.Point;
import android.media.MediaFile;
import android.mtp.MtpConstants;
import android.mtp.MtpObjectInfo;
+import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.ParcelFileDescriptor;
import android.os.storage.StorageManager;
@@ -35,6 +37,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.mtp.exceptions.BusyDeviceException;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -163,17 +166,25 @@
try {
openDevice(parentIdentifier.mDeviceId);
if (parentIdentifier.mDocumentType == MtpDatabaseConstants.DOCUMENT_TYPE_DEVICE) {
- final Identifier singleStorageIdentifier =
- mDatabase.getSingleStorageIdentifier(parentDocumentId);
- if (singleStorageIdentifier == null) {
+ final String[] storageDocIds = mDatabase.getStorageDocumentIds(parentDocumentId);
+ if (storageDocIds.length == 0) {
+ // Remote device does not provide storages. Maybe it is locked.
+ return createErrorCursor(projection, R.string.error_locked_device);
+ } else if (storageDocIds.length > 1) {
// Returns storage list from database.
return mDatabase.queryChildDocuments(projection, parentDocumentId);
}
- parentIdentifier = singleStorageIdentifier;
+
+ // Exact one storage is found. Skip storage and returns object in the single
+ // storage.
+ parentIdentifier = mDatabase.createIdentifier(storageDocIds[0]);
}
+
// Returns object list from document loader.
return getDocumentLoader(parentIdentifier).queryChildDocuments(
projection, parentIdentifier);
+ } catch (BusyDeviceException exception) {
+ return createErrorCursor(projection, R.string.error_busy_device);
} catch (IOException exception) {
Log.e(MtpDocumentsProvider.TAG, "queryChildDocuments", exception);
throw new FileNotFoundException(exception.getMessage());
@@ -350,6 +361,16 @@
}
/**
+ * Obtains document ID for the given device ID.
+ * @param deviceId
+ * @return document ID
+ * @throws FileNotFoundException device ID has not been build.
+ */
+ public String getDeviceDocumentId(int deviceId) throws FileNotFoundException {
+ return mDatabase.getDeviceDocumentId(deviceId);
+ }
+
+ /**
* Resumes root scanner to handle the update of device list.
*/
void resumeRootScanner() {
@@ -442,6 +463,21 @@
}
}
+ /**
+ * Creates empty cursor with specific error message.
+ *
+ * @param projection Column names.
+ * @param stringResId String resource ID of error message.
+ * @return Empty cursor with error message.
+ */
+ private Cursor createErrorCursor(String[] projection, int stringResId) {
+ final Bundle bundle = new Bundle();
+ bundle.putString(DocumentsContract.EXTRA_ERROR, mResources.getString(stringResId));
+ final Cursor cursor = new MatrixCursor(projection);
+ cursor.setExtras(bundle);
+ return cursor;
+ }
+
private static class DeviceToolkit {
public final PipeManager mPipeManager;
public final DocumentLoader mDocumentLoader;
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
index 5519efd..0527790 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
@@ -33,6 +33,7 @@
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.mtp.exceptions.BusyDeviceException;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -101,7 +102,8 @@
}
if (!device.open(connection)) {
- throw new IOException("Failed to open a MTP device.");
+ // We cannot open connection when another application use the device.
+ throw new BusyDeviceException();
}
// Handle devices that fail to obtain storages just after opening a MTP session.
@@ -134,7 +136,7 @@
try {
roots = getRoots(device.getDeviceId());
} catch (IOException exp) {
- Log.e(MtpDocumentsProvider.TAG, exp.getMessage());
+ Log.e(MtpDocumentsProvider.TAG, "Failed to open device", exp);
// If we failed to fetch roots for the device, we still returns device model
// with an empty set of roots so that the device is shown DocumentsUI as long as
// the device is physically connected.
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/ReceiverActivity.java b/packages/MtpDocumentsProvider/src/com/android/mtp/ReceiverActivity.java
index c7206a7..84745b2 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/ReceiverActivity.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/ReceiverActivity.java
@@ -17,10 +17,15 @@
package com.android.mtp;
import android.app.Activity;
-import android.content.ComponentName;
import android.content.Intent;
+import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;
+import android.net.Uri;
import android.os.Bundle;
+import android.provider.DocumentsContract;
+import android.util.Log;
+
+import java.io.IOException;
/**
* Invisible activity to receive intents.
@@ -33,14 +38,21 @@
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(getIntent().getAction())) {
- // TODO: To obtain data URI for the attached device, we need to wait until RootScanner
- // found the device and add it to database. Set correct root URI, and use ACTION_BROWSE
- // to launch Documents UI.
- final Intent intent = new Intent(Intent.ACTION_MAIN);
- intent.addCategory(Intent.CATEGORY_LAUNCHER);
- intent.setComponent(new ComponentName(
- "com.android.documentsui", "com.android.documentsui.LauncherActivity"));
- this.startActivity(intent);
+ final UsbDevice device = getIntent().getParcelableExtra(UsbManager.EXTRA_DEVICE);
+ try {
+ final MtpDocumentsProvider provider = MtpDocumentsProvider.getInstance();
+ provider.openDevice(device.getDeviceId());
+ final String deviceRootId = provider.getDeviceDocumentId(device.getDeviceId());
+ final Uri uri = DocumentsContract.buildRootUri(
+ MtpDocumentsProvider.AUTHORITY, deviceRootId);
+
+ final Intent intent = new Intent(DocumentsContract.ACTION_BROWSE);
+ intent.setData(uri);
+ intent.addCategory(Intent.CATEGORY_DEFAULT);
+ this.startActivity(intent);
+ } catch (IOException exception) {
+ Log.e(MtpDocumentsProvider.TAG, "Failed to open device", exception);
+ }
}
finish();
}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/exceptions/BusyDeviceException.java b/packages/MtpDocumentsProvider/src/com/android/mtp/exceptions/BusyDeviceException.java
new file mode 100644
index 0000000..55f55b0
--- /dev/null
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/exceptions/BusyDeviceException.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mtp.exceptions;
+
+import java.io.IOException;
+
+/**
+ * Exception thrown when the device is busy and the requested operation cannon be completed.
+ */
+public class BusyDeviceException extends IOException {
+}
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
index 5b0f557..01fcc55 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
@@ -29,6 +29,8 @@
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.MediumTest;
+import com.android.mtp.exceptions.BusyDeviceException;
+
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Arrays;
@@ -526,6 +528,52 @@
}
}
+ public void testBusyDevice() throws Exception {
+ mMtpManager = new TestMtpManager(getContext()) {
+ @Override
+ void openDevice(int deviceId) throws IOException {
+ throw new BusyDeviceException();
+ }
+ };
+ setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
+ mMtpManager.addValidDevice(new MtpDeviceRecord(
+ 0, "Device A", false /* unopened */, new MtpRoot[0], null, null));
+
+ mProvider.resumeRootScanner();
+ mResolver.waitForNotification(ROOTS_URI, 1);
+
+ try (final Cursor cursor = mProvider.queryRoots(null)) {
+ assertEquals(1, cursor.getCount());
+ }
+
+ try (final Cursor cursor = mProvider.queryChildDocuments("1", null, null)) {
+ assertEquals(0, cursor.getCount());
+ assertEquals(
+ "error_busy_device",
+ cursor.getExtras().getString(DocumentsContract.EXTRA_ERROR));
+ }
+ }
+
+ public void testLockedDevice() throws Exception {
+ setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
+ mMtpManager.addValidDevice(new MtpDeviceRecord(
+ 0, "Device A", false /* unopened */, new MtpRoot[0], null, null));
+
+ mProvider.resumeRootScanner();
+ mResolver.waitForNotification(ROOTS_URI, 1);
+
+ try (final Cursor cursor = mProvider.queryRoots(null)) {
+ assertEquals(1, cursor.getCount());
+ }
+
+ try (final Cursor cursor = mProvider.queryChildDocuments("1", null, null)) {
+ assertEquals(0, cursor.getCount());
+ assertEquals(
+ "error_locked_device",
+ cursor.getExtras().getString(DocumentsContract.EXTRA_ERROR));
+ }
+ }
+
private void setupProvider(int flag) {
mDatabase = new MtpDatabase(getContext(), flag);
mProvider = new MtpDocumentsProvider();
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResources.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResources.java
index b23038b..8676b5a 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResources.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResources.java
@@ -24,6 +24,10 @@
switch (id) {
case R.string.root_name:
return "%1$s %2$s";
+ case R.string.error_busy_device:
+ return "error_busy_device";
+ case R.string.error_locked_device:
+ return "error_locked_device";
}
throw new NotFoundException();
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerAdapter.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerAdapter.java
index 73171c7..1d6197a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerAdapter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerAdapter.java
@@ -65,7 +65,7 @@
}
public Tile getTile(int position) {
- return mItems.get(position).tile;
+ return mItems.get(position) != null ? mItems.get(position).tile : null;
}
@Override
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 8c555a6..bad7e20 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -88,8 +88,8 @@
* <p>
* The workflow is:
* <ol>
- * <li>When {@code dumpstate} starts, it sends a {@code BUGREPORT_STARTED} with its pid and the
- * estimated total effort.
+ * <li>When {@code dumpstate} starts, it sends a {@code BUGREPORT_STARTED} with a sequential id,
+ * its pid, and the estimated total effort.
* <li>{@link BugreportReceiver} receives the intent and delegates it to this service.
* <li>Upon start, this service:
* <ol>
@@ -132,6 +132,7 @@
static final String EXTRA_BUGREPORT = "android.intent.extra.BUGREPORT";
static final String EXTRA_SCREENSHOT = "android.intent.extra.SCREENSHOT";
+ static final String EXTRA_ID = "android.intent.extra.ID";
static final String EXTRA_PID = "android.intent.extra.PID";
static final String EXTRA_MAX = "android.intent.extra.MAX";
static final String EXTRA_NAME = "android.intent.extra.NAME";
@@ -177,7 +178,7 @@
*/
private static final String SCREENSHOT_DIR = "bugreports";
- /** Managed dumpstate processes (keyed by pid) */
+ /** Managed dumpstate processes (keyed by id) */
private final SparseArray<BugreportInfo> mProcesses = new SparseArray<>();
private Context mContext;
@@ -222,7 +223,7 @@
}
// If service is killed it cannot be recreated because it would not know which
- // dumpstate PIDs it would have to watch.
+ // dumpstate IDs it would have to watch.
return START_NOT_STICKY;
}
@@ -299,38 +300,41 @@
}
final String action = intent.getAction();
final int pid = intent.getIntExtra(EXTRA_PID, 0);
+ // TODO: temporarily using pid as id until test cases and dumpstate are changed.
+ final int id = intent.getIntExtra(EXTRA_ID, pid);
final int max = intent.getIntExtra(EXTRA_MAX, -1);
final String name = intent.getStringExtra(EXTRA_NAME);
- if (DEBUG) Log.v(TAG, "action: " + action + ", name: " + name + ", pid: " + pid
- + ", max: "+ max);
+ if (DEBUG)
+ Log.v(TAG, "action: " + action + ", name: " + name + ", id: " + id + ", pid: "
+ + pid + ", max: " + max);
switch (action) {
case INTENT_BUGREPORT_STARTED:
- if (!startProgress(name, pid, max)) {
+ if (!startProgress(name, id, pid, max)) {
stopSelfWhenDone();
return;
}
poll();
break;
case INTENT_BUGREPORT_FINISHED:
- if (pid == 0) {
+ if (id == 0) {
// Shouldn't happen, unless BUGREPORT_FINISHED is received from a legacy,
// out-of-sync dumpstate process.
- Log.w(TAG, "Missing " + EXTRA_PID + " on intent " + intent);
+ Log.w(TAG, "Missing " + EXTRA_ID + " on intent " + intent);
}
- onBugreportFinished(pid, intent);
+ onBugreportFinished(id, intent);
break;
case INTENT_BUGREPORT_INFO_LAUNCH:
- launchBugreportInfoDialog(pid);
+ launchBugreportInfoDialog(id);
break;
case INTENT_BUGREPORT_SCREENSHOT:
- takeScreenshot(pid, true);
+ takeScreenshot(id, true);
break;
case INTENT_BUGREPORT_SHARE:
- shareBugreport(pid, (BugreportInfo) intent.getParcelableExtra(EXTRA_INFO));
+ shareBugreport(id, (BugreportInfo) intent.getParcelableExtra(EXTRA_INFO));
break;
case INTENT_BUGREPORT_CANCEL:
- cancel(pid);
+ cancel(id);
break;
default:
Log.w(TAG, "Unsupported intent: " + action);
@@ -367,10 +371,10 @@
}
}
- private BugreportInfo getInfo(int pid) {
- final BugreportInfo info = mProcesses.get(pid);
+ private BugreportInfo getInfo(int id) {
+ final BugreportInfo info = mProcesses.get(id);
if (info == null) {
- Log.w(TAG, "Not monitoring process with PID " + pid);
+ Log.w(TAG, "Not monitoring process with ID " + id);
}
return info;
}
@@ -381,10 +385,14 @@
*
* @return whether it succeeded or not.
*/
- private boolean startProgress(String name, int pid, int max) {
+ private boolean startProgress(String name, int id, int pid, int max) {
if (name == null) {
Log.w(TAG, "Missing " + EXTRA_NAME + " on start intent");
}
+ if (id == -1) {
+ Log.e(TAG, "Missing " + EXTRA_ID + " on start intent");
+ return false;
+ }
if (pid == -1) {
Log.e(TAG, "Missing " + EXTRA_PID + " on start intent");
return false;
@@ -394,14 +402,14 @@
return false;
}
- final BugreportInfo info = new BugreportInfo(mContext, pid, name, max);
- if (mProcesses.indexOfKey(pid) >= 0) {
- Log.w(TAG, "PID " + pid + " already watched");
+ final BugreportInfo info = new BugreportInfo(mContext, id, pid, name, max);
+ if (mProcesses.indexOfKey(id) >= 0) {
+ Log.w(TAG, "ID " + id + " already watched");
} else {
- mProcesses.put(info.pid, info);
+ mProcesses.put(info.id, info);
}
// Take initial screenshot.
- takeScreenshot(pid, false);
+ takeScreenshot(id, false);
updateProgress(info);
return true;
}
@@ -423,22 +431,22 @@
com.android.internal.R.string.cancel), newCancelIntent(mContext, info)).build();
final Intent infoIntent = new Intent(mContext, BugreportProgressService.class);
infoIntent.setAction(INTENT_BUGREPORT_INFO_LAUNCH);
- infoIntent.putExtra(EXTRA_PID, info.pid);
+ infoIntent.putExtra(EXTRA_ID, info.id);
final Action infoAction = new Action.Builder(null,
mContext.getString(R.string.bugreport_info_action),
- PendingIntent.getService(mContext, info.pid, infoIntent,
+ PendingIntent.getService(mContext, info.id, infoIntent,
PendingIntent.FLAG_UPDATE_CURRENT)).build();
final Intent screenshotIntent = new Intent(mContext, BugreportProgressService.class);
screenshotIntent.setAction(INTENT_BUGREPORT_SCREENSHOT);
- screenshotIntent.putExtra(EXTRA_PID, info.pid);
+ screenshotIntent.putExtra(EXTRA_ID, info.id);
PendingIntent screenshotPendingIntent = mTakingScreenshot ? null : PendingIntent
- .getService(mContext, info.pid, screenshotIntent,
+ .getService(mContext, info.id, screenshotIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
final Action screenshotAction = new Action.Builder(null,
mContext.getString(R.string.bugreport_screenshot_action),
screenshotPendingIntent).build();
- final String title = mContext.getString(R.string.bugreport_in_progress_title, info.pid);
+ final String title = mContext.getString(R.string.bugreport_in_progress_title, info.id);
final String name =
info.name != null ? info.name : mContext.getString(R.string.bugreport_unnamed);
@@ -464,8 +472,8 @@
+ info + ")");
return;
}
- Log.v(TAG, "Sending 'Progress' notification for pid " + info.pid + ": " + percentText);
- NotificationManager.from(mContext).notify(TAG, info.pid, notification);
+ Log.v(TAG, "Sending 'Progress' notification for id " + info.id + ": " + percentText);
+ NotificationManager.from(mContext).notify(TAG, info.id, notification);
}
/**
@@ -474,38 +482,38 @@
private static PendingIntent newCancelIntent(Context context, BugreportInfo info) {
final Intent intent = new Intent(INTENT_BUGREPORT_CANCEL);
intent.setClass(context, BugreportProgressService.class);
- intent.putExtra(EXTRA_PID, info.pid);
- return PendingIntent.getService(context, info.pid, intent,
+ intent.putExtra(EXTRA_ID, info.id);
+ return PendingIntent.getService(context, info.id, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
}
/**
* Finalizes the progress on a given bugreport and cancel its notification.
*/
- private void stopProgress(int pid) {
- if (mProcesses.indexOfKey(pid) < 0) {
- Log.w(TAG, "PID not watched: " + pid);
+ private void stopProgress(int id) {
+ if (mProcesses.indexOfKey(id) < 0) {
+ Log.w(TAG, "ID not watched: " + id);
} else {
- Log.d(TAG, "Removing PID " + pid);
- mProcesses.remove(pid);
+ Log.d(TAG, "Removing ID " + id);
+ mProcesses.remove(id);
}
stopSelfWhenDone();
- Log.v(TAG, "stopProgress(" + pid + "): cancel notification");
- NotificationManager.from(mContext).cancel(TAG, pid);
+ Log.v(TAG, "stopProgress(" + id + "): cancel notification");
+ NotificationManager.from(mContext).cancel(TAG, id);
}
/**
* Cancels a bugreport upon user's request.
*/
- private void cancel(int pid) {
- Log.v(TAG, "cancel: pid=" + pid);
- final BugreportInfo info = getInfo(pid);
+ private void cancel(int id) {
+ Log.v(TAG, "cancel: ID=" + id);
+ final BugreportInfo info = getInfo(id);
if (info != null && !info.finished) {
- Log.i(TAG, "Cancelling bugreport service (pid=" + pid + ") on user's request");
+ Log.i(TAG, "Cancelling bugreport service (ID=" + id + ") on user's request");
setSystemProperty(CTL_STOP, BUGREPORT_SERVICE);
deleteScreenshots(info);
}
- stopProgress(pid);
+ stopProgress(id);
}
/**
@@ -522,14 +530,15 @@
for (int i = 0; i < total; i++) {
final BugreportInfo info = mProcesses.valueAt(i);
if (info == null) {
- Log.wtf(TAG, "pollProgress(): null info at index " + i + "(pid = "
+ Log.wtf(TAG, "pollProgress(): null info at index " + i + "(ID = "
+ mProcesses.keyAt(i) + ")");
continue;
}
final int pid = info.pid;
+ final int id = info.id;
if (info.finished) {
- if (DEBUG) Log.v(TAG, "Skipping finished process " + pid);
+ if (DEBUG) Log.v(TAG, "Skipping finished process " + pid + "(id: " + id + ")");
continue;
}
activeProcesses++;
@@ -544,13 +553,13 @@
if (progressChanged || maxChanged) {
if (progressChanged) {
- if (DEBUG) Log.v(TAG, "Updating progress for PID " + pid + " from "
- + info.progress + " to " + progress);
+ if (DEBUG) Log.v(TAG, "Updating progress for PID " + pid + "(id: " + id
+ + ") from " + info.progress + " to " + progress);
info.progress = progress;
}
if (maxChanged) {
- Log.i(TAG, "Updating max progress for PID " + pid + " from " + info.max
- + " to " + max);
+ Log.i(TAG, "Updating max progress for PID " + pid + "(id: " + id
+ + ") from " + info.max + " to " + max);
info.max = max;
}
info.lastUpdate = System.currentTimeMillis();
@@ -558,9 +567,9 @@
} else {
long inactiveTime = System.currentTimeMillis() - info.lastUpdate;
if (inactiveTime >= INACTIVITY_TIMEOUT) {
- Log.w(TAG, "No progress update for process " + pid + " since "
+ Log.w(TAG, "No progress update for PID " + pid + " since "
+ info.getFormattedLastUpdate());
- stopProgress(info.pid);
+ stopProgress(info.id);
}
}
}
@@ -572,19 +581,16 @@
* Fetches a {@link BugreportInfo} for a given process and launches a dialog where the user can
* change its values.
*/
- private void launchBugreportInfoDialog(int pid) {
+ private void launchBugreportInfoDialog(int id) {
// Copy values so it doesn't lock mProcesses while UI is being updated
final String name, title, description;
- final BugreportInfo info = getInfo(pid);
+ final BugreportInfo info = getInfo(id);
if (info == null) {
return;
}
- name = info.name;
- title = info.title;
- description = info.description;
collapseNotificationBar();
- mInfoDialog.initialize(mContext, pid, name, title, description);
+ mInfoDialog.initialize(mContext, info);
}
/**
@@ -597,7 +603,7 @@
* Typical usage is delaying when taken from the notification action, and taking it right away
* upon receiving a {@link #INTENT_BUGREPORT_STARTED}.
*/
- private void takeScreenshot(int pid, boolean delayed) {
+ private void takeScreenshot(int id, boolean delayed) {
setTakingScreenshot(true);
if (delayed) {
collapseNotificationBar();
@@ -608,28 +614,28 @@
// Show a toast just once, otherwise it might be captured in the screenshot.
Toast.makeText(mContext, msg, Toast.LENGTH_SHORT).show();
- takeScreenshot(pid, SCREENSHOT_DELAY_SECONDS);
+ takeScreenshot(id, SCREENSHOT_DELAY_SECONDS);
} else {
- takeScreenshot(pid, 0);
+ takeScreenshot(id, 0);
}
}
/**
* Takes a screenshot after {@code delay} seconds.
*/
- private void takeScreenshot(int pid, int delay) {
+ private void takeScreenshot(int id, int delay) {
if (delay > 0) {
- Log.d(TAG, "Taking screenshot for " + pid + " in " + delay + " seconds");
+ Log.d(TAG, "Taking screenshot for " + id + " in " + delay + " seconds");
final Message msg = mMainHandler.obtainMessage();
msg.what = MSG_DELAYED_SCREENSHOT;
- msg.arg1 = pid;
+ msg.arg1 = id;
msg.arg2 = delay - 1;
mMainHandler.sendMessageDelayed(msg, DateUtils.SECOND_IN_MILLIS);
return;
}
// It's time to take the screenshot: let the proper thread handle it
- final BugreportInfo info = getInfo(pid);
+ final BugreportInfo info = getInfo(id);
if (info == null) {
return;
}
@@ -638,7 +644,7 @@
final Message requestMsg = new Message();
requestMsg.what = MSG_SCREENSHOT_REQUEST;
- requestMsg.arg1 = pid;
+ requestMsg.arg1 = id;
requestMsg.obj = screenshotPath;
mScreenshotHandler.sendMessage(requestMsg);
}
@@ -715,30 +721,30 @@
*/
private void stopSelfWhenDone() {
if (mProcesses.size() > 0) {
- if (DEBUG) Log.v(TAG, "Staying alive, waiting for pids " + mProcesses);
+ if (DEBUG) Log.d(TAG, "Staying alive, waiting for IDs " + mProcesses);
return;
}
- Log.v(TAG, "No more pids to handle, shutting down");
+ Log.v(TAG, "No more processes to handle, shutting down");
stopSelf();
}
/**
* Handles the BUGREPORT_FINISHED intent sent by {@code dumpstate}.
*/
- private void onBugreportFinished(int pid, Intent intent) {
+ private void onBugreportFinished(int id, Intent intent) {
final File bugreportFile = getFileExtra(intent, EXTRA_BUGREPORT);
if (bugreportFile == null) {
// Should never happen, dumpstate always set the file.
Log.wtf(TAG, "Missing " + EXTRA_BUGREPORT + " on intent " + intent);
return;
}
- mInfoDialog.onBugreportFinished(pid);
- BugreportInfo info = getInfo(pid);
+ mInfoDialog.onBugreportFinished(id);
+ BugreportInfo info = getInfo(id);
if (info == null) {
// Happens when BUGREPORT_FINISHED was received without a BUGREPORT_STARTED first.
- Log.v(TAG, "Creating info for untracked pid " + pid);
- info = new BugreportInfo(mContext, pid);
- mProcesses.put(pid, info);
+ Log.v(TAG, "Creating info for untracked ID " + id);
+ info = new BugreportInfo(mContext, id);
+ mProcesses.put(id, info);
}
info.renameScreenshots(mScreenshotsDir);
info.bugreportFile = bugreportFile;
@@ -765,7 +771,7 @@
if (!info.bugreportFile.exists() || !info.bugreportFile.canRead()) {
Log.e(TAG, "Could not read bugreport file " + info.bugreportFile);
Toast.makeText(context, R.string.bugreport_unreadable_text, Toast.LENGTH_LONG).show();
- stopProgress(info.pid);
+ stopProgress(info.id);
return;
}
@@ -837,12 +843,12 @@
* Shares the bugreport upon user's request by issuing a {@link Intent#ACTION_SEND_MULTIPLE}
* intent, but issuing a warning dialog the first time.
*/
- private void shareBugreport(int pid, BugreportInfo sharedInfo) {
- BugreportInfo info = getInfo(pid);
+ private void shareBugreport(int id, BugreportInfo sharedInfo) {
+ BugreportInfo info = getInfo(id);
if (info == null) {
// Service was terminated but notification persisted
info = sharedInfo;
- Log.d(TAG, "shareBugreport(): no info for PID " + pid + " on managed processes ("
+ Log.d(TAG, "shareBugreport(): no info for ID " + id + " on managed processes ("
+ mProcesses + "), using info from intent instead (" + info + ")");
}
@@ -863,7 +869,7 @@
mContext.startActivity(notifIntent);
// ... and stop watching this process.
- stopProgress(pid);
+ stopProgress(id);
}
/**
@@ -877,16 +883,16 @@
final Intent shareIntent = new Intent(INTENT_BUGREPORT_SHARE);
shareIntent.setClass(context, BugreportProgressService.class);
shareIntent.setAction(INTENT_BUGREPORT_SHARE);
- shareIntent.putExtra(EXTRA_PID, info.pid);
+ shareIntent.putExtra(EXTRA_ID, info.id);
shareIntent.putExtra(EXTRA_INFO, info);
- final String title = context.getString(R.string.bugreport_finished_title, info.pid);
+ final String title = context.getString(R.string.bugreport_finished_title, info.id);
final Notification.Builder builder = new Notification.Builder(context)
.setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
.setContentTitle(title)
.setTicker(title)
.setContentText(context.getString(R.string.bugreport_finished_text))
- .setContentIntent(PendingIntent.getService(context, info.pid, shareIntent,
+ .setContentIntent(PendingIntent.getService(context, info.id, shareIntent,
PendingIntent.FLAG_UPDATE_CURRENT))
.setDeleteIntent(newCancelIntent(context, info))
.setLocalOnly(true)
@@ -897,8 +903,8 @@
builder.setContentInfo(info.name);
}
- Log.v(TAG, "Sending 'Share' notification for pid " + info.pid + ": " + title);
- NotificationManager.from(context).notify(TAG, info.pid, builder.build());
+ Log.v(TAG, "Sending 'Share' notification for ID " + info.id + ": " + title);
+ NotificationManager.from(context).notify(TAG, info.id, builder.build());
}
/**
@@ -906,7 +912,7 @@
* finishes - at this point there is nothing to be done other than waiting, hence it has no
* pending action.
*/
- private static void sendBugreportBeingUpdatedNotification(Context context, int pid) {
+ private static void sendBugreportBeingUpdatedNotification(Context context, int id) {
final String title = context.getString(R.string.bugreport_updating_title);
final Notification.Builder builder = new Notification.Builder(context)
.setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
@@ -916,8 +922,8 @@
.setLocalOnly(true)
.setColor(context.getColor(
com.android.internal.R.color.system_notification_accent_color));
- Log.v(TAG, "Sending 'Updating zip' notification for pid " + pid + ": " + title);
- NotificationManager.from(context).notify(TAG, pid, builder.build());
+ Log.v(TAG, "Sending 'Updating zip' notification for ID " + id + ": " + title);
+ NotificationManager.from(context).notify(TAG, id, builder.build());
}
/**
@@ -985,7 +991,7 @@
// It's not possible to add a new entry into an existing file, so we need to create a new
// zip, copy all entries, then rename it.
- sendBugreportBeingUpdatedNotification(context, info.pid); // ...and that takes time
+ sendBugreportBeingUpdatedNotification(context, info.id); // ...and that takes time
final File dir = info.bugreportFile.getParentFile();
final File tmpZip = new File(dir, "tmp-" + info.bugreportFile.getName());
Log.d(TAG, "Writing temporary zip file (" + tmpZip + ") with title and/or description");
@@ -1113,8 +1119,8 @@
/**
* Updates the user-provided details of a bugreport.
*/
- private void updateBugreportInfo(int pid, String name, String title, String description) {
- final BugreportInfo info = getInfo(pid);
+ private void updateBugreportInfo(int id, String name, String title, String description) {
+ final BugreportInfo info = getInfo(id);
if (info == null) {
return;
}
@@ -1179,6 +1185,7 @@
private EditText mInfoDescription;
private AlertDialog mDialog;
private Button mOkButton;
+ private int mId;
private int mPid;
/**
@@ -1207,8 +1214,7 @@
/**
* Sets its internal state and displays the dialog.
*/
- private void initialize(Context context, int pid, String name, String title,
- String description) {
+ private void initialize(Context context, BugreportInfo info) {
// First initializes singleton.
if (mDialog == null) {
@SuppressLint("InflateParams")
@@ -1232,7 +1238,7 @@
mDialog = new AlertDialog.Builder(context)
.setView(view)
- .setTitle(context.getString(R.string.bugreport_info_dialog_title, pid))
+ .setTitle(context.getString(R.string.bugreport_info_dialog_title, info.id))
.setCancelable(false)
.setPositiveButton(context.getString(com.android.internal.R.string.ok),
null)
@@ -1258,16 +1264,17 @@
}
// Then set fields.
- mSavedName = mTempName = name;
- mPid = pid;
- if (!TextUtils.isEmpty(name)) {
- mInfoName.setText(name);
+ mSavedName = mTempName = info.name;
+ mId = info.id;
+ mPid = info.pid;
+ if (!TextUtils.isEmpty(info.name)) {
+ mInfoName.setText(info.name);
}
- if (!TextUtils.isEmpty(title)) {
- mInfoTitle.setText(title);
+ if (!TextUtils.isEmpty(info.title)) {
+ mInfoTitle.setText(info.title);
}
- if (!TextUtils.isEmpty(description)) {
- mInfoDescription.setText(description);
+ if (!TextUtils.isEmpty(info.description)) {
+ mInfoDescription.setText(info.description);
}
// And finally display it.
@@ -1290,7 +1297,7 @@
final String title = mInfoTitle.getText().toString();
final String description = mInfoDescription.getText().toString();
- updateBugreportInfo(mPid, name, title, description);
+ updateBugreportInfo(mId, name, title, description);
mDialog.dismiss();
}
});
@@ -1328,7 +1335,7 @@
// Must update system property for the cases where dumpstate finishes
// while the user is still entering other fields (like title or
// description)
- setBugreportNameProperty(mPid, name);
+ setBugreportNameProperty(mId, name);
}
/**
@@ -1337,7 +1344,7 @@
* <p>Once the bugreport is finished dumpstate has already generated the final files, so
* changing the name would have no effect.
*/
- private void onBugreportFinished(int pid) {
+ private void onBugreportFinished(int id) {
if (mInfoName != null) {
mInfoName.setEnabled(false);
mInfoName.setText(mSavedName);
@@ -1353,6 +1360,11 @@
private final Context context;
/**
+ * Sequential, user-friendly id used to identify the bugreport.
+ */
+ final int id;
+
+ /**
* {@code pid} of the {@code dumpstate} process generating the bugreport.
*/
final int pid;
@@ -1426,8 +1438,9 @@
/**
* Constructor for tracked bugreports - typically called upon receiving BUGREPORT_STARTED.
*/
- BugreportInfo(Context context, int pid, String name, int max) {
+ BugreportInfo(Context context, int id, int pid, String name, int max) {
this.context = context;
+ this.id = id;
this.pid = pid;
this.name = name;
this.max = max;
@@ -1437,8 +1450,8 @@
* Constructor for untracked bugreports - typically called upon receiving BUGREPORT_FINISHED
* without a previous call to BUGREPORT_STARTED.
*/
- BugreportInfo(Context context, int pid) {
- this(context, pid, null, 0);
+ BugreportInfo(Context context, int id) {
+ this(context, id, id, null, 0);
this.finished = true;
}
@@ -1494,7 +1507,7 @@
@Override
public String toString() {
final float percent = ((float) progress * 100 / max);
- return "pid: " + pid + ", name: " + name + ", finished: " + finished
+ return "id: " + id + ", pid: " + pid + ", name: " + name + ", finished: " + finished
+ "\n\ttitle: " + title + "\n\tdescription: " + description
+ "\n\tfile: " + bugreportFile + "\n\tscreenshots: " + screenshotFiles
+ "\n\tprogress: " + progress + "/" + max + "(" + percent + ")"
@@ -1506,6 +1519,7 @@
// Parcelable contract
protected BugreportInfo(Parcel in) {
context = null;
+ id = in.readInt();
pid = in.readInt();
name = in.readString();
title = in.readString();
@@ -1527,6 +1541,7 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(id);
dest.writeInt(pid);
dest.writeString(name);
dest.writeString(title);
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index a9b8df2..4cd920a 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -52,8 +52,12 @@
<!-- Tint color for the content on the notification overflow card. -->
<color name="keyguard_overflow_content_color">#ff686868</color>
+ <!-- The disabled recents task bar background color. -->
+ <color name="recents_task_bar_disabled_background_color">#ff676767</color>
<!-- The default recents task bar background color. -->
<color name="recents_task_bar_default_background_color">#ffe6e6e6</color>
+ <!-- The default recents task view background color. -->
+ <color name="recents_task_view_default_background_color">#fff3f3f3</color>
<!-- The recents task bar light text color to be drawn on top of dark backgrounds. -->
<color name="recents_task_bar_light_text_color">#ffeeeeee</color>
<!-- The recents task bar dark text color to be drawn on top of light backgrounds. -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 5d4789a..4116962 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -714,6 +714,8 @@
<string name="recents_search_bar_label">search</string>
<!-- Recents: Launch error string. [CHAR LIMIT=NONE] -->
<string name="recents_launch_error_message">Could not start <xliff:g id="app" example="Calendar">%s</xliff:g>.</string>
+ <!-- Recents: Launch disabled string. [CHAR LIMIT=NONE] -->
+ <string name="recents_launch_disabled_message"><xliff:g id="app" example="Calendar">%s</xliff:g> is disabled in safe-mode.</string>
<!-- Recents: Show history string. [CHAR LIMIT=NONE] -->
<string name="recents_history_button_label">History</string>
<!-- Recents: History clear all string. [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
index 454d1ce..4845425 100755
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
@@ -62,6 +62,7 @@
mPlusPaint;
private float mTextHeight, mWarningTextHeight;
private int mIconTint = Color.WHITE;
+ private float mOldDarkIntensity = 0f;
private int mHeight;
private int mWidth;
@@ -295,6 +296,9 @@
}
public void setDarkIntensity(float darkIntensity) {
+ if (darkIntensity == mOldDarkIntensity) {
+ return;
+ }
int backgroundColor = getBackgroundColor(darkIntensity);
int fillColor = getFillColor(darkIntensity);
mIconTint = fillColor;
@@ -302,6 +306,7 @@
mBoltPaint.setColor(fillColor);
mChargeColor = fillColor;
invalidateSelf();
+ mOldDarkIntensity = darkIntensity;
}
private int getBackgroundColor(float darkIntensity) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 958572f..90d56f7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -475,6 +475,23 @@
break;
}
}
+
+ @Override
+ public void onFingerprintAuthFailed() {
+ final int currentUser = KeyguardUpdateMonitor.getCurrentUser();
+ if (mLockPatternUtils.isSecure(currentUser)) {
+ mLockPatternUtils.getDevicePolicyManager().reportFailedFingerprintAttempt(
+ currentUser);
+ }
+ }
+
+ @Override
+ public void onFingerprintAuthenticated(int userId) {
+ if (mLockPatternUtils.isSecure(userId)) {
+ mLockPatternUtils.getDevicePolicyManager().reportSuccessfulFingerprintAttempt(
+ userId);
+ }
+ }
};
ViewMediatorCallback mViewMediatorCallback = new ViewMediatorCallback() {
@@ -1370,8 +1387,9 @@
* @see #KEYGUARD_DONE
*/
private void handleKeyguardDone(boolean authenticated) {
- if (mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())) {
- mLockPatternUtils.getDevicePolicyManager().reportKeyguardDismissed();
+ final int currentUser = KeyguardUpdateMonitor.getCurrentUser();
+ if (mLockPatternUtils.isSecure(currentUser)) {
+ mLockPatternUtils.getDevicePolicyManager().reportKeyguardDismissed(currentUser);
}
if (DEBUG) Log.d(TAG, "handleKeyguardDone");
synchronized (this) {
@@ -1484,8 +1502,9 @@
* @see #SHOW
*/
private void handleShow(Bundle options) {
- if (mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())) {
- mLockPatternUtils.getDevicePolicyManager().reportKeyguardSecured();
+ final int currentUser = KeyguardUpdateMonitor.getCurrentUser();
+ if (mLockPatternUtils.isSecure(currentUser)) {
+ mLockPatternUtils.getDevicePolicyManager().reportKeyguardSecured(currentUser);
}
synchronized (KeyguardViewMediator.this) {
if (!mSystemReady) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 9da5c2b..e0efaa5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -837,11 +837,13 @@
* Draws the header of a task used for the window animation into a bitmap.
*/
private Bitmap drawThumbnailTransitionBitmap(Task toTask, TaskViewTransform toTransform) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
if (toTransform != null && toTask.key != null) {
Bitmap thumbnail;
synchronized (mHeaderBarLock) {
int toHeaderWidth = (int) toTransform.rect.width();
int toHeaderHeight = (int) (mHeaderBar.getMeasuredHeight() * toTransform.scale);
+ boolean disabledInSafeMode = !toTask.isSystemApp && ssp.isInSafeMode();
mHeaderBar.onTaskViewSizeChanged((int) toTransform.rect.width(),
(int) toTransform.rect.height());
thumbnail = Bitmap.createBitmap(toHeaderWidth, toHeaderHeight,
@@ -851,7 +853,8 @@
} else {
Canvas c = new Canvas(thumbnail);
c.scale(toTransform.scale, toTransform.scale);
- mHeaderBar.rebindToTask(toTask, false /* touchExplorationEnabled */);
+ mHeaderBar.rebindToTask(toTask, false /* touchExplorationEnabled */,
+ disabledInSafeMode);
mHeaderBar.draw(c);
c.setBitmap(null);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java b/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java
index 244c0df..95aa10f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java
@@ -17,6 +17,7 @@
package com.android.systemui.recents.misc;
import android.os.Handler;
+import android.view.ViewDebug;
/**
* A dozer is a class that fires a trigger after it falls asleep.
@@ -26,8 +27,11 @@
Handler mHandler;
+ @ViewDebug.ExportedProperty(category="recents")
boolean mIsDozing;
+ @ViewDebug.ExportedProperty(category="recents")
boolean mHasTriggered;
+ @ViewDebug.ExportedProperty(category="recents")
int mDozeDurationMilliseconds;
Runnable mOnSleepRunnable;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 22ab794..8b4474f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -112,6 +112,8 @@
Display mDisplay;
String mRecentsPackage;
ComponentName mAssistComponent;
+
+ boolean mIsSafeMode;
boolean mHasFreeformWorkspaceSupport;
Bitmap mDummyIcon;
@@ -137,6 +139,7 @@
mPm.hasSystemFeature(PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT) ||
Settings.Global.getInt(context.getContentResolver(),
DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0;
+ mIsSafeMode = mPm.isSafeMode();
// Get the dummy thumbnail width/heights
Resources res = context.getResources();
@@ -187,7 +190,8 @@
rti.firstActiveTime = rti.lastActiveTime = i;
if (i % 2 == 0) {
rti.taskDescription = new ActivityManager.TaskDescription(description,
- Bitmap.createBitmap(mDummyIcon),
+ Bitmap.createBitmap(mDummyIcon), null,
+ 0xFF000000 | (0xFFFFFF & new Random().nextInt()),
0xFF000000 | (0xFFFFFF & new Random().nextInt()));
} else {
rti.taskDescription = new ActivityManager.TaskDescription();
@@ -260,6 +264,13 @@
return mHasFreeformWorkspaceSupport;
}
+ /**
+ * Returns whether this device is in the safe mode.
+ */
+ public boolean isInSafeMode() {
+ return mIsSafeMode;
+ }
+
/** Returns whether the recents is currently running */
public boolean isRecentsTopMost(ActivityManager.RunningTaskInfo topTask,
MutableBoolean isHomeTopMost) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
index c51aa7c..016e6d3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -18,6 +18,7 @@
import android.app.ActivityManager;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
@@ -188,11 +189,15 @@
: null;
Bitmap thumbnail = loader.getAndUpdateThumbnail(taskKey, false);
int activityColor = loader.getActivityPrimaryColor(t.taskDescription);
+ int backgroundColor = loader.getActivityBackgroundColor(t.taskDescription);
+ boolean isSystemApp = (loader.getAndUpdateActivityInfo(taskKey).applicationInfo.flags
+ & ApplicationInfo.FLAG_SYSTEM) != 0;
// Add the task to the stack
Task task = new Task(taskKey, t.affiliatedTaskId, t.affiliatedTaskColor, icon,
thumbnail, title, contentDescription, dismissDescription, activityColor,
- !isStackTask, isLaunchTarget, t.bounds, t.taskDescription);
+ backgroundColor, !isStackTask, isLaunchTarget, isSystemApp, t.bounds,
+ t.taskDescription);
allTasks.add(task);
affiliatedTaskCounts.put(taskKey.id, affiliatedTaskCounts.get(taskKey.id, 0) + 1);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
index 26130ab..5e1af12 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -264,13 +264,16 @@
private int mNumVisibleThumbnailsLoaded;
int mDefaultTaskBarBackgroundColor;
+ int mDefaultTaskViewBackgroundColor;
BitmapDrawable mDefaultIcon;
Bitmap mDefaultThumbnail;
public RecentsTaskLoader(Context context) {
Resources res = context.getResources();
mDefaultTaskBarBackgroundColor =
- res.getColor(R.color.recents_task_bar_default_background_color);
+ context.getColor(R.color.recents_task_bar_default_background_color);
+ mDefaultTaskViewBackgroundColor =
+ context.getColor(R.color.recents_task_view_default_background_color);
mMaxThumbnailCacheSize = res.getInteger(R.integer.config_recents_max_thumbnail_count);
mMaxIconCacheSize = res.getInteger(R.integer.config_recents_max_icon_count);
int iconCacheSize = RecentsDebugFlags.Static.DisableBackgroundCache ? 1 :
@@ -556,10 +559,20 @@
}
/**
+ * Returns the task's background color if possible.
+ */
+ int getActivityBackgroundColor(ActivityManager.TaskDescription td) {
+ if (td != null && td.getBackgroundColor() != 0) {
+ return td.getBackgroundColor();
+ }
+ return mDefaultTaskViewBackgroundColor;
+ }
+
+ /**
* Returns the activity info for the given task key, retrieving one from the system if the
* task key is expired.
*/
- private ActivityInfo getAndUpdateActivityInfo(Task.TaskKey taskKey) {
+ ActivityInfo getAndUpdateActivityInfo(Task.TaskKey taskKey) {
SystemServicesProxy ssp = Recents.getSystemServices();
ComponentName cn = taskKey.getComponent();
ActivityInfo activityInfo = mActivityInfoCache.get(cn);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index 1c277d5..8ed6dd7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -23,6 +23,7 @@
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.view.ViewDebug;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.misc.SystemServicesProxy;
@@ -48,11 +49,17 @@
/* The Task Key represents the unique primary key for the task */
public static class TaskKey {
+ @ViewDebug.ExportedProperty(category="recents")
public final int id;
+ @ViewDebug.ExportedProperty(category="recents")
public int stackId;
+ @ViewDebug.ExportedProperty(category="recents")
public final Intent baseIntent;
+ @ViewDebug.ExportedProperty(category="recents")
public final int userId;
+ @ViewDebug.ExportedProperty(category="recents")
public long firstActiveTime;
+ @ViewDebug.ExportedProperty(category="recents")
public long lastActiveTime;
private int mHashCode;
@@ -105,17 +112,21 @@
}
}
+ @ViewDebug.ExportedProperty(deepExport=true, prefix="key_")
public TaskKey key;
/**
* The group will be computed separately from the initialization of the task
*/
+ @ViewDebug.ExportedProperty(deepExport=true, prefix="group_")
public TaskGrouping group;
/**
* The affiliationTaskId is the task id of the parent task or itself if it is not affiliated
* with any task.
*/
+ @ViewDebug.ExportedProperty(category="recents")
public int affiliationTaskId;
+ @ViewDebug.ExportedProperty(category="recents")
public int affiliationColor;
/**
@@ -124,15 +135,23 @@
*/
public Drawable icon;
public Bitmap thumbnail;
+ @ViewDebug.ExportedProperty(category="recents")
public String title;
+ @ViewDebug.ExportedProperty(category="recents")
public String contentDescription;
+ @ViewDebug.ExportedProperty(category="recents")
public String dismissDescription;
+ @ViewDebug.ExportedProperty(category="recents")
public int colorPrimary;
+ @ViewDebug.ExportedProperty(category="recents")
+ public int colorBackground;
+ @ViewDebug.ExportedProperty(category="recents")
public boolean useLightOnPrimaryColor;
/**
* The bounds of the task, used only if it is a freeform task.
*/
+ @ViewDebug.ExportedProperty(category="recents")
public Rect bounds;
/**
@@ -143,8 +162,12 @@
/**
* The state isLaunchTarget will be set for the correct task upon launching Recents.
*/
+ @ViewDebug.ExportedProperty(category="recents")
public boolean isLaunchTarget;
+ @ViewDebug.ExportedProperty(category="recents")
public boolean isHistorical;
+ @ViewDebug.ExportedProperty(category="recents")
+ public boolean isSystemApp;
private ArrayList<TaskCallbacks> mCallbacks = new ArrayList<>();
@@ -154,8 +177,8 @@
public Task(TaskKey key, int affiliationTaskId, int affiliationColor, Drawable icon,
Bitmap thumbnail, String title, String contentDescription,
- String dismissDescription, int colorPrimary, boolean isHistorical,
- boolean isLaunchTarget, Rect bounds,
+ String dismissDescription, int colorPrimary, int colorBackground,
+ boolean isHistorical, boolean isLaunchTarget, boolean isSystemApp, Rect bounds,
ActivityManager.TaskDescription taskDescription) {
boolean isInAffiliationGroup = (affiliationTaskId != key.id);
boolean hasAffiliationGroupColor = isInAffiliationGroup && (affiliationColor != 0);
@@ -168,12 +191,14 @@
this.contentDescription = contentDescription;
this.dismissDescription = dismissDescription;
this.colorPrimary = hasAffiliationGroupColor ? affiliationColor : colorPrimary;
+ this.colorBackground = colorBackground;
this.useLightOnPrimaryColor = Utilities.computeContrastBetweenColors(this.colorPrimary,
Color.WHITE) > 3f;
this.bounds = bounds;
this.taskDescription = taskDescription;
this.isLaunchTarget = isLaunchTarget;
this.isHistorical = isHistorical;
+ this.isSystemApp = isSystemApp;
}
/** Copies the other task. */
@@ -188,10 +213,12 @@
this.contentDescription = o.contentDescription;
this.dismissDescription = o.dismissDescription;
this.colorPrimary = o.colorPrimary;
+ this.colorBackground = o.colorBackground;
this.useLightOnPrimaryColor = o.useLightOnPrimaryColor;
this.bounds = o.bounds;
this.isLaunchTarget = o.isLaunchTarget;
this.isHistorical = o.isHistorical;
+ this.isSystemApp = o.isSystemApp;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java b/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
index 5842095..4a65374 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
@@ -19,16 +19,22 @@
import android.graphics.Outline;
import android.graphics.Rect;
import android.view.View;
+import android.view.ViewDebug;
import android.view.ViewOutlineProvider;
/* An outline provider that has a clip and outline that can be animated. */
public class AnimateableViewBounds extends ViewOutlineProvider {
View mSourceView;
+ @ViewDebug.ExportedProperty(category="recents")
Rect mClipRect = new Rect();
+ @ViewDebug.ExportedProperty(category="recents")
Rect mClipBounds = new Rect();
+ @ViewDebug.ExportedProperty(category="recents")
Rect mLastClipBounds = new Rect();
+ @ViewDebug.ExportedProperty(category="recents")
int mCornerRadius;
+ @ViewDebug.ExportedProperty(category="recents")
float mAlpha = 1f;
final float mMinAlpha = 0.25f;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 42aaa97..c4db485 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -16,6 +16,8 @@
package com.android.systemui.recents.views;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
@@ -32,6 +34,7 @@
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewDebug;
import android.view.ViewOutlineProvider;
import android.view.ViewPropertyAnimator;
import android.view.WindowInsets;
@@ -79,8 +82,6 @@
import java.util.ArrayList;
import java.util.List;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-
/**
* This view is the the top level layout that contains TaskStacks (which are laid out according
* to their SpaceNode bounds.
@@ -103,6 +104,8 @@
private boolean mAwaitingFirstLayout = true;
private boolean mLastTaskLaunchedWasFreeform;
+
+ @ViewDebug.ExportedProperty(category="recents")
private Rect mSystemInsets = new Rect();
private int mDividerSize;
@@ -110,6 +113,7 @@
private Animator mBackgroundScrimAnimator;
private RecentsTransitionHelper mTransitionHelper;
+ @ViewDebug.ExportedProperty(deepExport=true, prefix="touch_")
private RecentsViewTouchHandler mTouchHandler;
private final FlingAnimationUtils mFlingAnimationUtils;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
index 346ce16..016d937 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
@@ -20,6 +20,7 @@
import android.graphics.Point;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
+import android.view.ViewDebug;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsConfiguration;
@@ -61,12 +62,18 @@
private RecentsView mRv;
+ @ViewDebug.ExportedProperty(deepExport=true, prefix="drag_task")
private Task mDragTask;
+ @ViewDebug.ExportedProperty(deepExport=true, prefix="drag_task_view_")
private TaskView mTaskView;
+ @ViewDebug.ExportedProperty(category="recents")
private Point mTaskViewOffset = new Point();
+ @ViewDebug.ExportedProperty(category="recents")
private Point mDownPos = new Point();
+ @ViewDebug.ExportedProperty(category="recents")
private boolean mDragRequested;
+ @ViewDebug.ExportedProperty(category="recents")
private boolean mIsDragging;
private float mDragSlop;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
index 19ac1e7..98bc92f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
@@ -25,6 +25,7 @@
import android.util.ArraySet;
import android.util.FloatProperty;
import android.util.Property;
+import android.view.ViewDebug;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
@@ -216,15 +217,20 @@
private TaskStackLayoutAlgorithmCallbacks mCb;
// The task bounds (untransformed) for layout. This rect is anchored at mTaskRoot.
+ @ViewDebug.ExportedProperty(category="recents")
public Rect mTaskRect = new Rect();
// The freeform workspace bounds, inset from the top by the search bar, and is a fixed height
+ @ViewDebug.ExportedProperty(category="recents")
public Rect mFreeformRect = new Rect();
// The stack bounds, inset from the top by the search bar, and runs to
// the bottom of the screen
+ @ViewDebug.ExportedProperty(category="recents")
public Rect mStackRect = new Rect();
// This is the current system insets
+ @ViewDebug.ExportedProperty(category="recents")
public Rect mSystemInsets = new Rect();
// This is the bounds of the history button above the stack rect
+ @ViewDebug.ExportedProperty(category="recents")
public Rect mHistoryButtonRect = new Rect();
// The visible ranges when the stack is focused and unfocused
@@ -232,14 +238,17 @@
private Range mFocusedRange;
// The offset from the top when scrolled to the top of the stack
+ @ViewDebug.ExportedProperty(category="recents")
private int mFocusedPeekHeight;
// The offset from the top of the stack to the top of the bounds when the stack is scrolled to
// the end
+ @ViewDebug.ExportedProperty(category="recents")
private int mStackTopOffset;
// The offset from the bottom of the stack to the bottom of the bounds when the stack is
// scrolled to the front
+ @ViewDebug.ExportedProperty(category="recents")
private int mStackBottomOffset;
// The paths defining the motion of the tasks when the stack is focused and unfocused
@@ -250,27 +259,36 @@
// The state of the stack focus (0..1), which controls the transition of the stack from the
// focused to non-focused state
+ @ViewDebug.ExportedProperty(category="recents")
private float mFocusState;
// The animator used to reset the focused state
private ObjectAnimator mFocusStateAnimator;
// The smallest scroll progress, at this value, the back most task will be visible
+ @ViewDebug.ExportedProperty(category="recents")
float mMinScrollP;
// The largest scroll progress, at this value, the front most task will be visible above the
// navigation bar
+ @ViewDebug.ExportedProperty(category="recents")
float mMaxScrollP;
// The initial progress that the scroller is set when you first enter recents
+ @ViewDebug.ExportedProperty(category="recents")
float mInitialScrollP;
// The task progress for the front-most task in the stack
+ @ViewDebug.ExportedProperty(category="recents")
float mFrontMostTaskP;
// The last computed task counts
+ @ViewDebug.ExportedProperty(category="recents")
int mNumStackTasks;
+ @ViewDebug.ExportedProperty(category="recents")
int mNumFreeformTasks;
// The min/max z translations
+ @ViewDebug.ExportedProperty(category="recents")
int mMinTranslationZ;
+ @ViewDebug.ExportedProperty(category="recents")
int mMaxTranslationZ;
// Optimization, allows for quick lookup of task -> index
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index fb3515a..9560eb4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -20,8 +20,6 @@
import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.ComponentName;
@@ -40,11 +38,10 @@
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewDebug;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.animation.Interpolator;
-import android.view.animation.PathInterpolator;
import android.widget.FrameLayout;
import com.android.internal.logging.MetricsLogger;
@@ -121,8 +118,11 @@
LayoutInflater mInflater;
TaskStack mStack;
+ @ViewDebug.ExportedProperty(deepExport=true, prefix="layout_")
TaskStackLayoutAlgorithm mLayoutAlgorithm;
+ @ViewDebug.ExportedProperty(deepExport=true, prefix="scroller_")
TaskStackViewScroller mStackScroller;
+ @ViewDebug.ExportedProperty(deepExport=true, prefix="touch_")
TaskStackViewTouchHandler mTouchHandler;
TaskStackAnimationHelper mAnimationHelper;
GradientDrawable mFreeformWorkspaceBackground;
@@ -134,25 +134,36 @@
ArraySet<Task.TaskKey> mIgnoreTasks = new ArraySet<>();
AnimationProps mDeferredTaskViewLayoutAnimation = null;
+ @ViewDebug.ExportedProperty(deepExport=true, prefix="doze_")
DozeTrigger mUIDozeTrigger;
+ @ViewDebug.ExportedProperty(deepExport=true, prefix="focused_task_")
Task mFocusedTask;
int mTaskCornerRadiusPx;
private int mDividerSize;
private int mStartTimerIndicatorDuration;
+ @ViewDebug.ExportedProperty(category="recents")
boolean mTaskViewsClipDirty = true;
+ @ViewDebug.ExportedProperty(category="recents")
boolean mAwaitingFirstLayout = true;
+ @ViewDebug.ExportedProperty(category="recents")
boolean mInMeasureLayout = false;
+ @ViewDebug.ExportedProperty(category="recents")
boolean mEnterAnimationComplete = false;
+ @ViewDebug.ExportedProperty(category="recents")
boolean mTouchExplorationEnabled;
+ @ViewDebug.ExportedProperty(category="recents")
boolean mScreenPinningEnabled;
// The stable stack bounds are the full bounds that we were measured with from RecentsView
+ @ViewDebug.ExportedProperty(category="recents")
private Rect mStableStackBounds = new Rect();
// The current stack bounds are dynamic and may change as the user drags and drops
+ @ViewDebug.ExportedProperty(category="recents")
private Rect mStackBounds = new Rect();
+ @ViewDebug.ExportedProperty(category="recents")
private int[] mTmpVisibleRange = new int[2];
private Rect mTmpRect = new Rect();
private ArrayMap<Task.TaskKey, TaskView> mTmpTaskViewMap = new ArrayMap<>();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
index d1bce55..b54ea1d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
@@ -23,6 +23,7 @@
import android.util.FloatProperty;
import android.util.Log;
import android.util.Property;
+import android.view.ViewDebug;
import android.widget.OverScroller;
import com.android.systemui.Interpolators;
@@ -63,6 +64,7 @@
TaskStackLayoutAlgorithm mLayoutAlgorithm;
TaskStackViewScrollerCallbacks mCb;
+ @ViewDebug.ExportedProperty(category="recents")
float mStackScrollP;
float mFlingDownScrollP;
int mFlingDownY;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index 5d1bb66..55f9fba 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -28,6 +28,7 @@
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
+import android.view.ViewDebug;
import android.view.ViewParent;
import android.view.animation.Animation;
import android.view.animation.Interpolator;
@@ -69,6 +70,7 @@
FlingAnimationUtils mFlingAnimUtils;
ValueAnimator mScrollFlingAnimator;
+ @ViewDebug.ExportedProperty(category="recents")
boolean mIsScrolling;
float mDownScrollP;
int mDownX, mDownY;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 850e36e7..0f485ac 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -35,8 +35,10 @@
import android.util.Property;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewDebug;
import android.view.ViewOutlineProvider;
import android.view.animation.AccelerateInterpolator;
+import android.widget.Toast;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.MetricsProto.MetricsEvent;
@@ -105,31 +107,46 @@
}
};
+ @ViewDebug.ExportedProperty(category="recents")
float mTaskProgress;
+ @ViewDebug.ExportedProperty(category="recents")
float mMaxDimScale;
+ @ViewDebug.ExportedProperty(category="recents")
int mDimAlpha;
AccelerateInterpolator mDimInterpolator = new AccelerateInterpolator(3f);
PorterDuffColorFilter mDimColorFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_ATOP);
Paint mDimLayerPaint = new Paint();
float mActionButtonTranslationZ;
+ @ViewDebug.ExportedProperty(deepExport=true, prefix="task_")
Task mTask;
+ @ViewDebug.ExportedProperty(category="recents")
boolean mTaskDataLoaded;
+ @ViewDebug.ExportedProperty(category="recents")
boolean mClipViewInStack = true;
+ @ViewDebug.ExportedProperty(category="recents")
boolean mTouchExplorationEnabled;
+ @ViewDebug.ExportedProperty(category="recents")
+ boolean mIsDisabledInSafeMode;
+ @ViewDebug.ExportedProperty(deepExport=true, prefix="view_bounds_")
AnimateableViewBounds mViewBounds;
private AnimatorSet mTransformAnimation;
private ArrayList<Animator> mTmpAnimators = new ArrayList<>();
View mContent;
+ @ViewDebug.ExportedProperty(deepExport=true, prefix="thumbnail_")
TaskViewThumbnail mThumbnailView;
+ @ViewDebug.ExportedProperty(deepExport=true, prefix="header_")
TaskViewHeader mHeaderView;
View mActionButtonView;
TaskViewCallbacks mCb;
+ @ViewDebug.ExportedProperty(category="recents")
Point mDownTouchPos = new Point();
+ private Toast mDisabledAppToast;
+
public TaskView(Context context) {
this(context, null);
}
@@ -549,15 +566,17 @@
/**** TaskCallbacks Implementation ****/
public void onTaskBound(Task t) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
mTask = t;
mTask.addCallback(this);
+ mIsDisabledInSafeMode = !mTask.isSystemApp && ssp.isInSafeMode();
}
@Override
public void onTaskDataLoaded(Task task) {
// Bind each of the views to the new task data
- mThumbnailView.rebindToTask(mTask);
- mHeaderView.rebindToTask(mTask, mTouchExplorationEnabled);
+ mThumbnailView.rebindToTask(mTask, mIsDisabledInSafeMode);
+ mHeaderView.rebindToTask(mTask, mTouchExplorationEnabled, mIsDisabledInSafeMode);
mTaskDataLoaded = true;
}
@@ -572,13 +591,24 @@
@Override
public void onTaskStackIdChanged() {
- mHeaderView.rebindToTask(mTask, mTouchExplorationEnabled);
+ mHeaderView.rebindToTask(mTask, mTouchExplorationEnabled, mIsDisabledInSafeMode);
}
/**** View.OnClickListener Implementation ****/
@Override
public void onClick(final View v) {
+ if (mIsDisabledInSafeMode) {
+ Context context = getContext();
+ String msg = context.getString(R.string.recents_launch_disabled_message, mTask.title);
+ if (mDisabledAppToast != null) {
+ mDisabledAppToast.cancel();
+ }
+ mDisabledAppToast = Toast.makeText(context, msg, Toast.LENGTH_SHORT);
+ mDisabledAppToast.show();
+ return;
+ }
+
boolean screenPinningRequested = false;
if (v == mActionButtonView) {
// Reset the translation of the action button before we animate it out
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index c91a833..bb56a52 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -36,6 +36,7 @@
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewAnimationUtils;
+import android.view.ViewDebug;
import android.view.ViewStub;
import android.widget.FrameLayout;
import android.widget.ImageView;
@@ -74,6 +75,8 @@
private Paint mHighlightPaint = new Paint();
private Paint mBackgroundPaint = new Paint();
+ private int mColor;
+ private float mDimAlpha;
public HighlightColorDrawable() {
mBackgroundPaint.setColor(Color.argb(255, 0, 0, 0));
@@ -83,15 +86,19 @@
}
public void setColorAndDim(int color, float dimAlpha) {
- mBackgroundPaint.setColor(color);
+ if (mColor != color || Float.compare(mDimAlpha, dimAlpha) != 0) {
+ mColor = color;
+ mDimAlpha = dimAlpha;
+ mBackgroundPaint.setColor(color);
- ColorUtils.colorToHSL(color, mTmpHSL);
- // TODO: Consider using the saturation of the color to adjust the lightness as well
- mTmpHSL[2] = Math.min(1f,
- mTmpHSL[2] + HIGHLIGHT_LIGHTNESS_INCREMENT * (1.0f - dimAlpha));
- mHighlightPaint.setColor(ColorUtils.HSLToColor(mTmpHSL));
+ ColorUtils.colorToHSL(color, mTmpHSL);
+ // TODO: Consider using the saturation of the color to adjust the lightness as well
+ mTmpHSL[2] = Math.min(1f,
+ mTmpHSL[2] + HIGHLIGHT_LIGHTNESS_INCREMENT * (1.0f - dimAlpha));
+ mHighlightPaint.setColor(ColorUtils.HSLToColor(mTmpHSL));
- invalidateSelf();
+ invalidateSelf();
+ }
}
@Override
@@ -121,6 +128,10 @@
public int getOpacity() {
return PixelFormat.OPAQUE;
}
+
+ public int getColor() {
+ return mColor;
+ }
}
Task mTask;
@@ -139,9 +150,11 @@
ProgressBar mFocusTimerIndicator;
// Header drawables
+ @ViewDebug.ExportedProperty(category="recents")
Rect mTaskViewRect = new Rect();
int mCornerRadius;
int mHighlightHeight;
+ @ViewDebug.ExportedProperty(category="recents")
float mDimAlpha;
Drawable mLightDismissDrawable;
Drawable mDarkDismissDrawable;
@@ -153,6 +166,7 @@
Drawable mDarkInfoIcon;
int mTaskBarViewLightTextColor;
int mTaskBarViewDarkTextColor;
+ int mDisabledTaskBarBackgroundColor;
int mMoveTaskTargetStackId = INVALID_STACK_ID;
// Header background
@@ -195,6 +209,8 @@
mDarkFullscreenIcon = context.getDrawable(R.drawable.recents_move_task_fullscreen_dark);
mLightInfoIcon = context.getDrawable(R.drawable.recents_info_light);
mDarkInfoIcon = context.getDrawable(R.drawable.recents_info_dark);
+ mDisabledTaskBarBackgroundColor =
+ context.getColor(R.color.recents_task_bar_disabled_background_color);
// Configure the background and dim
mBackground = new HighlightColorDrawable();
@@ -331,17 +347,17 @@
*/
void setDimAlpha(float dimAlpha) {
mDimAlpha = dimAlpha;
- updateBackgroundColor(dimAlpha);
+ updateBackgroundColor(mBackground.getColor(), dimAlpha);
}
/**
* Updates the background and highlight colors for this header.
*/
- private void updateBackgroundColor(float dimAlpha) {
+ private void updateBackgroundColor(int color, float dimAlpha) {
if (mTask != null) {
- mBackground.setColorAndDim(mTask.colorPrimary, dimAlpha);
+ mBackground.setColorAndDim(color, dimAlpha);
// TODO: Consider using the saturation of the color to adjust the lightness as well
- ColorUtils.colorToHSL(mTask.colorPrimary, mTmpHSL);
+ ColorUtils.colorToHSL(color, mTmpHSL);
mTmpHSL[2] = Math.min(1f, mTmpHSL[2] + OVERLAY_LIGHTNESS_INCREMENT * (1.0f - dimAlpha));
mOverlayBackground.setColorAndDim(ColorUtils.HSLToColor(mTmpHSL), dimAlpha);
mDimLayerPaint.setAlpha((int) (dimAlpha * 255));
@@ -350,12 +366,15 @@
}
/** Binds the bar view to the task */
- public void rebindToTask(Task t, boolean touchExplorationEnabled) {
+ public void rebindToTask(Task t, boolean touchExplorationEnabled, boolean disabledInSafeMode) {
mTask = t;
// If an activity icon is defined, then we use that as the primary icon to show in the bar,
// otherwise, we fall back to the application icon
- updateBackgroundColor(mDimAlpha);
+ int primaryColor = disabledInSafeMode
+ ? mDisabledTaskBarBackgroundColor
+ : t.colorPrimary;
+ updateBackgroundColor(primaryColor, mDimAlpha);
if (t.icon != null) {
mIconView.setImageDrawable(t.icon);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
index f90951e..0fec9c3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
@@ -21,13 +21,17 @@
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
import android.graphics.LightingColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
+import android.graphics.Region;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.View;
+import android.view.ViewDebug;
import com.android.systemui.R;
import com.android.systemui.recents.model.Task;
@@ -39,27 +43,40 @@
*/
public class TaskViewThumbnail extends View {
+
+ private static final ColorMatrix TMP_FILTER_COLOR_MATRIX = new ColorMatrix();
+ private static final ColorMatrix TMP_BRIGHTNESS_COLOR_MATRIX = new ColorMatrix();
+
private Task mTask;
// Drawing
+ @ViewDebug.ExportedProperty(category="recents")
Rect mThumbnailRect = new Rect();
+ @ViewDebug.ExportedProperty(category="recents")
Rect mTaskViewRect = new Rect();
int mCornerRadius;
+ @ViewDebug.ExportedProperty(category="recents")
float mDimAlpha;
Matrix mScaleMatrix = new Matrix();
Paint mDrawPaint = new Paint();
+ Paint mBgFillPaint = new Paint();
BitmapShader mBitmapShader;
LightingColorFilter mLightingColorFilter = new LightingColorFilter(0xffffffff, 0);
// Task bar clipping, the top of this thumbnail can be clipped against the opaque header
// bar that overlaps this thumbnail
View mTaskBar;
+ @ViewDebug.ExportedProperty(category="recents")
Rect mClipRect = new Rect();
// Visibility optimization, if the thumbnail height is less than the height of the header
// bar for the task view, then just mark this thumbnail view as invisible
+ @ViewDebug.ExportedProperty(category="recents")
boolean mInvisible;
+ @ViewDebug.ExportedProperty(category="recents")
+ boolean mDisabledInSafeMode;
+
public TaskViewThumbnail(Context context) {
this(context, null);
}
@@ -79,6 +96,7 @@
mDrawPaint.setAntiAlias(true);
mCornerRadius = getResources().getDimensionPixelSize(
R.dimen.recents_task_view_rounded_corners_radius);
+ mBgFillPaint.setColor(Color.WHITE);
}
/**
@@ -100,10 +118,39 @@
if (mInvisible) {
return;
}
- // Draw the thumbnail with the rounded corners
- canvas.drawRoundRect(0, 0, mTaskViewRect.width(), mTaskViewRect.height(),
- mCornerRadius,
- mCornerRadius, mDrawPaint);
+
+ int thumbnailHeight = (int) (((float) mTaskViewRect.width() / mThumbnailRect.width()) *
+ mThumbnailRect.height());
+ if (thumbnailHeight >= mTaskViewRect.height()) {
+ // The thumbnail fills the full task view bounds, so just draw it
+ canvas.drawRoundRect(0, 0, mTaskViewRect.width(), mTaskViewRect.height(),
+ mCornerRadius, mCornerRadius, mDrawPaint);
+ } else {
+ int count = 0;
+ if (thumbnailHeight > 0) {
+ // The thumbnail only covers part of the task view bounds, so fill in the
+ // non-thumbnail space with the default background color. This is the equivalent of
+ // the GL border texture mode.
+ count = canvas.save();
+
+ // Since we only want the top corners to be rounded, draw slightly beyond the
+ // thumbnail height, but clip to the thumbnail height
+ canvas.clipRect(0, 0, mTaskViewRect.width(), thumbnailHeight, Region.Op.REPLACE);
+ canvas.drawRoundRect(0, 0, mTaskViewRect.width(), thumbnailHeight + mCornerRadius,
+ mCornerRadius, mCornerRadius, mDrawPaint);
+ }
+
+ // In the remaining space, draw the background color
+ canvas.clipRect(0, thumbnailHeight, mTaskViewRect.width(), mTaskViewRect.height(),
+ Region.Op.REPLACE);
+ canvas.drawRoundRect(0, Math.max(0, thumbnailHeight - mCornerRadius),
+ mTaskViewRect.width(), mTaskViewRect.height(), mCornerRadius, mCornerRadius,
+ mBgFillPaint);
+
+ if (thumbnailHeight > 0) {
+ canvas.restoreToCount(count);
+ }
+ }
}
/** Sets the thumbnail to a given bitmap. */
@@ -128,9 +175,27 @@
}
int mul = (int) ((1.0f - mDimAlpha) * 255);
if (mBitmapShader != null) {
- mLightingColorFilter.setColorMultiply(Color.argb(255, mul, mul, mul));
- mDrawPaint.setColorFilter(mLightingColorFilter);
- mDrawPaint.setColor(0xffffffff);
+ if (mDisabledInSafeMode) {
+ // Brightness: C-new = C-old*(1-amount) + amount
+ TMP_FILTER_COLOR_MATRIX.setSaturation(0);
+ float scale = 1f - mDimAlpha;
+ float[] mat = TMP_BRIGHTNESS_COLOR_MATRIX.getArray();
+ mat[0] = scale;
+ mat[6] = scale;
+ mat[12] = scale;
+ mat[4] = mDimAlpha;
+ mat[9] = mDimAlpha;
+ mat[14] = mDimAlpha;
+ TMP_FILTER_COLOR_MATRIX.preConcat(TMP_BRIGHTNESS_COLOR_MATRIX);
+ ColorMatrixColorFilter filter = new ColorMatrixColorFilter(TMP_FILTER_COLOR_MATRIX);
+ mDrawPaint.setColorFilter(filter);
+ mBgFillPaint.setColorFilter(filter);
+ } else {
+ mLightingColorFilter.setColorMultiply(Color.argb(255, mul, mul, mul));
+ mDrawPaint.setColorFilter(mLightingColorFilter);
+ mDrawPaint.setColor(0xFFffffff);
+ mBgFillPaint.setColorFilter(mLightingColorFilter);
+ }
} else {
int grey = mul;
mDrawPaint.setColorFilter(null);
@@ -196,10 +261,14 @@
}
/** Binds the thumbnail view to the task */
- void rebindToTask(Task t) {
+ void rebindToTask(Task t, boolean disabledInSafeMode) {
mTask = t;
+ mDisabledInSafeMode = disabledInSafeMode;
if (t.thumbnail != null) {
setThumbnail(t.thumbnail);
+ if (t.colorBackground != 0) {
+ mBgFillPaint.setColor(t.colorBackground);
+ }
} else {
setThumbnail(null);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 7d37ad2..39a36cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -38,6 +38,7 @@
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.ContentObserver;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.AsyncTask;
@@ -116,7 +117,7 @@
ExpandableNotificationRow.ExpansionLogger, NotificationData.Environment,
ExpandableNotificationRow.OnExpandClickListener {
public static final String TAG = "StatusBar";
- public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ public static final boolean DEBUG = false;
public static final boolean MULTIUSER_DEBUG = false;
public static final boolean ENABLE_REMOTE_INPUT =
@@ -194,6 +195,7 @@
private final SparseBooleanArray mUsersAllowingPrivateNotifications = new SparseBooleanArray();
private UserManager mUserManager;
+ private int mDensity;
// UI-specific methods
@@ -633,18 +635,22 @@
mLocale = currentConfig.locale;
mLayoutDirection = TextUtils.getLayoutDirectionFromLocale(mLocale);
mFontScale = currentConfig.fontScale;
+ mDensity = currentConfig.densityDpi;
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
// Connect in to the status bar manager service
mCommandQueue = new CommandQueue(this);
- int[] switches = new int[8];
+ int[] switches = new int[9];
ArrayList<IBinder> binders = new ArrayList<IBinder>();
ArrayList<String> iconSlots = new ArrayList<>();
ArrayList<StatusBarIcon> icons = new ArrayList<>();
+ Rect fullscreenStackBounds = new Rect();
+ Rect dockedStackBounds = new Rect();
try {
- mBarService.registerStatusBar(mCommandQueue, iconSlots, icons, switches, binders);
+ mBarService.registerStatusBar(mCommandQueue, iconSlots, icons, switches, binders,
+ fullscreenStackBounds, dockedStackBounds);
} catch (RemoteException ex) {
// If the system process isn't there we're doomed anyway.
}
@@ -653,7 +659,8 @@
mSettingsObserver.onChange(false); // set up
disable(switches[0], switches[6], false /* animate */);
- setSystemUiVisibility(switches[1], 0xffffffff);
+ setSystemUiVisibility(switches[1], switches[7], switches[8], 0xffffffff,
+ fullscreenStackBounds, dockedStackBounds);
topAppWindowChanged(switches[2] != 0);
// StatusBarManagerService has a back up of IME token and it's restored here.
setImeWindowStatus(binders.get(0), switches[3], switches[4], switches[5] != 0);
@@ -813,8 +820,13 @@
final Locale locale = mContext.getResources().getConfiguration().locale;
final int ld = TextUtils.getLayoutDirectionFromLocale(locale);
final float fontScale = newConfig.fontScale;
-
- if (! locale.equals(mLocale) || ld != mLayoutDirection || fontScale != mFontScale) {
+ final int density = newConfig.densityDpi;
+ if (density != mDensity || mFontScale != fontScale) {
+ reInflateViews();
+ mDensity = density;
+ mFontScale = fontScale;
+ }
+ if (! locale.equals(mLocale) || ld != mLayoutDirection) {
if (DEBUG) {
Log.v(TAG, String.format(
"config changed locale/LD: %s (%d) -> %s (%d)", mLocale, mLayoutDirection,
@@ -826,6 +838,21 @@
}
}
+ protected void reInflateViews() {
+ ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
+ for (int i = 0; i < activeNotifications.size(); i++) {
+ Entry entry = activeNotifications.get(i);
+ boolean exposedGuts = entry.row.getGuts() == mNotificationGutsExposed;
+ entry.row.reInflateViews();
+ if (exposedGuts) {
+ mNotificationGutsExposed = entry.row.getGuts();
+ bindGuts(entry.row);
+ }
+ entry.cacheContentViews(mContext, null /* updatedNotification */);
+ inflateViews(entry, mStackScroller);
+ }
+ }
+
protected View bindVetoButtonClickListener(View row, StatusBarNotification n) {
View vetoButton = row.findViewById(R.id.veto);
final String _pkg = n.getPackageName();
@@ -2080,8 +2107,25 @@
return false;
}
- if (isSnoozedPackage(sbn)) {
- if (DEBUG) Log.d(TAG, "No peeking: snoozed package: " + sbn.getKey());
+ boolean inUse = mPowerManager.isScreenOn()
+ && (!mStatusBarKeyguardViewManager.isShowing()
+ || mStatusBarKeyguardViewManager.isOccluded())
+ && !mStatusBarKeyguardViewManager.isInputRestricted();
+ try {
+ inUse = inUse && !mDreamManager.isDreaming();
+ } catch (RemoteException e) {
+ Log.d(TAG, "failed to query dream manager", e);
+ }
+
+ if (!inUse) {
+ if (DEBUG) {
+ Log.d(TAG, "No peeking: not in use: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (mNotificationData.shouldSuppressScreenOn(sbn.getKey())) {
+ if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey());
return false;
}
@@ -2090,15 +2134,17 @@
return false;
}
- if (sbn.getNotification().fullScreenIntent != null
- && mAccessibilityManager.isTouchExplorationEnabled()) {
- if (DEBUG) Log.d(TAG, "No peeking: accessible fullscreen: " + sbn.getKey());
- return false;
+ if (sbn.getNotification().fullScreenIntent != null) {
+ if (mAccessibilityManager.isTouchExplorationEnabled()) {
+ if (DEBUG) Log.d(TAG, "No peeking: accessible fullscreen: " + sbn.getKey());
+ return false;
+ } else {
+ return true;
+ }
}
-
- if (mNotificationData.shouldSuppressScreenOn(sbn.getKey())) {
- if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey());
+ if (isSnoozedPackage(sbn)) {
+ if (DEBUG) Log.d(TAG, "No peeking: snoozed package: " + sbn.getKey());
return false;
}
@@ -2107,17 +2153,7 @@
return false;
}
- boolean inUse = mPowerManager.isScreenOn()
- && (!mStatusBarKeyguardViewManager.isShowing()
- || mStatusBarKeyguardViewManager.isOccluded())
- && !mStatusBarKeyguardViewManager.isInputRestricted();
- try {
- inUse = inUse && !mDreamManager.isDreaming();
- } catch (RemoteException e) {
- Log.d(TAG, "failed to query dream manager", e);
- }
- if (DEBUG) Log.d(TAG, "peek if device in use: " + inUse);
- return inUse;
+ return true;
}
protected abstract boolean isSnoozedPackage(StatusBarNotification sbn);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 71347c4..3b960ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -17,12 +17,14 @@
package com.android.systemui.statusbar;
import android.content.ComponentName;
+import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.util.Pair;
+import com.android.internal.os.SomeArgs;
import com.android.internal.statusbar.IStatusBar;
import com.android.internal.statusbar.StatusBarIcon;
@@ -94,7 +96,8 @@
public void animateExpandNotificationsPanel();
public void animateCollapsePanels(int flags);
public void animateExpandSettingsPanel(String obj);
- public void setSystemUiVisibility(int vis, int mask);
+ public void setSystemUiVisibility(int vis, int fullscreenStackVis,
+ int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds);
public void topAppWindowChanged(boolean visible);
public void setImeWindowStatus(IBinder token, int vis, int backDisposition,
boolean showImeSwitcher);
@@ -169,11 +172,19 @@
}
}
- public void setSystemUiVisibility(int vis, int mask) {
+ public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
+ int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
synchronized (mLock) {
// Don't coalesce these, since it might have one time flags set such as
// STATUS_BAR_UNHIDE which might get lost.
- mHandler.obtainMessage(MSG_SET_SYSTEMUI_VISIBILITY, vis, mask, null).sendToTarget();
+ SomeArgs args = SomeArgs.obtain();
+ args.argi1 = vis;
+ args.argi2 = fullscreenStackVis;
+ args.argi3 = dockedStackVis;
+ args.argi4 = mask;
+ args.arg1 = fullscreenStackBounds;
+ args.arg2 = dockedStackBounds;
+ mHandler.obtainMessage(MSG_SET_SYSTEMUI_VISIBILITY, args).sendToTarget();
}
}
@@ -377,7 +388,10 @@
mCallbacks.animateExpandSettingsPanel((String) msg.obj);
break;
case MSG_SET_SYSTEMUI_VISIBILITY:
- mCallbacks.setSystemUiVisibility(msg.arg1, msg.arg2);
+ SomeArgs args = (SomeArgs) msg.obj;
+ mCallbacks.setSystemUiVisibility(args.argi1, args.argi2, args.argi3,
+ args.argi4, (Rect) args.arg1, (Rect) args.arg2);
+ args.recycle();
break;
case MSG_TOP_APP_WINDOW_CHANGED:
mCallbacks.topAppWindowChanged(msg.arg1 != 0);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 84b2031..264655c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -25,6 +25,7 @@
import android.os.Build;
import android.service.notification.StatusBarNotification;
import android.util.AttributeSet;
+import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.NotificationHeaderView;
import android.view.View;
@@ -50,11 +51,11 @@
private static final int DEFAULT_DIVIDER_ALPHA = 0x29;
private static final int COLORED_DIVIDER_ALPHA = 0x7B;
- private final int mNotificationMinHeightLegacy;
- private final int mMaxHeadsUpHeightLegacy;
- private final int mMaxHeadsUpHeight;
- private final int mNotificationMinHeight;
- private final int mNotificationMaxHeight;
+ private int mNotificationMinHeightLegacy;
+ private int mMaxHeadsUpHeightLegacy;
+ private int mMaxHeadsUpHeight;
+ private int mNotificationMinHeight;
+ private int mNotificationMaxHeight;
/** Does this row contain layouts that can adapt to row expansion */
private boolean mExpandable;
@@ -507,6 +508,27 @@
mHeadsUpManager = headsUpManager;
}
+ public void reInflateViews() {
+ initDimens();
+ if (mIsSummaryWithChildren) {
+ removeView(mNotificationHeader);
+ mNotificationHeader = null;
+ recreateNotificationHeader();
+ if (mChildrenContainer != null) {
+ mChildrenContainer.reInflateViews();
+ }
+ }
+ if (mGuts != null) {
+ View oldGuts = mGuts;
+ int index = indexOfChild(oldGuts);
+ removeView(oldGuts);
+ mGuts = (NotificationGuts) LayoutInflater.from(mContext).inflate(
+ R.layout.notification_guts, this, false);
+ mGuts.setVisibility(oldGuts.getVisibility());
+ addView(mGuts, index);
+ }
+ }
+
public interface ExpansionLogger {
public void logNotificationExpansion(String key, boolean userAction, boolean expanded);
}
@@ -514,6 +536,10 @@
public ExpandableNotificationRow(Context context, AttributeSet attrs) {
super(context, attrs);
mFalsingManager = FalsingManager.getInstance(context);
+ initDimens();
+ }
+
+ private void initDimens() {
mNotificationMinHeightLegacy = getResources().getDimensionPixelSize(
R.dimen.notification_min_height_legacy);
mNotificationMinHeight = getResources().getDimensionPixelSize(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
index 6801e5f..9aa5ea0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
@@ -21,6 +21,7 @@
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Color;
+import android.graphics.Rect;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
import android.telephony.SubscriptionInfo;
@@ -80,6 +81,7 @@
private ArrayList<PhoneState> mPhoneStates = new ArrayList<PhoneState>();
private int mIconTint = Color.WHITE;
private float mDarkIntensity;
+ private final Rect mTintArea = new Rect();
ViewGroup mEthernetGroup, mWifiGroup;
View mNoSimsCombo;
@@ -490,23 +492,31 @@
}
}
- public void setIconTint(int tint, float darkIntensity) {
- boolean changed = tint != mIconTint || darkIntensity != mDarkIntensity;
+ public void setIconTint(int tint, float darkIntensity, Rect tintArea) {
+ boolean changed = tint != mIconTint || darkIntensity != mDarkIntensity
+ || !mTintArea.equals(tintArea);
mIconTint = tint;
mDarkIntensity = darkIntensity;
+ mTintArea.set(tintArea);
if (changed && isAttachedToWindow()) {
applyIconTint();
}
}
private void applyIconTint() {
- setTint(mVpn, mIconTint);
- setTint(mAirplane, mIconTint);
- applyDarkIntensity(mDarkIntensity, mNoSims, mNoSimsDark);
- applyDarkIntensity(mDarkIntensity, mWifi, mWifiDark);
- applyDarkIntensity(mDarkIntensity, mEthernet, mEthernetDark);
+ setTint(mVpn, StatusBarIconController.getTint(mTintArea, mVpn, mIconTint));
+ setTint(mAirplane, StatusBarIconController.getTint(mTintArea, mAirplane, mIconTint));
+ applyDarkIntensity(
+ StatusBarIconController.getDarkIntensity(mTintArea, mNoSims, mDarkIntensity),
+ mNoSims, mNoSimsDark);
+ applyDarkIntensity(
+ StatusBarIconController.getDarkIntensity(mTintArea, mWifi, mDarkIntensity),
+ mWifi, mWifiDark);
+ applyDarkIntensity(
+ StatusBarIconController.getDarkIntensity(mTintArea, mEthernet, mDarkIntensity),
+ mEthernet, mEthernetDark);
for (int i = 0; i < mPhoneStates.size(); i++) {
- mPhoneStates.get(i).setIconTint(mIconTint, mDarkIntensity);
+ mPhoneStates.get(i).setIconTint(mIconTint, mDarkIntensity, mTintArea);
}
}
@@ -613,9 +623,11 @@
}
}
- public void setIconTint(int tint, float darkIntensity) {
- applyDarkIntensity(darkIntensity, mMobile, mMobileDark);
- setTint(mMobileType, tint);
+ public void setIconTint(int tint, float darkIntensity, Rect tintArea) {
+ applyDarkIntensity(
+ StatusBarIconController.getDarkIntensity(tintArea, mMobile, darkIntensity),
+ mMobile, mMobileDark);
+ setTint(mMobileType, StatusBarIconController.getTint(tintArea, mMobileType, tint));
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightStatusBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightStatusBarController.java
new file mode 100644
index 0000000..f98b9e5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightStatusBarController.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.graphics.Rect;
+import android.view.View;
+
+import com.android.systemui.statusbar.policy.BatteryController;
+
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
+
+/**
+ * Controls how light status bar flag applies to the icons.
+ */
+public class LightStatusBarController {
+
+ private final StatusBarIconController mIconController;
+ private final BatteryController mBatteryController;
+ private FingerprintUnlockController mFingerprintUnlockController;
+
+ private int mFullscreenStackVisibility;
+ private int mDockedStackVisibility;
+ private boolean mFullscreenLight;
+ private boolean mDockedLight;
+
+ private final Rect mLastFullscreenBounds = new Rect();
+ private final Rect mLastDockedBounds = new Rect();
+
+ public LightStatusBarController(StatusBarIconController iconController,
+ BatteryController batteryController) {
+ mIconController = iconController;
+ mBatteryController = batteryController;
+ }
+
+ public void setFingerprintUnlockController(
+ FingerprintUnlockController fingerprintUnlockController) {
+ mFingerprintUnlockController = fingerprintUnlockController;
+ }
+
+ public void onSystemUiVisibilityChanged(int fullscreenStackVis, int dockedStackVis, int mask,
+ Rect fullscreenStackBounds, Rect dockedStackBounds, boolean sbModeChanged,
+ int statusBarMode) {
+ int oldFullscreen = mFullscreenStackVisibility;
+ int newFullscreen = (oldFullscreen & ~mask) | (fullscreenStackVis & mask);
+ int diffFullscreen = newFullscreen ^ oldFullscreen;
+ int oldDocked = mDockedStackVisibility;
+ int newDocked = (oldDocked & ~mask) | (dockedStackVis & mask);
+ int diffDocked = newDocked ^ oldDocked;
+ if ((diffFullscreen & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0
+ || (diffDocked & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0
+ || sbModeChanged
+ || !mLastFullscreenBounds.equals(fullscreenStackBounds)
+ || !mLastDockedBounds.equals(dockedStackBounds)) {
+
+ mFullscreenLight = isLight(newFullscreen, statusBarMode);
+ mDockedLight = isLight(newDocked, statusBarMode);
+ update(fullscreenStackBounds, dockedStackBounds);
+ }
+ mFullscreenStackVisibility = newFullscreen;
+ mDockedStackVisibility = newDocked;
+ mLastFullscreenBounds.set(fullscreenStackBounds);
+ mLastDockedBounds.set(dockedStackBounds);
+ }
+
+ private boolean isLight(int vis, int statusBarMode) {
+ boolean isTransparentBar = (statusBarMode == MODE_TRANSPARENT
+ || statusBarMode == MODE_LIGHTS_OUT_TRANSPARENT);
+ boolean allowLight = isTransparentBar && !mBatteryController.isPowerSave();
+ boolean light = (vis & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0;
+ return allowLight && light;
+ }
+
+ private boolean animateChange() {
+ if (mFingerprintUnlockController == null) {
+ return false;
+ }
+ int unlockMode = mFingerprintUnlockController.getMode();
+ return unlockMode != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
+ && unlockMode != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK;
+ }
+
+ private void update(Rect fullscreenStackBounds, Rect dockedStackBounds) {
+ boolean hasDockedStack = !dockedStackBounds.isEmpty();
+
+ // If both are light or fullscreen is light and there is no docked stack, all icons get
+ // dark.
+ if ((mFullscreenLight && mDockedLight) || (mFullscreenLight && !hasDockedStack)) {
+ mIconController.setIconsDarkArea(null);
+ mIconController.setIconsDark(true, animateChange());
+
+ }
+
+ // If no one is light or the fullscreen is not light and there is no docked stack,
+ // all icons become white.
+ else if ((!mFullscreenLight && !mDockedLight) || (!mFullscreenLight && !hasDockedStack)) {
+ mIconController.setIconsDark(false, animateChange());
+
+ }
+
+ // Not the same for every stack, magic!
+ else {
+ Rect bounds = mFullscreenLight ? fullscreenStackBounds : dockedStackBounds;
+ if (bounds.isEmpty()) {
+ mIconController.setIconsDarkArea(null);
+ } else {
+ mIconController.setIconsDarkArea(bounds);
+ }
+ mIconController.setIconsDark(true, animateChange());
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 03a597c..6e345f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -4,6 +4,7 @@
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Color;
+import android.graphics.Rect;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
@@ -32,6 +33,7 @@
protected View mNotificationIconArea;
private IconMerger mNotificationIcons;
private ImageView mMoreIcon;
+ private final Rect mTintArea = new Rect();
public NotificationIconAreaController(Context context, PhoneStatusBar phoneStatusBar) {
mPhoneStatusBar = phoneStatusBar;
@@ -67,6 +69,20 @@
}
/**
+ * See {@link StatusBarIconController#setIconsDarkArea}.
+ *
+ * @param tintArea the area in which to tint the icons, specified in screen coordinates
+ */
+ public void setTintArea(Rect tintArea) {
+ if (tintArea == null) {
+ mTintArea.setEmpty();
+ } else {
+ mTintArea.set(tintArea);
+ }
+ applyNotificationIconsTint();
+ }
+
+ /**
* Sets the color that should be used to tint any icons in the notification area. If this
* method is not called, the default tint is {@link Color#WHITE}.
*/
@@ -145,7 +161,8 @@
boolean isPreL = Boolean.TRUE.equals(v.getTag(R.id.icon_is_pre_L));
boolean colorize = !isPreL || NotificationUtils.isGrayscale(v, mNotificationColorUtil);
if (colorize) {
- v.setImageTintList(ColorStateList.valueOf(mIconTint));
+ v.setImageTintList(ColorStateList.valueOf(
+ StatusBarIconController.getTint(mTintArea, v, mIconTint)));
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index f382fe0..4ff7154 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -69,6 +69,7 @@
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.Vibrator;
@@ -296,6 +297,7 @@
BrightnessMirrorController mBrightnessMirrorController;
AccessibilityController mAccessibilityController;
FingerprintUnlockController mFingerprintUnlockController;
+ LightStatusBarController mLightStatusBarController;
int mNaturalBarHeight = -1;
@@ -361,6 +363,8 @@
// tracking calls to View.setSystemUiVisibility()
int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE;
+ private final Rect mLastFullscreenStackBounds = new Rect();
+ private final Rect mLastDockedStackBounds = new Rect();
// last value sent to window manager
private int mLastDispatchedSystemUiVisibility = ~View.SYSTEM_UI_FLAG_VISIBLE;
@@ -754,19 +758,8 @@
mStackScroller.setOverflowContainer(mKeyguardIconOverflowContainer);
- mEmptyShadeView = (EmptyShadeView) LayoutInflater.from(mContext).inflate(
- R.layout.status_bar_no_notifications, mStackScroller, false);
- mStackScroller.setEmptyShadeView(mEmptyShadeView);
- mDismissView = (DismissView) LayoutInflater.from(mContext).inflate(
- R.layout.status_bar_notification_dismiss_all, mStackScroller, false);
- mDismissView.setOnButtonClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- MetricsLogger.action(mContext, MetricsEvent.ACTION_DISMISS_ALL_NOTES);
- clearAllNotifications();
- }
- });
- mStackScroller.setDismissView(mDismissView);
+ inflateEmptyShadeView();
+ inflateDismissView();
mExpandedContents = mStackScroller;
mBackdrop = (BackDropView) mStatusBarWindow.findViewById(R.id.backdrop);
@@ -855,6 +848,8 @@
mAccessibilityController = new AccessibilityController(mContext);
mKeyguardBottomArea.setAccessibilityController(mAccessibilityController);
mNextAlarmController = new NextAlarmController(mContext);
+ mLightStatusBarController = new LightStatusBarController(mIconController,
+ mBatteryController);
mKeyguardMonitor = new KeyguardMonitor(mContext);
if (UserManager.get(mContext).isUserSwitcherEnabled()) {
mUserSwitcherController = new UserSwitcherController(mContext, mKeyguardMonitor,
@@ -930,6 +925,34 @@
return mStatusBarView;
}
+ @Override
+ protected void reInflateViews() {
+ super.reInflateViews();
+ inflateDismissView();
+ updateClearAll();
+ inflateEmptyShadeView();
+ updateEmptyShadeView();
+ }
+
+ private void inflateEmptyShadeView() {
+ mEmptyShadeView = (EmptyShadeView) LayoutInflater.from(mContext).inflate(
+ R.layout.status_bar_no_notifications, mStackScroller, false);
+ mStackScroller.setEmptyShadeView(mEmptyShadeView);
+ }
+
+ private void inflateDismissView() {
+ mDismissView = (DismissView) LayoutInflater.from(mContext).inflate(
+ R.layout.status_bar_notification_dismiss_all, mStackScroller, false);
+ mDismissView.setOnButtonClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ MetricsLogger.action(mContext, MetricsEvent.ACTION_DISMISS_ALL_NOTES);
+ clearAllNotifications();
+ }
+ });
+ mStackScroller.setDismissView(mDismissView);
+ }
+
protected void createUserSwitcher() {
mKeyguardUserSwitcher = new KeyguardUserSwitcher(mContext,
(ViewStub) mStatusBarWindow.findViewById(R.id.keyguard_user_switcher),
@@ -1072,6 +1095,7 @@
mFingerprintUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
mRemoteInputController.addCallback(mStatusBarKeyguardViewManager);
mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback();
+ mLightStatusBarController.setFingerprintUnlockController(mFingerprintUnlockController);
}
@Override
@@ -2507,7 +2531,8 @@
}
@Override // CommandQueue
- public void setSystemUiVisibility(int vis, int mask) {
+ public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
+ int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
final int oldVal = mSystemUiVisibility;
final int newVal = (oldVal&~mask) | (vis&mask);
final int diff = newVal ^ oldVal;
@@ -2516,6 +2541,7 @@
Integer.toHexString(vis), Integer.toHexString(mask),
Integer.toHexString(oldVal), Integer.toHexString(newVal),
Integer.toHexString(diff)));
+ boolean sbModeChanged = false;
if (diff != 0) {
// we never set the recents bit via this method, so save the prior state to prevent
// clobbering the bit below
@@ -2549,7 +2575,7 @@
oldVal, newVal, mNavigationBarView.getBarTransitions(),
View.NAVIGATION_BAR_TRANSIENT, View.NAVIGATION_BAR_TRANSLUCENT,
View.NAVIGATION_BAR_TRANSPARENT);
- final boolean sbModeChanged = sbMode != -1;
+ sbModeChanged = sbMode != -1;
final boolean nbModeChanged = nbMode != -1;
boolean checkBarModes = false;
if (sbModeChanged && sbMode != mStatusBarMode) {
@@ -2576,18 +2602,6 @@
mSystemUiVisibility &= ~View.NAVIGATION_BAR_UNHIDE;
}
- if ((diff & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0 || sbModeChanged) {
- boolean isTransparentBar = (mStatusBarMode == MODE_TRANSPARENT
- || mStatusBarMode == MODE_LIGHTS_OUT_TRANSPARENT);
- boolean allowLight = isTransparentBar && !mBatteryController.isPowerSave();
- boolean light = (vis & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0;
- boolean animate = mFingerprintUnlockController == null
- || (mFingerprintUnlockController.getMode()
- != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
- && mFingerprintUnlockController.getMode()
- != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK);
- mIconController.setIconsDark(allowLight && light, animate);
- }
// restore the recents bit
if (wasRecentsVisible) {
mSystemUiVisibility |= View.RECENT_APPS_VISIBLE;
@@ -2596,6 +2610,9 @@
// send updated sysui visibility to window manager
notifyUiVisibilityChanged(mSystemUiVisibility);
}
+
+ mLightStatusBarController.onSystemUiVisibilityChanged(fullscreenStackVis, dockedStackVis,
+ mask, fullscreenStackBounds, dockedStackBounds, sbModeChanged, mStatusBarMode);
}
private int computeBarMode(int oldVis, int newVis, BarTransitions transitions,
@@ -2723,9 +2740,12 @@
public void setLightsOn(boolean on) {
Log.v(TAG, "setLightsOn(" + on + ")");
if (on) {
- setSystemUiVisibility(0, View.SYSTEM_UI_FLAG_LOW_PROFILE);
+ setSystemUiVisibility(0, 0, 0, View.SYSTEM_UI_FLAG_LOW_PROFILE,
+ mLastFullscreenStackBounds, mLastDockedStackBounds);
} else {
- setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE, View.SYSTEM_UI_FLAG_LOW_PROFILE);
+ setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE, 0, 0,
+ View.SYSTEM_UI_FLAG_LOW_PROFILE, mLastFullscreenStackBounds,
+ mLastDockedStackBounds);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index 45aae2d..f61f31e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -22,6 +22,7 @@
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Color;
+import android.graphics.Rect;
import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.os.Handler;
@@ -58,8 +59,8 @@
public class StatusBarIconController extends StatusBarIconList implements Tunable {
public static final long DEFAULT_TINT_ANIMATION_DURATION = 120;
-
public static final String ICON_BLACKLIST = "icon_blacklist";
+ public static final int DEFAULT_ICON_TINT = Color.WHITE;
private Context mContext;
private PhoneStatusBar mPhoneStatusBar;
@@ -79,8 +80,11 @@
private int mIconSize;
private int mIconHPadding;
- private int mIconTint = Color.WHITE;
+ private int mIconTint = DEFAULT_ICON_TINT;
private float mDarkIntensity;
+ private final Rect mTintArea = new Rect();
+ private static final Rect sTmpRect = new Rect();
+ private static final int[] sTmpInt2 = new int[2];
private boolean mTransitionPending;
private boolean mTintChangePending;
@@ -395,6 +399,25 @@
}
}
+ /**
+ * Sets the dark area so {@link #setIconsDark} only affects the icons in the specified area.
+ *
+ * @param darkArea the area in which icons should change it's tint, in logical screen
+ * coordinates
+ */
+ public void setIconsDarkArea(Rect darkArea) {
+ if (darkArea == null && mTintArea.isEmpty()) {
+ return;
+ }
+ if (darkArea == null) {
+ mTintArea.setEmpty();
+ } else {
+ mTintArea.set(darkArea);
+ }
+ applyIconTint();
+ mNotificationIconAreaController.setTintArea(darkArea);
+ }
+
public void setIconsDark(boolean dark, boolean animate) {
if (!animate) {
setIconTintInternal(dark ? 1.0f : 0.0f);
@@ -446,14 +469,60 @@
mPendingDarkIntensity = darkIntensity;
}
+ /**
+ * @return the tint to apply to {@param view} depending on the desired tint {@param color} and
+ * the screen {@param tintArea} in which to apply that tint
+ */
+ public static int getTint(Rect tintArea, View view, int color) {
+ if (isInArea(tintArea, view)) {
+ return color;
+ } else {
+ return DEFAULT_ICON_TINT;
+ }
+ }
+
+ /**
+ * @return the dark intensity to apply to {@param view} depending on the desired dark
+ * {@param intensity} and the screen {@param tintArea} in which to apply that intensity
+ */
+ public static float getDarkIntensity(Rect tintArea, View view, float intensity) {
+ if (isInArea(tintArea, view)) {
+ return intensity;
+ } else {
+ return 0f;
+ }
+ }
+
+ /**
+ * @return true if more than half of the {@param view} area are in {@param area}, false
+ * otherwise
+ */
+ private static boolean isInArea(Rect area, View view) {
+ if (area.isEmpty()) {
+ return true;
+ }
+ sTmpRect.set(area);
+ view.getLocationOnScreen(sTmpInt2);
+ int left = sTmpInt2[0];
+
+ int intersectStart = Math.max(left, area.left);
+ int intersectEnd = Math.min(left + view.getWidth(), area.right);
+ int intersectAmount = Math.max(0, intersectEnd - intersectStart);
+
+ boolean coversFullStatusBar = area.top <= 0;
+ boolean majorityOfWidth = 2 * intersectAmount > view.getWidth();
+ return majorityOfWidth && coversFullStatusBar;
+ }
+
private void applyIconTint() {
for (int i = 0; i < mStatusIcons.getChildCount(); i++) {
StatusBarIconView v = (StatusBarIconView) mStatusIcons.getChildAt(i);
- v.setImageTintList(ColorStateList.valueOf(mIconTint));
+ v.setImageTintList(ColorStateList.valueOf(getTint(mTintArea, v, mIconTint)));
}
- mSignalCluster.setIconTint(mIconTint, mDarkIntensity);
- mBatteryMeterView.setDarkIntensity(mDarkIntensity);
- mClock.setTextColor(mIconTint);
+ mSignalCluster.setIconTint(mIconTint, mDarkIntensity, mTintArea);
+ mBatteryMeterView.setDarkIntensity(
+ isInArea(mTintArea, mBatteryMeterView) ? mDarkIntensity : 0);
+ mClock.setTextColor(getTint(mTintArea, mClock, mIconTint));
}
public void appTransitionPending() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
index 5cfcd89..49aec42 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -43,16 +43,16 @@
private static final int NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED = 5;
private static final int NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED = 8;
- private final int mChildPadding;
- private final int mDividerHeight;
- private final int mMaxNotificationHeight;
private final List<View> mDividers = new ArrayList<>();
private final List<ExpandableNotificationRow> mChildren = new ArrayList<>();
- private final int mNotificationHeaderHeight;
- private final int mNotificationAppearDistance;
- private final int mNotificatonTopPadding;
private final HybridNotificationViewManager mHybridViewManager;
- private final float mCollapsedBottompadding;
+ private int mChildPadding;
+ private int mDividerHeight;
+ private int mMaxNotificationHeight;
+ private int mNotificationHeaderHeight;
+ private int mNotificationAppearDistance;
+ private int mNotificatonTopPadding;
+ private float mCollapsedBottompadding;
private ViewInvertHelper mOverflowInvertHelper;
private boolean mChildrenExpanded;
private ExpandableNotificationRow mNotificationParent;
@@ -76,6 +76,11 @@
public NotificationChildrenContainer(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ initDimens();
+ mHybridViewManager = new HybridNotificationViewManager(getContext(), this);
+ }
+
+ private void initDimens() {
mChildPadding = getResources().getDimensionPixelSize(
R.dimen.notification_children_padding);
mDividerHeight = Math.max(1, getResources().getDimensionPixelSize(
@@ -89,7 +94,6 @@
mNotificatonTopPadding = getResources().getDimensionPixelSize(
R.dimen.notification_children_container_top_padding);
mCollapsedBottompadding = 11.5f * getResources().getDisplayMetrics().density;
- mHybridViewManager = new HybridNotificationViewManager(getContext(), this);
}
@Override
@@ -461,4 +465,16 @@
mOverflowInvertHelper.setInverted(dark, fade, delay);
}
}
+
+ public void reInflateViews() {
+ initDimens();
+ for (int i = 0; i < mDividers.size(); i++) {
+ View prevDivider = mDividers.get(i);
+ int index = indexOfChild(prevDivider);
+ removeView(prevDivider);
+ View divider = inflateDivider();
+ addView(divider, index);
+ mDividers.set(i, divider);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index a75ac82..bf4245b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -2138,7 +2138,7 @@
}
private void updateAnimationState(View child) {
- updateAnimationState((mAnimationsEnabled || isPinnedHeadsUp(child)) && mIsExpanded, child);
+ updateAnimationState(mAnimationsEnabled && (mIsExpanded || isPinnedHeadsUp(child)), child);
}
@@ -2874,13 +2874,23 @@
}
public void setDismissView(DismissView dismissView) {
+ int index = -1;
+ if (mDismissView != null) {
+ index = indexOfChild(mDismissView);
+ removeView(mDismissView);
+ }
mDismissView = dismissView;
- addView(mDismissView);
+ addView(mDismissView, index);
}
public void setEmptyShadeView(EmptyShadeView emptyShadeView) {
+ int index = -1;
+ if (mEmptyShadeView != null) {
+ index = indexOfChild(mEmptyShadeView);
+ removeView(mEmptyShadeView);
+ }
mEmptyShadeView = emptyShadeView;
- addView(mEmptyShadeView);
+ addView(mEmptyShadeView, index);
}
public void updateEmptyShadeView(boolean visible) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index 784f610..110258c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.tv;
import android.content.ComponentName;
+import android.graphics.Rect;
import android.os.IBinder;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
@@ -68,7 +69,8 @@
}
@Override
- public void setSystemUiVisibility(int vis, int mask) {
+ public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
+ int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
}
@Override
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 830b0f2..f8bf59d2 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -87,6 +87,7 @@
import android.util.AtomicFile;
import android.util.EventLog;
import android.util.Log;
+import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.StringBuilderPrinter;
@@ -142,6 +143,7 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.InflaterInputStream;
@@ -786,8 +788,9 @@
case MSG_OP_COMPLETE:
{
try {
- BackupRestoreTask task = (BackupRestoreTask) msg.obj;
- task.operationComplete(msg.arg1);
+ Pair<BackupRestoreTask, Long> taskWithResult =
+ (Pair<BackupRestoreTask, Long>) msg.obj;
+ taskWithResult.first.operationComplete(taskWithResult.second);
} catch (ClassCastException e) {
Slog.e(TAG, "Invalid completion in flight, obj=" + msg.obj);
}
@@ -1044,7 +1047,7 @@
// If Encrypted file systems is enabled or disabled, this call will return the
// correct directory.
- mBaseStateDir = new File(Environment.getSecureDataDirectory(), "backup");
+ mBaseStateDir = new File(Environment.getDataDirectory(), "backup");
mBaseStateDir.mkdirs();
if (!SELinux.restorecon(mBaseStateDir)) {
Slog.e(TAG, "SELinux restorecon failed on " + mBaseStateDir);
@@ -2460,7 +2463,7 @@
void execute();
// An operation that wanted a callback has completed
- void operationComplete(int result);
+ void operationComplete(long result);
// An operation that wanted a callback has timed out
void handleTimeout();
@@ -3095,7 +3098,7 @@
}
@Override
- public void operationComplete(int unusedResult) {
+ public void operationComplete(long unusedResult) {
// The agent reported back to us!
if (mBackupData == null) {
@@ -3494,7 +3497,7 @@
*/
int preflightFullBackup(PackageInfo pkg, IBackupAgent agent);
- long expectedSize();
+ long getExpectedSizeOrErrorCode();
};
class FullBackupEngine {
@@ -4532,7 +4535,7 @@
// a standalone thread. The runner owns this half of the pipe, and closes
// it to indicate EOD to the other end.
class SinglePackageBackupPreflight implements BackupRestoreTask, FullBackupPreflight {
- final AtomicInteger mResult = new AtomicInteger();
+ final AtomicLong mResult = new AtomicLong();
final CountDownLatch mLatch = new CountDownLatch(1);
final IBackupTransport mTransport;
@@ -4554,7 +4557,11 @@
// now wait to get our result back
mLatch.await();
- int totalSize = mResult.get();
+ long totalSize = mResult.get();
+ // If preflight timeouted, mResult will contain error code as int.
+ if (totalSize < 0) {
+ return (int) totalSize;
+ }
if (MORE_DEBUG) {
Slog.v(TAG, "Got preflight response; size=" + totalSize);
}
@@ -4581,7 +4588,7 @@
}
@Override
- public void operationComplete(int result) {
+ public void operationComplete(long result) {
// got the callback, and our preflightFullBackup() method is waiting for the result
if (MORE_DEBUG) {
Slog.i(TAG, "Preflight op complete, result=" + result);
@@ -4600,7 +4607,7 @@
}
@Override
- public long expectedSize() {
+ public long getExpectedSizeOrErrorCode() {
try {
mLatch.await();
return mResult.get();
@@ -4649,7 +4656,7 @@
}
long expectedSize() {
- return mPreflight.expectedSize();
+ return mPreflight.getExpectedSizeOrErrorCode();
}
}
}
@@ -8558,7 +8565,7 @@
}
@Override
- public void operationComplete(int unusedResult) {
+ public void operationComplete(long unusedResult) {
if (MORE_DEBUG) {
Slog.i(TAG, "operationComplete() during restore: target="
+ mCurrentPackage.packageName
@@ -9643,9 +9650,8 @@
// The completion callback, if any, is invoked on the handler
if (op != null && op.callback != null) {
- Message msg = mBackupHandler.obtainMessage(MSG_OP_COMPLETE, op.callback);
- // NB: this cannot distinguish between results > 2 gig
- msg.arg1 = (result > Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) result;
+ Pair<BackupRestoreTask, Long> callbackAndResult = Pair.create(op.callback, result);
+ Message msg = mBackupHandler.obtainMessage(MSG_OP_COMPLETE, callbackAndResult);
mBackupHandler.sendMessage(msg);
}
}
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index bbf881b..e745263 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -54,7 +54,7 @@
public Trampoline(Context context) {
mContext = context;
- File dir = new File(Environment.getSecureDataDirectory(), "backup");
+ File dir = new File(Environment.getDataDirectory(), "backup");
dir.mkdirs();
mSuppressFile = new File(dir, BACKUP_SUPPRESS_FILENAME);
mGlobalDisable = SystemProperties.getBoolean(BACKUP_DISABLE_PROPERTY, false);
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index a4455e9..07d472d 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -16,6 +16,8 @@
package com.android.server;
+import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED;
+
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
@@ -882,6 +884,11 @@
@Override
public int checkAudioOperation(int code, int usage, int uid, String packageName) {
+ if (isApplicationSuspended(packageName, uid)) {
+ Log.i(TAG, "Audio disabled for suspended package=" + packageName + " for uid=" + uid);
+ return AppOpsManager.MODE_IGNORED;
+ }
+
synchronized (this) {
final int mode = checkRestrictionLocked(code, usage, uid, packageName);
if (mode != AppOpsManager.MODE_ALLOWED) {
@@ -891,6 +898,23 @@
return checkOperation(code, uid, packageName);
}
+ private boolean isApplicationSuspended(String pkg, int uid) {
+ int userId = UserHandle.getUserId(uid);
+
+ ApplicationInfo ai;
+ try {
+ ai = AppGlobals.getPackageManager().getApplicationInfo(pkg, 0, userId);
+ if (ai == null) {
+ Log.w(TAG, "No application info for package " + pkg + " and user " + userId);
+ return false;
+ }
+ } catch (RemoteException re) {
+ throw new SecurityException("Could not talk to package manager service");
+ }
+
+ return ((ai.flags & FLAG_SUSPENDED) != 0);
+ }
+
private int checkRestrictionLocked(int code, int usage, int uid, String packageName) {
final SparseArray<Restriction> usageRestrictions = mAudioRestrictions.get(code);
if (usageRestrictions != null) {
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index 4dbb490..c318140 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -17,6 +17,7 @@
package com.android.server;
import android.app.ActivityManagerNative;
+import android.app.KeyguardManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -34,6 +35,7 @@
import android.content.res.Resources;
import static android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE;
+import static android.content.Context.KEYGUARD_SERVICE;
import static android.content.Context.USER_SERVICE;
import static android.Manifest.permission.READ_CONTACTS;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
@@ -124,7 +126,7 @@
@Override
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
- mLockSettingsService.maybeShowEncryptionNotification(UserHandle.ALL);
+ mLockSettingsService.maybeShowEncryptionNotifications();
} else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
// TODO
}
@@ -176,22 +178,48 @@
* If the account is credential-encrypted, show notification requesting the user to unlock
* the device.
*/
- private void maybeShowEncryptionNotification(UserHandle userHandle) {
- if (UserHandle.ALL.equals(userHandle)) {
- final List<UserInfo> users = mUserManager.getUsers();
- for (int i = 0; i < users.size(); i++) {
- UserHandle user = users.get(i).getUserHandle();
- if (!mUserManager.isUserUnlocked(user)) {
- showEncryptionNotification(user);
+ private void maybeShowEncryptionNotifications() {
+ final List<UserInfo> users = mUserManager.getUsers();
+ for (int i = 0; i < users.size(); i++) {
+ UserInfo user = users.get(i);
+ UserHandle userHandle = user.getUserHandle();
+ if (!mUserManager.isUserUnlocked(userHandle)) {
+ if (!user.isManagedProfile()) {
+ showEncryptionNotification(userHandle);
+ } else {
+ UserInfo parent = mUserManager.getProfileParent(user.id);
+ if (parent != null && mUserManager.isUserUnlocked(parent.getUserHandle())) {
+ // Only show notifications for managed profiles once their parent
+ // user is unlocked.
+ showEncryptionNotificationForProfile(userHandle);
+ }
}
}
- } else if (!mUserManager.isUserUnlocked(userHandle)){
- showEncryptionNotification(userHandle);
}
}
+ private void showEncryptionNotificationForProfile(UserHandle user) {
+ Resources r = mContext.getResources();
+ CharSequence title = r.getText(
+ com.android.internal.R.string.user_encrypted_title);
+ CharSequence message = r.getText(
+ com.android.internal.R.string.profile_encrypted_message);
+ CharSequence detail = r.getText(
+ com.android.internal.R.string.profile_encrypted_detail);
+
+ final KeyguardManager km = (KeyguardManager) mContext.getSystemService(KEYGUARD_SERVICE);
+ final Intent unlockIntent = km.createConfirmDeviceCredentialIntent(null, null, user.getIdentifier());
+ if (unlockIntent == null) {
+ return;
+ }
+ unlockIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ PendingIntent intent = PendingIntent.getActivity(mContext, 0, unlockIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+
+ showEncryptionNotification(user, title, message, detail, intent);
+ }
+
private void showEncryptionNotification(UserHandle user) {
- if (DEBUG) Slog.v(TAG, "showing encryption notification, user: " + user.getIdentifier());
Resources r = mContext.getResources();
CharSequence title = r.getText(
com.android.internal.R.string.user_encrypted_title);
@@ -203,6 +231,12 @@
PendingIntent intent = PendingIntent.getBroadcast(mContext, 0, ACTION_NULL,
PendingIntent.FLAG_UPDATE_CURRENT);
+ showEncryptionNotification(user, title, message, detail, intent);
+ }
+
+ private void showEncryptionNotification(UserHandle user, CharSequence title, CharSequence message,
+ CharSequence detail, PendingIntent intent) {
+ if (DEBUG) Slog.v(TAG, "showing encryption notification, user: " + user.getIdentifier());
Notification notification = new Notification.Builder(mContext)
.setSmallIcon(com.android.internal.R.drawable.ic_user_secure)
.setWhen(0)
@@ -230,8 +264,21 @@
hideEncryptionNotification(new UserHandle(userId));
}
- public void onUnlockUser(int userHandle) {
- hideEncryptionNotification(new UserHandle(userHandle));
+ public void onUnlockUser(int userId) {
+ hideEncryptionNotification(new UserHandle(userId));
+
+ // Now we have unlocked the parent user we should show notifications
+ // about any profiles that exist.
+ List<UserInfo> profiles = mUserManager.getProfiles(userId);
+ for (int i = 0; i < profiles.size(); i++) {
+ UserInfo profile = profiles.get(i);
+ if (profile.isManagedProfile()) {
+ UserHandle userHandle = profile.getUserHandle();
+ if (!mUserManager.isUserUnlocked(userHandle)) {
+ showEncryptionNotificationForProfile(userHandle);
+ }
+ }
+ }
}
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index cbd477a..d09edc8 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -1473,7 +1473,7 @@
}
mSettingsFile = new AtomicFile(
- new File(Environment.getSystemSecureDirectory(), "storage.xml"));
+ new File(Environment.getDataSystemDirectory(), "storage.xml"));
synchronized (mLock) {
readSettingsLocked();
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 95f5734..799d0bd 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -2086,7 +2086,7 @@
final int oldUidFirewallRule = uidFirewallRules.get(uid, FIREWALL_RULE_DEFAULT);
if (DBG) {
Slog.d(TAG, "oldRule = " + oldUidFirewallRule
- + ", newRule=" + rule + " for uid=" + uid);
+ + ", newRule=" + rule + " for uid=" + uid + " on chain " + chain);
}
if (oldUidFirewallRule == rule) {
if (DBG) Slog.d(TAG, "!!!!! Skipping change");
diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java
index b984e19..879bb6f 100644
--- a/services/core/java/com/android/server/NetworkScoreService.java
+++ b/services/core/java/com/android/server/NetworkScoreService.java
@@ -234,12 +234,24 @@
// safety as scores should never be compared across apps; in practice, Settings should
// only be allowing valid apps to be set as scorers, so failure here should be rare.
clearInternal();
+ // Get the scorer that is about to be replaced, if any, so we can notify it directly.
+ NetworkScorerAppData prevScorer = NetworkScorerAppManager.getActiveScorer(mContext);
boolean result = NetworkScorerAppManager.setActiveScorer(mContext, packageName);
- if (result) {
+ if (result) { // new scorer successfully set
registerPackageReceiverIfNeeded();
Intent intent = new Intent(NetworkScoreManager.ACTION_SCORER_CHANGED);
- intent.putExtra(NetworkScoreManager.EXTRA_NEW_SCORER, packageName);
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ if (prevScorer != null) { // Directly notify the old scorer.
+ intent.setPackage(prevScorer.mPackageName);
+ // TODO: Need to update when we support per-user scorers. http://b/23422763
+ mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
+ }
+
+ if (packageName != null) { // Then notify the new scorer
+ intent.putExtra(NetworkScoreManager.EXTRA_NEW_SCORER, packageName);
+ intent.setPackage(packageName);
+ // TODO: Need to update when we support per-user scorers. http://b/23422763
+ mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
+ }
}
return result;
} finally {
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 2683be6..ca1e371 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -3809,7 +3809,7 @@
}
private static String getDatabaseName(int userId) {
- File systemDir = Environment.getSystemSecureDirectory();
+ File systemDir = Environment.getDataSystemDirectory();
File databaseFile = new File(Environment.getUserSystemDirectory(userId), DATABASE_NAME);
if (userId == 0) {
// Migrate old file, if it exists, to the new location.
diff --git a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
index bef6f0a..1b9d968 100644
--- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
+++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
@@ -62,7 +62,7 @@
static final boolean DEBUG_LRU = DEBUG_ALL || false;
static final boolean DEBUG_MU = DEBUG_ALL || false;
static final boolean DEBUG_OOM_ADJ = DEBUG_ALL || false;
- static final boolean DEBUG_PAUSE = DEBUG_ALL || true;
+ static final boolean DEBUG_PAUSE = DEBUG_ALL || false;
static final boolean DEBUG_POWER = DEBUG_ALL || false;
static final boolean DEBUG_POWER_QUICK = DEBUG_POWER || false;
static final boolean DEBUG_PROCESS_OBSERVERS = DEBUG_ALL || false;
@@ -85,7 +85,7 @@
static final boolean DEBUG_UID_OBSERVERS = DEBUG_ALL || false;
static final boolean DEBUG_URI_PERMISSION = DEBUG_ALL || false;
static final boolean DEBUG_USER_LEAVING = DEBUG_ALL || false;
- static final boolean DEBUG_VISIBILITY = DEBUG_ALL || true;
+ static final boolean DEBUG_VISIBILITY = DEBUG_ALL || false;
static final boolean DEBUG_VISIBLE_BEHIND = DEBUG_ALL_ACTIVITIES || false;
static final boolean DEBUG_USAGE_STATS = DEBUG_ALL || false;
static final boolean DEBUG_PERMISSIONS_REVIEW = DEBUG_ALL || false;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 104217a..5061901 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2851,31 +2851,39 @@
@Override
public void setFocusedStack(int stackId) {
+ enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "setFocusedStack()");
if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "setFocusedStack: stackId=" + stackId);
- synchronized (ActivityManagerService.this) {
- ActivityStack stack = mStackSupervisor.getStack(stackId);
- if (stack != null) {
- ActivityRecord r = stack.topRunningActivityLocked();
- if (r != null) {
- setFocusedActivityLocked(r, "setFocusedStack");
+ final long callingId = Binder.clearCallingIdentity();
+ try {
+ synchronized (this) {
+ final ActivityStack stack = mStackSupervisor.getStack(stackId);
+ if (stack == null) {
+ return;
+ }
+ final ActivityRecord r = stack.topRunningActivityLocked();
+ if (setFocusedActivityLocked(r, "setFocusedStack")) {
mStackSupervisor.resumeFocusedStackTopActivityLocked();
}
}
+ } finally {
+ Binder.restoreCallingIdentity(callingId);
}
}
@Override
public void setFocusedTask(int taskId) {
+ enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "setFocusedTask()");
if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "setFocusedTask: taskId=" + taskId);
- long callingId = Binder.clearCallingIdentity();
+ final long callingId = Binder.clearCallingIdentity();
try {
- synchronized (ActivityManagerService.this) {
- TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
- if (task != null) {
- final ActivityRecord r = task.topRunningActivityLocked();
- if (setFocusedActivityLocked(r, "setFocusedTask")) {
- mStackSupervisor.resumeFocusedStackTopActivityLocked();
- }
+ synchronized (this) {
+ final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+ if (task == null) {
+ return;
+ }
+ final ActivityRecord r = task.topRunningActivityLocked();
+ if (setFocusedActivityLocked(r, "setFocusedTask")) {
+ mStackSupervisor.resumeFocusedStackTopActivityLocked();
}
}
} finally {
@@ -20794,7 +20802,7 @@
pkgUid = pm.getPackageUid(packageName, MATCH_DEBUG_TRIAGED_MISSING, userId);
} catch (RemoteException e) {
}
- if (pkgUid == -1) {
+ if (userId != UserHandle.USER_ALL && pkgUid == -1) {
throw new IllegalArgumentException(
"Cannot kill dependents of non-existing package " + packageName);
}
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 75dabc96..1103ea4 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -768,7 +768,6 @@
r.state = ActivityState.RESUMED;
if (DEBUG_STATES) Slog.v(TAG_STATES,
"Moving to RESUMED: " + r + " (starting new instance)");
- r.stopped = false;
mResumedActivity = r;
r.task.touchActiveTime();
mRecentTasks.addLocked(r.task);
@@ -1228,9 +1227,11 @@
* this function updates the rest of our state to match that fact.
*/
private void completeResumeLocked(ActivityRecord next) {
+ next.visible = true;
next.idle = false;
next.results = null;
next.newIntents = null;
+ next.stopped = false;
if (next.isHomeActivity()) {
ProcessRecord app = next.task.mActivities.get(0).app;
@@ -1729,6 +1730,8 @@
// This activity is not currently visible, but is running. Tell it to become visible.
if (r.state == ActivityState.RESUMED || r == starting) {
+ Slog.d(TAG_VISIBILITY, "Not making visible, r=" + r + " state=" + r.state
+ + " starting=" + starting);
return;
}
@@ -2291,7 +2294,6 @@
// From this point on, if something goes wrong there is no way
// to recover the activity.
try {
- next.visible = true;
completeResumeLocked(next);
} catch (Exception e) {
// If any exception gets thrown, toss away this
@@ -2302,8 +2304,6 @@
if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return true;
}
- next.stopped = false;
-
} else {
// Whoops, need to restart this activity!
if (!next.hasBeenLaunched) {
@@ -4283,6 +4283,12 @@
// "restart!".
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
"Config is relaunching resumed " + r);
+
+ if (DEBUG_STATES && !r.visible) {
+ Slog.v(TAG_STATES, "Config is relaunching resumed invisible activity " + r
+ + " called by " + Debug.getCallers(4));
+ }
+
relaunchActivityLocked(r, r.configChangeFlags, true, preserveWindow);
} else {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
@@ -4433,9 +4439,21 @@
}
if (andResume) {
- r.results = null;
- r.newIntents = null;
+ if (DEBUG_STATES) {
+ Slog.d(TAG_STATES, "Resumed after relaunch " + r);
+ }
r.state = ActivityState.RESUMED;
+ // Relaunch-resume could happen either when the app is already in the front,
+ // or while it's being brought to front. In the latter case, it's marked RESUMED
+ // but not yet visible (or stopped). We need to complete the resume here as the
+ // code in resumeTopActivityInnerLocked to complete the resume might be skipped.
+ if (!r.visible || r.stopped) {
+ mWindowManager.setAppVisibility(r.appToken, true);
+ completeResumeLocked(r);
+ } else {
+ r.results = null;
+ r.newIntents = null;
+ }
} else {
mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
r.state = ActivityState.PAUSED;
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 26108a3..93a36eb 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1046,10 +1046,14 @@
}
ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId) {
+ return resolveIntent(intent, resolvedType, userId, 0);
+ }
+
+ ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId, int flags) {
try {
return AppGlobals.getPackageManager().resolveIntent(intent, resolvedType,
- PackageManager.MATCH_DEFAULT_ONLY
- | ActivityManagerService.STOCK_PM_FLAGS, userId);
+ PackageManager.MATCH_DEFAULT_ONLY | flags
+ | ActivityManagerService.STOCK_PM_FLAGS, userId);
} catch (RemoteException e) {
}
return null;
@@ -1938,9 +1942,6 @@
}
final boolean updated = stack.ensureActivityConfigurationLocked(r, 0,
preserveWindows);
- // And we need to make sure at this point that all other activities
- // are made visible with the correct configuration.
- ensureActivitiesVisibleLocked(r, 0, preserveWindows);
if (!updated) {
resumeFocusedStackTopActivityLocked();
}
diff --git a/services/core/java/com/android/server/am/ActivityStartInterceptor.java b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
index 1ed749f..9b2bca0 100644
--- a/services/core/java/com/android/server/am/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.android.server.am;
import static android.app.ActivityManager.INTENT_SENDER_ACTIVITY;
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 28be456..cca6fc5 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.android.server.am;
import static android.app.Activity.RESULT_CANCELED;
@@ -74,7 +90,9 @@
import android.content.IntentSender;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Binder;
@@ -84,6 +102,7 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.os.UserManager;
import android.service.voice.IVoiceInteractionSession;
import android.util.EventLog;
import android.util.Slog;
@@ -582,6 +601,22 @@
intent = new Intent(intent);
ResolveInfo rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId);
+ if (rInfo == null) {
+ UserInfo userInfo = mSupervisor.getUserInfo(userId);
+ if (userInfo != null && userInfo.isManagedProfile()) {
+ // Special case for managed profiles, if attempting to launch non-cryto aware
+ // app in a locked managed profile from an unlocked parent allow it to resolve
+ // as user will be sent via confirm credentials to unlock the profile.
+ UserManager userManager = UserManager.get(mService.mContext);
+ UserInfo parent = userManager.getProfileParent(userId);
+ if (parent != null
+ && userManager.isUserUnlocked(parent.getUserHandle())
+ && !userManager.isUserUnlocked(userInfo.getUserHandle())) {
+ rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId,
+ PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE);
+ }
+ }
+ }
// Collect information about the target of the Intent.
ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 98a7ead..16fd909 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -70,6 +70,7 @@
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER;
import static android.content.pm.ActivityInfo.RESIZE_MODE_CROP_WINDOWS;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
@@ -122,9 +123,6 @@
private static final String ATTR_TASK_AFFILIATION_COLOR = "task_affiliation_color";
private static final String ATTR_CALLING_UID = "calling_uid";
private static final String ATTR_CALLING_PACKAGE = "calling_package";
- // TODO(b/26847884): Currently needed while migrating to resize_mode.
- // Can be removed at some later point.
- private static final String ATTR_RESIZEABLE = "resizeable";
private static final String ATTR_RESIZE_MODE = "resize_mode";
private static final String ATTR_PRIVILEGED = "privileged";
private static final String ATTR_NON_FULLSCREEN_BOUNDS = "non_fullscreen_bounds";
@@ -1011,6 +1009,7 @@
String label = null;
String iconFilename = null;
int colorPrimary = 0;
+ int colorBackground = 0;
for (--activityNdx; activityNdx >= 0; --activityNdx) {
final ActivityRecord r = mActivities.get(activityNdx);
if (r.taskDescription != null) {
@@ -1023,9 +1022,13 @@
if (colorPrimary == 0) {
colorPrimary = r.taskDescription.getPrimaryColor();
}
+ if (colorBackground == 0) {
+ colorBackground = r.taskDescription.getBackgroundColor();
+ }
}
}
- lastTaskDescription = new TaskDescription(label, colorPrimary, iconFilename);
+ lastTaskDescription = new TaskDescription(label, null, iconFilename, colorPrimary,
+ colorBackground);
// Update the task affiliation color if we are the parent of the group
if (taskId == mAffiliatedTaskId) {
mAffiliatedTaskColor = lastTaskDescription.getPrimaryColor();
@@ -1169,7 +1172,7 @@
int nextTaskId = INVALID_TASK_ID;
int callingUid = -1;
String callingPackage = "";
- int resizeMode = RESIZE_MODE_UNRESIZEABLE;
+ int resizeMode = RESIZE_MODE_FORCE_RESIZEABLE;
boolean privileged = false;
Rect bounds = null;
@@ -1231,11 +1234,10 @@
callingUid = Integer.valueOf(attrValue);
} else if (ATTR_CALLING_PACKAGE.equals(attrName)) {
callingPackage = attrValue;
- } else if (ATTR_RESIZEABLE.equals(attrName)) {
- resizeMode = Boolean.valueOf(attrValue)
- ? RESIZE_MODE_RESIZEABLE : RESIZE_MODE_CROP_WINDOWS;
} else if (ATTR_RESIZE_MODE.equals(attrName)) {
resizeMode = Integer.valueOf(attrValue);
+ resizeMode = (resizeMode == RESIZE_MODE_CROP_WINDOWS)
+ ? RESIZE_MODE_FORCE_RESIZEABLE : resizeMode;
} else if (ATTR_PRIVILEGED.equals(attrName)) {
privileged = Boolean.valueOf(attrValue);
} else if (ATTR_NON_FULLSCREEN_BOUNDS.equals(attrName)) {
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index a355fa4..8e860fa 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -165,15 +165,15 @@
void register(ContentResolver resolver) {
resolver.registerContentObserver(mUserSetupComplete, false, this, UserHandle.USER_ALL);
synchronized (mService) {
- updateCurrentUserSetupCompleteLocked();
+ updateUserSetupCompleteLocked(UserHandle.USER_ALL);
}
}
@Override
- public void onChange(boolean selfChange, Uri uri) {
+ public void onChange(boolean selfChange, Uri uri, int userId) {
if (mUserSetupComplete.equals(uri)) {
synchronized (mService) {
- updateCurrentUserSetupCompleteLocked();
+ updateUserSetupCompleteLocked(userId);
}
}
}
@@ -297,6 +297,22 @@
null, null, AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
userId);
+ if (getUserInfo(userId).isManagedProfile()) {
+ UserInfo parent = getUserManager().getProfileParent(userId);
+ if (parent != null) {
+ final Intent profileUnlockedIntent = new Intent(
+ Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
+ unlockedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId));
+ unlockedIntent.addFlags(
+ Intent.FLAG_RECEIVER_REGISTERED_ONLY
+ | Intent.FLAG_RECEIVER_FOREGROUND);
+ mService.broadcastIntentLocked(null, null, profileUnlockedIntent,
+ null, null, 0, null, null, null, AppOpsManager.OP_NONE,
+ null, false, false, MY_PID, SYSTEM_UID,
+ parent.id);
+ }
+ }
+
final Intent bootIntent = new Intent(Intent.ACTION_BOOT_COMPLETED, null);
bootIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
bootIntent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
@@ -655,7 +671,7 @@
final Integer userIdInt = userId;
mUserLru.remove(userIdInt);
mUserLru.add(userIdInt);
- updateCurrentUserSetupCompleteLocked();
+ updateUserSetupCompleteLocked(userId);
if (foreground) {
mCurrentUserId = userId;
@@ -870,11 +886,22 @@
mUserSwitchObservers.finishBroadcast();
}
- void updateCurrentUserSetupCompleteLocked() {
+ void updateUserSetupCompleteLocked(int userId) {
+ int[] users;
+ if (userId != UserHandle.USER_ALL) {
+ users = new int[] {userId};
+ } else {
+ users = new int[mStartedUsers.size()];
+ for (int i = mStartedUsers.size() - 1; i >= 0; i--) {
+ users[i] = mStartedUsers.keyAt(i);
+ }
+ }
final ContentResolver cr = mService.mContext.getContentResolver();
- final boolean setupComplete =
- Settings.Secure.getIntForUser(cr, USER_SETUP_COMPLETE, 0, mCurrentUserId) != 0;
- mSetupCompletedUsers.put(mCurrentUserId, setupComplete);
+ for (int i = 0; i < users.length; i++) {
+ final boolean setupComplete =
+ Settings.Secure.getIntForUser(cr, USER_SETUP_COMPLETE, 0, users[i]) != 0;
+ mSetupCompletedUsers.put(users[i], setupComplete);
+ }
}
boolean isUserSetupCompleteLocked(int userId) {
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 8dec587..2f87f80 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -62,6 +62,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.Messenger;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -70,41 +71,42 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.os.WorkSource;
-import android.os.Messenger;
import android.provider.Settings;
import android.text.format.DateUtils;
import android.text.format.Time;
import android.util.EventLog;
import android.util.Log;
-import android.util.Slog;
import android.util.Pair;
-
+import android.util.Slog;
import android.util.SparseArray;
+
+import com.google.android.collect.Lists;
+import com.google.android.collect.Maps;
+
import com.android.internal.R;
import com.android.internal.app.IBatteryStats;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.accounts.AccountManagerService;
+import com.android.server.backup.AccountSyncSettingsBackupHelper;
import com.android.server.content.SyncStorageEngine.AuthorityInfo;
import com.android.server.content.SyncStorageEngine.EndPoint;
import com.android.server.content.SyncStorageEngine.OnSyncRequestListener;
-import com.google.android.collect.Lists;
-import com.google.android.collect.Maps;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Random;
-import java.util.List;
-import java.util.Set;
-import java.util.HashSet;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
-import java.util.Map;
-import java.util.Arrays;
import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
import java.util.Objects;
+import java.util.Random;
+import java.util.Set;
/**
* Implementation details:
@@ -510,12 +512,12 @@
mSyncStorageEngine.setPeriodicSyncAddedListener(
new SyncStorageEngine.PeriodicSyncAddedListener() {
- @Override
- public void onPeriodicSyncAdded(EndPoint target, Bundle extras, long pollFrequency,
- long flex) {
- updateOrAddPeriodicSync(target, pollFrequency, flex, extras);
- }
- });
+ @Override
+ public void onPeriodicSyncAdded(EndPoint target, Bundle extras, long pollFrequency,
+ long flex) {
+ updateOrAddPeriodicSync(target, pollFrequency, flex, extras);
+ }
+ });
mSyncStorageEngine.setOnAuthorityRemovedListener(new SyncStorageEngine.OnAuthorityRemovedListener() {
@Override
@@ -750,8 +752,8 @@
* @param onlyThoseWithUnkownSyncableState Only sync authorities that have unknown state.
*/
public void scheduleSync(Account requestedAccount, int userId, int reason,
- String requestedAuthority, Bundle extras, long beforeRuntimeMillis,
- long runtimeMillis, boolean onlyThoseWithUnkownSyncableState) {
+ String requestedAuthority, Bundle extras, long beforeRuntimeMillis,
+ long runtimeMillis, boolean onlyThoseWithUnkownSyncableState) {
final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
if (extras == null) {
extras = new Bundle();
@@ -941,7 +943,7 @@
* flexMillis will be updated.
*/
public void updateOrAddPeriodicSync(EndPoint target, long pollFrequency, long flex,
- Bundle extras) {
+ Bundle extras) {
UpdatePeriodicSyncMessagePayload payload = new UpdatePeriodicSyncMessagePayload(target,
pollFrequency, flex, extras);
mSyncHandler.obtainMessage(SyncHandler.MESSAGE_UPDATE_PERIODIC_SYNC, payload)
@@ -995,7 +997,7 @@
}
private void sendSyncFinishedOrCanceledMessage(ActiveSyncContext syncContext,
- SyncResult syncResult) {
+ SyncResult syncResult) {
if (Log.isLoggable(TAG, Log.VERBOSE)) Slog.v(TAG, "sending MESSAGE_SYNC_FINISHED");
Message msg = mSyncHandler.obtainMessage();
msg.what = SyncHandler.MESSAGE_SYNC_FINISHED;
@@ -1053,7 +1055,7 @@
public final SyncResult syncResult;
SyncFinishedOrCancelledMessagePayload(ActiveSyncContext syncContext,
- SyncResult syncResult) {
+ SyncResult syncResult) {
this.activeSyncContext = syncContext;
this.syncResult = syncResult;
}
@@ -1066,7 +1068,7 @@
public final Bundle extras;
UpdatePeriodicSyncMessagePayload(EndPoint target, long pollFrequency, long flex,
- Bundle extras) {
+ Bundle extras) {
this.target = target;
this.pollFrequency = pollFrequency;
this.flex = flex;
@@ -1297,7 +1299,7 @@
JobInfo.NETWORK_TYPE_UNMETERED : JobInfo.NETWORK_TYPE_ANY;
JobInfo.Builder b = new JobInfo.Builder(syncOperation.jobId,
- new ComponentName(mContext, SyncJobService.class))
+ new ComponentName(mContext, SyncJobService.class))
.setExtras(syncOperation.toJobInfoExtras())
.setRequiredNetworkType(networkType)
.setPersisted(true)
@@ -1502,7 +1504,7 @@
* for this sync. This is used to attribute the wakelock hold to that application.
*/
public ActiveSyncContext(SyncOperation syncOperation, long historyRowId,
- int syncAdapterUid) {
+ int syncAdapterUid) {
super();
mSyncAdapterUid = syncAdapterUid;
mSyncOperation = syncOperation;
@@ -1731,7 +1733,7 @@
new Comparator<RegisteredServicesCache.ServiceInfo<SyncAdapterType>>() {
@Override
public int compare(RegisteredServicesCache.ServiceInfo<SyncAdapterType> lhs,
- RegisteredServicesCache.ServiceInfo<SyncAdapterType> rhs) {
+ RegisteredServicesCache.ServiceInfo<SyncAdapterType> rhs) {
return lhs.type.authority.compareTo(rhs.type.authority);
}
});
@@ -2546,6 +2548,7 @@
Slog.v(TAG, "Pushing back running sync due to a higher priority sync");
}
deferActiveSyncH(asc);
+ break;
}
}
}
@@ -2571,6 +2574,7 @@
}
private void updateRunningAccountsH(EndPoint syncTargets) {
+ AccountAndUser[] oldAccounts = mRunningAccounts;
mRunningAccounts = AccountManagerService.getSingleton().getRunningAccounts();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Slog.v(TAG, "Accounts list: ");
@@ -2594,6 +2598,17 @@
}
}
+ // On account add, check if there are any settings to be restored.
+ for (AccountAndUser aau : mRunningAccounts) {
+ if (!containsAccountAndUser(oldAccounts, aau.account, aau.userId)) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Account " + aau.account + " added, checking sync restore data");
+ }
+ AccountSyncSettingsBackupHelper.accountAdded(mContext);
+ break;
+ }
+ }
+
List<SyncOperation> ops = getAllPendingSyncsFromCache();
for (SyncOperation op: ops) {
if (!containsAccountAndUser(accounts, op.target.account, op.target.userId)) {
@@ -2617,7 +2632,7 @@
* @param flexMillis new flex time in milliseconds.
*/
private void maybeUpdateSyncPeriodH(SyncOperation syncOperation, long pollFrequencyMillis,
- long flexMillis) {
+ long flexMillis) {
if (!(pollFrequencyMillis == syncOperation.periodMillis
&& flexMillis == syncOperation.flexMillis)) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
@@ -2632,7 +2647,7 @@
}
private void updateOrAddPeriodicSyncH(EndPoint target, long pollFrequency, long flex,
- Bundle extras) {
+ Bundle extras) {
final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
verifyJobScheduler(); // Will fill in mScheduledSyncs cache if it is not already filled.
final long pollFrequencyMillis = pollFrequency * 1000L;
@@ -2820,7 +2835,7 @@
}
private void runBoundToAdapterH(final ActiveSyncContext activeSyncContext,
- IBinder syncAdapter) {
+ IBinder syncAdapter) {
final SyncOperation syncOperation = activeSyncContext.mSyncOperation;
try {
activeSyncContext.mIsLinkedToDeath = true;
@@ -2888,7 +2903,7 @@
}
private void runSyncFinishedOrCanceledH(SyncResult syncResult,
- ActiveSyncContext activeSyncContext) {
+ ActiveSyncContext activeSyncContext) {
final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
final SyncOperation syncOperation = activeSyncContext.mSyncOperation;
@@ -3022,7 +3037,7 @@
}
private void installHandleTooManyDeletesNotification(Account account, String authority,
- long numDeletes, int userId) {
+ long numDeletes, int userId) {
if (mNotificationMgr == null) return;
final ProviderInfo providerInfo = mContext.getPackageManager().resolveContentProvider(
@@ -3098,7 +3113,7 @@
}
public void stopSyncEvent(long rowId, SyncOperation syncOperation, String resultMessage,
- int upstreamActivity, int downstreamActivity, long elapsedTime) {
+ int upstreamActivity, int downstreamActivity, long elapsedTime) {
EventLog.writeEvent(2720,
syncOperation.toEventLog(SyncStorageEngine.EVENT_STOP));
mSyncStorageEngine.stopSyncEvent(rowId, elapsedTime,
@@ -3262,4 +3277,4 @@
return mContext;
}
}
-}
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index f8e3e48..965054f 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -438,9 +438,7 @@
if (sSyncStorageEngine != null) {
return;
}
- // This call will return the correct directory whether Encrypted File Systems is
- // enabled or not.
- File dataDir = Environment.getSecureDataDirectory();
+ File dataDir = Environment.getDataDirectory();
sSyncStorageEngine = new SyncStorageEngine(context, dataDir);
}
diff --git a/services/core/java/com/android/server/firewall/IntentFirewall.java b/services/core/java/com/android/server/firewall/IntentFirewall.java
index 62114cd..7e19c66 100644
--- a/services/core/java/com/android/server/firewall/IntentFirewall.java
+++ b/services/core/java/com/android/server/firewall/IntentFirewall.java
@@ -52,7 +52,7 @@
static final String TAG = "IntentFirewall";
// e.g. /data/system/ifw or /data/secure/system/ifw
- private static final File RULES_DIR = new File(Environment.getSystemSecureDirectory(), "ifw");
+ private static final File RULES_DIR = new File(Environment.getDataSystemDirectory(), "ifw");
private static final int LOG_PACKAGES_MAX_LENGTH = 150;
private static final int LOG_PACKAGES_SUFFICIENT_LENGTH = 125;
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index e5e86ac..4028372 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -330,7 +330,7 @@
private void cancelJobImpl(JobStatus cancelled) {
if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString());
- stopTrackingJob(cancelled);
+ stopTrackingJob(cancelled, true /* writeBack */);
synchronized (mJobs) {
// Remove from pending queue.
mPendingJobs.remove(cancelled);
@@ -509,12 +509,12 @@
* Called when we want to remove a JobStatus object that we've finished executing. Returns the
* object removed.
*/
- private boolean stopTrackingJob(JobStatus jobStatus) {
+ private boolean stopTrackingJob(JobStatus jobStatus, boolean writeBack) {
boolean removed;
boolean rocking;
synchronized (mJobs) {
// Remove from store as well as controllers.
- removed = mJobs.remove(jobStatus);
+ removed = mJobs.remove(jobStatus, writeBack);
rocking = mReadyToRock;
}
if (removed && rocking) {
@@ -645,7 +645,9 @@
if (DEBUG) {
Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule);
}
- if (!stopTrackingJob(jobStatus)) {
+ // Do not write back immediately if this is a periodic job. The job may get lost if system
+ // shuts down before it is added back.
+ if (!stopTrackingJob(jobStatus, !jobStatus.getJob().isPeriodic())) {
if (DEBUG) {
Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
}
diff --git a/services/core/java/com/android/server/job/JobStore.java b/services/core/java/com/android/server/job/JobStore.java
index f796164..f6a6778 100644
--- a/services/core/java/com/android/server/job/JobStore.java
+++ b/services/core/java/com/android/server/job/JobStore.java
@@ -159,9 +159,10 @@
/**
* Remove the provided job. Will also delete the job if it was persisted.
+ * @param writeBack If true, the job will be deleted (if it was persisted) immediately.
* @return Whether or not the job existed to be removed.
*/
- public boolean remove(JobStatus jobStatus) {
+ public boolean remove(JobStatus jobStatus, boolean writeBack) {
boolean removed = mJobSet.remove(jobStatus);
if (!removed) {
if (DEBUG) {
@@ -169,7 +170,7 @@
}
return false;
}
- if (jobStatus.isPersisted()) {
+ if (writeBack && jobStatus.isPersisted()) {
maybeWriteStatusToDiskAsync();
}
return removed;
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index ffc52b3..c1eb844 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -1557,15 +1557,13 @@
* called from native code to update SV info
*/
private void reportSvStatus() {
- int svCount = native_read_sv_status(mPrnWithFlags, mSnrs, mSvElevations, mSvAzimuths,
- mConstellationTypes);
+ int svCount = native_read_sv_status(mSvidWithFlags, mSnrs, mSvElevations, mSvAzimuths);
mListenerHelper.onSvStatusChanged(
svCount,
- mPrnWithFlags,
+ mSvidWithFlags,
mSnrs,
mSvElevations,
- mSvAzimuths,
- mConstellationTypes);
+ mSvAzimuths);
if (VERBOSE) {
Log.v(TAG, "SV count: " + svCount);
@@ -1573,19 +1571,19 @@
// Calculate number of sets used in fix.
int usedInFixCount = 0;
for (int i = 0; i < svCount; i++) {
- if ((mPrnWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_USED_IN_FIX) != 0) {
+ if ((mSvidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_USED_IN_FIX) != 0) {
++usedInFixCount;
}
if (VERBOSE) {
- Log.v(TAG, "prn: " + (mPrnWithFlags[i] >> GnssStatus.PRN_SHIFT_WIDTH) +
+ Log.v(TAG, "svid: " + (mSvidWithFlags[i] >> GnssStatus.SVID_SHIFT_WIDTH) +
" snr: " + mSnrs[i]/10 +
" elev: " + mSvElevations[i] +
" azimuth: " + mSvAzimuths[i] +
- ((mPrnWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA) == 0
+ ((mSvidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA) == 0
? " " : " E") +
- ((mPrnWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_ALMANAC_DATA) == 0
+ ((mSvidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_ALMANAC_DATA) == 0
? " " : " A") +
- ((mPrnWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_USED_IN_FIX) == 0
+ ((mSvidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_USED_IN_FIX) == 0
? "" : "U"));
}
}
@@ -2398,14 +2396,13 @@
}
// for GPS SV statistics
- private static final int MAX_SVS = 512;
+ private static final int MAX_SVS = 64;
// preallocated arrays, to avoid memory allocation in reportStatus()
- private int mPrnWithFlags[] = new int[MAX_SVS];
+ private int mSvidWithFlags[] = new int[MAX_SVS];
private float mSnrs[] = new float[MAX_SVS];
private float mSvElevations[] = new float[MAX_SVS];
private float mSvAzimuths[] = new float[MAX_SVS];
- private int mConstellationTypes[] = new int[MAX_SVS];
private int mSvCount;
// preallocated to avoid memory allocation in reportNmea()
private byte[] mNmeaBuffer = new byte[120];
@@ -2426,7 +2423,7 @@
// returns number of SVs
// mask[0] is ephemeris mask and mask[1] is almanac mask
private native int native_read_sv_status(int[] prnWithFlags, float[] snrs, float[] elevations,
- float[] azimuths, int[] constellationTypes);
+ float[] azimuths);
private native int native_read_nmea(byte[] buffer, int bufferSize);
private native void native_inject_location(double latitude, double longitude, float accuracy);
diff --git a/services/core/java/com/android/server/location/GnssStatusListenerHelper.java b/services/core/java/com/android/server/location/GnssStatusListenerHelper.java
index 9840c61..0b3111c 100644
--- a/services/core/java/com/android/server/location/GnssStatusListenerHelper.java
+++ b/services/core/java/com/android/server/location/GnssStatusListenerHelper.java
@@ -77,8 +77,7 @@
final int[] prnWithFlags,
final float[] snrs,
final float[] elevations,
- final float[] azimuths,
- final int[] constellationTypes) {
+ final float[] azimuths) {
Operation operation = new Operation() {
@Override
public void execute(IGnssStatusListener listener) throws RemoteException {
@@ -87,8 +86,7 @@
prnWithFlags,
snrs,
elevations,
- azimuths,
- constellationTypes);
+ azimuths);
}
};
foreach(operation);
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index b2e6adf..24bb845 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -1864,31 +1864,58 @@
@Override
public void addRestrictBackgroundWhitelistedUid(int uid) {
mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
- Slog.i(TAG, "adding uid " + uid + " to restrict background whitelist");
+ if (!isUidValidForRules(uid)) return;
+ final boolean changed;
synchronized (mRulesLock) {
+ final boolean oldStatus = mRestrictBackgroundWhitelistUids.get(uid);
+ if (oldStatus) {
+ if (LOGD) Slog.d(TAG, "uid " + uid + " is already whitelisted");
+ return;
+ }
+ Slog.i(TAG, "adding uid " + uid + " to restrict background whitelist");
mRestrictBackgroundWhitelistUids.append(uid, true);
- updateRulesForGlobalChangeLocked(true);
+ changed = mRestrictBackground && !oldStatus;
+ if (changed && hasInternetPermissions(uid)) {
+ setUidNetworkRules(uid, false);
+ }
writePolicyLocked();
}
- mHandler.obtainMessage(MSG_RESTRICT_BACKGROUND_WHITELIST_CHANGED, uid, 0).sendToTarget();
+ if (changed) {
+ mHandler.obtainMessage(MSG_RESTRICT_BACKGROUND_WHITELIST_CHANGED, uid, 0)
+ .sendToTarget();
+ }
}
@Override
public void removeRestrictBackgroundWhitelistedUid(int uid) {
mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
- Slog.i(TAG, "removing uid " + uid + " from restrict background whitelist");
+ if (!isUidValidForRules(uid)) return;
+ final boolean changed;
synchronized (mRulesLock) {
- removeRestrictBackgroundWhitelistedUidLocked(uid, true);
+ changed = removeRestrictBackgroundWhitelistedUidLocked(uid, true);
}
- mHandler.obtainMessage(MSG_RESTRICT_BACKGROUND_WHITELIST_CHANGED, uid, 0).sendToTarget();
+ if (changed) {
+ mHandler.obtainMessage(MSG_RESTRICT_BACKGROUND_WHITELIST_CHANGED, uid, 0)
+ .sendToTarget();
+ }
}
- private void removeRestrictBackgroundWhitelistedUidLocked(int uid, boolean updateNow) {
+ private boolean removeRestrictBackgroundWhitelistedUidLocked(int uid, boolean updateNow) {
+ final boolean oldStatus = mRestrictBackgroundWhitelistUids.get(uid);
+ if (!oldStatus) {
+ if (LOGD) Slog.d(TAG, "uid " + uid + " was not whitelisted before");
+ return false;
+ }
+ Slog.i(TAG, "removing uid " + uid + " from restrict background whitelist");
+ final boolean changed = mRestrictBackground && oldStatus;
mRestrictBackgroundWhitelistUids.delete(uid);
if (updateNow) {
- updateRulesForGlobalChangeLocked(true);
+ if (changed && hasInternetPermissions(uid)) {
+ setUidNetworkRules(uid, true);
+ }
writePolicyLocked();
}
+ return changed;
}
@Override
@@ -2408,22 +2435,28 @@
}
/**
- * Applies network rules to bandwidth and firewall controllers based on uid policy.
- * @param uid The uid for which to apply the latest policy
+ * Checks if an uid has INTERNET permissions.
+ * <p>
+ * Useful for the cases where the lack of network access can simplify the rules.
*/
- void updateRulesForUidLocked(int uid) {
- if (!isUidValidForRules(uid)) return;
-
- // quick check: if this uid doesn't have INTERNET permission, it doesn't have
- // network access anyway, so it is a waste to mess with it here.
+ private boolean hasInternetPermissions(int uid) {
final IPackageManager ipm = AppGlobals.getPackageManager();
try {
if (ipm.checkUidPermission(Manifest.permission.INTERNET, uid)
!= PackageManager.PERMISSION_GRANTED) {
- return;
+ return false;
}
} catch (RemoteException e) {
}
+ return true;
+ }
+
+ /**
+ * Applies network rules to bandwidth and firewall controllers based on uid policy.
+ * @param uid The uid for which to apply the latest policy
+ */
+ void updateRulesForUidLocked(int uid) {
+ if (!isUidValidForRules(uid) || !hasInternetPermissions(uid)) return;
final int uidPolicy = mUidPolicy.get(uid, POLICY_NONE);
final boolean uidForeground = isUidForegroundLocked(uid);
@@ -2598,7 +2631,6 @@
intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
}
-
return true;
}
case MSG_ADVISE_PERSIST_THRESHOLD: {
@@ -2831,13 +2863,5 @@
removeRestrictBackgroundWhitelistedUidLocked(uid, true);
}
}
-
- @Override
- public void onPackageRemovedAllUsers(String packageName, int uid) {
- if (LOGV) Slog.v(TAG, "onPackageRemovedAllUsers: " + packageName + " ->" + uid);
- synchronized (mRulesLock) {
- removeRestrictBackgroundWhitelistedUidLocked(uid, true);
- }
- }
}
}
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index d82bb3d..c6613f5 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -201,8 +201,7 @@
long ident = Binder.clearCallingIdentity();
try {
List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(mainIntent,
- PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
- user.getIdentifier());
+ PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE, user.getIdentifier());
return new ParceledListSlice<>(apps);
} finally {
Binder.restoreCallingIdentity(ident);
@@ -220,7 +219,7 @@
long ident = Binder.clearCallingIdentity();
try {
ResolveInfo app = mPm.resolveActivityAsUser(intent,
- PackageManager.MATCH_DEBUG_TRIAGED_MISSING, user.getIdentifier());
+ PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE, user.getIdentifier());
return app;
} finally {
Binder.restoreCallingIdentity(ident);
@@ -239,7 +238,7 @@
try {
IPackageManager pm = AppGlobals.getPackageManager();
PackageInfo info = pm.getPackageInfo(packageName,
- PackageManager.MATCH_DEBUG_TRIAGED_MISSING, user.getIdentifier());
+ PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE, user.getIdentifier());
return info != null && info.applicationInfo.enabled;
} finally {
Binder.restoreCallingIdentity(ident);
@@ -277,7 +276,7 @@
try {
IPackageManager pm = AppGlobals.getPackageManager();
ActivityInfo info = pm.getActivityInfo(component,
- PackageManager.MATCH_DEBUG_TRIAGED_MISSING, user.getIdentifier());
+ PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE, user.getIdentifier());
return info != null;
} finally {
Binder.restoreCallingIdentity(ident);
@@ -303,7 +302,7 @@
try {
IPackageManager pm = AppGlobals.getPackageManager();
ActivityInfo info = pm.getActivityInfo(component,
- PackageManager.MATCH_DEBUG_TRIAGED_MISSING, user.getIdentifier());
+ PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE, user.getIdentifier());
if (!info.exported) {
throw new SecurityException("Cannot launch non-exported components "
+ component);
@@ -313,7 +312,7 @@
// as calling startActivityAsUser ignores the category and just
// resolves based on the component if present.
List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(launchIntent,
- PackageManager.MATCH_DEBUG_TRIAGED_MISSING, user.getIdentifier());
+ PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE, user.getIdentifier());
final int size = apps.size();
for (int i = 0; i < size; ++i) {
ActivityInfo activityInfo = apps.get(i).activityInfo;
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index da62a2d..94b3b2d 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -55,8 +55,6 @@
public class OtaDexoptService extends IOtaDexopt.Stub {
private final static String TAG = "OTADexopt";
private final static boolean DEBUG_DEXOPT = true;
- // Apps used in the last 7 days.
- private final static long DEXOPT_LRU_THRESHOLD_IN_MINUTES = 7 * 24 * 60;
private final Context mContext;
private final PackageDexOptimizer mPackageDexOptimizer;
@@ -94,69 +92,9 @@
if (mDexoptPackages != null) {
throw new IllegalStateException("already called prepare()");
}
-
- mDexoptPackages = new LinkedList<>();
-
- ArrayList<PackageParser.Package> pkgs;
synchronized (mPackageManagerService.mPackages) {
- pkgs = new ArrayList<PackageParser.Package>(mPackageManagerService.mPackages.values());
- }
-
- // Sort apps by importance for dexopt ordering. Important apps are given more priority
- // in case the device runs out of space.
-
- // Give priority to core apps.
- for (PackageParser.Package pkg : pkgs) {
- if (pkg.coreApp) {
- if (DEBUG_DEXOPT) {
- Log.i(TAG, "Adding core app " + mDexoptPackages.size() + ": " + pkg.packageName);
- }
- mDexoptPackages.add(pkg);
- }
- }
- pkgs.removeAll(mDexoptPackages);
-
- // Give priority to system apps that listen for pre boot complete.
- Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
- ArraySet<String> pkgNames = getPackageNamesForIntent(intent, UserHandle.USER_SYSTEM);
- for (PackageParser.Package pkg : pkgs) {
- if (pkgNames.contains(pkg.packageName)) {
- if (DEBUG_DEXOPT) {
- Log.i(TAG, "Adding pre boot system app " + mDexoptPackages.size() + ": " +
- pkg.packageName);
- }
- mDexoptPackages.add(pkg);
- }
- }
- pkgs.removeAll(mDexoptPackages);
-
- // Filter out packages that aren't recently used, add all remaining apps.
- // TODO: add a property to control this?
- if (mPackageManagerService.isHistoricalPackageUsageAvailable()) {
- filterRecentlyUsedApps(pkgs, DEXOPT_LRU_THRESHOLD_IN_MINUTES * 60 * 1000);
- }
- mDexoptPackages.addAll(pkgs);
-
- // Now go ahead and also add the libraries required for these packages.
- // TODO: Think about interleaving things.
- Set<PackageParser.Package> dependencies = new HashSet<>();
- for (PackageParser.Package p : mDexoptPackages) {
- dependencies.addAll(mPackageManagerService.findSharedNonSystemLibraries(p));
- }
- if (!dependencies.isEmpty()) {
- dependencies.removeAll(mDexoptPackages);
- }
- mDexoptPackages.addAll(dependencies);
-
- if (DEBUG_DEXOPT) {
- StringBuilder sb = new StringBuilder();
- for (PackageParser.Package pkg : mDexoptPackages) {
- if (sb.length() > 0) {
- sb.append(", ");
- }
- sb.append(pkg.packageName);
- }
- Log.i(TAG, "Packages to be optimized: " + sb.toString());
+ mDexoptPackages = PackageManagerServiceUtils.getPackagesForDexopt(
+ mPackageManagerService.mPackages.values(), mPackageManagerService);
}
}
@@ -228,29 +166,6 @@
return pkgNames;
}
- private void filterRecentlyUsedApps(Collection<PackageParser.Package> pkgs,
- long dexOptLRUThresholdInMills) {
- // Filter out packages that aren't recently used.
- int total = pkgs.size();
- int skipped = 0;
- long now = System.currentTimeMillis();
- for (Iterator<PackageParser.Package> i = pkgs.iterator(); i.hasNext();) {
- PackageParser.Package pkg = i.next();
- long then = pkg.mLastPackageUsageTimeInMills;
- if (then + dexOptLRUThresholdInMills < now) {
- if (DEBUG_DEXOPT) {
- Log.i(TAG, "Skipping dexopt of " + pkg.packageName + " last resumed: " +
- ((then == 0) ? "never" : new Date(then)));
- }
- i.remove();
- skipped++;
- }
- }
- if (DEBUG_DEXOPT) {
- Log.i(TAG, "Skipped optimizing " + skipped + " of " + total);
- }
- }
-
private static class OTADexoptPackageDexOptimizer extends
PackageDexOptimizer.ForcedUpdatePackageDexOptimizer {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 23a58d0..928c19f 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -212,8 +212,8 @@
mCallbacks = new Callbacks(mInstallThread.getLooper());
mSessionsFile = new AtomicFile(
- new File(Environment.getSystemSecureDirectory(), "install_sessions.xml"));
- mSessionsDir = new File(Environment.getSystemSecureDirectory(), "install_sessions");
+ new File(Environment.getDataSystemDirectory(), "install_sessions.xml"));
+ mSessionsDir = new File(Environment.getDataSystemDirectory(), "install_sessions");
mSessionsDir.mkdirs();
synchronized (mSessions) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e47d514..92343d6 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -282,6 +282,7 @@
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -6778,32 +6779,19 @@
// Extract pacakges only if profile-guided compilation is enabled because
// otherwise BackgroundDexOptService will not dexopt them later.
if (mUseJitProfiles) {
- ArraySet<String> pkgs = getOptimizablePackages();
- if (pkgs != null) {
- for (String pkg : pkgs) {
- performDexOpt(pkg, null /* instructionSet */, false /* useProfiles */,
- true /* extractOnly */, false /* force */);
+ List<PackageParser.Package> pkgs;
+ synchronized (mPackages) {
+ pkgs = PackageManagerServiceUtils.getPackagesForDexopt(mPackages.values(), this);
+ }
+ for (PackageParser.Package pkg : pkgs) {
+ if (PackageDexOptimizer.canOptimizePackage(pkg)) {
+ performDexOpt(pkg.packageName, null /* instructionSet */,
+ false /* useProfiles */, true /* extractOnly */, false /* force */);
}
}
}
}
- private ArraySet<String> getPackageNamesForIntent(Intent intent, int userId) {
- List<ResolveInfo> ris = null;
- try {
- ris = AppGlobals.getPackageManager().queryIntentReceivers(
- intent, null, 0, userId);
- } catch (RemoteException e) {
- }
- ArraySet<String> pkgNames = new ArraySet<String>();
- if (ris != null) {
- for (ResolveInfo ri : ris) {
- pkgNames.add(ri.activityInfo.packageName);
- }
- }
- return pkgNames;
- }
-
@Override
public void notifyPackageUse(String packageName) {
synchronized (mPackages) {
@@ -12724,9 +12712,6 @@
String pkgName = pkg.packageName;
if (DEBUG_INSTALL) Slog.d(TAG, "installNewPackageLI: " + pkg);
- // TODO: b/23350563
- final boolean dataDirExists = Environment
- .getDataUserPackageDirectory(volumeUuid, UserHandle.USER_SYSTEM, pkgName).exists();
synchronized(mPackages) {
if (mSettings.mRenamedPackages.containsKey(pkgName)) {
@@ -12752,18 +12737,15 @@
System.currentTimeMillis(), user);
updateSettingsLI(newPackage, installerPackageName, null, null, res, user);
- prepareAppDataAfterInstall(newPackage);
- // delete the partially installed application. the data directory will have to be
- // restored if it was already existing
- if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
- // remove package from internal structures. Note that we want deletePackageX to
- // delete the package data and cache directories that it created in
- // scanPackageLocked, unless those directories existed before we even tried to
- // install.
+ if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
+ prepareAppDataAfterInstall(newPackage);
+
+ } else {
+ // Remove package from internal structures, but keep around any
+ // data that might have already existed
deletePackageLI(pkgName, UserHandle.ALL, false, null, null,
- dataDirExists ? PackageManager.DELETE_KEEP_DATA : 0,
- res.removedInfo, true, null);
+ PackageManager.DELETE_KEEP_DATA, res.removedInfo, true, null);
}
} catch (PackageManagerException e) {
@@ -13274,16 +13256,19 @@
// of the package implies that the user actually wants to run that new code,
// so we enable the package.
PackageSetting ps = mSettings.mPackages.get(pkgName);
+ final int userId = user.getIdentifier();
if (ps != null) {
if (isSystemApp(newPackage)) {
- // NB: implicit assumption that system package upgrades apply to all users
if (DEBUG_INSTALL) {
Slog.d(TAG, "Implicitly enabling system package on upgrade: " + pkgName);
}
+ // Enable system package for requested users
if (res.origUsers != null) {
- for (int userHandle : res.origUsers) {
- ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT,
- userHandle, installerPackageName);
+ for (int origUserId : res.origUsers) {
+ if (userId == UserHandle.USER_ALL || userId == origUserId) {
+ ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT,
+ origUserId, installerPackageName);
+ }
}
}
// Also convey the prior install/uninstall state
@@ -13301,7 +13286,6 @@
}
// It's implied that when a user requests installation, they want the app to be
// installed and enabled.
- int userId = user.getIdentifier();
if (userId != UserHandle.USER_ALL) {
ps.setInstalled(true, userId);
ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, userId, installerPackageName);
@@ -17423,8 +17407,9 @@
* recycled.
*/
private void reconcileUsers(String volumeUuid) {
+ // TODO: also reconcile DE directories
final File[] files = FileUtils
- .listFilesOrEmpty(Environment.getDataUserDirectory(volumeUuid));
+ .listFilesOrEmpty(Environment.getDataUserCeDirectory(volumeUuid));
for (File file : files) {
if (!file.isDirectory()) continue;
@@ -17553,8 +17538,8 @@
Slog.v(TAG, "reconcileAppsData for " + volumeUuid + " u" + userId + " 0x"
+ Integer.toHexString(flags));
- final File ceDir = Environment.getDataUserCredentialEncryptedDirectory(volumeUuid, userId);
- final File deDir = Environment.getDataUserDeviceEncryptedDirectory(volumeUuid, userId);
+ final File ceDir = Environment.getDataUserCeDirectory(volumeUuid, userId);
+ final File deDir = Environment.getDataUserDeDirectory(volumeUuid, userId);
boolean restoreconNeeded = false;
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
new file mode 100644
index 0000000..a3ac514
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.app.AppGlobals;
+import android.content.Intent;
+import android.content.pm.PackageParser;
+import android.content.pm.PackageParser.Package;
+import android.content.pm.ResolveInfo;
+import android.os.UserHandle;
+import android.os.RemoteException;
+import android.util.ArraySet;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT;
+import static com.android.server.pm.PackageManagerService.TAG;
+
+/**
+ * Class containing helper methods for the PackageManagerService.
+ *
+ * {@hide}
+ */
+public class PackageManagerServiceUtils {
+ private final static long SEVEN_DAYS_IN_MILLISECONDS = 7 * 24 * 60 * 60 * 1000;
+
+ private static ArraySet<String> getPackageNamesForIntent(Intent intent, int userId) {
+ List<ResolveInfo> ris = null;
+ try {
+ ris = AppGlobals.getPackageManager().queryIntentReceivers(intent, null, 0, userId);
+ } catch (RemoteException e) {
+ }
+ ArraySet<String> pkgNames = new ArraySet<String>();
+ if (ris != null) {
+ for (ResolveInfo ri : ris) {
+ pkgNames.add(ri.activityInfo.packageName);
+ }
+ }
+ return pkgNames;
+ }
+
+ private static void filterRecentlyUsedApps(Collection<PackageParser.Package> pkgs,
+ long dexOptLRUThresholdInMills) {
+ // Filter out packages that aren't recently used.
+ int total = pkgs.size();
+ int skipped = 0;
+ long now = System.currentTimeMillis();
+ for (Iterator<PackageParser.Package> i = pkgs.iterator(); i.hasNext();) {
+ PackageParser.Package pkg = i.next();
+ long then = pkg.mLastPackageUsageTimeInMills;
+ if (then + dexOptLRUThresholdInMills < now) {
+ if (DEBUG_DEXOPT) {
+ Log.i(TAG, "Skipping dexopt of " + pkg.packageName + " last resumed: " +
+ ((then == 0) ? "never" : new Date(then)));
+ }
+ i.remove();
+ skipped++;
+ }
+ }
+ if (DEBUG_DEXOPT) {
+ Log.i(TAG, "Skipped dexopt " + skipped + " of " + total);
+ }
+ }
+
+ // Sort apps by importance for dexopt ordering. Important apps are given
+ // more priority in case the device runs out of space.
+ public static List<PackageParser.Package> getPackagesForDexopt(
+ Collection<PackageParser.Package> packages,
+ PackageManagerService packageManagerService) {
+ ArrayList<PackageParser.Package> remainingPkgs = new ArrayList<>(packages);
+ LinkedList<PackageParser.Package> result = new LinkedList<>();
+
+ // Give priority to core apps.
+ for (PackageParser.Package pkg : remainingPkgs) {
+ if (pkg.coreApp) {
+ if (DEBUG_DEXOPT) {
+ Log.i(TAG, "Adding core app " + result.size() + ": " + pkg.packageName);
+ }
+ result.add(pkg);
+ }
+ }
+ remainingPkgs.removeAll(result);
+
+ // Give priority to system apps that listen for pre boot complete.
+ Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
+ ArraySet<String> pkgNames = getPackageNamesForIntent(intent, UserHandle.USER_SYSTEM);
+ for (PackageParser.Package pkg : remainingPkgs) {
+ if (pkgNames.contains(pkg.packageName)) {
+ if (DEBUG_DEXOPT) {
+ Log.i(TAG, "Adding pre boot system app " + result.size() + ": " +
+ pkg.packageName);
+ }
+ result.add(pkg);
+ }
+ }
+ remainingPkgs.removeAll(result);
+
+ // Filter out packages that aren't recently used, add all remaining apps.
+ // TODO: add a property to control this?
+ if (packageManagerService.isHistoricalPackageUsageAvailable()) {
+ filterRecentlyUsedApps(remainingPkgs, SEVEN_DAYS_IN_MILLISECONDS);
+ }
+ result.addAll(remainingPkgs);
+
+ // Now go ahead and also add the libraries required for these packages.
+ // TODO: Think about interleaving things.
+ Set<PackageParser.Package> dependencies = new HashSet<>();
+ for (PackageParser.Package p : result) {
+ dependencies.addAll(packageManagerService.findSharedNonSystemLibraries(p));
+ }
+ if (!dependencies.isEmpty()) {
+ // We might have packages already in `result` that are dependencies
+ // of other packages. Make sure we don't add those to the list twice.
+ dependencies.removeAll(result);
+ }
+ result.addAll(dependencies);
+
+ if (DEBUG_DEXOPT) {
+ StringBuilder sb = new StringBuilder();
+ for (PackageParser.Package pkg : result) {
+ if (sb.length() > 0) {
+ sb.append(", ");
+ }
+ sb.append(pkg.packageName);
+ }
+ Log.i(TAG, "Packages to be dexopted: " + sb.toString());
+ }
+
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 3cc7b10..76d6b28 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1789,6 +1789,10 @@
Log.w(LOG_TAG, "Cannot add user. DISALLOW_ADD_USER is enabled.");
return null;
}
+ return createUserInternalUnchecked(name, flags, parentId);
+ }
+
+ private UserInfo createUserInternalUnchecked(String name, int flags, int parentId) {
if (ActivityManager.isLowRamDeviceStatic()) {
return null;
}
@@ -1930,13 +1934,18 @@
if (user == null) {
return null;
}
- setUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS, true, user.id);
- // Change the setting before applying the DISALLOW_SHARE_LOCATION restriction, otherwise
- // the putIntForUser() will fail.
- android.provider.Settings.Secure.putIntForUser(mContext.getContentResolver(),
- android.provider.Settings.Secure.LOCATION_MODE,
- android.provider.Settings.Secure.LOCATION_MODE_OFF, user.id);
- setUserRestriction(UserManager.DISALLOW_SHARE_LOCATION, true, user.id);
+ long identity = Binder.clearCallingIdentity();
+ try {
+ setUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS, true, user.id);
+ // Change the setting before applying the DISALLOW_SHARE_LOCATION restriction, otherwise
+ // the putIntForUser() will fail.
+ android.provider.Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ android.provider.Settings.Secure.LOCATION_MODE,
+ android.provider.Settings.Secure.LOCATION_MODE_OFF, user.id);
+ setUserRestriction(UserManager.DISALLOW_SHARE_LOCATION, true, user.id);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
return user;
}
@@ -2975,6 +2984,17 @@
am.switchUser(UserHandle.USER_SYSTEM);
}
}
+
+ @Override
+ public UserInfo createUserEvenWhenDisallowed(String name, int flags) {
+ UserInfo user = createUserInternalUnchecked(name, flags, UserHandle.USER_NULL);
+ // Keep this in sync with UserManager.createUser
+ if (user != null && !user.isAdmin()) {
+ setUserRestriction(UserManager.DISALLOW_SMS, true, user.id);
+ setUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS, true, user.id);
+ }
+ return user;
+ }
}
/* Remove all the users except of the system one. */
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index c046ba6..bff0650 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -18,6 +18,8 @@
import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.HOME_STACK_ID;
import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
import static android.content.pm.PackageManager.FEATURE_TELEVISION;
import static android.content.pm.PackageManager.FEATURE_WATCH;
@@ -32,6 +34,7 @@
import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
import android.app.ActivityManager;
+import android.app.ActivityManager.StackId;
import android.app.ActivityManagerInternal;
import android.app.ActivityManagerInternal.SleepToken;
import android.app.ActivityManagerNative;
@@ -135,6 +138,7 @@
import com.android.server.LocalServices;
import com.android.server.policy.keyguard.KeyguardServiceDelegate;
import com.android.server.policy.keyguard.KeyguardServiceDelegate.DrawnListener;
+import com.android.server.statusbar.StatusBarManagerInternal;
import java.io.File;
import java.io.FileReader;
@@ -280,6 +284,7 @@
DreamManagerInternal mDreamManagerInternal;
PowerManagerInternal mPowerManagerInternal;
IStatusBarService mStatusBarService;
+ StatusBarManagerInternal mStatusBarManagerInternal;
boolean mPreloadedRecentApps;
final Object mServiceAquireLock = new Object();
Vibrator mVibrator; // Vibrator for giving feedback of orientation changes
@@ -488,6 +493,13 @@
int mResettingSystemUiFlags = 0;
// Bits that we are currently always keeping cleared.
int mForceClearedSystemUiFlags = 0;
+ int mLastFullscreenStackSysUiFlags;
+ int mLastDockedStackSysUiFlags;
+ final Rect mNonDockedStackBounds = new Rect();
+ final Rect mDockedStackBounds = new Rect();
+ final Rect mLastNonDockedStackBounds = new Rect();
+ final Rect mLastDockedStackBounds = new Rect();
+
// What we last reported to system UI about whether the compatibility
// menu needs to be displayed.
boolean mLastFocusNeedsMenu = false;
@@ -508,6 +520,8 @@
WindowState mTopFullscreenOpaqueWindowState;
WindowState mTopFullscreenOpaqueOrDimmingWindowState;
+ WindowState mTopDockedOpaqueWindowState;
+ WindowState mTopDockedOpaqueOrDimmingWindowState;
HashSet<IApplicationToken> mAppsToBeHidden = new HashSet<IApplicationToken>();
HashSet<IApplicationToken> mAppsThatDismissKeyguard = new HashSet<IApplicationToken>();
boolean mTopIsFullscreen;
@@ -844,6 +858,16 @@
}
}
+ StatusBarManagerInternal getStatusBarManagerInternal() {
+ synchronized (mServiceAquireLock) {
+ if (mStatusBarManagerInternal == null) {
+ mStatusBarManagerInternal =
+ LocalServices.getService(StatusBarManagerInternal.class);
+ }
+ return mStatusBarManagerInternal;
+ }
+ }
+
/*
* We always let the sensor be switched on by default except when
* the user has explicitly disabled sensor based rotation or when the
@@ -4552,6 +4576,8 @@
public void beginPostLayoutPolicyLw(int displayWidth, int displayHeight) {
mTopFullscreenOpaqueWindowState = null;
mTopFullscreenOpaqueOrDimmingWindowState = null;
+ mTopDockedOpaqueWindowState = null;
+ mTopDockedOpaqueOrDimmingWindowState = null;
mAppsToBeHidden.clear();
mAppsThatDismissKeyguard.clear();
mForceStatusBar = false;
@@ -4597,7 +4623,7 @@
&& attrs.type < FIRST_SYSTEM_WINDOW;
final boolean showWhenLocked = (fl & FLAG_SHOW_WHEN_LOCKED) != 0;
final boolean dismissKeyguard = (fl & FLAG_DISMISS_KEYGUARD) != 0;
-
+ final int stackId = win.getStackId();
if (mTopFullscreenOpaqueWindowState == null &&
win.isVisibleOrBehindKeyguardLw() && !win.isGoneForLayoutLw()) {
if ((fl & FLAG_FORCE_NOT_FULLSCREEN) != 0) {
@@ -4646,9 +4672,7 @@
} else {
mAppsToBeHidden.add(appToken);
}
- if (attrs.x == 0 && attrs.y == 0
- && attrs.width == WindowManager.LayoutParams.MATCH_PARENT
- && attrs.height == WindowManager.LayoutParams.MATCH_PARENT) {
+ if (isFullscreen(attrs) && StackId.normallyFullscreenWindows(stackId)) {
if (DEBUG_LAYOUT) Slog.v(TAG, "Fullscreen window: " + win);
mTopFullscreenOpaqueWindowState = win;
if (mTopFullscreenOpaqueOrDimmingWindowState == null) {
@@ -4692,11 +4716,37 @@
mWinShowWhenLocked = win;
}
}
- if (mTopFullscreenOpaqueOrDimmingWindowState == null
- && win.isVisibleOrBehindKeyguardLw() && !win.isGoneForLayoutLw()
- && win.isDimming()) {
+
+ // Keep track of the window if it's dimming but not necessarily fullscreen.
+ final boolean reallyVisible = win.isVisibleOrBehindKeyguardLw() && !win.isGoneForLayoutLw();
+ if (mTopFullscreenOpaqueOrDimmingWindowState == null && reallyVisible
+ && win.isDimming() && StackId.normallyFullscreenWindows(stackId)) {
mTopFullscreenOpaqueOrDimmingWindowState = win;
}
+
+ // We need to keep track of the top "fullscreen" opaque window for the docked stack
+ // separately, because both the "real fullscreen" opaque window and the one for the docked
+ // stack can control View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.
+ if (mTopDockedOpaqueWindowState == null && reallyVisible && appWindow && attached == null
+ && isFullscreen(attrs) && stackId == DOCKED_STACK_ID) {
+ mTopDockedOpaqueWindowState = win;
+ if (mTopDockedOpaqueOrDimmingWindowState == null) {
+ mTopDockedOpaqueOrDimmingWindowState = win;
+ }
+ }
+
+ // Also keep track of any windows that are dimming but not necessarily fullscreen in the
+ // docked stack.
+ if (mTopDockedOpaqueOrDimmingWindowState == null && reallyVisible && win.isDimming()
+ && stackId == DOCKED_STACK_ID) {
+ mTopDockedOpaqueOrDimmingWindowState = win;
+ }
+ }
+
+ private boolean isFullscreen(WindowManager.LayoutParams attrs) {
+ return attrs.x == 0 && attrs.y == 0
+ && attrs.width == WindowManager.LayoutParams.MATCH_PARENT
+ && attrs.height == WindowManager.LayoutParams.MATCH_PARENT;
}
/** {@inheritDoc} */
@@ -6838,42 +6888,52 @@
tmpVisibility |= StatusBarManager.DISABLE_RECENT;
}
- tmpVisibility = updateLightStatusBarLw(tmpVisibility);
+ final int fullscreenVisibility = updateLightStatusBarLw(0 /* vis */,
+ mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState);
+ final int dockedVisibility = updateLightStatusBarLw(0 /* vis */,
+ mTopDockedOpaqueWindowState, mTopDockedOpaqueOrDimmingWindowState);
+ mWindowManagerFuncs.getStackBounds(HOME_STACK_ID, mNonDockedStackBounds);
+ mWindowManagerFuncs.getStackBounds(DOCKED_STACK_ID, mDockedStackBounds);
final int visibility = updateSystemBarsLw(win, mLastSystemUiFlags, tmpVisibility);
final int diff = visibility ^ mLastSystemUiFlags;
+ final int fullscreenDiff = fullscreenVisibility ^ mLastFullscreenStackSysUiFlags;
+ final int dockedDiff = dockedVisibility ^ mLastDockedStackSysUiFlags;
final boolean needsMenu = win.getNeedsMenuLw(mTopFullscreenOpaqueWindowState);
- if (diff == 0 && mLastFocusNeedsMenu == needsMenu
- && mFocusedApp == win.getAppToken()) {
+ if (diff == 0 && fullscreenDiff == 0 && dockedDiff == 0 && mLastFocusNeedsMenu == needsMenu
+ && mFocusedApp == win.getAppToken()
+ && mLastNonDockedStackBounds.equals(mNonDockedStackBounds)
+ && mLastDockedStackBounds.equals(mDockedStackBounds)) {
return 0;
}
mLastSystemUiFlags = visibility;
+ mLastFullscreenStackSysUiFlags = fullscreenVisibility;
+ mLastDockedStackSysUiFlags = dockedVisibility;
mLastFocusNeedsMenu = needsMenu;
mFocusedApp = win.getAppToken();
+ final Rect fullscreenStackBounds = new Rect(mNonDockedStackBounds);
+ final Rect dockedStackBounds = new Rect(mDockedStackBounds);
mHandler.post(new Runnable() {
@Override
public void run() {
- try {
- IStatusBarService statusbar = getStatusBarService();
- if (statusbar != null) {
- statusbar.setSystemUiVisibility(visibility, 0xffffffff, win.toString());
- statusbar.topAppWindowChanged(needsMenu);
- }
- } catch (RemoteException e) {
- // re-acquire status bar service next time it is needed.
- mStatusBarService = null;
+ StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
+ if (statusbar != null) {
+ statusbar.setSystemUiVisibility(visibility, fullscreenVisibility,
+ dockedVisibility, 0xffffffff, fullscreenStackBounds,
+ dockedStackBounds, win.toString());
+ statusbar.topAppWindowChanged(needsMenu);
}
}
});
return diff;
}
- private int updateLightStatusBarLw(int vis) {
+ private int updateLightStatusBarLw(int vis, WindowState opaque, WindowState opaqueOrDimming) {
WindowState statusColorWin = isStatusBarKeyguard() && !mHideLockScreen
? mStatusBar
- : mTopFullscreenOpaqueOrDimmingWindowState;
+ : opaqueOrDimming;
if (statusColorWin != null) {
- if (statusColorWin == mTopFullscreenOpaqueWindowState) {
+ if (statusColorWin == opaque) {
// If the top fullscreen-or-dimming window is also the top fullscreen, respect
// its light flag.
vis &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 25d646d..cbbcdae 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -16,6 +16,7 @@
package com.android.server.statusbar;
+import android.graphics.Rect;
import android.os.Bundle;
import com.android.server.notification.NotificationDelegate;
@@ -29,4 +30,7 @@
void showAssistDisclosure();
void startAssist(Bundle args);
void onCameraLaunchGestureDetected(int source);
+ void topAppWindowChanged(boolean menuVisible);
+ void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis, int mask,
+ Rect fullscreenBounds, Rect dockedBounds, String cause);
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 90340d5..6eab8d4 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -20,6 +20,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.graphics.Rect;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -70,6 +71,10 @@
private Object mLock = new Object();
// encompasses lights-out mode and other flags defined on View
private int mSystemUiVisibility = 0;
+ private int mFullscreenStackSysUiVisibility;
+ private int mDockedStackSysUiVisibility;
+ private final Rect mFullscreenStackBounds = new Rect();
+ private final Rect mDockedStackBounds = new Rect();
private boolean mMenuVisible = false;
private int mImeWindowVis = 0;
private int mImeBackDisposition;
@@ -186,6 +191,19 @@
}
}
}
+
+ @Override
+ public void topAppWindowChanged(boolean menuVisible) {
+ StatusBarManagerService.this.topAppWindowChanged(menuVisible);
+ }
+
+ @Override
+ public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
+ int mask,
+ Rect fullscreenBounds, Rect dockedBounds, String cause) {
+ StatusBarManagerService.this.setSystemUiVisibility(vis, fullscreenStackVis,
+ dockedStackVis, mask, fullscreenBounds, dockedBounds, cause);
+ }
};
// ================================================================================
@@ -390,8 +408,7 @@
* response to a window with {@link android.view.WindowManager.LayoutParams#needsMenuKey} set
* to {@link android.view.WindowManager.LayoutParams#NEEDS_MENU_SET_TRUE}.
*/
- @Override
- public void topAppWindowChanged(final boolean menuVisible) {
+ private void topAppWindowChanged(final boolean menuVisible) {
enforceStatusBar();
if (SPEW) Slog.d(TAG, (menuVisible?"showing":"hiding") + " MENU key");
@@ -399,15 +416,15 @@
synchronized(mLock) {
mMenuVisible = menuVisible;
mHandler.post(new Runnable() {
- public void run() {
- if (mBar != null) {
- try {
- mBar.topAppWindowChanged(menuVisible);
- } catch (RemoteException ex) {
- }
+ public void run() {
+ if (mBar != null) {
+ try {
+ mBar.topAppWindowChanged(menuVisible);
+ } catch (RemoteException ex) {
}
}
- });
+ }
+ });
}
}
@@ -443,13 +460,19 @@
@Override
public void setSystemUiVisibility(int vis, int mask, String cause) {
+ setSystemUiVisibility(vis, 0, 0, mask, mFullscreenStackBounds, mDockedStackBounds, cause);
+ }
+
+ private void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis, int mask,
+ Rect fullscreenBounds, Rect dockedBounds, String cause) {
// also allows calls from window manager which is in this process.
enforceStatusBarService();
if (SPEW) Slog.d(TAG, "setSystemUiVisibility(0x" + Integer.toHexString(vis) + ")");
synchronized (mLock) {
- updateUiVisibilityLocked(vis, mask);
+ updateUiVisibilityLocked(vis, fullscreenStackVis, dockedStackVis, mask,
+ fullscreenBounds, dockedBounds);
disableLocked(
mCurrentUserId,
vis & StatusBarManager.DISABLE_MASK,
@@ -458,14 +481,25 @@
}
}
- private void updateUiVisibilityLocked(final int vis, final int mask) {
- if (mSystemUiVisibility != vis) {
+ private void updateUiVisibilityLocked(final int vis,
+ final int fullscreenStackVis, final int dockedStackVis, final int mask,
+ final Rect fullscreenBounds, final Rect dockedBounds) {
+ if (mSystemUiVisibility != vis
+ || mFullscreenStackSysUiVisibility != fullscreenStackVis
+ || mDockedStackSysUiVisibility != dockedStackVis
+ || !mFullscreenStackBounds.equals(fullscreenBounds)
+ || !mDockedStackBounds.equals(dockedBounds)) {
mSystemUiVisibility = vis;
+ mFullscreenStackSysUiVisibility = fullscreenStackVis;
+ mDockedStackSysUiVisibility = dockedStackVis;
+ mFullscreenStackBounds.set(fullscreenBounds);
+ mDockedStackBounds.set(dockedBounds);
mHandler.post(new Runnable() {
public void run() {
if (mBar != null) {
try {
- mBar.setSystemUiVisibility(vis, mask);
+ mBar.setSystemUiVisibility(vis, fullscreenStackVis, dockedStackVis,
+ mask, fullscreenBounds, dockedBounds);
} catch (RemoteException ex) {
}
}
@@ -617,7 +651,8 @@
// ================================================================================
@Override
public void registerStatusBar(IStatusBar bar, List<String> iconSlots,
- List<StatusBarIcon> iconList, int switches[], List<IBinder> binders) {
+ List<StatusBarIcon> iconList, int switches[], List<IBinder> binders,
+ Rect fullscreenStackBounds, Rect dockedStackBounds) {
enforceStatusBarService();
Slog.i(TAG, "registerStatusBar bar=" + bar);
@@ -636,7 +671,11 @@
switches[4] = mImeBackDisposition;
switches[5] = mShowImeSwitcher ? 1 : 0;
switches[6] = gatherDisableActionsLocked(mCurrentUserId, 2);
+ switches[7] = mFullscreenStackSysUiVisibility;
+ switches[8] = mDockedStackSysUiVisibility;
binders.add(mImeToken);
+ fullscreenStackBounds.set(mFullscreenStackBounds);
+ dockedStackBounds.set(mDockedStackBounds);
}
}
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
index c3a6f5d..f3b120f 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
@@ -29,8 +29,9 @@
import android.os.Binder;
import android.os.Process;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.provider.Settings;
-import android.provider.Settings.Secure;
+import android.provider.Settings.Global;
import android.util.AndroidRuntimeException;
import android.util.Slog;
import android.webkit.IWebViewUpdateService;
@@ -90,7 +91,7 @@
// change provider when the new version of the package is being installed).
if (intent.getAction().equals(Intent.ACTION_PACKAGE_REMOVED)
&& intent.getExtras().getBoolean(Intent.EXTRA_REPLACING)) {
- synchronized(this) {
+ synchronized(WebViewUpdateService.this) {
if (mCurrentWebViewPackage == null) return;
String webViewPackage = "package:" + mCurrentWebViewPackage.packageName;
@@ -141,7 +142,7 @@
// only kills dependents of packages that are being removed.
try {
ActivityManagerNative.getDefault().killPackageDependents(
- oldProviderName, getContext().getUserId());
+ oldProviderName, UserHandle.USER_ALL);
} catch (RemoteException e) {
}
}
@@ -209,7 +210,7 @@
try {
if (oldPackage != null) {
ActivityManagerNative.getDefault().killPackageDependents(
- oldPackage.packageName, getContext().getUserId());
+ oldPackage.packageName, UserHandle.USER_ALL);
}
} catch (RemoteException e) {
}
@@ -267,13 +268,13 @@
}
private static String getUserChosenWebViewProvider() {
- return Settings.Secure.getString(AppGlobals.getInitialApplication().getContentResolver(),
- Settings.Secure.WEBVIEW_PROVIDER);
+ return Settings.Global.getString(AppGlobals.getInitialApplication().getContentResolver(),
+ Settings.Global.WEBVIEW_PROVIDER);
}
private void updateUserSetting(String newProviderName) {
- Settings.Secure.putString(getContext().getContentResolver(),
- Settings.Secure.WEBVIEW_PROVIDER,
+ Settings.Global.putString(getContext().getContentResolver(),
+ Settings.Global.WEBVIEW_PROVIDER,
newProviderName == null ? "" : newProviderName);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java b/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
index 0979cd3..e229c5e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
+++ b/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
@@ -37,8 +37,8 @@
static final boolean DEBUG = false;
static final boolean DEBUG_ADD_REMOVE = false;
static final boolean DEBUG_FOCUS = false;
- static final boolean DEBUG_FOCUS_LIGHT = DEBUG_FOCUS || false;
- static final boolean DEBUG_ANIM = false;
+ static final boolean DEBUG_FOCUS_LIGHT = DEBUG_FOCUS || true;
+ static final boolean DEBUG_ANIM = true;
static final boolean DEBUG_KEYGUARD = false;
static final boolean DEBUG_LAYOUT = false;
static final boolean DEBUG_LAYERS = false;
@@ -50,7 +50,7 @@
static final boolean DEBUG_ORIENTATION = false;
static final boolean DEBUG_APP_ORIENTATION = false;
static final boolean DEBUG_CONFIGURATION = false;
- static final boolean DEBUG_APP_TRANSITIONS = false;
+ static final boolean DEBUG_APP_TRANSITIONS = true;
static final boolean DEBUG_STARTING_WINDOW = false;
static final boolean DEBUG_WALLPAPER = false;
static final boolean DEBUG_WALLPAPER_LIGHT = false || DEBUG_WALLPAPER;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index d1ffaa0..f01ffb5 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1192,7 +1192,7 @@
break;
}
}
- if (DEBUG_FOCUS_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
+ if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
"Based on layer: Adding window " + win + " at " + (i + 1) + " of "
+ windows.size());
windows.add(i + 1, win);
@@ -1224,7 +1224,7 @@
//apptoken note that the window could be a floating window
//that was created later or a window at the top of the list of
//windows associated with this token.
- if (DEBUG_FOCUS_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
+ if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
"not Base app: Adding window " + win + " at " + (newIdx + 1) + " of "
+ windows.size());
windows.add(newIdx + 1, win);
@@ -1262,7 +1262,7 @@
}
}
i++;
- if (DEBUG_FOCUS_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
+ if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
"Free window: Adding window " + win + " at " + i + " of " + windows.size());
windows.add(i, win);
mWindowsChanged = true;
@@ -1333,7 +1333,7 @@
}
private void addWindowToListInOrderLocked(final WindowState win, boolean addToToken) {
- if (DEBUG_FOCUS_LIGHT) Slog.d(TAG_WM, "addWindowToListInOrderLocked: win=" + win +
+ if (DEBUG_FOCUS) Slog.d(TAG_WM, "addWindowToListInOrderLocked: win=" + win +
" Callers=" + Debug.getCallers(4));
if (win.mAttachedWindow == null) {
final WindowToken token = win.mToken;
@@ -4236,7 +4236,6 @@
mOpeningApps.remove(wtoken);
mClosingApps.remove(wtoken);
- wtoken.mAppStopped = false;
wtoken.waitingToShow = false;
wtoken.hiddenRequested = !visible;
@@ -4246,6 +4245,8 @@
// if made visible again.
wtoken.appDied = false;
wtoken.removeAllWindows();
+ } else if (visible) {
+ wtoken.mAppStopped = false;
}
// If we are preparing an app transition, then delay changing
@@ -4863,6 +4864,7 @@
}
}
+ @Override
public void getStackBounds(int stackId, Rect bounds) {
synchronized (mWindowMap) {
final TaskStack stack = mStackIdToStack.get(stackId);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 2096ea0..5b9206d 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2088,7 +2088,8 @@
return mTmpRect;
}
- private int getStackId() {
+ @Override
+ public int getStackId() {
final TaskStack stack = getStack();
if (stack == null) {
return INVALID_STACK_ID;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 0201296..f8f8363 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1347,7 +1347,7 @@
}
} else {
if (DEBUG_ANIM && isAnimating()) {
- Slog.v(TAG, "prepareSurface: No changes in animation for " + this);
+ //Slog.v(TAG, "prepareSurface: No changes in animation for " + this);
}
displayed = true;
}
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index e75775f..cdd5519 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -68,13 +68,14 @@
static const GpsNavigationMessageInterface* sGpsNavigationMessageInterface = NULL;
static const GnssConfigurationInterface* sGnssConfigurationInterface = NULL;
-#define MAX_SATELLITE_COUNT 512
-#define MAX_GPS_SATELLITE_COUNT 512
+#define GPS_MAX_SATELLITE_COUNT 32
+#define GNSS_MAX_SATELLITE_COUNT 64
-#define PRN_SHIFT_WIDTH 3
+#define SVID_SHIFT_WIDTH 7
+#define CONSTELLATION_TYPE_SHIFT_WIDTH 3
// temporary storage for GPS callbacks
-static GnssSvInfo sGnssSvList[MAX_SATELLITE_COUNT];
+static GnssSvInfo sGnssSvList[GNSS_MAX_SATELLITE_COUNT];
static size_t sGnssSvListSize;
static const char* sNmeaString;
static int sNmeaStringLength;
@@ -113,56 +114,75 @@
{
JNIEnv* env = AndroidRuntime::getJNIEnv();
size_t status_size = sv_status->size;
- // Some drive doesn't set the size field correctly. Assume GpsSvStatus_v1 if
- // it doesn't provide a valid size.
+ // Some drives doesn't set the size field correctly. Assume GpsSvStatus_v1
+ // if it doesn't provide a valid size.
if (status_size == 0) {
- status_size = sizeof(GpsSvStatus_v1);
+ ALOGW("Invalid size of GpsSvStatus found: %zd.", status_size);
}
- if (status_size == sizeof(GpsSvStatus)) {
- sGnssSvListSize = sv_status->gnss_sv_list_size;
- // Cramp the list size
- if (sGnssSvListSize > MAX_SATELLITE_COUNT) {
- sGnssSvListSize = MAX_SATELLITE_COUNT;
- }
- // Copy GNSS SV info into sGnssSvList, if any.
- if (sGnssSvListSize > 0 && sv_status->gnss_sv_list) {
- memcpy(sGnssSvList, sv_status->gnss_sv_list, sizeof(GnssSvInfo) * sGnssSvListSize);
- }
- } else if (status_size == sizeof(GpsSvStatus_v1)) {
- sGnssSvListSize = sv_status->num_svs;
- // Cramp the list size
- if (sGnssSvListSize > MAX_GPS_SATELLITE_COUNT) {
- sGnssSvListSize = MAX_GPS_SATELLITE_COUNT;
- }
- uint32_t ephemeris_mask = sv_status->ephemeris_mask;
- uint32_t almanac_mask = sv_status->almanac_mask;
- uint32_t used_in_fix_mask = sv_status->used_in_fix_mask;
- for (size_t i = 0; i < sGnssSvListSize; i++) {
- GnssSvInfo& info = sGnssSvList[i];
+ sGnssSvListSize = sv_status->num_svs;
+ // Clamp the list size. Legacy GpsSvStatus has only 32 elements in sv_list.
+ if (sGnssSvListSize > GPS_MAX_SATELLITE_COUNT) {
+ ALOGW("Too many satellites %zd. Clamps to %d.",
+ sGnssSvListSize,
+ GPS_MAX_SATELLITE_COUNT);
+ sGnssSvListSize = GPS_MAX_SATELLITE_COUNT;
+ }
+ uint32_t ephemeris_mask = sv_status->ephemeris_mask;
+ uint32_t almanac_mask = sv_status->almanac_mask;
+ uint32_t used_in_fix_mask = sv_status->used_in_fix_mask;
+ for (size_t i = 0; i < sGnssSvListSize; i++) {
+ GnssSvInfo& info = sGnssSvList[i];
+ info.svid = sv_status->sv_list[i].prn;
+ // TODO: implement the correct logic to derive the constellation type
+ // based on PRN ranges.
+ if (info.svid >=1 && info.svid <= 32) {
info.constellation = GNSS_CONSTELLATION_GPS;
- info.prn = sv_status->sv_list[i].prn;
- info.snr = sv_status->sv_list[i].snr;
- info.elevation = sv_status->sv_list[i].elevation;
- info.azimuth = sv_status->sv_list[i].azimuth;
- info.flags = GNSS_SV_FLAGS_NONE;
- if (info.prn > 0 && info.prn <= 32) {
- int32_t this_prn_mask = (1 << (info.prn - 1));
- if ((ephemeris_mask & this_prn_mask) != 0) {
+ } else {
+ info.constellation = GNSS_CONSTELLATION_UNKNOWN;
+ }
+ info.snr = sv_status->sv_list[i].snr;
+ info.elevation = sv_status->sv_list[i].elevation;
+ info.azimuth = sv_status->sv_list[i].azimuth;
+ info.flags = GNSS_SV_FLAGS_NONE;
+ if (info.svid > 0 && info.svid <= 32) {
+ int32_t this_svid_mask = (1 << (info.svid - 1));
+ if ((ephemeris_mask & this_svid_mask) != 0) {
info.flags |= GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA;
- }
- if ((almanac_mask & this_prn_mask) != 0) {
+ }
+ if ((almanac_mask & this_svid_mask) != 0) {
info.flags |= GNSS_SV_FLAGS_HAS_ALMANAC_DATA;
- }
- if ((used_in_fix_mask & this_prn_mask) != 0) {
+ }
+ if ((used_in_fix_mask & this_svid_mask) != 0) {
info.flags |= GNSS_SV_FLAGS_USED_IN_FIX;
- }
}
}
- } else {
- sGnssSvListSize = 0;
- ALOGE("Invalid size of GpsSvStatus found: %zd.", status_size);
+ }
+ env->CallVoidMethod(mCallbacksObj, method_reportSvStatus);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+static void gnss_sv_status_callback(GnssSvStatus* sv_status) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ size_t status_size = sv_status->size;
+ // Check the size, and reject the object that has invalid size.
+ if (status_size != sizeof(GnssSvStatus)) {
+ ALOGE("Invalid size of GnssSvStatus found: %zd.", status_size);
return;
}
+ sGnssSvListSize = sv_status->num_svs;
+ // Clamp the list size
+ if (sGnssSvListSize > GNSS_MAX_SATELLITE_COUNT) {
+ ALOGD("Too many satellites %zd. Clamps to %d.",
+ sGnssSvListSize,
+ GNSS_MAX_SATELLITE_COUNT);
+ sGnssSvListSize = GNSS_MAX_SATELLITE_COUNT;
+ }
+ // Copy GNSS SV info into sGnssSvList, if any.
+ if (sGnssSvListSize > 0) {
+ memcpy(sGnssSvList,
+ sv_status->gnss_sv_list,
+ sizeof(GnssSvInfo) * sGnssSvListSize);
+ }
env->CallVoidMethod(mCallbacksObj, method_reportSvStatus);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
}
@@ -228,6 +248,7 @@
create_thread_callback,
request_utc_time_callback,
set_system_info_callback,
+ gnss_sv_status_callback,
};
static void xtra_download_request_callback()
@@ -677,31 +698,30 @@
}
static jint android_location_GnssLocationProvider_read_sv_status(JNIEnv* env, jobject /* obj */,
- jintArray prnWithFlagArray, jfloatArray snrArray, jfloatArray elevArray,
- jfloatArray azumArray, jintArray constellationTypeArray)
+ jintArray svidWithFlagArray, jfloatArray snrArray, jfloatArray elevArray,
+ jfloatArray azumArray)
{
// this should only be called from within a call to reportSvStatus
- jint* prnWithFlags = env->GetIntArrayElements(prnWithFlagArray, 0);
+ jint* svidWithFlags = env->GetIntArrayElements(svidWithFlagArray, 0);
jfloat* snrs = env->GetFloatArrayElements(snrArray, 0);
jfloat* elev = env->GetFloatArrayElements(elevArray, 0);
jfloat* azim = env->GetFloatArrayElements(azumArray, 0);
- jint* constellationTypes = env->GetIntArrayElements(constellationTypeArray, 0);
// GNSS SV info.
for (size_t i = 0; i < sGnssSvListSize; ++i) {
const GnssSvInfo& info = sGnssSvList[i];
- constellationTypes[i] = info.constellation;
- prnWithFlags[i] = (info.prn << PRN_SHIFT_WIDTH) | info.flags;
+ svidWithFlags[i] = (info.svid << SVID_SHIFT_WIDTH) |
+ (info.constellation << CONSTELLATION_TYPE_SHIFT_WIDTH) |
+ info.flags;
snrs[i] = info.snr;
elev[i] = info.elevation;
azim[i] = info.azimuth;
}
- env->ReleaseIntArrayElements(prnWithFlagArray, prnWithFlags, 0);
+ env->ReleaseIntArrayElements(svidWithFlagArray, svidWithFlags, 0);
env->ReleaseFloatArrayElements(snrArray, snrs, 0);
env->ReleaseFloatArrayElements(elevArray, elev, 0);
env->ReleaseFloatArrayElements(azumArray, azim, 0);
- env->ReleaseIntArrayElements(constellationTypeArray, constellationTypes, 0);
return (jint) sGnssSvListSize;
}
@@ -968,370 +988,341 @@
return JNI_FALSE;
}
-static jobject translate_gps_clock(JNIEnv* env, void* data, size_t size) {
- const char* doubleSignature = "(D)V";
- const char* longSignature = "(J)V";
+template<class T>
+class JavaMethodHelper {
+ public:
+ // Helper function to call setter on a Java object.
+ static void callJavaMethod(
+ JNIEnv* env,
+ jclass clazz,
+ jobject object,
+ const char* method_name,
+ T value);
- GpsClock* clock = reinterpret_cast<GpsClock*>(data);
+ private:
+ static const char *const signature_;
+};
- jclass gpsClockClass = env->FindClass("android/location/GnssClock");
- jmethodID gpsClockCtor = env->GetMethodID(gpsClockClass, "<init>", "()V");
+template<class T>
+void JavaMethodHelper<T>::callJavaMethod(
+ JNIEnv* env,
+ jclass clazz,
+ jobject object,
+ const char* method_name,
+ T value) {
+ jmethodID method = env->GetMethodID(clazz, method_name, signature_);
+ env->CallVoidMethod(object, method, value);
+}
- jobject gpsClockObject = env->NewObject(gpsClockClass, gpsClockCtor);
+class JavaObject {
+ public:
+ JavaObject(JNIEnv* env, const char* class_name);
+ virtual ~JavaObject();
+
+ template<class T>
+ void callSetter(const char* method_name, T value);
+ template<class T>
+ void callSetter(const char* method_name, T* value, size_t size);
+ jobject get();
+
+ private:
+ JNIEnv* env_;
+ jclass clazz_;
+ jobject object_;
+};
+
+JavaObject::JavaObject(JNIEnv* env, const char* class_name) : env_(env) {
+ clazz_ = env_->FindClass(class_name);
+ jmethodID ctor = env->GetMethodID(clazz_, "<init>", "()V");
+ object_ = env_->NewObject(clazz_, ctor);
+}
+
+JavaObject::~JavaObject() {
+ env_->DeleteLocalRef(clazz_);
+}
+
+template<class T>
+void JavaObject::callSetter(const char* method_name, T value) {
+ JavaMethodHelper<T>::callJavaMethod(
+ env_, clazz_, object_, method_name, value);
+}
+
+template<>
+void JavaObject::callSetter(
+ const char* method_name, uint8_t* value, size_t size) {
+ jbyteArray array = env_->NewByteArray(size);
+ env_->SetByteArrayRegion(array, 0, size, (jbyte*) value);
+ jmethodID method = env_->GetMethodID(
+ clazz_,
+ method_name,
+ "([B)V");
+ env_->CallVoidMethod(object_, method, array);
+}
+
+jobject JavaObject::get() {
+ return object_;
+}
+
+// Define Java method signatures for all known types.
+
+template<>
+const char *const JavaMethodHelper<uint8_t>::signature_ = "(B)V";
+template<>
+const char *const JavaMethodHelper<int8_t>::signature_ = "(B)V";
+template<>
+const char *const JavaMethodHelper<int16_t>::signature_ = "(S)V";
+template<>
+const char *const JavaMethodHelper<uint16_t>::signature_ = "(S)V";
+template<>
+const char *const JavaMethodHelper<int>::signature_ = "(I)V";
+template<>
+const char *const JavaMethodHelper<int64_t>::signature_ = "(J)V";
+template<>
+const char *const JavaMethodHelper<float>::signature_ = "(F)V";
+template<>
+const char *const JavaMethodHelper<double>::signature_ = "(D)V";
+template<>
+const char *const JavaMethodHelper<bool>::signature_ = "(Z)V";
+
+#define SET(setter, value) object.callSetter("set" # setter, (value))
+#define SET_IF(flag, setter, value) \
+ if (flags & (flag)) object.callSetter("set" # setter, (value))
+
+static jobject translate_gps_clock(JNIEnv* env, GpsClock* clock) {
+ JavaObject object(env, "android/location/GnssClock");
GpsClockFlags flags = clock->flags;
- if (flags & GPS_CLOCK_HAS_LEAP_SECOND) {
- jmethodID setterMethod = env->GetMethodID(gpsClockClass, "setLeapSecond", "(S)V");
- env->CallVoidMethod(gpsClockObject, setterMethod, clock->leap_second);
- }
+ SET_IF(GPS_CLOCK_HAS_LEAP_SECOND, LeapSecond, clock->leap_second);
+ SET(Type, clock->type);
+ SET(TimeInNs, clock->time_ns);
+ SET_IF(GPS_CLOCK_HAS_TIME_UNCERTAINTY,
+ TimeUncertaintyInNs,
+ clock->time_uncertainty_ns);
+ SET_IF(GPS_CLOCK_HAS_FULL_BIAS, FullBiasInNs, clock->full_bias_ns);
+ SET_IF(GPS_CLOCK_HAS_BIAS, BiasInNs, clock->bias_ns);
+ SET_IF(GPS_CLOCK_HAS_BIAS_UNCERTAINTY,
+ BiasUncertaintyInNs,
+ clock->bias_uncertainty_ns);
+ SET_IF(GPS_CLOCK_HAS_DRIFT, DriftInNsPerSec, clock->drift_nsps);
+ SET_IF(GPS_CLOCK_HAS_DRIFT_UNCERTAINTY,
+ DriftUncertaintyInNsPerSec,
+ clock->drift_uncertainty_nsps);
- jmethodID typeSetterMethod = env->GetMethodID(gpsClockClass, "setType", "(B)V");
- env->CallVoidMethod(gpsClockObject, typeSetterMethod, clock->type);
-
- jmethodID setterMethod = env->GetMethodID(gpsClockClass, "setTimeInNs", longSignature);
- env->CallVoidMethod(gpsClockObject, setterMethod, clock->time_ns);
-
- if (flags & GPS_CLOCK_HAS_TIME_UNCERTAINTY) {
- jmethodID setterMethod =
- env->GetMethodID(gpsClockClass, "setTimeUncertaintyInNs", doubleSignature);
- env->CallVoidMethod(gpsClockObject, setterMethod, clock->time_uncertainty_ns);
- }
-
- if (flags & GPS_CLOCK_HAS_FULL_BIAS) {
- jmethodID setterMethod = env->GetMethodID(gpsClockClass, "setFullBiasInNs", longSignature);
- env->CallVoidMethod(gpsClockObject, setterMethod, clock->full_bias_ns);
- }
-
- if (flags & GPS_CLOCK_HAS_BIAS) {
- jmethodID setterMethod = env->GetMethodID(gpsClockClass, "setBiasInNs", doubleSignature);
- env->CallVoidMethod(gpsClockObject, setterMethod, clock->bias_ns);
- }
-
- if (flags & GPS_CLOCK_HAS_BIAS_UNCERTAINTY) {
- jmethodID setterMethod =
- env->GetMethodID(gpsClockClass, "setBiasUncertaintyInNs", doubleSignature);
- env->CallVoidMethod(gpsClockObject, setterMethod, clock->bias_uncertainty_ns);
- }
-
- if (flags & GPS_CLOCK_HAS_DRIFT) {
- jmethodID setterMethod =
- env->GetMethodID(gpsClockClass, "setDriftInNsPerSec", doubleSignature);
- env->CallVoidMethod(gpsClockObject, setterMethod, clock->drift_nsps);
- }
-
- if (flags & GPS_CLOCK_HAS_DRIFT_UNCERTAINTY) {
- jmethodID setterMethod =
- env->GetMethodID(gpsClockClass, "setDriftUncertaintyInNsPerSec", doubleSignature);
- env->CallVoidMethod(gpsClockObject, setterMethod, clock->drift_uncertainty_nsps);
- }
-
+ /*
if (flags & GPS_CLOCK_TYPE_LOCAL_HW_TIME) {
- if (size == sizeof(GpsClock)) {
+ if (size == sizeof(GnssClock)) {
jmethodID setterMethod =
env->GetMethodID(gpsClockClass,
"setTimeOfLastHwClockDiscontinuityInNs",
longSignature);
env->CallVoidMethod(gpsClockObject,
setterMethod,
- clock->time_of_last_hw_clock_discontinuity_ns);
+ reinterpret_cast<GnssClock*>(clock)->time_of_last_hw_clock_discontinuity_ns);
}
}
+ */
- env->DeleteLocalRef(gpsClockClass);
- return gpsClockObject;
+ return object.get();
}
-static jobject translate_gps_measurement(JNIEnv* env, void* data, size_t size) {
- const char* byteSignature = "(B)V";
- const char* shortSignature = "(S)V";
- const char* intSignature = "(I)V";
- const char* longSignature = "(J)V";
- const char* floatSignature = "(F)V";
- const char* doubleSignature = "(D)V";
+static jobject translate_gnss_clock(JNIEnv* env, GnssClock* clock) {
+ JavaObject object(env, "android/location/GnssClock");
+ GpsClockFlags flags = clock->flags;
- jclass gnssMeasurementClass = env->FindClass("android/location/GnssMeasurement");
- jmethodID gnssMeasurementCtor = env->GetMethodID(gnssMeasurementClass, "<init>", "()V");
- GpsMeasurement* measurement = reinterpret_cast<GpsMeasurement*>(data);
+ SET_IF(GPS_CLOCK_HAS_LEAP_SECOND, LeapSecond, clock->leap_second);
+ SET(Type, clock->type);
+ SET(TimeInNs, clock->time_ns);
+ SET_IF(GPS_CLOCK_HAS_TIME_UNCERTAINTY,
+ TimeUncertaintyInNs,
+ clock->time_uncertainty_ns);
+ SET_IF(GPS_CLOCK_HAS_FULL_BIAS, FullBiasInNs, clock->full_bias_ns);
+ SET_IF(GPS_CLOCK_HAS_BIAS, BiasInNs, clock->bias_ns);
+ SET_IF(GPS_CLOCK_HAS_BIAS_UNCERTAINTY,
+ BiasUncertaintyInNs,
+ clock->bias_uncertainty_ns);
+ SET_IF(GPS_CLOCK_HAS_DRIFT, DriftInNsPerSec, clock->drift_nsps);
+ SET_IF(GPS_CLOCK_HAS_DRIFT_UNCERTAINTY,
+ DriftUncertaintyInNsPerSec,
+ clock->drift_uncertainty_nsps);
- jobject gnssMeasurementObject = env->NewObject(gnssMeasurementClass, gnssMeasurementCtor);
+ SET_IF(GPS_CLOCK_TYPE_LOCAL_HW_TIME,
+ TimeOfLastHwClockDiscontinuityInNs,
+ clock->time_of_last_hw_clock_discontinuity_ns);
+
+ return object.get();
+}
+
+static jobject translate_gps_measurement(JNIEnv* env,
+ GpsMeasurement* measurement) {
+ JavaObject object(env, "android/location/GnssMeasurement");
GpsMeasurementFlags flags = measurement->flags;
- jmethodID prnSetterMethod = env->GetMethodID(gnssMeasurementClass, "setPrn", byteSignature);
- env->CallVoidMethod(gnssMeasurementObject, prnSetterMethod, measurement->prn);
+ SET(Svid, static_cast<int16_t>(measurement->prn));
+ SET(TimeOffsetInNs, measurement->time_offset_ns);
+ SET(State, measurement->state);
+ SET(ReceivedGpsTowInNs, measurement->received_gps_tow_ns);
+ SET(ReceivedGpsTowUncertaintyInNs,
+ measurement->received_gps_tow_uncertainty_ns);
+ SET(Cn0InDbHz, measurement->c_n0_dbhz);
+ SET(PseudorangeRateInMetersPerSec, measurement->pseudorange_rate_mps);
+ SET(PseudorangeRateUncertaintyInMetersPerSec,
+ measurement->pseudorange_rate_uncertainty_mps);
+ SET(AccumulatedDeltaRangeState, measurement->accumulated_delta_range_state);
+ SET(AccumulatedDeltaRangeInMeters, measurement->accumulated_delta_range_m);
+ SET(AccumulatedDeltaRangeUncertaintyInMeters,
+ measurement->accumulated_delta_range_uncertainty_m);
+ SET_IF(GPS_MEASUREMENT_HAS_PSEUDORANGE,
+ PseudorangeInMeters,
+ measurement->pseudorange_m);
+ SET_IF(GPS_MEASUREMENT_HAS_PSEUDORANGE_UNCERTAINTY,
+ PseudorangeUncertaintyInMeters,
+ measurement->pseudorange_uncertainty_m);
+ SET_IF(GPS_MEASUREMENT_HAS_CODE_PHASE,
+ CodePhaseInChips,
+ measurement->code_phase_chips);
+ SET_IF(GPS_MEASUREMENT_HAS_CODE_PHASE_UNCERTAINTY,
+ CodePhaseUncertaintyInChips,
+ measurement->code_phase_uncertainty_chips);
+ SET_IF(GPS_MEASUREMENT_HAS_CARRIER_FREQUENCY,
+ CarrierFrequencyInHz,
+ measurement->carrier_frequency_hz);
+ SET_IF(GPS_MEASUREMENT_HAS_CARRIER_CYCLES,
+ CarrierCycles,
+ measurement->carrier_cycles);
+ SET_IF(GPS_MEASUREMENT_HAS_CARRIER_PHASE,
+ CarrierPhase,
+ measurement->carrier_phase);
+ SET_IF(GPS_MEASUREMENT_HAS_CARRIER_PHASE_UNCERTAINTY,
+ CarrierPhaseUncertainty,
+ measurement->carrier_phase_uncertainty);
+ SET(LossOfLock, measurement->loss_of_lock);
+ SET_IF(GPS_MEASUREMENT_HAS_BIT_NUMBER, BitNumber, measurement->bit_number);
+ SET_IF(GPS_MEASUREMENT_HAS_TIME_FROM_LAST_BIT,
+ TimeFromLastBitInMs,
+ measurement->time_from_last_bit_ms);
+ SET_IF(GPS_MEASUREMENT_HAS_DOPPLER_SHIFT,
+ DopplerShiftInHz,
+ measurement->doppler_shift_hz);
+ SET_IF(GPS_MEASUREMENT_HAS_DOPPLER_SHIFT_UNCERTAINTY,
+ DopplerShiftUncertaintyInHz,
+ measurement->doppler_shift_uncertainty_hz);
+ SET(MultipathIndicator, measurement->multipath_indicator);
+ SET_IF(GPS_MEASUREMENT_HAS_SNR, SnrInDb, measurement->snr_db);
+ SET_IF(GPS_MEASUREMENT_HAS_ELEVATION,
+ ElevationInDeg,
+ measurement->elevation_deg);
+ SET_IF(GPS_MEASUREMENT_HAS_ELEVATION_UNCERTAINTY,
+ ElevationUncertaintyInDeg,
+ measurement->elevation_uncertainty_deg);
+ SET_IF(GPS_MEASUREMENT_HAS_AZIMUTH,
+ AzimuthInDeg,
+ measurement->azimuth_deg);
+ SET_IF(GPS_MEASUREMENT_HAS_AZIMUTH_UNCERTAINTY,
+ AzimuthUncertaintyInDeg,
+ measurement->azimuth_uncertainty_deg);
+ SET(UsedInFix,
+ (flags & GPS_MEASUREMENT_HAS_USED_IN_FIX) && measurement->used_in_fix);
- jmethodID timeOffsetSetterMethod =
- env->GetMethodID(gnssMeasurementClass, "setTimeOffsetInNs", doubleSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- timeOffsetSetterMethod,
- measurement->time_offset_ns);
-
- jmethodID stateSetterMethod = env->GetMethodID(gnssMeasurementClass, "setState", shortSignature);
- env->CallVoidMethod(gnssMeasurementObject, stateSetterMethod, measurement->state);
-
- jmethodID receivedGpsTowSetterMethod =
- env->GetMethodID(gnssMeasurementClass, "setReceivedGpsTowInNs", longSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- receivedGpsTowSetterMethod,
- measurement->received_gps_tow_ns);
-
- jmethodID receivedGpsTowUncertaintySetterMethod = env->GetMethodID(
- gnssMeasurementClass,
- "setReceivedGpsTowUncertaintyInNs",
- longSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- receivedGpsTowUncertaintySetterMethod,
- measurement->received_gps_tow_uncertainty_ns);
-
- jmethodID cn0SetterMethod =
- env->GetMethodID(gnssMeasurementClass, "setCn0InDbHz", doubleSignature);
- env->CallVoidMethod(gnssMeasurementObject, cn0SetterMethod, measurement->c_n0_dbhz);
-
- jmethodID pseudorangeRateSetterMethod = env->GetMethodID(
- gnssMeasurementClass,
- "setPseudorangeRateInMetersPerSec",
- doubleSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- pseudorangeRateSetterMethod,
- measurement->pseudorange_rate_mps);
-
- jmethodID pseudorangeRateUncertaintySetterMethod = env->GetMethodID(
- gnssMeasurementClass,
- "setPseudorangeRateUncertaintyInMetersPerSec",
- doubleSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- pseudorangeRateUncertaintySetterMethod,
- measurement->pseudorange_rate_uncertainty_mps);
-
- jmethodID accumulatedDeltaRangeStateSetterMethod =
- env->GetMethodID(gnssMeasurementClass, "setAccumulatedDeltaRangeState", shortSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- accumulatedDeltaRangeStateSetterMethod,
- measurement->accumulated_delta_range_state);
-
- jmethodID accumulatedDeltaRangeSetterMethod = env->GetMethodID(
- gnssMeasurementClass,
- "setAccumulatedDeltaRangeInMeters",
- doubleSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- accumulatedDeltaRangeSetterMethod,
- measurement->accumulated_delta_range_m);
-
- jmethodID accumulatedDeltaRangeUncertaintySetterMethod = env->GetMethodID(
- gnssMeasurementClass,
- "setAccumulatedDeltaRangeUncertaintyInMeters",
- doubleSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- accumulatedDeltaRangeUncertaintySetterMethod,
- measurement->accumulated_delta_range_uncertainty_m);
-
- if (flags & GPS_MEASUREMENT_HAS_PSEUDORANGE) {
- jmethodID setterMethod =
- env->GetMethodID(gnssMeasurementClass, "setPseudorangeInMeters", doubleSignature);
- env->CallVoidMethod(gnssMeasurementObject, setterMethod, measurement->pseudorange_m);
- }
-
- if (flags & GPS_MEASUREMENT_HAS_PSEUDORANGE_UNCERTAINTY) {
- jmethodID setterMethod = env->GetMethodID(
- gnssMeasurementClass,
- "setPseudorangeUncertaintyInMeters",
- doubleSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- setterMethod,
- measurement->pseudorange_uncertainty_m);
- }
-
- if (flags & GPS_MEASUREMENT_HAS_CODE_PHASE) {
- jmethodID setterMethod =
- env->GetMethodID(gnssMeasurementClass, "setCodePhaseInChips", doubleSignature);
- env->CallVoidMethod(gnssMeasurementObject, setterMethod, measurement->code_phase_chips);
- }
-
- if (flags & GPS_MEASUREMENT_HAS_CODE_PHASE_UNCERTAINTY) {
- jmethodID setterMethod = env->GetMethodID(
- gnssMeasurementClass,
- "setCodePhaseUncertaintyInChips",
- doubleSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- setterMethod,
- measurement->code_phase_uncertainty_chips);
- }
-
- if (flags & GPS_MEASUREMENT_HAS_CARRIER_FREQUENCY) {
- jmethodID setterMethod =
- env->GetMethodID(gnssMeasurementClass, "setCarrierFrequencyInHz", floatSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- setterMethod,
- measurement->carrier_frequency_hz);
- }
-
- if (flags & GPS_MEASUREMENT_HAS_CARRIER_CYCLES) {
- jmethodID setterMethod =
- env->GetMethodID(gnssMeasurementClass, "setCarrierCycles", longSignature);
- env->CallVoidMethod(gnssMeasurementObject, setterMethod, measurement->carrier_cycles);
- }
-
- if (flags & GPS_MEASUREMENT_HAS_CARRIER_PHASE) {
- jmethodID setterMethod =
- env->GetMethodID(gnssMeasurementClass, "setCarrierPhase", doubleSignature);
- env->CallVoidMethod(gnssMeasurementObject, setterMethod, measurement->carrier_phase);
- }
-
- if (flags & GPS_MEASUREMENT_HAS_CARRIER_PHASE_UNCERTAINTY) {
- jmethodID setterMethod = env->GetMethodID(
- gnssMeasurementClass,
- "setCarrierPhaseUncertainty",
- doubleSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- setterMethod,
- measurement->carrier_phase_uncertainty);
- }
-
- jmethodID lossOfLockSetterMethod =
- env->GetMethodID(gnssMeasurementClass, "setLossOfLock", byteSignature);
- env->CallVoidMethod(gnssMeasurementObject, lossOfLockSetterMethod, measurement->loss_of_lock);
-
- if (flags & GPS_MEASUREMENT_HAS_BIT_NUMBER) {
- jmethodID setterMethod =
- env->GetMethodID(gnssMeasurementClass, "setBitNumber", intSignature);
- env->CallVoidMethod(gnssMeasurementObject, setterMethod, measurement->bit_number);
- }
-
- if (flags & GPS_MEASUREMENT_HAS_TIME_FROM_LAST_BIT) {
- jmethodID setterMethod =
- env->GetMethodID(gnssMeasurementClass, "setTimeFromLastBitInMs", shortSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- setterMethod,
- measurement->time_from_last_bit_ms);
- }
-
- if (flags & GPS_MEASUREMENT_HAS_DOPPLER_SHIFT) {
- jmethodID setterMethod =
- env->GetMethodID(gnssMeasurementClass, "setDopplerShiftInHz", doubleSignature);
- env->CallVoidMethod(gnssMeasurementObject, setterMethod, measurement->doppler_shift_hz);
- }
-
- if (flags & GPS_MEASUREMENT_HAS_DOPPLER_SHIFT_UNCERTAINTY) {
- jmethodID setterMethod = env->GetMethodID(
- gnssMeasurementClass,
- "setDopplerShiftUncertaintyInHz",
- doubleSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- setterMethod,
- measurement->doppler_shift_uncertainty_hz);
- }
-
- jmethodID multipathIndicatorSetterMethod =
- env->GetMethodID(gnssMeasurementClass, "setMultipathIndicator", byteSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- multipathIndicatorSetterMethod,
- measurement->multipath_indicator);
-
- if (flags & GPS_MEASUREMENT_HAS_SNR) {
- jmethodID setterMethod =
- env->GetMethodID(gnssMeasurementClass, "setSnrInDb", doubleSignature);
- env->CallVoidMethod(gnssMeasurementObject, setterMethod, measurement->snr_db);
- }
-
- if (flags & GPS_MEASUREMENT_HAS_ELEVATION) {
- jmethodID setterMethod =
- env->GetMethodID(gnssMeasurementClass, "setElevationInDeg", doubleSignature);
- env->CallVoidMethod(gnssMeasurementObject, setterMethod, measurement->elevation_deg);
- }
-
- if (flags & GPS_MEASUREMENT_HAS_ELEVATION_UNCERTAINTY) {
- jmethodID setterMethod =
- env->GetMethodID(gnssMeasurementClass, "setElevationUncertaintyInDeg", doubleSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- setterMethod,
- measurement->elevation_uncertainty_deg);
- }
-
- if (flags & GPS_MEASUREMENT_HAS_AZIMUTH) {
- jmethodID setterMethod =
- env->GetMethodID(gnssMeasurementClass, "setAzimuthInDeg", doubleSignature);
- env->CallVoidMethod(gnssMeasurementObject, setterMethod, measurement->azimuth_deg);
- }
-
- if (flags & GPS_MEASUREMENT_HAS_AZIMUTH_UNCERTAINTY) {
- jmethodID setterMethod = env->GetMethodID(
- gnssMeasurementClass,
- "setAzimuthUncertaintyInDeg",
- doubleSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- setterMethod,
- measurement->azimuth_uncertainty_deg);
- }
-
- jmethodID usedInFixSetterMethod = env->GetMethodID(gnssMeasurementClass, "setUsedInFix", "(Z)V");
- env->CallVoidMethod(
- gnssMeasurementObject,
- usedInFixSetterMethod,
- (flags & GPS_MEASUREMENT_HAS_USED_IN_FIX) && measurement->used_in_fix);
-
- if (size == sizeof(GpsMeasurement)) {
- jmethodID setterMethod =
- env->GetMethodID(gnssMeasurementClass,
- "setPseudorangeRateCarrierInMetersPerSec",
- doubleSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- setterMethod,
- measurement->pseudorange_rate_carrier_mps);
-
- setterMethod =
- env->GetMethodID(gnssMeasurementClass,
- "setPseudorangeRateCarrierUncertaintyInMetersPerSec",
- doubleSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- setterMethod,
- measurement->pseudorange_rate_carrier_uncertainty_mps);
- }
-
- env->DeleteLocalRef(gnssMeasurementClass);
- return gnssMeasurementObject;
+ return object.get();
}
-/**
- * <T> can only be GpsData or GpsData_v1. Must rewrite this function if more
- * types are introduced in the future releases.
- */
-template<class T>
-static jobjectArray translate_gps_measurements(JNIEnv* env, void* data) {
- T* gps_data = reinterpret_cast<T*>(data);
- size_t measurementCount = gps_data->measurement_count;
- if (measurementCount == 0) {
+static jobject translate_gnss_measurement(JNIEnv* env,
+ GnssMeasurement* measurement) {
+ JavaObject object(env, "android/location/GnssMeasurement");
+ GpsMeasurementFlags flags = measurement->flags;
+
+ SET(Svid, measurement->svid);
+ SET(TimeOffsetInNs, measurement->time_offset_ns);
+ SET(State, measurement->state);
+ SET(ReceivedGpsTowInNs, measurement->received_gps_tow_ns);
+ SET(ReceivedGpsTowUncertaintyInNs,
+ measurement->received_gps_tow_uncertainty_ns);
+ SET(Cn0InDbHz, measurement->c_n0_dbhz);
+ SET(PseudorangeRateInMetersPerSec, measurement->pseudorange_rate_mps);
+ SET(PseudorangeRateUncertaintyInMetersPerSec,
+ measurement->pseudorange_rate_uncertainty_mps);
+ SET(AccumulatedDeltaRangeState, measurement->accumulated_delta_range_state);
+ SET(AccumulatedDeltaRangeInMeters, measurement->accumulated_delta_range_m);
+ SET(AccumulatedDeltaRangeUncertaintyInMeters,
+ measurement->accumulated_delta_range_uncertainty_m);
+ SET_IF(GPS_MEASUREMENT_HAS_PSEUDORANGE,
+ PseudorangeInMeters,
+ measurement->pseudorange_m);
+ SET_IF(GPS_MEASUREMENT_HAS_PSEUDORANGE_UNCERTAINTY,
+ PseudorangeUncertaintyInMeters,
+ measurement->pseudorange_uncertainty_m);
+ SET_IF(GPS_MEASUREMENT_HAS_CODE_PHASE,
+ CodePhaseInChips,
+ measurement->code_phase_chips);
+ SET_IF(GPS_MEASUREMENT_HAS_CODE_PHASE_UNCERTAINTY,
+ CodePhaseUncertaintyInChips,
+ measurement->code_phase_uncertainty_chips);
+ SET_IF(GPS_MEASUREMENT_HAS_CARRIER_FREQUENCY,
+ CarrierFrequencyInHz,
+ measurement->carrier_frequency_hz);
+ SET_IF(GPS_MEASUREMENT_HAS_CARRIER_CYCLES,
+ CarrierCycles,
+ measurement->carrier_cycles);
+ SET_IF(GPS_MEASUREMENT_HAS_CARRIER_PHASE,
+ CarrierPhase,
+ measurement->carrier_phase);
+ SET_IF(GPS_MEASUREMENT_HAS_CARRIER_PHASE_UNCERTAINTY,
+ CarrierPhaseUncertainty,
+ measurement->carrier_phase_uncertainty);
+ SET(LossOfLock, measurement->loss_of_lock);
+ SET_IF(GPS_MEASUREMENT_HAS_BIT_NUMBER, BitNumber, measurement->bit_number);
+ SET_IF(GPS_MEASUREMENT_HAS_TIME_FROM_LAST_BIT,
+ TimeFromLastBitInMs,
+ measurement->time_from_last_bit_ms);
+ SET_IF(GPS_MEASUREMENT_HAS_DOPPLER_SHIFT,
+ DopplerShiftInHz,
+ measurement->doppler_shift_hz);
+ SET_IF(GPS_MEASUREMENT_HAS_DOPPLER_SHIFT_UNCERTAINTY,
+ DopplerShiftUncertaintyInHz,
+ measurement->doppler_shift_uncertainty_hz);
+ SET(MultipathIndicator, measurement->multipath_indicator);
+ SET_IF(GPS_MEASUREMENT_HAS_SNR, SnrInDb, measurement->snr_db);
+ SET_IF(GPS_MEASUREMENT_HAS_ELEVATION,
+ ElevationInDeg,
+ measurement->elevation_deg);
+ SET_IF(GPS_MEASUREMENT_HAS_ELEVATION_UNCERTAINTY,
+ ElevationUncertaintyInDeg,
+ measurement->elevation_uncertainty_deg);
+ SET_IF(GPS_MEASUREMENT_HAS_AZIMUTH,
+ AzimuthInDeg,
+ measurement->azimuth_deg);
+ SET_IF(GPS_MEASUREMENT_HAS_AZIMUTH_UNCERTAINTY,
+ AzimuthUncertaintyInDeg,
+ measurement->azimuth_uncertainty_deg);
+ SET(UsedInFix,
+ (flags & GPS_MEASUREMENT_HAS_USED_IN_FIX) && measurement->used_in_fix);
+
+ SET(PseudorangeRateCarrierInMetersPerSec,
+ measurement->pseudorange_rate_carrier_mps);
+ SET(PseudorangeRateCarrierUncertaintyInMetersPerSec,
+ measurement->pseudorange_rate_carrier_uncertainty_mps);
+
+ return object.get();
+}
+
+static jobjectArray translate_gps_measurements(JNIEnv* env,
+ GpsMeasurement* measurements,
+ size_t count) {
+ if (count == 0) {
return NULL;
}
- jclass gnssMeasurementClass = env->FindClass("android/location/GnssMeasurement");
+ jclass gnssMeasurementClass = env->FindClass(
+ "android/location/GnssMeasurement");
jobjectArray gnssMeasurementArray = env->NewObjectArray(
- measurementCount,
+ count,
gnssMeasurementClass,
NULL /* initialElement */);
- for (uint16_t i = 0; i < measurementCount; ++i) {
+ for (uint16_t i = 0; i < count; ++i) {
jobject gnssMeasurement = translate_gps_measurement(
env,
- &(gps_data->measurements[i]),
- sizeof(gps_data->measurements[0]));
+ &measurements[i]);
env->SetObjectArrayElement(gnssMeasurementArray, i, gnssMeasurement);
env->DeleteLocalRef(gnssMeasurement);
}
@@ -1340,27 +1331,37 @@
return gnssMeasurementArray;
}
-static void measurement_callback(GpsData* data) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
- if (data == NULL) {
- ALOGE("Invalid data provided to gps_measurement_callback");
- return;
- }
- if (data->size != sizeof(GpsData) && data->size != sizeof(GpsData_v1)) {
- ALOGE("Invalid GpsData size found in gps_measurement_callback, size=%zd", data->size);
- return;
+static jobjectArray translate_gnss_measurements(JNIEnv* env,
+ GnssMeasurement* measurements,
+ size_t count) {
+ if (count == 0) {
+ return NULL;
}
- jobject gpsClock;
- jobjectArray measurementArray;
- if (data->size == sizeof(GpsData)) {
- gpsClock = translate_gps_clock(env, &data->clock, sizeof(GpsClock));
- measurementArray = translate_gps_measurements<GpsData>(env, data);
- } else {
- gpsClock = translate_gps_clock(env, &data->clock, sizeof(GpsClock_v1));
- measurementArray = translate_gps_measurements<GpsData_v1>(env, data);
+ jclass gnssMeasurementClass = env->FindClass(
+ "android/location/GnssMeasurement");
+ jobjectArray gnssMeasurementArray = env->NewObjectArray(
+ count,
+ gnssMeasurementClass,
+ NULL /* initialElement */);
+
+ for (uint16_t i = 0; i < count; ++i) {
+ jobject gnssMeasurement = translate_gnss_measurement(
+ env,
+ &measurements[i]);
+ env->SetObjectArrayElement(gnssMeasurementArray, i, gnssMeasurement);
+ env->DeleteLocalRef(gnssMeasurement);
}
- jclass gnssMeasurementsEventClass = env->FindClass("android/location/GnssMeasurementsEvent");
+
+ env->DeleteLocalRef(gnssMeasurementClass);
+ return gnssMeasurementArray;
+}
+
+static void set_measurement_data(JNIEnv *env,
+ jobject clock,
+ jobjectArray measurementArray) {
+ jclass gnssMeasurementsEventClass = env->FindClass(
+ "android/location/GnssMeasurementsEvent");
jmethodID gnssMeasurementsEventCtor = env->GetMethodID(
gnssMeasurementsEventClass,
"<init>",
@@ -1369,21 +1370,68 @@
jobject gnssMeasurementsEvent = env->NewObject(
gnssMeasurementsEventClass,
gnssMeasurementsEventCtor,
- gpsClock,
+ clock,
measurementArray);
-
- env->CallVoidMethod(mCallbacksObj, method_reportMeasurementData, gnssMeasurementsEvent);
+ env->CallVoidMethod(mCallbacksObj,
+ method_reportMeasurementData,
+ gnssMeasurementsEvent);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
-
- env->DeleteLocalRef(gpsClock);
- env->DeleteLocalRef(measurementArray);
env->DeleteLocalRef(gnssMeasurementsEventClass);
env->DeleteLocalRef(gnssMeasurementsEvent);
}
+static void measurement_callback(GpsData* data) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ if (data == NULL) {
+ ALOGE("Invalid data provided to gps_measurement_callback");
+ return;
+ }
+ if (data->size != sizeof(GpsData)) {
+ ALOGE("Invalid GpsData size found in gps_measurement_callback, "
+ "size=%zd",
+ data->size);
+ return;
+ }
+
+ jobject clock;
+ jobjectArray measurementArray;
+ clock = translate_gps_clock(env, &data->clock);
+ measurementArray = translate_gps_measurements(
+ env, data->measurements, data->measurement_count);
+ set_measurement_data(env, clock, measurementArray);
+
+ env->DeleteLocalRef(clock);
+ env->DeleteLocalRef(measurementArray);
+}
+
+static void gnss_measurement_callback(GnssData* data) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ if (data == NULL) {
+ ALOGE("Invalid data provided to gps_measurement_callback");
+ return;
+ }
+ if (data->size != sizeof(GpsData)) {
+ ALOGE("Invalid GpsData size found in gps_measurement_callback, "
+ "size=%zd",
+ data->size);
+ return;
+ }
+
+ jobject clock;
+ jobjectArray measurementArray;
+ clock = translate_gnss_clock(env, &data->clock);
+ measurementArray = translate_gnss_measurements(
+ env, data->measurements, data->measurement_count);
+ set_measurement_data(env, clock, measurementArray);
+
+ env->DeleteLocalRef(clock);
+ env->DeleteLocalRef(measurementArray);
+}
+
GpsMeasurementCallbacks sGpsMeasurementCallbacks = {
sizeof(GpsMeasurementCallbacks),
measurement_callback,
+ gnss_measurement_callback,
};
static jboolean android_location_GnssLocationProvider_is_measurement_supported(
@@ -1431,69 +1479,86 @@
ALOGE("Invalid Navigation Message found: data=%p, length=%zd", data, dataLength);
return NULL;
}
+ JavaObject object(env, "android/location/GnssNavigationMessage");
+ SET(Type, message->type);
+ SET(Svid, static_cast<int16_t>(message->prn));
+ SET(MessageId, message->message_id);
+ SET(SubmessageId, message->submessage_id);
+ object.callSetter("setData", data, dataLength);
+ return object.get();
+}
- jclass navigationMessageClass = env->FindClass("android/location/GnssNavigationMessage");
- jmethodID navigationMessageCtor = env->GetMethodID(navigationMessageClass, "<init>", "()V");
- jobject navigationMessageObject = env->NewObject(navigationMessageClass, navigationMessageCtor);
+static jobject translate_gnss_navigation_message(
+ JNIEnv* env, GnssNavigationMessage* message) {
+ size_t dataLength = message->data_length;
+ uint8_t* data = message->data;
+ if (dataLength == 0 || data == NULL) {
+ ALOGE("Invalid Navigation Message found: data=%p, length=%zd", data, dataLength);
+ return NULL;
+ }
+ JavaObject object(env, "android/location/GnssNavigationMessage");
+ SET(Type, message->type);
+ SET(Svid, message->svid);
+ SET(MessageId, message->message_id);
+ SET(SubmessageId, message->submessage_id);
+ object.callSetter("setData", data, dataLength);
+ return object.get();
+}
- jmethodID setTypeMethod = env->GetMethodID(navigationMessageClass, "setType", "(B)V");
- env->CallVoidMethod(navigationMessageObject, setTypeMethod, message->type);
-
- jmethodID setPrnMethod = env->GetMethodID(navigationMessageClass, "setPrn", "(B)V");
- env->CallVoidMethod(navigationMessageObject, setPrnMethod, message->prn);
-
- jmethodID setMessageIdMethod = env->GetMethodID(navigationMessageClass, "setMessageId", "(S)V");
- env->CallVoidMethod(navigationMessageObject, setMessageIdMethod, message->message_id);
-
- jmethodID setSubmessageIdMethod =
- env->GetMethodID(navigationMessageClass, "setSubmessageId", "(S)V");
- env->CallVoidMethod(navigationMessageObject, setSubmessageIdMethod, message->submessage_id);
-
- jbyteArray dataArray = env->NewByteArray(dataLength);
- env->SetByteArrayRegion(dataArray, 0, dataLength, (jbyte*) data);
- jmethodID setDataMethod = env->GetMethodID(navigationMessageClass, "setData", "([B)V");
- env->CallVoidMethod(navigationMessageObject, setDataMethod, dataArray);
-
- env->DeleteLocalRef(navigationMessageClass);
- env->DeleteLocalRef(dataArray);
- return navigationMessageObject;
+static void set_navigation_message(jobject navigationMessage) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jclass navigationMessageEventClass =
+ env->FindClass("android/location/GnssNavigationMessageEvent");
+ jmethodID navigationMessageEventCtor = env->GetMethodID(
+ navigationMessageEventClass,
+ "<init>",
+ "(Landroid/location/GnssNavigationMessage;)V");
+ jobject navigationMessageEvent = env->NewObject(
+ navigationMessageEventClass,
+ navigationMessageEventCtor,
+ navigationMessage);
+ env->CallVoidMethod(mCallbacksObj,
+ method_reportNavigationMessages,
+ navigationMessageEvent);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ env->DeleteLocalRef(navigationMessageEventClass);
+ env->DeleteLocalRef(navigationMessageEvent);
}
static void navigation_message_callback(GpsNavigationMessage* message) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
if (message == NULL) {
ALOGE("Invalid Navigation Message provided to callback");
return;
}
-
- if (message->size == sizeof(GpsNavigationMessage)) {
- jobject navigationMessage = translate_gps_navigation_message(env, message);
-
- jclass navigationMessageEventClass =
- env->FindClass("android/location/GnssNavigationMessageEvent");
- jmethodID navigationMessageEventCtor = env->GetMethodID(
- navigationMessageEventClass,
- "<init>",
- "(Landroid/location/GnssNavigationMessage;)V");
- jobject navigationMessageEvent = env->NewObject(
- navigationMessageEventClass,
- navigationMessageEventCtor,
- navigationMessage);
-
- env->CallVoidMethod(mCallbacksObj, method_reportNavigationMessages, navigationMessageEvent);
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
-
- env->DeleteLocalRef(navigationMessage);
- env->DeleteLocalRef(navigationMessageEventClass);
- env->DeleteLocalRef(navigationMessageEvent);
- } else {
+ if (message->size != sizeof(GpsNavigationMessage)) {
ALOGE("Invalid GpsNavigationMessage size found: %zd", message->size);
+ return;
}
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jobject navigationMessage = translate_gps_navigation_message(env, message);
+ set_navigation_message(navigationMessage);
+ env->DeleteLocalRef(navigationMessage);
+}
+
+static void gnss_navigation_message_callback(GnssNavigationMessage* message) {
+ if (message == NULL) {
+ ALOGE("Invalid Navigation Message provided to callback");
+ return;
+ }
+ if (message->size != sizeof(GnssNavigationMessage)) {
+ ALOGE("Invalid GnssNavigationMessage size found: %zd", message->size);
+ return;
+ }
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jobject navigationMessage = translate_gnss_navigation_message(env, message);
+ set_navigation_message(navigationMessage);
+ env->DeleteLocalRef(navigationMessage);
}
GpsNavigationMessageCallbacks sGpsNavigationMessageCallbacks = {
sizeof(GpsNavigationMessageCallbacks),
navigation_message_callback,
+ gnss_navigation_message_callback,
};
static jboolean android_location_GnssLocationProvider_is_navigation_message_supported(
@@ -1567,7 +1632,7 @@
"(I)V",
(void*)android_location_GnssLocationProvider_delete_aiding_data},
{"native_read_sv_status",
- "([I[F[F[F[I)I",
+ "([I[F[F[F)I",
(void*)android_location_GnssLocationProvider_read_sv_status},
{"native_read_nmea", "([BI)I", (void*)android_location_GnssLocationProvider_read_nmea},
{"native_inject_time", "(JJI)V", (void*)android_location_GnssLocationProvider_inject_time},
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index eacf11f..916b66d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -116,6 +116,7 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
+import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.Xml;
@@ -274,6 +275,8 @@
private static final int PROFILE_KEYGUARD_FEATURES =
PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER | PROFILE_KEYGUARD_FEATURES_AFFECT_PROFILE;
+ private static final int DEVICE_ADMIN_DEACTIVATE_TIMEOUT = 10000;
+
final Context mContext;
final Injector mInjector;
final IPackageManager mIPackageManager;
@@ -281,6 +284,13 @@
final UserManagerInternal mUserManagerInternal;
private final LockPatternUtils mLockPatternUtils;
+ /**
+ * Contains (package-user) pairs to remove. An entry (p, u) implies that removal of package p
+ * is requested for user u.
+ */
+ private final Set<Pair<String, Integer>> mPackagesToRemove =
+ new ArraySet<Pair<String, Integer>>();
+
final LocalService mLocalService;
// Stores and loads state on device and profile owners.
@@ -1239,7 +1249,9 @@
if (packageName == null || packageName.equals(adminPackage)) {
if (mIPackageManager.getPackageInfo(adminPackage, 0, userHandle) == null
|| mIPackageManager.getReceiverInfo(
- aa.info.getComponent(), 0, userHandle) == null) {
+ aa.info.getComponent(),
+ PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE,
+ userHandle) == null) {
removed = true;
policy.mAdminList.remove(i);
policy.mAdminMap.remove(aa.info.getComponent());
@@ -2015,35 +2027,19 @@
final ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver, userHandle);
if (admin != null) {
getUserData(userHandle).mRemovingAdmins.add(adminReceiver);
-
sendAdminCommandLocked(admin,
DeviceAdminReceiver.ACTION_DEVICE_ADMIN_DISABLED,
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- synchronized (DevicePolicyManagerService.this) {
- int userHandle = admin.getUserHandle().getIdentifier();
- DevicePolicyData policy = getUserData(userHandle);
- boolean doProxyCleanup = admin.info.usesPolicy(
- DeviceAdminInfo.USES_POLICY_SETS_GLOBAL_PROXY);
- policy.mAdminList.remove(admin);
- policy.mAdminMap.remove(adminReceiver);
- validatePasswordOwnerLocked(policy);
- if (doProxyCleanup) {
- resetGlobalProxyLocked(getUserData(userHandle));
- }
- saveSettingsLocked(userHandle);
- updateMaximumTimeToLockLocked(userHandle);
- policy.mRemovingAdmins.remove(adminReceiver);
- }
- // The removed admin might have disabled camera, so update user
- // restrictions.
- pushUserRestrictions(userHandle);
+ removeAdminArtifacts(adminReceiver, userHandle);
+ removePackageIfRequired(adminReceiver.getPackageName(), userHandle);
}
});
}
}
+
public DeviceAdminInfo findAdmin(ComponentName adminName, int userHandle,
boolean throwForMissiongPermission) {
if (!mHasFeature) {
@@ -3528,11 +3524,14 @@
@Override
public int getCurrentFailedPasswordAttempts(int userHandle, boolean parent) {
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
- // This API can only be called by an active device admin,
- // so try to retrieve it to check that the caller is one.
- getActiveAdminForCallerLocked(
- null, DeviceAdminInfo.USES_POLICY_WATCH_LOGIN, parent);
+ if (!isCallerWithSystemUid()) {
+ // This API can only be called by an active device admin,
+ // so try to retrieve it to check that the caller is one.
+ getActiveAdminForCallerLocked(
+ null, DeviceAdminInfo.USES_POLICY_WATCH_LOGIN, parent);
+ }
DevicePolicyData policy = getUserDataUnchecked(getCredentialOwner(userHandle, parent));
@@ -4492,7 +4491,8 @@
}
if (mInjector.securityLogIsLoggingEnabled()) {
- SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT, /*result*/ 0);
+ SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT, /*result*/ 0,
+ /*method strength*/ 1);
}
}
@@ -4522,23 +4522,50 @@
}
if (mInjector.securityLogIsLoggingEnabled()) {
- SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT, /*result*/ 1);
+ SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT, /*result*/ 1,
+ /*method strength*/ 1);
}
}
@Override
- public void reportKeyguardDismissed() {
+ public void reportFailedFingerprintAttempt(int userHandle) {
+ enforceFullCrossUsersPermission(userHandle);
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BIND_DEVICE_ADMIN, null);
if (mInjector.securityLogIsLoggingEnabled()) {
+ SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT, /*result*/ 0,
+ /*method strength*/ 0);
+ }
+ }
+
+ @Override
+ public void reportSuccessfulFingerprintAttempt(int userHandle) {
+ enforceFullCrossUsersPermission(userHandle);
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BIND_DEVICE_ADMIN, null);
+ if (mInjector.securityLogIsLoggingEnabled()) {
+ SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT, /*result*/ 1,
+ /*method strength*/ 0);
+ }
+ }
+
+ @Override
+ public void reportKeyguardDismissed(int userHandle) {
+ enforceFullCrossUsersPermission(userHandle);
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BIND_DEVICE_ADMIN, null);
+
+ if (mInjector.securityLogIsLoggingEnabled()) {
SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISSED);
}
}
@Override
- public void reportKeyguardSecured() {
+ public void reportKeyguardSecured(int userHandle) {
+ enforceFullCrossUsersPermission(userHandle);
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BIND_DEVICE_ADMIN, null);
+
if (mInjector.securityLogIsLoggingEnabled()) {
SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_SECURED);
}
@@ -6769,6 +6796,10 @@
throw new IllegalArgumentException("profileOwner " + profileOwner + " and admin "
+ admin + " are not in the same package");
}
+ // Only allow the system user to use this method
+ if (!mInjector.binderGetCallingUserHandle().isSystem()) {
+ throw new SecurityException("createAndManageUser was called from non-system user");
+ }
// Create user.
UserHandle user = null;
synchronized (this) {
@@ -6780,7 +6811,8 @@
if ((flags & DevicePolicyManager.MAKE_USER_EPHEMERAL) != 0) {
userInfoFlags |= UserInfo.FLAG_EPHEMERAL;
}
- UserInfo userInfo = mUserManager.createUser(name, userInfoFlags);
+ UserInfo userInfo = mUserManagerInternal.createUserEvenWhenDisallowed(name,
+ userInfoFlags);
if (userInfo != null) {
user = userInfo.getUserHandle();
}
@@ -7110,7 +7142,7 @@
List<ResolveInfo> activitiesToEnable = mIPackageManager.queryIntentActivities(
intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
- 0, // no flags
+ PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE,
parentUserId);
if (VERBOSE_LOG) {
@@ -8009,7 +8041,8 @@
boolean isPackageInstalledForUser(String packageName, int userHandle) {
try {
- PackageInfo pi = mIPackageManager.getPackageInfo(packageName, 0, userHandle);
+ PackageInfo pi = mInjector.getIPackageManager().getPackageInfo(packageName, 0,
+ userHandle);
return (pi != null) && (pi.applicationInfo.flags != 0);
} catch (RemoteException re) {
throw new RuntimeException("Package manager has died", re);
@@ -8465,4 +8498,137 @@
List<SecurityEvent> logs = mSecurityLogMonitor.retrieveLogs();
return logs != null ? new ParceledListSlice<SecurityEvent>(logs) : null;
}
+
+ private void enforceCanManageDeviceAdmin() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS,
+ null);
+ }
+
+ @Override
+ public boolean isUninstallInQueue(final String packageName) {
+ enforceCanManageDeviceAdmin();
+ final int userId = mInjector.userHandleGetCallingUserId();
+ Pair<String, Integer> packageUserPair = new Pair<>(packageName, userId);
+ synchronized (this) {
+ return mPackagesToRemove.contains(packageUserPair);
+ }
+ }
+
+ @Override
+ public void uninstallPackageWithActiveAdmins(final String packageName) {
+ enforceCanManageDeviceAdmin();
+ Preconditions.checkArgument(!TextUtils.isEmpty(packageName));
+
+ final int userId = mInjector.userHandleGetCallingUserId();
+
+ final ComponentName profileOwner = getProfileOwner(userId);
+ if (profileOwner != null && packageName.equals(profileOwner.getPackageName())) {
+ throw new IllegalArgumentException("Cannot uninstall a package with a profile owner");
+ }
+
+ final ComponentName deviceOwner = getDeviceOwnerComponent(/* callingUserOnly= */ false);
+ if (getDeviceOwnerUserId() == userId && deviceOwner != null
+ && packageName.equals(deviceOwner.getPackageName())) {
+ throw new IllegalArgumentException("Cannot uninstall a package with a device owner");
+ }
+
+ final Pair<String, Integer> packageUserPair = new Pair<>(packageName, userId);
+ synchronized (this) {
+ mPackagesToRemove.add(packageUserPair);
+ }
+
+ // All active admins on the user.
+ final List<ComponentName> allActiveAdmins = getActiveAdmins(userId);
+
+ // Active admins in the target package.
+ final List<ComponentName> packageActiveAdmins = new ArrayList<>();
+ if (allActiveAdmins != null) {
+ for (ComponentName activeAdmin : allActiveAdmins) {
+ if (packageName.equals(activeAdmin.getPackageName())) {
+ packageActiveAdmins.add(activeAdmin);
+ removeActiveAdmin(activeAdmin, userId);
+ }
+ }
+ }
+ if (packageActiveAdmins.size() == 0) {
+ startUninstallIntent(packageName, userId);
+ } else {
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ for (ComponentName activeAdmin : packageActiveAdmins) {
+ removeAdminArtifacts(activeAdmin, userId);
+ }
+ startUninstallIntent(packageName, userId);
+ }
+ }, DEVICE_ADMIN_DEACTIVATE_TIMEOUT); // Start uninstall after timeout anyway.
+ }
+ }
+
+ private void removePackageIfRequired(final String packageName, final int userId) {
+ if (!packageHasActiveAdmins(packageName, userId)) {
+ // Will not do anything if uninstall was not requested or was already started.
+ startUninstallIntent(packageName, userId);
+ }
+ }
+
+ private void startUninstallIntent(final String packageName, final int userId) {
+ final Pair<String, Integer> packageUserPair = new Pair<>(packageName, userId);
+ synchronized (this) {
+ if (!mPackagesToRemove.contains(packageUserPair)) {
+ // Do nothing if uninstall was not requested or was already started.
+ return;
+ }
+ mPackagesToRemove.remove(packageUserPair);
+ }
+ try {
+ if (mInjector.getIPackageManager().getPackageInfo(packageName, 0, userId) == null) {
+ // Package does not exist. Nothing to do.
+ return;
+ }
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Failure talking to PackageManager while getting package info");
+ }
+
+ try { // force stop the package before uninstalling
+ mInjector.getIActivityManager().forceStopPackage(packageName, userId);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Failure talking to ActivityManager while force stopping package");
+ }
+ final Uri packageURI = Uri.parse("package:" + packageName);
+ final Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageURI);
+ uninstallIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivityAsUser(uninstallIntent, UserHandle.of(userId));
+ }
+
+ /**
+ * Removes the admin from the policy. Ideally called after the admin's
+ * {@link DeviceAdminReceiver#onDisabled(Context, Intent)} has been successfully completed.
+ *
+ * @param adminReceiver The admin to remove
+ * @param userHandle The user for which this admin has to be removed.
+ */
+ private void removeAdminArtifacts(final ComponentName adminReceiver, final int userHandle) {
+ synchronized (this) {
+ final ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver, userHandle);
+ if (admin == null) {
+ return;
+ }
+ final DevicePolicyData policy = getUserData(userHandle);
+ final boolean doProxyCleanup = admin.info.usesPolicy(
+ DeviceAdminInfo.USES_POLICY_SETS_GLOBAL_PROXY);
+ policy.mAdminList.remove(admin);
+ policy.mAdminMap.remove(adminReceiver);
+ validatePasswordOwnerLocked(policy);
+ if (doProxyCleanup) {
+ resetGlobalProxyLocked(policy);
+ }
+ saveSettingsLocked(userHandle);
+ updateMaximumTimeToLockLocked(userHandle);
+ policy.mRemovingAdmins.remove(adminReceiver);
+ }
+ // The removed admin might have disabled camera, so update user
+ // restrictions.
+ pushUserRestrictions(userHandle);
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index 880f810..68fd0f6 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -716,11 +716,11 @@
}
File getLegacyConfigFileWithTestOverride() {
- return new File(Environment.getSystemSecureDirectory(), DEVICE_OWNER_XML_LEGACY);
+ return new File(Environment.getDataSystemDirectory(), DEVICE_OWNER_XML_LEGACY);
}
File getDeviceOwnerFileWithTestOverride() {
- return new File(Environment.getSystemSecureDirectory(), DEVICE_OWNER_XML);
+ return new File(Environment.getDataSystemDirectory(), DEVICE_OWNER_XML);
}
File getProfileOwnerFileWithTestOverride(int userId) {
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index 2329b42..ceabfce 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -27,7 +27,6 @@
import android.content.IntentFilter;
import android.net.DhcpResults;
import android.net.BaseDhcpStateMachine;
-import android.net.DhcpStateMachine;
import android.net.InterfaceConfiguration;
import android.net.LinkAddress;
import android.net.NetworkUtils;
@@ -103,12 +102,35 @@
// t=0, t=2, t=6, t=14, t=30, allowing for 10% jitter.
private static final int DHCP_TIMEOUT_MS = 36 * SECONDS;
+ private static final int PUBLIC_BASE = Protocol.BASE_DHCP;
+
+ /* Commands from controller to start/stop DHCP */
+ public static final int CMD_START_DHCP = PUBLIC_BASE + 1;
+ public static final int CMD_STOP_DHCP = PUBLIC_BASE + 2;
+ public static final int CMD_RENEW_DHCP = PUBLIC_BASE + 3;
+
+ /* Notification from DHCP state machine prior to DHCP discovery/renewal */
+ public static final int CMD_PRE_DHCP_ACTION = PUBLIC_BASE + 4;
+ /* Notification from DHCP state machine post DHCP discovery/renewal. Indicates
+ * success/failure */
+ public static final int CMD_POST_DHCP_ACTION = PUBLIC_BASE + 5;
+ /* Notification from DHCP state machine before quitting */
+ public static final int CMD_ON_QUIT = PUBLIC_BASE + 6;
+
+ /* Command from controller to indicate DHCP discovery/renewal can continue
+ * after pre DHCP action is complete */
+ public static final int CMD_PRE_DHCP_ACTION_COMPLETE = PUBLIC_BASE + 7;
+
+ /* Message.arg1 arguments to CMD_POST_DHCP notification */
+ public static final int DHCP_SUCCESS = 1;
+ public static final int DHCP_FAILURE = 2;
+
// Messages.
- private static final int BASE = Protocol.BASE_DHCP + 100;
- private static final int CMD_KICK = BASE + 1;
- private static final int CMD_RECEIVED_PACKET = BASE + 2;
- private static final int CMD_TIMEOUT = BASE + 3;
- private static final int CMD_ONESHOT_TIMEOUT = BASE + 4;
+ private static final int PRIVATE_BASE = Protocol.BASE_DHCP + 100;
+ private static final int CMD_KICK = PRIVATE_BASE + 1;
+ private static final int CMD_RECEIVED_PACKET = PRIVATE_BASE + 2;
+ private static final int CMD_TIMEOUT = PRIVATE_BASE + 3;
+ private static final int CMD_ONESHOT_TIMEOUT = PRIVATE_BASE + 4;
// DHCP parameters that we request.
private static final byte[] REQUESTED_PARAMS = new byte[] {
@@ -211,7 +233,7 @@
// Used to time out PacketRetransmittingStates.
mTimeoutAlarm = makeWakeupMessage("TIMEOUT", CMD_TIMEOUT);
// Used to schedule DHCP renews.
- mRenewAlarm = makeWakeupMessage("RENEW", DhcpStateMachine.CMD_RENEW_DHCP);
+ mRenewAlarm = makeWakeupMessage("RENEW", CMD_RENEW_DHCP);
// Used to tell the caller when its request (CMD_START_DHCP or CMD_RENEW_DHCP) timed out.
// TODO: when the legacy DHCP client is gone, make the client fully asynchronous and
// remove this.
@@ -400,13 +422,12 @@
}
private void notifySuccess() {
- mController.sendMessage(DhcpStateMachine.CMD_POST_DHCP_ACTION,
- DhcpStateMachine.DHCP_SUCCESS, 0, new DhcpResults(mDhcpLease));
+ mController.sendMessage(
+ CMD_POST_DHCP_ACTION, DHCP_SUCCESS, 0, new DhcpResults(mDhcpLease));
}
private void notifyFailure() {
- mController.sendMessage(DhcpStateMachine.CMD_POST_DHCP_ACTION,
- DhcpStateMachine.DHCP_FAILURE, 0, null);
+ mController.sendMessage(CMD_POST_DHCP_ACTION, DHCP_FAILURE, 0, null);
}
private void clearDhcpState() {
@@ -428,7 +449,7 @@
protected void onQuitting() {
Log.d(TAG, "onQuitting");
- mController.sendMessage(DhcpStateMachine.CMD_ON_QUIT);
+ mController.sendMessage(CMD_ON_QUIT);
}
private void maybeLog(String msg) {
@@ -442,17 +463,17 @@
private String messageName(int what) {
switch (what) {
- case DhcpStateMachine.CMD_START_DHCP:
+ case CMD_START_DHCP:
return "CMD_START_DHCP";
- case DhcpStateMachine.CMD_STOP_DHCP:
+ case CMD_STOP_DHCP:
return "CMD_STOP_DHCP";
- case DhcpStateMachine.CMD_RENEW_DHCP:
+ case CMD_RENEW_DHCP:
return "CMD_RENEW_DHCP";
- case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
+ case CMD_PRE_DHCP_ACTION:
return "CMD_PRE_DHCP_ACTION";
- case DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE:
+ case CMD_PRE_DHCP_ACTION_COMPLETE:
return "CMD_PRE_DHCP_ACTION_COMPLETE";
- case DhcpStateMachine.CMD_POST_DHCP_ACTION:
+ case CMD_POST_DHCP_ACTION:
return "CMD_POST_DHCP_ACTION";
case CMD_KICK:
return "CMD_KICK";
@@ -495,14 +516,14 @@
@Override
public void enter() {
super.enter();
- mController.sendMessage(DhcpStateMachine.CMD_PRE_DHCP_ACTION);
+ mController.sendMessage(CMD_PRE_DHCP_ACTION);
}
@Override
public boolean processMessage(Message message) {
super.processMessage(message);
switch (message.what) {
- case DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE:
+ case CMD_PRE_DHCP_ACTION_COMPLETE:
transitionTo(mOtherState);
return HANDLED;
default:
@@ -532,7 +553,7 @@
public boolean processMessage(Message message) {
super.processMessage(message);
switch (message.what) {
- case DhcpStateMachine.CMD_START_DHCP:
+ case CMD_START_DHCP:
scheduleOneshotTimeout();
if (mRegisteredForPreDhcpNotification) {
transitionTo(mWaitBeforeStartState);
@@ -588,7 +609,7 @@
public boolean processMessage(Message message) {
super.processMessage(message);
switch (message.what) {
- case DhcpStateMachine.CMD_STOP_DHCP:
+ case CMD_STOP_DHCP:
transitionTo(mStoppedState);
return HANDLED;
case CMD_ONESHOT_TIMEOUT:
@@ -810,7 +831,7 @@
public boolean processMessage(Message message) {
super.processMessage(message);
switch (message.what) {
- case DhcpStateMachine.CMD_RENEW_DHCP:
+ case CMD_RENEW_DHCP:
if (mRegisteredForPreDhcpNotification) {
transitionTo(mWaitBeforeRenewalState);
} else {
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
index 06b6ee7..a388a26 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -61,7 +61,6 @@
* @hide
*/
public class IpManager extends StateMachine {
- private static final String TAG = IpManager.class.getSimpleName();
private static final boolean DBG = true;
private static final boolean VDBG = false;
@@ -92,13 +91,18 @@
*/
// Implementations must call IpManager#completedPreDhcpAction().
+ // TODO: Remove this requirement, perhaps via some
+ // registerForPreDhcpAction()-style mechanism.
public void onPreDhcpAction() {}
public void onPostDhcpAction() {}
- // TODO: Kill with fire once DHCP and static configuration are moved
- // out of WifiStateMachine.
- public void onIPv4ProvisioningSuccess(DhcpResults dhcpResults) {}
- public void onIPv4ProvisioningFailure() {}
+ // This is purely advisory and not an indication of provisioning
+ // success or failure. This is only here for callers that want to
+ // expose DHCPv4 results to other APIs (e.g., WifiInfo#setInetAddress).
+ // DHCPv4 or static IPv4 configuration failure or success can be
+ // determined by whether or not the passed-in DhcpResults object is
+ // null or not.
+ public void onNewDhcpResults(DhcpResults dhcpResults) {}
public void onProvisioningSuccess(LinkProperties newLp) {}
public void onProvisioningFailure(LinkProperties newLp) {}
@@ -122,8 +126,10 @@
private final Object mLock = new Object();
private final State mStoppedState = new StoppedState();
+ private final State mStoppingState = new StoppingState();
private final State mStartedState = new StartedState();
+ private final String mTag;
private final Context mContext;
private final String mInterfaceName;
@VisibleForTesting
@@ -150,11 +156,11 @@
public IpManager(Context context, String ifName, Callback callback)
throws IllegalArgumentException {
- super(TAG + "." + ifName);
+ super(IpManager.class.getSimpleName() + "." + ifName);
+ mTag = getName();
mContext = context;
mInterfaceName = ifName;
-
mCallback = callback;
mNwService = INetworkManagementService.Stub.asInterface(
@@ -171,7 +177,7 @@
try {
mNwService.registerObserver(mNetlinkTracker);
} catch (RemoteException e) {
- Log.e(TAG, "Couldn't register NetlinkTracker: " + e.toString());
+ Log.e(mTag, "Couldn't register NetlinkTracker: " + e.toString());
}
resetLinkProperties();
@@ -179,6 +185,8 @@
// Super simple StateMachine.
addState(mStoppedState);
addState(mStartedState);
+ addState(mStoppingState);
+
setInitialState(mStoppedState);
setLogRecSize(MAX_LOG_RECORDS);
super.start();
@@ -192,7 +200,9 @@
*/
@VisibleForTesting
protected IpManager(String ifName, Callback callback) {
- super(TAG + ".test-" + ifName);
+ super(IpManager.class.getSimpleName() + ".test-" + ifName);
+ mTag = getName();
+
mInterfaceName = ifName;
mCallback = callback;
@@ -203,13 +213,11 @@
public void startProvisioning(StaticIpConfiguration staticIpConfig) {
getInterfaceIndex();
-
sendMessage(CMD_START, staticIpConfig);
}
public void startProvisioning() {
getInterfaceIndex();
-
sendMessage(CMD_START);
}
@@ -236,12 +244,48 @@
* Internals.
*/
+ @Override
+ protected String getWhatToString(int what) {
+ // TODO: Investigate switching to reflection.
+ switch (what) {
+ case CMD_STOP:
+ return "CMD_STOP";
+ case CMD_START:
+ return "CMD_START";
+ case CMD_CONFIRM:
+ return "CMD_CONFIRM";
+ case EVENT_PRE_DHCP_ACTION_COMPLETE:
+ return "EVENT_PRE_DHCP_ACTION_COMPLETE";
+ case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
+ return "EVENT_NETLINK_LINKPROPERTIES_CHANGED";
+ case DhcpClient.CMD_PRE_DHCP_ACTION:
+ return "DhcpClient.CMD_PRE_DHCP_ACTION";
+ case DhcpClient.CMD_POST_DHCP_ACTION:
+ return "DhcpClient.CMD_POST_DHCP_ACTION";
+ case DhcpClient.CMD_ON_QUIT:
+ return "DhcpClient.CMD_ON_QUIT";
+ }
+ return "UNKNOWN:" + Integer.toString(what);
+ }
+
+ @Override
+ protected String getLogRecString(Message msg) {
+ final String logLine = String.format(
+ "iface{%s/%d} arg1{%d} arg2{%d} obj{%s}",
+ mInterfaceName, mInterfaceIndex,
+ msg.arg1, msg.arg2, Objects.toString(msg.obj));
+ if (VDBG) {
+ Log.d(mTag, getWhatToString(msg.what) + " " + logLine);
+ }
+ return logLine;
+ }
+
private void getInterfaceIndex() {
try {
mInterfaceIndex = NetworkInterface.getByName(mInterfaceName).getIndex();
} catch (SocketException | NullPointerException e) {
// TODO: throw new IllegalStateException.
- Log.e(TAG, "ALERT: Failed to get interface index: ", e);
+ Log.e(mTag, "ALERT: Failed to get interface index: ", e);
}
}
@@ -260,16 +304,93 @@
}
}
+ // For now: use WifiStateMachine's historical notion of provisioned.
+ private static boolean isProvisioned(LinkProperties lp) {
+ // For historical reasons, we should connect even if all we have is
+ // an IPv4 address and nothing else.
+ return lp.isProvisioned() || lp.hasIPv4Address();
+ }
+
+ // TODO: Investigate folding all this into the existing static function
+ // LinkProperties.compareProvisioning() or some other single function that
+ // takes two LinkProperties objects and returns a ProvisioningChange
+ // object that is a correct and complete assessment of what changed, taking
+ // account of the asymmetries described in the comments in this function.
+ // Then switch to using it everywhere (IpReachabilityMonitor, etc.).
+ private static ProvisioningChange compareProvisioning(
+ LinkProperties oldLp, LinkProperties newLp) {
+ ProvisioningChange delta;
+
+ final boolean wasProvisioned = isProvisioned(oldLp);
+ final boolean isProvisioned = isProvisioned(newLp);
+
+ if (!wasProvisioned && isProvisioned) {
+ delta = ProvisioningChange.GAINED_PROVISIONING;
+ } else if (wasProvisioned && isProvisioned) {
+ delta = ProvisioningChange.STILL_PROVISIONED;
+ } else if (!wasProvisioned && !isProvisioned) {
+ delta = ProvisioningChange.STILL_NOT_PROVISIONED;
+ } else {
+ // (wasProvisioned && !isProvisioned)
+ //
+ // Note that this is true even if we lose a configuration element
+ // (e.g., a default gateway) that would not be required to advance
+ // into provisioned state. This is intended: if we have a default
+ // router and we lose it, that's a sure sign of a problem, but if
+ // we connect to a network with no IPv4 DNS servers, we consider
+ // that to be a network without DNS servers and connect anyway.
+ //
+ // See the comment below.
+ delta = ProvisioningChange.LOST_PROVISIONING;
+ }
+
+ // Additionally:
+ //
+ // Partial configurations (e.g., only an IPv4 address with no DNS
+ // servers and no default route) are accepted as long as DHCPv4
+ // succeeds. On such a network, isProvisioned() will always return
+ // false, because the configuration is not complete, but we want to
+ // connect anyway. It might be a disconnected network such as a
+ // Chromecast or a wireless printer, for example.
+ //
+ // Because on such a network isProvisioned() will always return false,
+ // delta will never be LOST_PROVISIONING. So check for loss of
+ // provisioning here too.
+ if ((oldLp.hasIPv4Address() && !newLp.hasIPv4Address()) ||
+ (oldLp.isIPv6Provisioned() && !newLp.isIPv6Provisioned())) {
+ delta = ProvisioningChange.LOST_PROVISIONING;
+ }
+
+ return delta;
+ }
+
+ private void dispatchCallback(ProvisioningChange delta, LinkProperties newLp) {
+ switch (delta) {
+ case GAINED_PROVISIONING:
+ if (VDBG) { Log.d(mTag, "onProvisioningSuccess()"); }
+ mCallback.onProvisioningSuccess(newLp);
+ break;
+
+ case LOST_PROVISIONING:
+ if (VDBG) { Log.d(mTag, "onProvisioningFailure()"); }
+ mCallback.onProvisioningFailure(newLp);
+ break;
+
+ default:
+ if (VDBG) { Log.d(mTag, "onLinkPropertiesChange()"); }
+ mCallback.onLinkPropertiesChange(newLp);
+ break;
+ }
+ }
+
private ProvisioningChange setLinkProperties(LinkProperties newLp) {
if (mIpReachabilityMonitor != null) {
mIpReachabilityMonitor.updateLinkProperties(newLp);
}
- // TODO: Figure out whether and how to incorporate static configuration
- // into the notion of provisioning.
ProvisioningChange delta;
synchronized (mLock) {
- delta = LinkProperties.compareProvisioning(mLinkProperties, newLp);
+ delta = compareProvisioning(mLinkProperties, newLp);
mLinkProperties = new LinkProperties(newLp);
}
@@ -277,7 +398,7 @@
switch (delta) {
case GAINED_PROVISIONING:
case LOST_PROVISIONING:
- Log.d(TAG, "provisioning: " + delta);
+ Log.d(mTag, "provisioning: " + delta);
break;
}
}
@@ -333,7 +454,7 @@
}
if (VDBG) {
- Log.d(TAG, "newLp{" + newLp + "}");
+ Log.d(mTag, "newLp{" + newLp + "}");
}
return newLp;
@@ -345,21 +466,51 @@
ifcg.setLinkAddress(new LinkAddress("0.0.0.0/0"));
mNwService.setInterfaceConfig(mInterfaceName, ifcg);
} catch (RemoteException e) {
- Log.e(TAG, "ALERT: Failed to clear IPv4 address on interface " + mInterfaceName, e);
+ Log.e(mTag, "ALERT: Failed to clear IPv4 address on interface " + mInterfaceName, e);
}
}
private void handleIPv4Success(DhcpResults dhcpResults) {
mDhcpResults = new DhcpResults(dhcpResults);
- setLinkProperties(assembleLinkProperties());
- mCallback.onIPv4ProvisioningSuccess(dhcpResults);
+ final LinkProperties newLp = assembleLinkProperties();
+ final ProvisioningChange delta = setLinkProperties(newLp);
+
+ if (VDBG) {
+ Log.d(mTag, "onNewDhcpResults(" + Objects.toString(dhcpResults) + ")");
+ }
+ mCallback.onNewDhcpResults(dhcpResults);
+
+ dispatchCallback(delta, newLp);
}
private void handleIPv4Failure() {
+ // TODO: Figure out to de-dup this and the same code in DhcpClient.
clearIPv4Address();
mDhcpResults = null;
- setLinkProperties(assembleLinkProperties());
- mCallback.onIPv4ProvisioningFailure();
+ final LinkProperties newLp = assembleLinkProperties();
+ ProvisioningChange delta = setLinkProperties(newLp);
+ // If we've gotten here and we're still not provisioned treat that as
+ // a total loss of provisioning.
+ //
+ // Either (a) static IP configuration failed or (b) DHCPv4 failed AND
+ // there was no usable IPv6 obtained before the DHCPv4 timeout.
+ //
+ // Regardless: GAME OVER.
+ //
+ // TODO: Make the DHCP client not time out and just continue in
+ // exponential backoff. Callers such as Wi-Fi which need a timeout
+ // should implement it themselves.
+ if (delta == ProvisioningChange.STILL_NOT_PROVISIONED) {
+ delta = ProvisioningChange.LOST_PROVISIONING;
+ }
+
+ if (VDBG) { Log.d(mTag, "onNewDhcpResults(null)"); }
+ mCallback.onNewDhcpResults(null);
+
+ dispatchCallback(delta, newLp);
+ if (delta == ProvisioningChange.LOST_PROVISIONING) {
+ transitionTo(mStoppingState);
+ }
}
class StoppedState extends State {
@@ -369,7 +520,7 @@
mNwService.disableIpv6(mInterfaceName);
mNwService.clearInterfaceAddresses(mInterfaceName);
} catch (Exception e) {
- Log.e(TAG, "Failed to clear addresses or disable IPv6" + e);
+ Log.e(mTag, "Failed to clear addresses or disable IPv6" + e);
}
resetLinkProperties();
@@ -390,14 +541,9 @@
setLinkProperties(assembleLinkProperties());
break;
- case DhcpStateMachine.CMD_ON_QUIT:
- // CMD_ON_QUIT is really more like "EVENT_ON_QUIT".
- // Shutting down DHCPv4 progresses simultaneously with
- // transitioning to StoppedState, so we can receive this
- // message after we've already transitioned here.
- //
- // TODO: Figure out if this is actually useful and if not
- // expunge it.
+ case DhcpClient.CMD_ON_QUIT:
+ // Everything is already stopped.
+ Log.e(mTag, "Unexpected CMD_ON_QUIT (already stopped).");
break;
default:
@@ -407,6 +553,30 @@
}
}
+ class StoppingState extends State {
+ @Override
+ public void enter() {
+ if (mDhcpStateMachine == null) {
+ // There's no DHCPv4 for which to wait; proceed to stopped.
+ transitionTo(mStoppedState);
+ }
+ }
+
+ @Override
+ public boolean processMessage(Message msg) {
+ switch (msg.what) {
+ case DhcpClient.CMD_ON_QUIT:
+ mDhcpStateMachine = null;
+ transitionTo(mStoppedState);
+ break;
+
+ default:
+ deferMessage(msg);
+ }
+ return HANDLED;
+ }
+ }
+
class StartedState extends State {
@Override
public void enter() {
@@ -416,9 +586,9 @@
mNwService.enableIpv6(mInterfaceName);
// TODO: Perhaps clearIPv4Address() as well.
} catch (RemoteException re) {
- Log.e(TAG, "Unable to change interface settings: " + re);
+ Log.e(mTag, "Unable to change interface settings: " + re);
} catch (IllegalStateException ie) {
- Log.e(TAG, "Unable to change interface settings: " + ie);
+ Log.e(mTag, "Unable to change interface settings: " + ie);
}
mIpReachabilityMonitor = new IpReachabilityMonitor(
@@ -439,13 +609,15 @@
if (applyStaticIpConfig()) {
handleIPv4Success(new DhcpResults(mStaticIpConfig));
} else {
- handleIPv4Failure();
+ if (VDBG) { Log.d(mTag, "onProvisioningFailure()"); }
+ mCallback.onProvisioningFailure(getLinkProperties());
+ transitionTo(mStoppingState);
}
} else {
// Start DHCPv4.
makeDhcpStateMachine();
mDhcpStateMachine.registerForPreDhcpNotification();
- mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP);
+ mDhcpStateMachine.sendMessage(DhcpClient.CMD_START_DHCP);
}
}
@@ -455,9 +627,8 @@
mIpReachabilityMonitor = null;
if (mDhcpStateMachine != null) {
- mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_STOP_DHCP);
+ mDhcpStateMachine.sendMessage(DhcpClient.CMD_STOP_DHCP);
mDhcpStateMachine.doQuit();
- mDhcpStateMachine = null;
}
resetLinkProperties();
@@ -471,7 +642,7 @@
break;
case CMD_START:
- Log.e(TAG, "ALERT: START received in StartedState. Please fix caller.");
+ Log.e(mTag, "ALERT: START received in StartedState. Please fix caller.");
break;
case CMD_CONFIRM:
@@ -489,8 +660,7 @@
// calls completedPreDhcpAction() after provisioning with
// a static IP configuration.
if (mDhcpStateMachine != null) {
- mDhcpStateMachine.sendMessage(
- DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE);
+ mDhcpStateMachine.sendMessage(DhcpClient.CMD_PRE_DHCP_ACTION_COMPLETE);
}
break;
@@ -500,57 +670,43 @@
break;
}
final ProvisioningChange delta = setLinkProperties(newLp);
-
- // NOTE: The only receiver of these callbacks currently
- // treats all three of them identically, namely it calls
- // IpManager#getLinkProperties() and makes its own determination.
- switch (delta) {
- case GAINED_PROVISIONING:
- mCallback.onProvisioningSuccess(newLp);
- break;
-
- case LOST_PROVISIONING:
- mCallback.onProvisioningFailure(newLp);
- break;
-
- default:
- // TODO: Only notify on STILL_PROVISIONED?
- mCallback.onLinkPropertiesChange(newLp);
- break;
+ dispatchCallback(delta, newLp);
+ if (delta == ProvisioningChange.LOST_PROVISIONING) {
+ transitionTo(mStoppedState);
}
break;
}
- case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
+ case DhcpClient.CMD_PRE_DHCP_ACTION:
+ if (VDBG) { Log.d(mTag, "onPreDhcpAction()"); }
mCallback.onPreDhcpAction();
break;
- case DhcpStateMachine.CMD_POST_DHCP_ACTION: {
+ case DhcpClient.CMD_POST_DHCP_ACTION: {
// Note that onPostDhcpAction() is likely to be
// asynchronous, and thus there is no guarantee that we
// will be able to observe any of its effects here.
+ if (VDBG) { Log.d(mTag, "onPostDhcpAction()"); }
mCallback.onPostDhcpAction();
final DhcpResults dhcpResults = (DhcpResults) msg.obj;
switch (msg.arg1) {
- case DhcpStateMachine.DHCP_SUCCESS:
+ case DhcpClient.DHCP_SUCCESS:
handleIPv4Success(dhcpResults);
break;
- case DhcpStateMachine.DHCP_FAILURE:
+ case DhcpClient.DHCP_FAILURE:
handleIPv4Failure();
break;
default:
- Log.e(TAG, "Unknown CMD_POST_DHCP_ACTION status:" + msg.arg1);
+ Log.e(mTag, "Unknown CMD_POST_DHCP_ACTION status:" + msg.arg1);
}
break;
}
- case DhcpStateMachine.CMD_ON_QUIT:
- // CMD_ON_QUIT is really more like "EVENT_ON_QUIT".
- // Regardless, we ignore it.
- //
- // TODO: Figure out if this is actually useful and if not
- // expunge it.
+ case DhcpClient.CMD_ON_QUIT:
+ // DHCPv4 quit early for some reason.
+ Log.e(mTag, "Unexpected CMD_ON_QUIT.");
+ mDhcpStateMachine = null;
break;
default:
@@ -565,9 +721,9 @@
ifcg.setInterfaceUp();
try {
mNwService.setInterfaceConfig(mInterfaceName, ifcg);
- if (DBG) Log.d(TAG, "Static IP configuration succeeded");
+ if (DBG) Log.d(mTag, "Static IP configuration succeeded");
} catch (IllegalStateException | RemoteException e) {
- Log.e(TAG, "Static IP configuration failed: ", e);
+ Log.e(mTag, "Static IP configuration failed: ", e);
return false;
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 64f60d93..467ecd7 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -22,9 +22,12 @@
import android.app.admin.DevicePolicyManagerInternal;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.wifi.WifiInfo;
import android.os.Build.VERSION_CODES;
+import android.os.Build;
import android.os.Bundle;
import android.os.Process;
import android.os.UserHandle;
@@ -995,6 +998,7 @@
public void testApplicationRestrictionsManagingApp() throws Exception {
setAsProfileOwner(admin1);
+ final String nonExistAppRestrictionsManagerPackage = "com.google.app.restrictions.manager2";
final String appRestrictionsManagerPackage = "com.google.app.restrictions.manager";
final int appRestrictionsManagerAppId = 20987;
final int appRestrictionsManagerUid = UserHandle.getUid(
@@ -1004,6 +1008,14 @@
eq(DpmMockContext.CALLER_USER_HANDLE));
mContext.binder.callingUid = appRestrictionsManagerUid;
+ final PackageInfo pi = new PackageInfo();
+ pi.applicationInfo = new ApplicationInfo();
+ pi.applicationInfo.flags = ApplicationInfo.FLAG_HAS_CODE;
+ doReturn(pi).when(mContext.ipackageManager).getPackageInfo(
+ eq(appRestrictionsManagerPackage),
+ anyInt(),
+ eq(DpmMockContext.CALLER_USER_HANDLE));
+
// appRestrictionsManager package shouldn't be able to manage restrictions as the PO hasn't
// delegated that permission yet.
assertFalse(dpm.isCallerApplicationRestrictionsManagingPackage());
@@ -1028,6 +1040,16 @@
mContext.binder.callingUid = DpmMockContext.CALLER_UID;
assertEquals(0, dpm.getApplicationRestrictions(admin1, "pkg1").size());
+ // Check the API does not allow setting a non-existent package
+ try {
+ dpm.setApplicationRestrictionsManagingPackage(admin1,
+ nonExistAppRestrictionsManagerPackage);
+ fail("Non-existent app set as app restriction manager.");
+ } catch (IllegalArgumentException expected) {
+ MoreAsserts.assertContainsRegex(
+ "is not installed on the current user", expected.getMessage());
+ }
+
// Let appRestrictionsManagerPackage manage app restrictions
dpm.setApplicationRestrictionsManagingPackage(admin1, appRestrictionsManagerPackage);
assertEquals(appRestrictionsManagerPackage,
diff --git a/telecomm/java/android/telecom/Log.java b/telecomm/java/android/telecom/Log.java
index 73cc4a5..3f32dbe 100644
--- a/telecomm/java/android/telecom/Log.java
+++ b/telecomm/java/android/telecom/Log.java
@@ -16,6 +16,8 @@
package android.telecom;
+import android.os.AsyncTask;
+
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.IllegalFormatException;
@@ -38,8 +40,26 @@
public static final boolean WARN = isLoggable(android.util.Log.WARN);
public static final boolean ERROR = isLoggable(android.util.Log.ERROR);
+ private static MessageDigest sMessageDigest;
+
private Log() {}
+ public static void initMd5Sum() {
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ public Void doInBackground(Void... args) {
+ MessageDigest md;
+ try {
+ md = MessageDigest.getInstance("SHA-1");
+ } catch (NoSuchAlgorithmException e) {
+ md = null;
+ }
+ sMessageDigest = md;
+ return null;
+ }
+ }.execute();
+ }
+
public static boolean isLoggable(int level) {
return FORCE_LOGGING || android.util.Log.isLoggable(TAG, level);
}
@@ -137,15 +157,14 @@
}
private static String secureHash(byte[] input) {
- MessageDigest messageDigest;
- try {
- messageDigest = MessageDigest.getInstance("SHA-1");
- } catch (NoSuchAlgorithmException e) {
- return null;
+ if (sMessageDigest != null) {
+ sMessageDigest.reset();
+ sMessageDigest.update(input);
+ byte[] result = sMessageDigest.digest();
+ return encodeHex(result);
+ } else {
+ return "Uninitialized SHA1";
}
- messageDigest.update(input);
- byte[] result = messageDigest.digest();
- return encodeHex(result);
}
private static String encodeHex(byte[] bytes) {
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index f1cbb9a..9f478df 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -498,6 +498,7 @@
mContext = context;
}
mTelecomServiceOverride = telecomServiceImpl;
+ android.telecom.Log.initMd5Sum();
}
/**
diff --git a/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl b/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl
index 23a69d1..69259d0 100644
--- a/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl
+++ b/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl
@@ -17,6 +17,7 @@
package com.android.ims.internal;
import com.android.ims.ImsReasonInfo;
+
/**
* A listener type for receiving notifications about the changes to
* the IMS connection(registration).
@@ -26,15 +27,36 @@
interface IImsRegistrationListener {
/**
* Notifies the application when the device is connected to the IMS network.
+ *
+ * @deprecated see {@link registrationConnectedWithRadioTech}
*/
void registrationConnected();
/**
* Notifies the application when the device is trying to connect the IMS network.
+ *
+ * @deprecated see {@link registrationProgressingWithRadioTech}
*/
void registrationProgressing();
/**
+ * Notifies the application when the device is connected to the IMS network.
+ *
+ * @param imsRadioTech the radio access technology. Valid values are {@code
+ * RIL_RADIO_TECHNOLOGY_*} defined in {@link ServiceState}.
+ */
+ void registrationConnectedWithRadioTech(int imsRadioTech);
+
+ /**
+ * Notifies the application when the device is trying to connect the IMS network.
+ *
+ * @param imsRadioTech the radio access technology. Valid values are {@code
+ * RIL_RADIO_TECHNOLOGY_*} defined in {@link ServiceState}.
+ */
+ void registrationProgressingWithRadioTech(int imsRadioTech);
+
+
+ /**
* Notifies the application when the device is disconnected from the IMS network.
*/
void registrationDisconnected(in ImsReasonInfo imsReasonInfo);
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 429839f..0d6c70b 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -92,7 +92,33 @@
int NO_SMS_TO_ACK = 48; /* ACK received when there is no SMS to ack */
int NETWORK_ERR = 49; /* Received error from network */
int REQUEST_RATE_LIMITED = 50; /* Operation denied due to overly-frequent requests */
-
+ // Below is list of OEM specific error codes which can by used by OEMs in case they don't want to
+ // reveal particular replacement for Generic failure
+ int OEM_ERROR_1 = 501;
+ int OEM_ERROR_2 = 502;
+ int OEM_ERROR_3 = 503;
+ int OEM_ERROR_4 = 504;
+ int OEM_ERROR_5 = 505;
+ int OEM_ERROR_6 = 506;
+ int OEM_ERROR_7 = 507;
+ int OEM_ERROR_8 = 508;
+ int OEM_ERROR_9 = 509;
+ int OEM_ERROR_10 = 510;
+ int OEM_ERROR_11 = 511;
+ int OEM_ERROR_12 = 512;
+ int OEM_ERROR_13 = 513;
+ int OEM_ERROR_14 = 514;
+ int OEM_ERROR_15 = 515;
+ int OEM_ERROR_16 = 516;
+ int OEM_ERROR_17 = 517;
+ int OEM_ERROR_18 = 518;
+ int OEM_ERROR_19 = 519;
+ int OEM_ERROR_20 = 520;
+ int OEM_ERROR_21 = 521;
+ int OEM_ERROR_22 = 522;
+ int OEM_ERROR_23 = 523;
+ int OEM_ERROR_24 = 524;
+ int OEM_ERROR_25 = 525;
/* NETWORK_MODE_* See ril.h RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE */
int NETWORK_MODE_WCDMA_PREF = 0; /* GSM/WCDMA (WCDMA preferred) */
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 4d9ba6c..18a1943 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -1883,8 +1883,6 @@
//printf("Comment of %s: %s\n", String8(e).string(),
// String8(cmt).string());
syms->appendComment(String8(e), String16(cmt), srcPos);
- } else {
- //printf("No comment for %s\n", String8(e).string());
}
syms->makeSymbolPublic(String8(e), srcPos);
} else if (strcmp16(block.getElementName(&len), uses_permission16.string()) == 0) {
@@ -2535,10 +2533,6 @@
fprintf(fp,
"%s/** %s\n",
getIndentSpace(indent), cmt.string());
- } else if (sym.isPublic && !includePrivate) {
- sym.sourcePos.warning("No comment for public symbol %s:%s/%s",
- assets->getPackage().string(), className.string(),
- String8(sym.name).string());
}
String16 typeComment(sym.typeComment);
if (typeComment.size() > 0) {
@@ -2581,10 +2575,6 @@
"%s */\n",
getIndentSpace(indent), cmt.string(),
getIndentSpace(indent));
- } else if (sym.isPublic && !includePrivate) {
- sym.sourcePos.warning("No comment for public symbol %s:%s/%s",
- assets->getPackage().string(), className.string(),
- String8(sym.name).string());
}
ann.printAnnotations(fp, getIndentSpace(indent));
fprintf(fp, "%spublic static final String %s=\"%s\";\n",
diff --git a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
index d0dd22f..a10ac00 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
@@ -388,21 +388,18 @@
@LayoutlibDelegate
/*package*/ static void native_addRoundRect(long nPath, float left, float top, float right,
float bottom, float[] radii, int dir) {
- // Java2D doesn't support different rounded corners in each corner, so just use the
- // first value.
- native_addRoundRect(nPath, left, top, right, bottom, radii[0], radii[1], dir);
- // there can be a case where this API is used but with similar values for all corners, so
- // in that case we don't warn.
- // we only care if 2 corners are different so just compare to the next one.
- for (int i = 0 ; i < 3 ; i++) {
- if (radii[i * 2] != radii[(i + 1) * 2] || radii[i * 2 + 1] != radii[(i + 1) * 2 + 1]) {
- Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
- "Different corner sizes are not supported in Path.addRoundRect.",
- null, null /*data*/);
- break;
- }
+ Path_Delegate pathDelegate = sManager.getDelegate(nPath);
+ if (pathDelegate == null) {
+ return;
}
+
+ float[] cornerDimensions = new float[radii.length];
+ for (int i = 0; i < radii.length; i++) {
+ cornerDimensions[i] = 2 * radii[i];
+ }
+ pathDelegate.mPath.append(new RoundRectangle(left, top, right - left, bottom - top,
+ cornerDimensions), false);
}
@LayoutlibDelegate
diff --git a/tools/layoutlib/bridge/src/android/graphics/RoundRectangle.java b/tools/layoutlib/bridge/src/android/graphics/RoundRectangle.java
new file mode 100644
index 0000000..edd36e5
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/RoundRectangle.java
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import java.awt.geom.AffineTransform;
+import java.awt.geom.PathIterator;
+import java.awt.geom.Rectangle2D;
+import java.awt.geom.RectangularShape;
+import java.awt.geom.RoundRectangle2D;
+import java.util.EnumSet;
+import java.util.NoSuchElementException;
+
+/**
+ * Defines a rectangle with rounded corners, where the sizes of the corners
+ * are potentially different.
+ */
+public class RoundRectangle extends RectangularShape {
+ public double x;
+ public double y;
+ public double width;
+ public double height;
+ public double ulWidth;
+ public double ulHeight;
+ public double urWidth;
+ public double urHeight;
+ public double lrWidth;
+ public double lrHeight;
+ public double llWidth;
+ public double llHeight;
+
+ private enum Zone {
+ CLOSE_OUTSIDE,
+ CLOSE_INSIDE,
+ MIDDLE,
+ FAR_INSIDE,
+ FAR_OUTSIDE
+ }
+
+ private final EnumSet<Zone> close = EnumSet.of(Zone.CLOSE_OUTSIDE, Zone.CLOSE_INSIDE);
+ private final EnumSet<Zone> far = EnumSet.of(Zone.FAR_OUTSIDE, Zone.FAR_INSIDE);
+
+ /**
+ * @param cornerDimensions array of 8 floating-point number corresponding to the width and
+ * the height of each corner in the following order: upper-left, upper-right, lower-right,
+ * lower-left. It assumes for the size the same convention as {@link RoundRectangle2D}, that
+ * is that the width and height of a corner correspond to the total width and height of the
+ * ellipse that corner is a quarter of.
+ */
+ public RoundRectangle(float x, float y, float width, float height, float[] cornerDimensions) {
+ if (cornerDimensions.length != 8) {
+ throw new IllegalArgumentException("The array of corner dimensions must have eight " +
+ "elements");
+ }
+
+ this.x = x;
+ this.y = y;
+ this.width = width;
+ this.height = height;
+
+ float[] dimensions = cornerDimensions.clone();
+ // If a value is negative, the corresponding corner is squared
+ for (int i = 0; i < dimensions.length; i += 2) {
+ if (dimensions[i] < 0 || dimensions[i + 1] < 0) {
+ dimensions[i] = 0;
+ dimensions[i + 1] = 0;
+ }
+ }
+
+ double topCornerWidth = (dimensions[0] + dimensions[2]) / 2d;
+ double bottomCornerWidth = (dimensions[4] + dimensions[6]) / 2d;
+ double leftCornerHeight = (dimensions[1] + dimensions[7]) / 2d;
+ double rightCornerHeight = (dimensions[3] + dimensions[5]) / 2d;
+
+ // Rescale the corner dimensions if they are bigger than the rectangle
+ double scale = Math.min(1.0, width / topCornerWidth);
+ scale = Math.min(scale, width / bottomCornerWidth);
+ scale = Math.min(scale, height / leftCornerHeight);
+ scale = Math.min(scale, height / rightCornerHeight);
+
+ this.ulWidth = dimensions[0] * scale;
+ this.ulHeight = dimensions[1] * scale;
+ this.urWidth = dimensions[2] * scale;
+ this.urHeight = dimensions[3] * scale;
+ this.lrWidth = dimensions[4] * scale;
+ this.lrHeight = dimensions[5] * scale;
+ this.llWidth = dimensions[6] * scale;
+ this.llHeight = dimensions[7] * scale;
+ }
+
+ @Override
+ public double getX() {
+ return x;
+ }
+
+ @Override
+ public double getY() {
+ return y;
+ }
+
+ @Override
+ public double getWidth() {
+ return width;
+ }
+
+ @Override
+ public double getHeight() {
+ return height;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return (width <= 0d) || (height <= 0d);
+ }
+
+ @Override
+ public void setFrame(double x, double y, double w, double h) {
+ this.x = x;
+ this.y = y;
+ this.width = w;
+ this.height = h;
+ }
+
+ @Override
+ public Rectangle2D getBounds2D() {
+ return new Rectangle2D.Double(x, y, width, height);
+ }
+
+ @Override
+ public boolean contains(double x, double y) {
+ if (isEmpty()) {
+ return false;
+ }
+
+ double x0 = getX();
+ double y0 = getY();
+ double x1 = x0 + getWidth();
+ double y1 = y0 + getHeight();
+ // Check for trivial rejection - point is outside bounding rectangle
+ if (x < x0 || y < y0 || x >= x1 || y >= y1) {
+ return false;
+ }
+
+ double insideTopX0 = x0 + ulWidth / 2d;
+ double insideLeftY0 = y0 + ulHeight / 2d;
+ if (x < insideTopX0 && y < insideLeftY0) {
+ // In the upper-left corner
+ return isInsideCorner(x - insideTopX0, y - insideLeftY0, ulWidth / 2d, ulHeight / 2d);
+ }
+
+ double insideTopX1 = x1 - urWidth / 2d;
+ double insideRightY0 = y0 + urHeight / 2d;
+ if (x > insideTopX1 && y < insideRightY0) {
+ // In the upper-right corner
+ return isInsideCorner(x - insideTopX1, y - insideRightY0, urWidth / 2d, urHeight / 2d);
+ }
+
+ double insideBottomX1 = x1 - lrWidth / 2d;
+ double insideRightY1 = y1 - lrHeight / 2d;
+ if (x > insideBottomX1 && y > insideRightY1) {
+ // In the lower-right corner
+ return isInsideCorner(x - insideBottomX1, y - insideRightY1, lrWidth / 2d,
+ lrHeight / 2d);
+ }
+
+ double insideBottomX0 = x0 + llWidth / 2d;
+ double insideLeftY1 = y1 - llHeight / 2d;
+ if (x < insideBottomX0 && y > insideLeftY1) {
+ // In the lower-left corner
+ return isInsideCorner(x - insideBottomX0, y - insideLeftY1, llWidth / 2d,
+ llHeight / 2d);
+ }
+
+ // In the central part of the rectangle
+ return true;
+ }
+
+ private boolean isInsideCorner(double x, double y, double width, double height) {
+ double squareDist = height * height * x * x + width * width * y * y;
+ return squareDist <= width * width * height * height;
+ }
+
+ private Zone classify(double coord, double side1, double arcSize1, double side2,
+ double arcSize2) {
+ if (coord < side1) {
+ return Zone.CLOSE_OUTSIDE;
+ } else if (coord < side1 + arcSize1) {
+ return Zone.CLOSE_INSIDE;
+ } else if (coord < side2 - arcSize2) {
+ return Zone.MIDDLE;
+ } else if (coord < side2) {
+ return Zone.FAR_INSIDE;
+ } else {
+ return Zone.FAR_OUTSIDE;
+ }
+ }
+
+ public boolean intersects(double x, double y, double w, double h) {
+ if (isEmpty() || w <= 0 || h <= 0) {
+ return false;
+ }
+ double x0 = getX();
+ double y0 = getY();
+ double x1 = x0 + getWidth();
+ double y1 = y0 + getHeight();
+ // Check for trivial rejection - bounding rectangles do not intersect
+ if (x + w <= x0 || x >= x1 || y + h <= y0 || y >= y1) {
+ return false;
+ }
+
+ double maxLeftCornerWidth = Math.max(ulWidth, llWidth) / 2d;
+ double maxRightCornerWidth = Math.max(urWidth, lrWidth) / 2d;
+ double maxUpperCornerHeight = Math.max(ulHeight, urHeight) / 2d;
+ double maxLowerCornerHeight = Math.max(llHeight, lrHeight) / 2d;
+ Zone x0class = classify(x, x0, maxLeftCornerWidth, x1, maxRightCornerWidth);
+ Zone x1class = classify(x + w, x0, maxLeftCornerWidth, x1, maxRightCornerWidth);
+ Zone y0class = classify(y, y0, maxUpperCornerHeight, y1, maxLowerCornerHeight);
+ Zone y1class = classify(y + h, y0, maxUpperCornerHeight, y1, maxLowerCornerHeight);
+
+ // Trivially accept if any point is inside inner rectangle
+ if (x0class == Zone.MIDDLE || x1class == Zone.MIDDLE || y0class == Zone.MIDDLE || y1class == Zone.MIDDLE) {
+ return true;
+ }
+ // Trivially accept if either edge spans inner rectangle
+ if ((close.contains(x0class) && far.contains(x1class)) || (close.contains(y0class) &&
+ far.contains(y1class))) {
+ return true;
+ }
+
+ // Since neither edge spans the center, then one of the corners
+ // must be in one of the rounded edges. We detect this case if
+ // a [xy]0class is 3 or a [xy]1class is 1. One of those two cases
+ // must be true for each direction.
+ // We now find a "nearest point" to test for being inside a rounded
+ // corner.
+ if (x1class == Zone.CLOSE_INSIDE && y1class == Zone.CLOSE_INSIDE) {
+ // Potentially in upper-left corner
+ x = x + w - x0 - ulWidth / 2d;
+ y = y + h - y0 - ulHeight / 2d;
+ return x > 0 || y > 0 || isInsideCorner(x, y, ulWidth / 2d, ulHeight / 2d);
+ }
+ if (x1class == Zone.CLOSE_INSIDE) {
+ // Potentially in lower-left corner
+ x = x + w - x0 - llWidth / 2d;
+ y = y - y1 + llHeight / 2d;
+ return x > 0 || y < 0 || isInsideCorner(x, y, llWidth / 2d, llHeight / 2d);
+ }
+ if (y1class == Zone.CLOSE_INSIDE) {
+ //Potentially in the upper-right corner
+ x = x - x1 + urWidth / 2d;
+ y = y + h - y0 - urHeight / 2d;
+ return x < 0 || y > 0 || isInsideCorner(x, y, urWidth / 2d, urHeight / 2d);
+ }
+ // Potentially in the lower-right corner
+ x = x - x1 + lrWidth / 2d;
+ y = y - y1 + lrHeight / 2d;
+ return x < 0 || y < 0 || isInsideCorner(x, y, lrWidth / 2d, lrHeight / 2d);
+ }
+
+ @Override
+ public boolean contains(double x, double y, double w, double h) {
+ if (isEmpty() || w <= 0 || h <= 0) {
+ return false;
+ }
+ return (contains(x, y) &&
+ contains(x + w, y) &&
+ contains(x, y + h) &&
+ contains(x + w, y + h));
+ }
+
+ @Override
+ public PathIterator getPathIterator(final AffineTransform at) {
+ return new PathIterator() {
+ int index;
+
+ // ArcIterator.btan(Math.PI/2)
+ public static final double CtrlVal = 0.5522847498307933;
+ private final double ncv = 1.0 - CtrlVal;
+
+ // Coordinates of control points for Bezier curves approximating the straight lines
+ // and corners of the rounded rectangle.
+ private final double[][] ctrlpts = {
+ {0.0, 0.0, 0.0, ulHeight},
+ {0.0, 0.0, 1.0, -llHeight},
+ {0.0, 0.0, 1.0, -llHeight * ncv, 0.0, ncv * llWidth, 1.0, 0.0, 0.0, llWidth,
+ 1.0, 0.0},
+ {1.0, -lrWidth, 1.0, 0.0},
+ {1.0, -lrWidth * ncv, 1.0, 0.0, 1.0, 0.0, 1.0, -lrHeight * ncv, 1.0, 0.0, 1.0,
+ -lrHeight},
+ {1.0, 0.0, 0.0, urHeight},
+ {1.0, 0.0, 0.0, ncv * urHeight, 1.0, -urWidth * ncv, 0.0, 0.0, 1.0, -urWidth,
+ 0.0, 0.0},
+ {0.0, ulWidth, 0.0, 0.0},
+ {0.0, ncv * ulWidth, 0.0, 0.0, 0.0, 0.0, 0.0, ncv * ulHeight, 0.0, 0.0, 0.0,
+ ulHeight},
+ {}
+ };
+ private final int[] types = {
+ SEG_MOVETO,
+ SEG_LINETO, SEG_CUBICTO,
+ SEG_LINETO, SEG_CUBICTO,
+ SEG_LINETO, SEG_CUBICTO,
+ SEG_LINETO, SEG_CUBICTO,
+ SEG_CLOSE,
+ };
+
+ @Override
+ public int getWindingRule() {
+ return WIND_NON_ZERO;
+ }
+
+ @Override
+ public boolean isDone() {
+ return index >= ctrlpts.length;
+ }
+
+ @Override
+ public void next() {
+ index++;
+ }
+
+ @Override
+ public int currentSegment(float[] coords) {
+ if (isDone()) {
+ throw new NoSuchElementException("roundrect iterator out of bounds");
+ }
+ int nc = 0;
+ double ctrls[] = ctrlpts[index];
+ for (int i = 0; i < ctrls.length; i += 4) {
+ coords[nc++] = (float) (x + ctrls[i] * width + ctrls[i + 1] / 2d);
+ coords[nc++] = (float) (y + ctrls[i + 2] * height + ctrls[i + 3] / 2d);
+ }
+ if (at != null) {
+ at.transform(coords, 0, coords, 0, nc / 2);
+ }
+ return types[index];
+ }
+
+ @Override
+ public int currentSegment(double[] coords) {
+ if (isDone()) {
+ throw new NoSuchElementException("roundrect iterator out of bounds");
+ }
+ int nc = 0;
+ double ctrls[] = ctrlpts[index];
+ for (int i = 0; i < ctrls.length; i += 4) {
+ coords[nc++] = x + ctrls[i] * width + ctrls[i + 1] / 2d;
+ coords[nc++] = y + ctrls[i + 2] * height + ctrls[i + 3] / 2d;
+ }
+ if (at != null) {
+ at.transform(coords, 0, coords, 0, nc / 2);
+ }
+ return types[index];
+ }
+ };
+ }
+}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 2ad3c2e..4921073 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -44,6 +44,7 @@
import com.android.internal.util.Protocol;
import java.net.InetAddress;
+import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
@@ -892,6 +893,24 @@
}
/**
+ * Sets whether or not the given network is metered from a network policy
+ * point of view. A network should be classified as metered when the user is
+ * sensitive to heavy data usage on that connection due to monetary costs,
+ * data limitations or battery/performance issues. A typical example would
+ * be a wifi connection where the user was being charged for usage.
+ * @param netId the integer that identifies the network configuration
+ * to the supplicant.
+ * @param isMetered True to mark the network as metered.
+ * @return {@code true} if the operation succeeded.
+ * @hide
+ */
+ @SystemApi
+ public boolean setMetered(int netId, boolean isMetered) {
+ // TODO(jjoslin): Implement
+ return false;
+ }
+
+ /**
* Remove the specified network from the list of configured networks.
* This may result in the asynchronous delivery of state change
* events.
@@ -1301,13 +1320,15 @@
* @return the list of access points found in the most recent scan. An app must hold
* {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or
* {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission
- * in order to get valid results.
+ * in order to get valid results. If there is a remote exception (e.g., either a communication
+ * problem with the system service or an exception within the framework) an empty list will be
+ * returned.
*/
public List<ScanResult> getScanResults() {
try {
return mService.getScanResults(mContext.getOpPackageName());
} catch (RemoteException e) {
- return null;
+ return new ArrayList<ScanResult>();
}
}
diff --git a/wifi/java/android/net/wifi/nan/WifiNanEventListener.java b/wifi/java/android/net/wifi/nan/WifiNanEventListener.java
index 5c18bd7..9e6ed4e 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanEventListener.java
+++ b/wifi/java/android/net/wifi/nan/WifiNanEventListener.java
@@ -36,7 +36,7 @@
*/
public class WifiNanEventListener {
private static final String TAG = "WifiNanEventListener";
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
private static final boolean VDBG = false; // STOPSHIP if true
/**
diff --git a/wifi/java/android/net/wifi/nan/WifiNanManager.java b/wifi/java/android/net/wifi/nan/WifiNanManager.java
index cb82268..667c4b1 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanManager.java
+++ b/wifi/java/android/net/wifi/nan/WifiNanManager.java
@@ -38,7 +38,7 @@
*/
public class WifiNanManager {
private static final String TAG = "WifiNanManager";
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
private static final boolean VDBG = false; // STOPSHIP if true
private IBinder mBinder;
diff --git a/wifi/java/android/net/wifi/nan/WifiNanSession.java b/wifi/java/android/net/wifi/nan/WifiNanSession.java
index d0a9410..bc1787f 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanSession.java
+++ b/wifi/java/android/net/wifi/nan/WifiNanSession.java
@@ -27,7 +27,7 @@
*/
public class WifiNanSession {
private static final String TAG = "WifiNanSession";
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
private static final boolean VDBG = false; // STOPSHIP if true
/**
diff --git a/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java b/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java
index 0925087..b9af7def 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java
+++ b/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java
@@ -43,7 +43,7 @@
*/
public class WifiNanSessionListener {
private static final String TAG = "WifiNanSessionListener";
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
private static final boolean VDBG = false; // STOPSHIP if true
/**