Merge "add more comments from the other file" into nyc-dev
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 2fe5cbe..cee8fdb 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -240,6 +240,7 @@
$(call add-clean-step, rm -f $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/media/java/android/media/IRemoteControlDisplay.*)
$(call add-clean-step, rm -f $(OUT_DIR)/target/common/obj/APPS/FeatureSplit1_intermediates/src/com/android/test/split/feature/R.java)
$(call add-clean-step, rm -f $(OUT_DIR)/target/common/obj/APPS/FeatureSplit2_intermediates/src/com/android/test/split/feature/R.java)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/hardware)
# ******************************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
diff --git a/api/current.txt b/api/current.txt
index 07505b3..8d59b61 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5258,7 +5258,7 @@
}
public static class NotificationManager.Policy implements android.os.Parcelable {
- ctor public deprecated NotificationManager.Policy(int, int, int);
+ ctor public NotificationManager.Policy(int, int, int);
ctor public NotificationManager.Policy(int, int, int, int);
method public int describeContents();
method public static java.lang.String priorityCategoriesToString(int);
@@ -10466,7 +10466,6 @@
method public boolean hasNext();
method public java.util.Iterator<android.database.CursorJoiner.Result> iterator();
method public android.database.CursorJoiner.Result next();
- method public void remove();
}
public static final class CursorJoiner.Result extends java.lang.Enum {
@@ -20935,6 +20934,7 @@
field public static final java.lang.String KEY_SLICE_HEIGHT = "slice-height";
field public static final java.lang.String KEY_STRIDE = "stride";
field public static final java.lang.String KEY_TEMPORAL_LAYERING = "ts-schema";
+ field public static final java.lang.String KEY_TRACK_ID = "track-id";
field public static final java.lang.String KEY_WIDTH = "width";
field public static final java.lang.String MIMETYPE_AUDIO_AAC = "audio/mp4a-latm";
field public static final java.lang.String MIMETYPE_AUDIO_AC3 = "audio/ac3";
@@ -29047,6 +29047,8 @@
ctor public Process();
method public static final long getElapsedCpuTime();
method public static final int getGidForName(java.lang.String);
+ method public static final long getStartElapsedRealtime();
+ method public static final long getStartUptimeMillis();
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();
@@ -31712,6 +31714,7 @@
public static final class DocumentsContract.Root {
field public static final java.lang.String COLUMN_AVAILABLE_BYTES = "available_bytes";
+ field public static final java.lang.String COLUMN_CAPACITY_BYTES = "capacity_bytes";
field public static final java.lang.String COLUMN_DOCUMENT_ID = "document_id";
field public static final java.lang.String COLUMN_FLAGS = "flags";
field public static final java.lang.String COLUMN_ICON = "icon";
@@ -34234,7 +34237,8 @@
ctor public CarrierMessagingService();
method public android.os.IBinder onBind(android.content.Intent);
method public void onDownloadMms(android.net.Uri, int, android.net.Uri, android.service.carrier.CarrierMessagingService.ResultCallback<java.lang.Integer>);
- method public void onFilterSms(android.service.carrier.MessagePdu, java.lang.String, int, int, android.service.carrier.CarrierMessagingService.ResultCallback<java.lang.Boolean>);
+ method public deprecated void onFilterSms(android.service.carrier.MessagePdu, java.lang.String, int, int, android.service.carrier.CarrierMessagingService.ResultCallback<java.lang.Boolean>);
+ method public void onReceiveTextSms(android.service.carrier.MessagePdu, java.lang.String, int, int, android.service.carrier.CarrierMessagingService.ResultCallback<java.lang.Integer>);
method public deprecated void onSendDataSms(byte[], int, java.lang.String, int, android.service.carrier.CarrierMessagingService.ResultCallback<android.service.carrier.CarrierMessagingService.SendSmsResult>);
method public void onSendDataSms(byte[], int, java.lang.String, int, int, android.service.carrier.CarrierMessagingService.ResultCallback<android.service.carrier.CarrierMessagingService.SendSmsResult>);
method public void onSendMms(android.net.Uri, int, android.net.Uri, android.service.carrier.CarrierMessagingService.ResultCallback<android.service.carrier.CarrierMessagingService.SendMmsResult>);
@@ -34245,6 +34249,9 @@
field public static final int DOWNLOAD_STATUS_ERROR = 2; // 0x2
field public static final int DOWNLOAD_STATUS_OK = 0; // 0x0
field public static final int DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK = 1; // 0x1
+ field public static final int RECEIVE_OPTIONS_DEFAULT = 0; // 0x0
+ field public static final int RECEIVE_OPTIONS_DROP = 1; // 0x1
+ field public static final int RECEIVE_OPTIONS_SKIP_NOTIFY_WHEN_CREDENTIAL_ENCRYPTED_STORAGE_UNAVAILABLE = 2; // 0x2
field public static final int SEND_FLAG_REQUEST_DELIVERY_STATUS = 1; // 0x1
field public static final int SEND_STATUS_ERROR = 2; // 0x2
field public static final int SEND_STATUS_OK = 0; // 0x0
@@ -38471,7 +38478,6 @@
method public boolean hasNext();
method public java.util.Iterator<java.lang.String> iterator();
method public java.lang.String next();
- method public void remove();
method public void setString(java.lang.String);
}
@@ -40083,6 +40089,7 @@
method public int describeContents();
method public static android.util.LocaleList forLanguageTags(java.lang.String);
method public java.util.Locale get(int);
+ method public static android.util.LocaleList getAdjustedDefault();
method public static android.util.LocaleList getDefault();
method public static android.util.LocaleList getEmptyLocaleList();
method public java.util.Locale getFirstMatch(java.lang.String[]);
@@ -57706,9 +57713,10 @@
}
public abstract interface Iterator {
+ method public default void forEachRemaining(java.util.function.Consumer<? super E>);
method public abstract boolean hasNext();
method public abstract E next();
- method public abstract void remove();
+ method public default void remove();
}
public class LinkedHashMap extends java.util.HashMap implements java.util.Map {
@@ -58059,6 +58067,31 @@
method public long orElseThrow(java.util.function.Supplier<X>) throws java.lang.Throwable;
}
+ public abstract interface PrimitiveIterator implements java.util.Iterator {
+ method public abstract void forEachRemaining(T_CONS);
+ }
+
+ public static abstract interface PrimitiveIterator.OfDouble implements java.util.PrimitiveIterator {
+ method public default void forEachRemaining(java.util.function.DoubleConsumer);
+ method public default void forEachRemaining(java.util.function.Consumer<? super java.lang.Double>);
+ method public default java.lang.Double next();
+ method public abstract double nextDouble();
+ }
+
+ public static abstract interface PrimitiveIterator.OfInt implements java.util.PrimitiveIterator {
+ method public default void forEachRemaining(java.util.function.IntConsumer);
+ method public default void forEachRemaining(java.util.function.Consumer<? super java.lang.Integer>);
+ method public default java.lang.Integer next();
+ method public abstract int nextInt();
+ }
+
+ public static abstract interface PrimitiveIterator.OfLong implements java.util.PrimitiveIterator {
+ method public default void forEachRemaining(java.util.function.LongConsumer);
+ method public default void forEachRemaining(java.util.function.Consumer<? super java.lang.Long>);
+ method public default java.lang.Long next();
+ method public abstract long nextLong();
+ }
+
public class PriorityQueue extends java.util.AbstractQueue implements java.io.Serializable {
ctor public PriorityQueue();
ctor public PriorityQueue(int);
@@ -58230,7 +58263,6 @@
method public short nextShort();
method public short nextShort(int);
method public int radix();
- method public void remove();
method public java.util.Scanner reset();
method public java.util.Scanner skip(java.util.regex.Pattern);
method public java.util.Scanner skip(java.lang.String);
diff --git a/api/system-current.txt b/api/system-current.txt
index 089ccd3..1b835fe 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5390,7 +5390,7 @@
}
public static class NotificationManager.Policy implements android.os.Parcelable {
- ctor public deprecated NotificationManager.Policy(int, int, int);
+ ctor public NotificationManager.Policy(int, int, int);
ctor public NotificationManager.Policy(int, int, int, int);
method public int describeContents();
method public static java.lang.String priorityCategoriesToString(int);
@@ -10186,6 +10186,8 @@
field public static final int INSTALL_PARSE_FAILED_NO_CERTIFICATES = -103; // 0xffffff99
field public static final int INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION = -102; // 0xffffff9a
field public static final int INSTALL_SUCCEEDED = 1; // 0x1
+ field public static final int INTENT_FILTER_VERIFICATION_FAILURE = -1; // 0xffffffff
+ field public static final int INTENT_FILTER_VERIFICATION_SUCCESS = 1; // 0x1
field public static final int MASK_PERMISSION_FLAGS = 255; // 0xff
field public static final int MATCH_ALL = 131072; // 0x20000
field public static final int MATCH_DEFAULT_ONLY = 65536; // 0x10000
@@ -10858,7 +10860,6 @@
method public boolean hasNext();
method public java.util.Iterator<android.database.CursorJoiner.Result> iterator();
method public android.database.CursorJoiner.Result next();
- method public void remove();
}
public static final class CursorJoiner.Result extends java.lang.Enum {
@@ -22425,6 +22426,7 @@
field public static final java.lang.String KEY_SLICE_HEIGHT = "slice-height";
field public static final java.lang.String KEY_STRIDE = "stride";
field public static final java.lang.String KEY_TEMPORAL_LAYERING = "ts-schema";
+ field public static final java.lang.String KEY_TRACK_ID = "track-id";
field public static final java.lang.String KEY_WIDTH = "width";
field public static final java.lang.String MIMETYPE_AUDIO_AAC = "audio/mp4a-latm";
field public static final java.lang.String MIMETYPE_AUDIO_AC3 = "audio/ac3";
@@ -26662,7 +26664,6 @@
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);
@@ -31339,6 +31340,8 @@
ctor public Process();
method public static final long getElapsedCpuTime();
method public static final int getGidForName(java.lang.String);
+ method public static final long getStartElapsedRealtime();
+ method public static final long getStartUptimeMillis();
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();
@@ -34096,6 +34099,7 @@
public static final class DocumentsContract.Root {
field public static final java.lang.String COLUMN_AVAILABLE_BYTES = "available_bytes";
+ field public static final java.lang.String COLUMN_CAPACITY_BYTES = "capacity_bytes";
field public static final java.lang.String COLUMN_DOCUMENT_ID = "document_id";
field public static final java.lang.String COLUMN_FLAGS = "flags";
field public static final java.lang.String COLUMN_ICON = "icon";
@@ -36723,7 +36727,8 @@
ctor public CarrierMessagingService();
method public android.os.IBinder onBind(android.content.Intent);
method public void onDownloadMms(android.net.Uri, int, android.net.Uri, android.service.carrier.CarrierMessagingService.ResultCallback<java.lang.Integer>);
- method public void onFilterSms(android.service.carrier.MessagePdu, java.lang.String, int, int, android.service.carrier.CarrierMessagingService.ResultCallback<java.lang.Boolean>);
+ method public deprecated void onFilterSms(android.service.carrier.MessagePdu, java.lang.String, int, int, android.service.carrier.CarrierMessagingService.ResultCallback<java.lang.Boolean>);
+ method public void onReceiveTextSms(android.service.carrier.MessagePdu, java.lang.String, int, int, android.service.carrier.CarrierMessagingService.ResultCallback<java.lang.Integer>);
method public deprecated void onSendDataSms(byte[], int, java.lang.String, int, android.service.carrier.CarrierMessagingService.ResultCallback<android.service.carrier.CarrierMessagingService.SendSmsResult>);
method public void onSendDataSms(byte[], int, java.lang.String, int, int, android.service.carrier.CarrierMessagingService.ResultCallback<android.service.carrier.CarrierMessagingService.SendSmsResult>);
method public void onSendMms(android.net.Uri, int, android.net.Uri, android.service.carrier.CarrierMessagingService.ResultCallback<android.service.carrier.CarrierMessagingService.SendMmsResult>);
@@ -36734,6 +36739,9 @@
field public static final int DOWNLOAD_STATUS_ERROR = 2; // 0x2
field public static final int DOWNLOAD_STATUS_OK = 0; // 0x0
field public static final int DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK = 1; // 0x1
+ field public static final int RECEIVE_OPTIONS_DEFAULT = 0; // 0x0
+ field public static final int RECEIVE_OPTIONS_DROP = 1; // 0x1
+ field public static final int RECEIVE_OPTIONS_SKIP_NOTIFY_WHEN_CREDENTIAL_ENCRYPTED_STORAGE_UNAVAILABLE = 2; // 0x2
field public static final int SEND_FLAG_REQUEST_DELIVERY_STATUS = 1; // 0x1
field public static final int SEND_STATUS_ERROR = 2; // 0x2
field public static final int SEND_STATUS_OK = 0; // 0x0
@@ -41237,7 +41245,6 @@
method public boolean hasNext();
method public java.util.Iterator<java.lang.String> iterator();
method public java.lang.String next();
- method public void remove();
method public void setString(java.lang.String);
}
@@ -42849,6 +42856,7 @@
method public int describeContents();
method public static android.util.LocaleList forLanguageTags(java.lang.String);
method public java.util.Locale get(int);
+ method public static android.util.LocaleList getAdjustedDefault();
method public static android.util.LocaleList getDefault();
method public static android.util.LocaleList getEmptyLocaleList();
method public java.util.Locale getFirstMatch(java.lang.String[]);
@@ -60809,9 +60817,10 @@
}
public abstract interface Iterator {
+ method public default void forEachRemaining(java.util.function.Consumer<? super E>);
method public abstract boolean hasNext();
method public abstract E next();
- method public abstract void remove();
+ method public default void remove();
}
public class LinkedHashMap extends java.util.HashMap implements java.util.Map {
@@ -61162,6 +61171,31 @@
method public long orElseThrow(java.util.function.Supplier<X>) throws java.lang.Throwable;
}
+ public abstract interface PrimitiveIterator implements java.util.Iterator {
+ method public abstract void forEachRemaining(T_CONS);
+ }
+
+ public static abstract interface PrimitiveIterator.OfDouble implements java.util.PrimitiveIterator {
+ method public default void forEachRemaining(java.util.function.DoubleConsumer);
+ method public default void forEachRemaining(java.util.function.Consumer<? super java.lang.Double>);
+ method public default java.lang.Double next();
+ method public abstract double nextDouble();
+ }
+
+ public static abstract interface PrimitiveIterator.OfInt implements java.util.PrimitiveIterator {
+ method public default void forEachRemaining(java.util.function.IntConsumer);
+ method public default void forEachRemaining(java.util.function.Consumer<? super java.lang.Integer>);
+ method public default java.lang.Integer next();
+ method public abstract int nextInt();
+ }
+
+ public static abstract interface PrimitiveIterator.OfLong implements java.util.PrimitiveIterator {
+ method public default void forEachRemaining(java.util.function.LongConsumer);
+ method public default void forEachRemaining(java.util.function.Consumer<? super java.lang.Long>);
+ method public default java.lang.Long next();
+ method public abstract long nextLong();
+ }
+
public class PriorityQueue extends java.util.AbstractQueue implements java.io.Serializable {
ctor public PriorityQueue();
ctor public PriorityQueue(int);
@@ -61333,7 +61367,6 @@
method public short nextShort();
method public short nextShort(int);
method public int radix();
- method public void remove();
method public java.util.Scanner reset();
method public java.util.Scanner skip(java.util.regex.Pattern);
method public java.util.Scanner skip(java.lang.String);
diff --git a/api/test-current.txt b/api/test-current.txt
index 21a9db0..b70b68f 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -5258,7 +5258,7 @@
}
public static class NotificationManager.Policy implements android.os.Parcelable {
- ctor public deprecated NotificationManager.Policy(int, int, int);
+ ctor public NotificationManager.Policy(int, int, int);
ctor public NotificationManager.Policy(int, int, int, int);
method public int describeContents();
method public static java.lang.String priorityCategoriesToString(int);
@@ -10476,7 +10476,6 @@
method public boolean hasNext();
method public java.util.Iterator<android.database.CursorJoiner.Result> iterator();
method public android.database.CursorJoiner.Result next();
- method public void remove();
}
public static final class CursorJoiner.Result extends java.lang.Enum {
@@ -20946,6 +20945,7 @@
field public static final java.lang.String KEY_SLICE_HEIGHT = "slice-height";
field public static final java.lang.String KEY_STRIDE = "stride";
field public static final java.lang.String KEY_TEMPORAL_LAYERING = "ts-schema";
+ field public static final java.lang.String KEY_TRACK_ID = "track-id";
field public static final java.lang.String KEY_WIDTH = "width";
field public static final java.lang.String MIMETYPE_AUDIO_AAC = "audio/mp4a-latm";
field public static final java.lang.String MIMETYPE_AUDIO_AC3 = "audio/ac3";
@@ -29058,6 +29058,8 @@
ctor public Process();
method public static final long getElapsedCpuTime();
method public static final int getGidForName(java.lang.String);
+ method public static final long getStartElapsedRealtime();
+ method public static final long getStartUptimeMillis();
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();
@@ -31727,6 +31729,7 @@
public static final class DocumentsContract.Root {
field public static final java.lang.String COLUMN_AVAILABLE_BYTES = "available_bytes";
+ field public static final java.lang.String COLUMN_CAPACITY_BYTES = "capacity_bytes";
field public static final java.lang.String COLUMN_DOCUMENT_ID = "document_id";
field public static final java.lang.String COLUMN_FLAGS = "flags";
field public static final java.lang.String COLUMN_ICON = "icon";
@@ -34251,7 +34254,8 @@
ctor public CarrierMessagingService();
method public android.os.IBinder onBind(android.content.Intent);
method public void onDownloadMms(android.net.Uri, int, android.net.Uri, android.service.carrier.CarrierMessagingService.ResultCallback<java.lang.Integer>);
- method public void onFilterSms(android.service.carrier.MessagePdu, java.lang.String, int, int, android.service.carrier.CarrierMessagingService.ResultCallback<java.lang.Boolean>);
+ method public deprecated void onFilterSms(android.service.carrier.MessagePdu, java.lang.String, int, int, android.service.carrier.CarrierMessagingService.ResultCallback<java.lang.Boolean>);
+ method public void onReceiveTextSms(android.service.carrier.MessagePdu, java.lang.String, int, int, android.service.carrier.CarrierMessagingService.ResultCallback<java.lang.Integer>);
method public deprecated void onSendDataSms(byte[], int, java.lang.String, int, android.service.carrier.CarrierMessagingService.ResultCallback<android.service.carrier.CarrierMessagingService.SendSmsResult>);
method public void onSendDataSms(byte[], int, java.lang.String, int, int, android.service.carrier.CarrierMessagingService.ResultCallback<android.service.carrier.CarrierMessagingService.SendSmsResult>);
method public void onSendMms(android.net.Uri, int, android.net.Uri, android.service.carrier.CarrierMessagingService.ResultCallback<android.service.carrier.CarrierMessagingService.SendMmsResult>);
@@ -34262,6 +34266,9 @@
field public static final int DOWNLOAD_STATUS_ERROR = 2; // 0x2
field public static final int DOWNLOAD_STATUS_OK = 0; // 0x0
field public static final int DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK = 1; // 0x1
+ field public static final int RECEIVE_OPTIONS_DEFAULT = 0; // 0x0
+ field public static final int RECEIVE_OPTIONS_DROP = 1; // 0x1
+ field public static final int RECEIVE_OPTIONS_SKIP_NOTIFY_WHEN_CREDENTIAL_ENCRYPTED_STORAGE_UNAVAILABLE = 2; // 0x2
field public static final int SEND_FLAG_REQUEST_DELIVERY_STATUS = 1; // 0x1
field public static final int SEND_STATUS_ERROR = 2; // 0x2
field public static final int SEND_STATUS_OK = 0; // 0x0
@@ -38490,7 +38497,6 @@
method public boolean hasNext();
method public java.util.Iterator<java.lang.String> iterator();
method public java.lang.String next();
- method public void remove();
method public void setString(java.lang.String);
}
@@ -40102,6 +40108,7 @@
method public int describeContents();
method public static android.util.LocaleList forLanguageTags(java.lang.String);
method public java.util.Locale get(int);
+ method public static android.util.LocaleList getAdjustedDefault();
method public static android.util.LocaleList getDefault();
method public static android.util.LocaleList getEmptyLocaleList();
method public java.util.Locale getFirstMatch(java.lang.String[]);
@@ -57725,9 +57732,10 @@
}
public abstract interface Iterator {
+ method public default void forEachRemaining(java.util.function.Consumer<? super E>);
method public abstract boolean hasNext();
method public abstract E next();
- method public abstract void remove();
+ method public default void remove();
}
public class LinkedHashMap extends java.util.HashMap implements java.util.Map {
@@ -58078,6 +58086,31 @@
method public long orElseThrow(java.util.function.Supplier<X>) throws java.lang.Throwable;
}
+ public abstract interface PrimitiveIterator implements java.util.Iterator {
+ method public abstract void forEachRemaining(T_CONS);
+ }
+
+ public static abstract interface PrimitiveIterator.OfDouble implements java.util.PrimitiveIterator {
+ method public default void forEachRemaining(java.util.function.DoubleConsumer);
+ method public default void forEachRemaining(java.util.function.Consumer<? super java.lang.Double>);
+ method public default java.lang.Double next();
+ method public abstract double nextDouble();
+ }
+
+ public static abstract interface PrimitiveIterator.OfInt implements java.util.PrimitiveIterator {
+ method public default void forEachRemaining(java.util.function.IntConsumer);
+ method public default void forEachRemaining(java.util.function.Consumer<? super java.lang.Integer>);
+ method public default java.lang.Integer next();
+ method public abstract int nextInt();
+ }
+
+ public static abstract interface PrimitiveIterator.OfLong implements java.util.PrimitiveIterator {
+ method public default void forEachRemaining(java.util.function.LongConsumer);
+ method public default void forEachRemaining(java.util.function.Consumer<? super java.lang.Long>);
+ method public default java.lang.Long next();
+ method public abstract long nextLong();
+ }
+
public class PriorityQueue extends java.util.AbstractQueue implements java.io.Serializable {
ctor public PriorityQueue();
ctor public PriorityQueue(int);
@@ -58249,7 +58282,6 @@
method public short nextShort();
method public short nextShort(int);
method public int radix();
- method public void remove();
method public java.util.Scanner reset();
method public java.util.Scanner skip(java.util.regex.Pattern);
method public java.util.Scanner skip(java.lang.String);
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index df0e5fc..fea6f0e 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -606,7 +606,7 @@
new File(mProfileFile),
ParcelFileDescriptor.MODE_CREATE |
ParcelFileDescriptor.MODE_TRUNCATE |
- ParcelFileDescriptor.MODE_READ_WRITE);
+ ParcelFileDescriptor.MODE_WRITE_ONLY);
} catch (FileNotFoundException e) {
System.err.println("Error: Unable to open file: " + mProfileFile);
System.err.println("Consider using a file under /data/local/tmp/");
@@ -903,7 +903,7 @@
fd = openForSystemServer(file,
ParcelFileDescriptor.MODE_CREATE |
ParcelFileDescriptor.MODE_TRUNCATE |
- ParcelFileDescriptor.MODE_READ_WRITE);
+ ParcelFileDescriptor.MODE_WRITE_ONLY);
} catch (FileNotFoundException e) {
System.err.println("Error: Unable to open file: " + filename);
System.err.println("Consider using a file under /data/local/tmp/");
@@ -992,7 +992,7 @@
new File(profileFile),
ParcelFileDescriptor.MODE_CREATE |
ParcelFileDescriptor.MODE_TRUNCATE |
- ParcelFileDescriptor.MODE_READ_WRITE);
+ ParcelFileDescriptor.MODE_WRITE_ONLY);
} catch (FileNotFoundException e) {
System.err.println("Error: Unable to open file: " + profileFile);
System.err.println("Consider using a file under /data/local/tmp/");
@@ -1052,7 +1052,7 @@
fd = openForSystemServer(file,
ParcelFileDescriptor.MODE_CREATE |
ParcelFileDescriptor.MODE_TRUNCATE |
- ParcelFileDescriptor.MODE_READ_WRITE);
+ ParcelFileDescriptor.MODE_WRITE_ONLY);
} catch (FileNotFoundException e) {
System.err.println("Error: Unable to open file: " + heapFile);
System.err.println("Consider using a file under /data/local/tmp/");
diff --git a/core/java/android/animation/AnimatorInflater.java b/core/java/android/animation/AnimatorInflater.java
index 20d71a6..8d03b55 100644
--- a/core/java/android/animation/AnimatorInflater.java
+++ b/core/java/android/animation/AnimatorInflater.java
@@ -108,7 +108,7 @@
float pathErrorScale) throws NotFoundException {
final ConfigurationBoundResourceCache<Animator> animatorCache = resources
.getAnimatorCache();
- Animator animator = animatorCache.getInstance(id, theme);
+ Animator animator = animatorCache.getInstance(id, resources, theme);
if (animator != null) {
if (DBG_ANIMATOR_INFLATER) {
Log.d(TAG, "loaded animator from cache, " + resources.getResourceName(id));
@@ -157,7 +157,7 @@
final ConfigurationBoundResourceCache<StateListAnimator> cache = resources
.getStateListAnimatorCache();
final Theme theme = context.getTheme();
- StateListAnimator animator = cache.getInstance(id, theme);
+ StateListAnimator animator = cache.getInstance(id, resources, theme);
if (animator != null) {
return animator;
}
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index e721de9..5ab2c1d 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -1029,8 +1029,16 @@
@Override
public void resume() {
- if (mPaused) {
+ if (Looper.myLooper() == null) {
+ throw new AndroidRuntimeException("Animators may only be resumed from the same " +
+ "thread that the animator was started on");
+ }
+ if (mPaused && !mResumed) {
mResumed = true;
+ if (mPauseTime > 0) {
+ AnimationHandler handler = AnimationHandler.getInstance();
+ handler.addAnimationFrameCallback(this, 0);
+ }
}
super.resume();
}
@@ -1235,9 +1243,8 @@
}
mLastFrameTime = frameTime;
if (mPaused) {
- if (mPauseTime < 0) {
- mPauseTime = frameTime;
- }
+ mPauseTime = frameTime;
+ handler.removeCallback(this);
return;
} else if (mResumed) {
mResumed = false;
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index be89b20..b87e9fa2 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -6599,7 +6599,8 @@
if (isAppDebuggable || isDlwarningEnabled) {
String dlwarning = getDlWarning();
if (dlwarning != null) {
- String appName = getString(mApplication.getApplicationInfo().labelRes);
+ String appName = getApplicationInfo().loadLabel(getPackageManager())
+ .toString();
String warning = "Detected problems with app native libraries\n" +
"(please consult log for detail):\n" + dlwarning;
if (isAppDebuggable) {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 4676cc4..a4e5b90 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -577,6 +577,16 @@
}
/**
+ * Return whether a stackId is a stack containing floating windows. Floating windows
+ * are laid out differently as they are allowed to extend past the display bounds
+ * without overscan insets.
+ */
+ public static boolean tasksAreFloating(int stackId) {
+ return stackId == FREEFORM_WORKSPACE_STACK_ID
+ || stackId == PINNED_STACK_ID;
+ }
+
+ /**
* Returns true if animation specs should be constructed for app transition that moves
* the task to the specified stack.
*/
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 9d8dca60..ff7f70d 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -823,7 +823,21 @@
reply.writeNoException();
return true;
}
-
+ case RESIZE_PINNED_STACK_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ final boolean hasBounds = data.readInt() != 0;
+ Rect bounds = null;
+ if (hasBounds) {
+ bounds = Rect.CREATOR.createFromParcel(data);
+ }
+ final boolean hasTempPinnedTaskBounds = data.readInt() != 0;
+ Rect tempPinnedTaskBounds = null;
+ if (hasTempPinnedTaskBounds) {
+ tempPinnedTaskBounds = Rect.CREATOR.createFromParcel(data);
+ }
+ resizePinnedStack(bounds, tempPinnedTaskBounds);
+ return true;
+ }
case RESIZE_DOCKED_STACK_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
final boolean hasBounds = data.readInt() != 0;
@@ -3914,6 +3928,31 @@
data.recycle();
reply.recycle();
}
+
+ @Override
+ public void resizePinnedStack(Rect pinnedBounds, Rect tempPinnedTaskBounds) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ if (pinnedBounds != null) {
+ data.writeInt(1);
+ pinnedBounds.writeToParcel(data, 0);
+ } else {
+ data.writeInt(0);
+ }
+ if (tempPinnedTaskBounds != null) {
+ data.writeInt(1);
+ tempPinnedTaskBounds.writeToParcel(data, 0);
+ } else {
+ data.writeInt(0);
+ }
+ mRemote.transact(RESIZE_PINNED_STACK_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
@Override
public void positionTaskInStack(int taskId, int stackId, int position) throws RemoteException
{
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 1e95c98..1bc33b8 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -1788,7 +1788,8 @@
}
/**
- * Creates the top level resources for the given package.
+ * Creates the top level resources for the given package. Will return an existing
+ * Resources if one has already been created.
*/
Resources getTopLevelResources(String resDir, String[] splitResDirs, String[] overlayDirs,
String[] libDirs, int displayId, Configuration overrideConfiguration,
@@ -1798,6 +1799,19 @@
pkgInfo.getClassLoader());
}
+ /**
+ * Creates a new top level resources for the given package. Will always create a new
+ * Resources, regardless if one has already been created.
+ */
+ Resources getNewTopLevelResources(String resDir, String[] splitResDirs, String[] overlayDirs,
+ String[] libDirs, int displayId, Configuration overrideConfiguration,
+ LoadedApk pkgInfo) {
+ mResourcesManager.removeTopLevelResources(
+ resDir, displayId, overrideConfiguration, pkgInfo.getCompatibilityInfo());
+ return getTopLevelResources(resDir, splitResDirs, overlayDirs, libDirs,
+ displayId, overrideConfiguration, pkgInfo);
+ }
+
final Handler getHandler() {
return mH;
}
@@ -4749,29 +4763,87 @@
final void handleDispatchPackageBroadcast(int cmd, String[] packages) {
boolean hasPkgInfo = false;
- if (packages != null) {
- synchronized (mResourcesManager) {
- for (int i=packages.length-1; i>=0; i--) {
- //Slog.i(TAG, "Cleaning old package: " + packages[i]);
- if (!hasPkgInfo) {
- WeakReference<LoadedApk> ref;
- ref = mPackages.get(packages[i]);
- if (ref != null && ref.get() != null) {
+ switch (cmd) {
+ case IApplicationThread.PACKAGE_REMOVED:
+ case IApplicationThread.PACKAGE_REMOVED_DONT_KILL:
+ {
+ final boolean killApp = cmd == IApplicationThread.PACKAGE_REMOVED;
+ if (packages == null) {
+ break;
+ }
+ synchronized (mResourcesManager) {
+ for (int i = packages.length - 1; i >= 0; i--) {
+ if (!hasPkgInfo) {
+ WeakReference<LoadedApk> ref = mPackages.get(packages[i]);
+ if (ref != null && ref.get() != null) {
+ hasPkgInfo = true;
+ } else {
+ ref = mResourcePackages.get(packages[i]);
+ if (ref != null && ref.get() != null) {
+ hasPkgInfo = true;
+ }
+ }
+ }
+ if (killApp) {
+ mPackages.remove(packages[i]);
+ mResourcePackages.remove(packages[i]);
+ }
+ }
+ }
+ break;
+ }
+ case IApplicationThread.PACKAGE_REPLACED:
+ {
+ if (packages == null) {
+ break;
+ }
+ synchronized (mResourcesManager) {
+ for (int i = packages.length - 1; i >= 0; i--) {
+ WeakReference<LoadedApk> ref = mPackages.get(packages[i]);
+ LoadedApk pkgInfo = ref != null ? ref.get() : null;
+ if (pkgInfo != null) {
hasPkgInfo = true;
} else {
ref = mResourcePackages.get(packages[i]);
- if (ref != null && ref.get() != null) {
+ pkgInfo = ref != null ? ref.get() : null;
+ if (pkgInfo != null) {
hasPkgInfo = true;
}
}
+ // If the package is being replaced, yet it still has a valid
+ // LoadedApk object, the package was updated with _DONT_KILL.
+ // Adjust it's internal references to the application info and
+ // resources.
+ if (pkgInfo != null) {
+ try {
+ final String packageName = packages[i];
+ final ApplicationInfo aInfo =
+ sPackageManager.getApplicationInfo(
+ packageName,
+ 0 /*flags*/,
+ UserHandle.myUserId());
+
+ if (mActivities.size() > 0) {
+ for (ActivityClientRecord ar : mActivities.values()) {
+ if (ar.activityInfo.applicationInfo.packageName
+ .equals(packageName)) {
+ ar.activityInfo.applicationInfo = aInfo;
+ ar.packageInfo = pkgInfo;
+ }
+ }
+ }
+ final List<String> oldPaths =
+ sPackageManager.getPreviousCodePaths(packageName);
+ pkgInfo.updateApplicationInfo(aInfo, oldPaths);
+ } catch (RemoteException e) {
+ }
+ }
}
- mPackages.remove(packages[i]);
- mResourcePackages.remove(packages[i]);
}
+ break;
}
}
- ApplicationPackageManager.handlePackageBroadcast(cmd, packages,
- hasPkgInfo);
+ ApplicationPackageManager.handlePackageBroadcast(cmd, packages, hasPkgInfo);
}
final void handleLowMemory() {
@@ -4909,6 +4981,9 @@
DdmVmInternal.enableRecentAllocations(true);
}
+ // Note when this process has started.
+ Process.setStartTimes(SystemClock.elapsedRealtime(), SystemClock.uptimeMillis());
+
mBoundApplication = data;
mConfiguration = new Configuration(data.config);
mCompatConfiguration = new Configuration(data.config);
diff --git a/core/java/android/app/ApplicationLoaders.java b/core/java/android/app/ApplicationLoaders.java
index b20c091..0fc097e 100644
--- a/core/java/android/app/ApplicationLoaders.java
+++ b/core/java/android/app/ApplicationLoaders.java
@@ -86,6 +86,18 @@
String libraryPermittedPath,
boolean isShared);
+ /**
+ * Adds a new path the classpath of the given loader.
+ * @throws IllegalStateException if the provided class loader is not a {@link PathClassLoader}.
+ */
+ void addPath(ClassLoader classLoader, String dexPath) {
+ if (!(classLoader instanceof PathClassLoader)) {
+ throw new IllegalStateException("class loader is not a PathClassLoader");
+ }
+ final PathClassLoader baseDexClassLoader = (PathClassLoader) classLoader;
+ baseDexClassLoader.addDexPath(dexPath);
+ }
+
private final ArrayMap<String, ClassLoader> mLoaders = new ArrayMap<String, ClassLoader>();
private static final ApplicationLoaders gApplicationLoaders
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index c841111..38f32f7 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1543,9 +1543,9 @@
}
@Override
- public void verifyIntentFilter(int id, int verificationCode, List<String> outFailedDomains) {
+ public void verifyIntentFilter(int id, int verificationCode, List<String> failedDomains) {
try {
- mPM.verifyIntentFilter(id, verificationCode, outFailedDomains);
+ mPM.verifyIntentFilter(id, verificationCode, failedDomains);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index d78f59f..70bff80 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -171,6 +171,16 @@
public void resizeDockedStack(Rect dockedBounds, Rect tempDockedTaskBounds,
Rect tempDockedTaskInsetBounds,
Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds) throws RemoteException;
+ /**
+ * Resizes the pinned stack.
+ *
+ * @param pinnedBounds The bounds for the pinned stack.
+ * @param tempPinnedTaskBounds The temporary bounds for the tasks in the pinned stack, which
+ * might be different from the stack bounds to allow more
+ * flexibility while resizing, or {@code null} if they should be the
+ * same as the stack bounds.
+ */
+ public void resizePinnedStack(Rect pinnedBounds, Rect tempPinnedTaskBounds) throws RemoteException;
public void positionTaskInStack(int taskId, int stackId, int position) throws RemoteException;
public List<StackInfo> getAllStackInfos() throws RemoteException;
public StackInfo getStackInfo(int stackId) throws RemoteException;
@@ -982,4 +992,5 @@
int REMOVE_STACK = IBinder.FIRST_CALL_TRANSACTION + 367;
int SET_LENIENT_BACKGROUND_CHECK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+368;
int GET_MEMORY_TRIM_LEVEL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+369;
+ int RESIZE_PINNED_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 370;
}
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index a3c9591..628bde0 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -123,8 +123,13 @@
void dumpHeap(boolean managed, String path, ParcelFileDescriptor fd)
throws RemoteException;
void setSchedulingGroup(int group) throws RemoteException;
+ // the package has been removed, clean up internal references
static final int PACKAGE_REMOVED = 0;
static final int EXTERNAL_STORAGE_UNAVAILABLE = 1;
+ // the package is being modified in-place, don't kill it and retain references to it
+ static final int PACKAGE_REMOVED_DONT_KILL = 2;
+ // a previously removed package was replaced with a new version [eg. upgrade, split added, ...]
+ static final int PACKAGE_REPLACED = 3;
void dispatchPackageBroadcast(int cmd, String[] packages) throws RemoteException;
void scheduleCrash(String msg) throws RemoteException;
void dumpActivity(FileDescriptor fd, IBinder servicetoken, String prefix, String[] args)
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index b65faa9..cd17078 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -58,6 +58,7 @@
import java.net.URL;
import java.util.List;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Objects;
@@ -83,24 +84,25 @@
private static final String TAG = "LoadedApk";
private final ActivityThread mActivityThread;
- private ApplicationInfo mApplicationInfo;
final String mPackageName;
- private final String mAppDir;
- private final String mResDir;
- private final String[] mSplitAppDirs;
- private final String[] mSplitResDirs;
- private final String[] mOverlayDirs;
- private final String[] mSharedLibraries;
- private final String mDataDir;
- private final String mLibDir;
- private final File mDataDirFile;
- private final File mDeviceEncryptedDataDirFile;
- private final File mCredentialEncryptedDataDirFile;
+ private ApplicationInfo mApplicationInfo;
+ private String mAppDir;
+ private String mResDir;
+ private String[] mSplitAppDirs;
+ private String[] mSplitResDirs;
+ private String[] mOverlayDirs;
+ private String[] mSharedLibraries;
+ private String mDataDir;
+ private String mLibDir;
+ private File mDataDirFile;
+ private File mDeviceEncryptedDataDirFile;
+ private File mCredentialEncryptedDataDirFile;
private final ClassLoader mBaseClassLoader;
private final boolean mSecurityViolation;
private final boolean mIncludeCode;
private final boolean mRegisterPackage;
private final DisplayAdjustments mDisplayAdjustments = new DisplayAdjustments();
+ /** WARNING: This may change. Don't hold external references to it. */
Resources mResources;
private ClassLoader mClassLoader;
private Application mApplication;
@@ -129,23 +131,10 @@
public LoadedApk(ActivityThread activityThread, ApplicationInfo aInfo,
CompatibilityInfo compatInfo, ClassLoader baseLoader,
boolean securityViolation, boolean includeCode, boolean registerPackage) {
- final int myUid = Process.myUid();
- aInfo = adjustNativeLibraryPaths(aInfo);
mActivityThread = activityThread;
- mApplicationInfo = aInfo;
+ setApplicationInfo(aInfo);
mPackageName = aInfo.packageName;
- mAppDir = aInfo.sourceDir;
- mResDir = aInfo.uid == myUid ? aInfo.sourceDir : aInfo.publicSourceDir;
- mSplitAppDirs = aInfo.splitSourceDirs;
- mSplitResDirs = aInfo.uid == myUid ? aInfo.splitSourceDirs : aInfo.splitPublicSourceDirs;
- mOverlayDirs = aInfo.resourceDirs;
- mSharedLibraries = aInfo.sharedLibraryFiles;
- mDataDir = aInfo.dataDir;
- mDataDirFile = FileUtils.newFileOrNull(mDataDir);
- mDeviceEncryptedDataDirFile = FileUtils.newFileOrNull(aInfo.deviceEncryptedDataDir);
- mCredentialEncryptedDataDirFile = FileUtils.newFileOrNull(aInfo.credentialEncryptedDataDir);
- mLibDir = aInfo.nativeLibraryDir;
mBaseClassLoader = baseLoader;
mSecurityViolation = securityViolation;
mIncludeCode = includeCode;
@@ -266,26 +255,165 @@
return ai.sharedLibraryFiles;
}
- public ClassLoader getClassLoader() {
- synchronized (this) {
- if (mClassLoader != null) {
- return mClassLoader;
- }
+ public void updateApplicationInfo(ApplicationInfo aInfo, List<String> oldPaths) {
+ setApplicationInfo(aInfo);
- if (mPackageName.equals("android")) {
- if (mBaseClassLoader == null) {
- mClassLoader = ClassLoader.getSystemClassLoader();
- } else {
- mClassLoader = mBaseClassLoader;
+ final List<String> newPaths = new ArrayList<>();
+ makePaths(mActivityThread, aInfo, newPaths, null /*libPaths*/);
+ final List<String> addedPaths = new ArrayList<>(newPaths.size());
+
+ if (oldPaths != null) {
+ for (String path : newPaths) {
+ final String apkName = path.substring(path.lastIndexOf(File.separator));
+ boolean match = false;
+ for (String oldPath : oldPaths) {
+ final String oldApkName = oldPath.substring(path.lastIndexOf(File.separator));
+ if (apkName.equals(oldApkName)) {
+ match = true;
+ break;
+ }
}
- return mClassLoader;
+ if (!match) {
+ addedPaths.add(path);
+ }
+ }
+ } else {
+ addedPaths.addAll(newPaths);
+ }
+ synchronized (this) {
+ mClassLoader = createOrUpdateClassLoaderLocked(addedPaths);
+ if (mResources != null) {
+ mResources = mActivityThread.getNewTopLevelResources(mResDir, mSplitResDirs,
+ mOverlayDirs, mApplicationInfo.sharedLibraryFiles, Display.DEFAULT_DISPLAY,
+ null /*overrideConfiguration*/, this);
+ }
+ }
+ }
+
+ private void setApplicationInfo(ApplicationInfo aInfo) {
+ final int myUid = Process.myUid();
+ aInfo = adjustNativeLibraryPaths(aInfo);
+ mApplicationInfo = aInfo;
+ mAppDir = aInfo.sourceDir;
+ mResDir = aInfo.uid == myUid ? aInfo.sourceDir : aInfo.publicSourceDir;
+ mSplitAppDirs = aInfo.splitSourceDirs;
+ mSplitResDirs = aInfo.uid == myUid ? aInfo.splitSourceDirs : aInfo.splitPublicSourceDirs;
+ mOverlayDirs = aInfo.resourceDirs;
+ mSharedLibraries = aInfo.sharedLibraryFiles;
+ mDataDir = aInfo.dataDir;
+ mLibDir = aInfo.nativeLibraryDir;
+ mDataDirFile = FileUtils.newFileOrNull(aInfo.dataDir);
+ mDeviceEncryptedDataDirFile = FileUtils.newFileOrNull(aInfo.deviceEncryptedDataDir);
+ mCredentialEncryptedDataDirFile = FileUtils.newFileOrNull(aInfo.credentialEncryptedDataDir);
+ }
+
+ public static void makePaths(ActivityThread activityThread, ApplicationInfo aInfo,
+ List<String> outZipPaths, List<String> outLibPaths) {
+ final String appDir = aInfo.sourceDir;
+ final String[] splitAppDirs = aInfo.splitSourceDirs;
+ final String libDir = aInfo.nativeLibraryDir;
+ final String[] sharedLibraries = aInfo.sharedLibraryFiles;
+
+ outZipPaths.clear();
+ outZipPaths.add(appDir);
+ if (splitAppDirs != null) {
+ Collections.addAll(outZipPaths, splitAppDirs);
+ }
+
+ if (outLibPaths != null) {
+ outLibPaths.clear();
+ }
+
+ /*
+ * The following is a bit of a hack to inject
+ * instrumentation into the system: If the app
+ * being started matches one of the instrumentation names,
+ * then we combine both the "instrumentation" and
+ * "instrumented" app into the path, along with the
+ * concatenation of both apps' shared library lists.
+ */
+
+ String instrumentationPackageName = activityThread.mInstrumentationPackageName;
+ String instrumentationAppDir = activityThread.mInstrumentationAppDir;
+ String[] instrumentationSplitAppDirs = activityThread.mInstrumentationSplitAppDirs;
+ String instrumentationLibDir = activityThread.mInstrumentationLibDir;
+
+ String instrumentedAppDir = activityThread.mInstrumentedAppDir;
+ String[] instrumentedSplitAppDirs = activityThread.mInstrumentedSplitAppDirs;
+ String instrumentedLibDir = activityThread.mInstrumentedLibDir;
+ String[] instrumentationLibs = null;
+
+ if (appDir.equals(instrumentationAppDir)
+ || appDir.equals(instrumentedAppDir)) {
+ outZipPaths.clear();
+ outZipPaths.add(instrumentationAppDir);
+ if (instrumentationSplitAppDirs != null) {
+ Collections.addAll(outZipPaths, instrumentationSplitAppDirs);
+ }
+ outZipPaths.add(instrumentedAppDir);
+ if (instrumentedSplitAppDirs != null) {
+ Collections.addAll(outZipPaths, instrumentedSplitAppDirs);
}
+ if (outLibPaths != null) {
+ outLibPaths.add(instrumentationLibDir);
+ outLibPaths.add(instrumentedLibDir);
+ }
+
+ if (!instrumentedAppDir.equals(instrumentationAppDir)) {
+ instrumentationLibs = getLibrariesFor(instrumentationPackageName);
+ }
+ }
+
+ if (outLibPaths != null) {
+ if (outLibPaths.isEmpty()) {
+ outLibPaths.add(libDir);
+ }
+
+ // Add path to libraries in apk for current abi. Do this now because more entries
+ // will be added to zipPaths that shouldn't be part of the library path.
+ if (aInfo.primaryCpuAbi != null) {
+ for (String apk : outZipPaths) {
+ outLibPaths.add(apk + "!/lib/" + aInfo.primaryCpuAbi);
+ }
+ }
+
+ if (aInfo.isSystemApp() && !aInfo.isUpdatedSystemApp()) {
+ // Add path to system libraries to libPaths;
+ // Access to system libs should be limited
+ // to bundled applications; this is why updated
+ // system apps are not included.
+ outLibPaths.add(System.getProperty("java.library.path"));
+ }
+ }
+
+ if (sharedLibraries != null) {
+ for (String lib : sharedLibraries) {
+ if (!outZipPaths.contains(lib)) {
+ outZipPaths.add(0, lib);
+ }
+ }
+ }
+
+ if (instrumentationLibs != null) {
+ for (String lib : instrumentationLibs) {
+ if (!outZipPaths.contains(lib)) {
+ outZipPaths.add(0, lib);
+ }
+ }
+ }
+
+ final String zip = TextUtils.join(File.pathSeparator, outZipPaths);
+ }
+
+ private ClassLoader createOrUpdateClassLoaderLocked(List<String> addedPaths) {
+ final ClassLoader classLoader;
+ if (mIncludeCode && !mPackageName.equals("android")) {
// Avoid the binder call when the package is the current application package.
// The activity manager will perform ensure that dexopt is performed before
// spinning up the process.
if (!Objects.equals(mPackageName, ActivityThread.currentPackageName())) {
- final String isa = VMRuntime.getRuntime().vmInstructionSet();
+ VMRuntime.getRuntime().vmInstructionSet();
try {
ActivityThread.getPackageManager().notifyPackageUse(mPackageName);
} catch (RemoteException re) {
@@ -294,7 +422,6 @@
}
final List<String> zipPaths = new ArrayList<>();
- final List<String> apkPaths = new ArrayList<>();
final List<String> libPaths = new ArrayList<>();
if (mRegisterPackage) {
@@ -305,91 +432,12 @@
}
}
- zipPaths.add(mAppDir);
- if (mSplitAppDirs != null) {
- Collections.addAll(zipPaths, mSplitAppDirs);
- }
-
- libPaths.add(mLibDir);
-
- /*
- * The following is a bit of a hack to inject
- * instrumentation into the system: If the app
- * being started matches one of the instrumentation names,
- * then we combine both the "instrumentation" and
- * "instrumented" app into the path, along with the
- * concatenation of both apps' shared library lists.
- */
-
- String instrumentationPackageName = mActivityThread.mInstrumentationPackageName;
- String instrumentationAppDir = mActivityThread.mInstrumentationAppDir;
- String[] instrumentationSplitAppDirs = mActivityThread.mInstrumentationSplitAppDirs;
- String instrumentationLibDir = mActivityThread.mInstrumentationLibDir;
-
- String instrumentedAppDir = mActivityThread.mInstrumentedAppDir;
- String[] instrumentedSplitAppDirs = mActivityThread.mInstrumentedSplitAppDirs;
- String instrumentedLibDir = mActivityThread.mInstrumentedLibDir;
- String[] instrumentationLibs = null;
-
- if (mAppDir.equals(instrumentationAppDir)
- || mAppDir.equals(instrumentedAppDir)) {
- zipPaths.clear();
- zipPaths.add(instrumentationAppDir);
- if (instrumentationSplitAppDirs != null) {
- Collections.addAll(zipPaths, instrumentationSplitAppDirs);
- }
- zipPaths.add(instrumentedAppDir);
- if (instrumentedSplitAppDirs != null) {
- Collections.addAll(zipPaths, instrumentedSplitAppDirs);
- }
-
- libPaths.clear();
- libPaths.add(instrumentationLibDir);
- libPaths.add(instrumentedLibDir);
-
- if (!instrumentedAppDir.equals(instrumentationAppDir)) {
- instrumentationLibs = getLibrariesFor(instrumentationPackageName);
- }
- }
-
- apkPaths.addAll(zipPaths);
-
- if (mSharedLibraries != null) {
- for (String lib : mSharedLibraries) {
- if (!zipPaths.contains(lib)) {
- zipPaths.add(0, lib);
- }
- }
- }
-
- if (instrumentationLibs != null) {
- for (String lib : instrumentationLibs) {
- if (!zipPaths.contains(lib)) {
- zipPaths.add(0, lib);
- }
- }
- }
-
- final String zip = mIncludeCode ? TextUtils.join(File.pathSeparator, zipPaths) : "";
-
- // Add path to libraries in apk for current abi
- if (mApplicationInfo.primaryCpuAbi != null) {
- for (String apk : apkPaths) {
- libPaths.add(apk + "!/lib/" + mApplicationInfo.primaryCpuAbi);
- }
- }
-
+ makePaths(mActivityThread, mApplicationInfo, zipPaths, libPaths);
+ final String zip = TextUtils.join(File.pathSeparator, zipPaths);
+ final boolean isBundledApp = mApplicationInfo.isSystemApp()
+ && !mApplicationInfo.isUpdatedSystemApp();
String libraryPermittedPath = mDataDir;
- boolean isBundledApp = false;
-
- if (mApplicationInfo.isSystemApp() && !mApplicationInfo.isUpdatedSystemApp()) {
- isBundledApp = true;
- // Add path to system libraries to libPaths;
- // Access to system libs should be limited
- // to bundled applications; this is why updated
- // system apps are not included.
- libPaths.add(System.getProperty("java.library.path"));
-
+ if (isBundledApp) {
// This is necessary to grant bundled apps access to
// libraries located in subdirectories of /system/lib
libraryPermittedPath += File.pathSeparator +
@@ -414,15 +462,42 @@
Slog.v(ActivityThread.TAG, "Class path: " + zip +
", JNI path: " + librarySearchPath);
- // Temporarily disable logging of disk reads on the Looper thread
- // as this is early and necessary.
- StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+ if (mClassLoader == null) {
+ // Temporarily disable logging of disk reads on the Looper thread
+ // as this is early and necessary.
+ StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
- mClassLoader = ApplicationLoaders.getDefault().getClassLoader(zip,
- mApplicationInfo.targetSdkVersion, isBundledApp, librarySearchPath,
- libraryPermittedPath, mBaseClassLoader);
+ classLoader = ApplicationLoaders.getDefault().getClassLoader(zip,
+ mApplicationInfo.targetSdkVersion, isBundledApp, librarySearchPath,
+ libraryPermittedPath, mBaseClassLoader);
- StrictMode.setThreadPolicy(oldPolicy);
+ StrictMode.setThreadPolicy(oldPolicy);
+ } else if (addedPaths != null && addedPaths.size() > 0) {
+ final String add = TextUtils.join(File.pathSeparator, addedPaths);
+ ApplicationLoaders.getDefault().addPath(mClassLoader, add);
+ classLoader = mClassLoader;
+ } else {
+ classLoader = mClassLoader;
+ }
+ } else {
+ if (mClassLoader == null) {
+ if (mBaseClassLoader == null) {
+ classLoader = ClassLoader.getSystemClassLoader();
+ } else {
+ classLoader = mBaseClassLoader;
+ }
+ } else {
+ classLoader = mClassLoader;
+ }
+ }
+ return classLoader;
+ }
+
+ public ClassLoader getClassLoader() {
+ synchronized (this) {
+ if (mClassLoader == null) {
+ mClassLoader = createOrUpdateClassLoaderLocked(null /*addedPaths*/);
+ }
return mClassLoader;
}
}
@@ -1103,7 +1178,6 @@
private RuntimeException mUnbindLocation;
- private boolean mDied;
private boolean mForgotten;
private static class ConnectionInfo {
@@ -1202,7 +1276,6 @@
ServiceDispatcher.ConnectionInfo old;
synchronized (this) {
- mDied = true;
old = mActiveConnections.remove(name);
if (old == null || old.binder != service) {
// Death for someone different than who we last
@@ -1237,7 +1310,6 @@
if (service != null) {
// A new service is being connected... set it all up.
- mDied = false;
info = new ConnectionInfo();
info.binder = service;
info.deathMonitor = new DeathMonitor(name, service);
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 057a4e9..16aee78 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -662,7 +662,7 @@
/**
* Notification policy configuration. Represents user-preferences for notification
- * filtering and prioritization.
+ * filtering.
*/
public static class Policy implements android.os.Parcelable {
/** Reminder notifications are prioritized. */
@@ -707,13 +707,13 @@
*/
public static final int SUPPRESSED_EFFECTS_UNSET = -1;
/**
- * Whether notification suppressed by DND should not interruption visually when the screen
- * is off.
+ * Whether notifications suppressed by DND should not interrupt visually (e.g. with
+ * notification lights or by turning the screen on) when the screen is off.
*/
public static final int SUPPRESSED_EFFECT_SCREEN_OFF = 1 << 0;
/**
- * Whether notification suppressed by DND should not interruption visually when the screen
- * is on.
+ * Whether notifications suppressed by DND should not interrupt visually when the screen
+ * is on (e.g. by peeking onto the screen).
*/
public static final int SUPPRESSED_EFFECT_SCREEN_ON = 1 << 1;
@@ -728,13 +728,27 @@
*/
public final int suppressedVisualEffects;
-
- @Deprecated
+ /**
+ * Constructs a policy for Do Not Disturb priority mode behavior.
+ *
+ * @param priorityCategories bitmask of categories of notifications that can bypass DND.
+ * @param priorityCallSenders which callers can bypass DND.
+ * @param priorityMessageSenders which message senders can bypass DND.
+ */
public Policy(int priorityCategories, int priorityCallSenders, int priorityMessageSenders) {
this(priorityCategories, priorityCallSenders, priorityMessageSenders,
SUPPRESSED_EFFECTS_UNSET);
}
+ /**
+ * Constructs a policy for Do Not Disturb priority mode behavior.
+ *
+ * @param priorityCategories bitmask of categories of notifications that can bypass DND.
+ * @param priorityCallSenders which callers can bypass DND.
+ * @param priorityMessageSenders which message senders can bypass DND.
+ * @param suppressedVisualEffects which visual interruptions should be suppressed from
+ * notifications that are filtered by DND.
+ */
public Policy(int priorityCategories, int priorityCallSenders, int priorityMessageSenders,
int suppressedVisualEffects) {
this.priorityCategories = priorityCategories;
@@ -865,7 +879,6 @@
return new Policy[size];
}
};
-
}
/**
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 94e584e..bc44553d 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -23,6 +23,7 @@
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.content.res.ResourcesImpl;
import android.content.res.ResourcesKey;
import android.hardware.display.DisplayManagerGlobal;
import android.util.ArrayMap;
@@ -250,7 +251,8 @@
} else {
config = getConfiguration();
}
- r = new Resources(assets, dm, config, compatInfo, classLoader);
+ r = new Resources(classLoader);
+ r.setImpl(new ResourcesImpl(assets, dm, config, compatInfo));
if (DEBUG) Slog.i(TAG, "Created app resources " + resDir + " " + r + ": "
+ r.getConfiguration() + " appScale=" + r.getCompatibilityInfo().applicationScale);
@@ -288,6 +290,19 @@
}
}
+ /**
+ * Removes the top level Resources for applications with the given compatibility info.
+ * @see #getTopLevelResources(String, String[], String[], String[], int, Configuration, CompatibilityInfo, ClassLoader)
+ */
+ void removeTopLevelResources(String resDir, int displayId, Configuration overrideConfiguration,
+ CompatibilityInfo compatInfo) {
+ final float scale = compatInfo.applicationScale;
+ final Configuration overrideConfigCopy = (overrideConfiguration != null)
+ ? new Configuration(overrideConfiguration) : null;
+ final ResourcesKey key = new ResourcesKey(resDir, displayId, overrideConfigCopy, scale);
+ mActiveResources.remove(key);
+ }
+
/* package */ void setDefaultLocalesLocked(LocaleList locales) {
final int bestLocale;
if (mHasNonSystemLocales) {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index a1ad590..c4037f8 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -344,6 +344,68 @@
= "android.app.action.PROVISION_FINALIZATION";
/**
+ * Action: Bugreport sharing with device owner has been accepted by the user.
+ *
+ * @hide
+ */
+ public static final String ACTION_BUGREPORT_SHARING_ACCEPTED =
+ "com.android.server.action.BUGREPORT_SHARING_ACCEPTED";
+
+ /**
+ * Action: Bugreport sharing with device owner has been declined by the user.
+ *
+ * @hide
+ */
+ public static final String ACTION_BUGREPORT_SHARING_DECLINED =
+ "com.android.server.action.BUGREPORT_SHARING_DECLINED";
+
+ /**
+ * Action: Bugreport has been collected and is dispatched to {@link DevicePolicyManagerService}.
+ *
+ * @hide
+ */
+ public static final String ACTION_REMOTE_BUGREPORT_DISPATCH =
+ "android.intent.action.REMOTE_BUGREPORT_DISPATCH";
+
+ /**
+ * Extra for shared bugreport's SHA-256 hash.
+ *
+ * @hide
+ */
+ public static final String EXTRA_REMOTE_BUGREPORT_HASH =
+ "android.intent.extra.REMOTE_BUGREPORT_HASH";
+
+ /**
+ * Extra for remote bugreport notification shown type.
+ *
+ * @hide
+ */
+ public static final String EXTRA_BUGREPORT_NOTIFICATION_TYPE =
+ "android.app.extra.bugreport_notification_type";
+
+ /**
+ * Notification type for a started remote bugreport flow.
+ *
+ * @hide
+ */
+ public static final int NOTIFICATION_BUGREPORT_STARTED = 1;
+
+ /**
+ * Notification type for a bugreport that has already been accepted to be shared, but is still
+ * being taken.
+ *
+ * @hide
+ */
+ public static final int NOTIFICATION_BUGREPORT_ACCEPTED_NOT_FINISHED = 2;
+
+ /**
+ * Notification type for a bugreport that has been taken and can be shared or declined.
+ *
+ * @hide
+ */
+ public static final int NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED = 3;
+
+ /**
* A {@link android.os.Parcelable} extra of type {@link android.os.PersistableBundle} that
* allows a mobile device management application or NFC programmer application which starts
* managed provisioning to pass data to the management application instance after provisioning.
diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
index 0a0d77d..8cdfee5 100644
--- a/core/java/android/app/admin/DevicePolicyManagerInternal.java
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -69,4 +69,13 @@
* @return true if the uid is an active admin with the given policy.
*/
public abstract boolean isActiveAdminWithPolicy(int uid, int reqPolicy);
+
+ /**
+ * Checks if a given package has a device or a profile owner for the given user
+ *
+ * @param packageName The package to check
+ * @param userId the userId to check for.
+ * @return true if package has a device or profile owner, false otherwise.
+ */
+ public abstract boolean hasDeviceOwnerOrProfileOwner(String packageName, int userId);
}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 9959f27..0389085 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -530,4 +530,7 @@
String getServicesSystemSharedLibraryPackageName();
boolean isPackageDeviceAdminOnAnyUser(String packageName);
+
+ List<String> getPreviousCodePaths(in String packageName);
+
}
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 2cbb782..700a40d 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1042,6 +1042,11 @@
}
/** {@hide} */
+ public void setInstallFlagsDontKillApp() {
+ installFlags |= PackageManager.INSTALL_DONT_KILL_APP;
+ }
+
+ /** {@hide} */
public void dump(IndentingPrintWriter pw) {
pw.printPair("mode", mode);
pw.printHexPair("installFlags", installFlags);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 27056a3..0dc856c 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -538,6 +538,7 @@
INSTALL_FORCE_VOLUME_UUID,
INSTALL_FORCE_PERMISSION_PROMPT,
INSTALL_EPHEMERAL,
+ INSTALL_DONT_KILL_APP,
})
@Retention(RetentionPolicy.SOURCE)
public @interface InstallFlags {}
@@ -640,6 +641,15 @@
public static final int INSTALL_EPHEMERAL = 0x00000800;
/**
+ * Flag parameter for {@link #installPackage} to indicate that this package contains
+ * a feature split to an existing application and the existing application should not
+ * be killed during the installation process.
+ *
+ * @hide
+ */
+ public static final int INSTALL_DONT_KILL_APP = 0x00001000;
+
+ /**
* Flag parameter for
* {@link #setComponentEnabledSetting(android.content.ComponentName, int, int)} to indicate
* that you don't want to kill the app containing the component. Be careful when you set this
@@ -1088,6 +1098,7 @@
DELETE_KEEP_DATA,
DELETE_ALL_USERS,
DELETE_SYSTEM_APP,
+ DELETE_DONT_KILL_APP,
})
@Retention(RetentionPolicy.SOURCE)
public @interface DeleteFlags {}
@@ -1120,6 +1131,15 @@
public static final int DELETE_SYSTEM_APP = 0x00000004;
/**
+ * Flag parameter for {@link #deletePackage} to indicate that, if you are calling
+ * uninstall on a package that is replaced to provide new feature splits, the
+ * existing application should not be killed during the removal process.
+ *
+ * @hide
+ */
+ public static final int DELETE_DONT_KILL_APP = 0x00000008;
+
+ /**
* Return code for when package deletion succeeds. This is passed to the
* {@link IPackageDeleteObserver} if the system succeeded in deleting the
* package.
@@ -1292,6 +1312,7 @@
*
* @hide
*/
+ @SystemApi
public static final int INTENT_FILTER_VERIFICATION_SUCCESS = 1;
/**
@@ -1301,6 +1322,7 @@
*
* @hide
*/
+ @SystemApi
public static final int INTENT_FILTER_VERIFICATION_FAILURE = -1;
/**
@@ -4711,8 +4733,8 @@
/**
* Allows a package listening to the
- * {@link Intent#ACTION_INTENT_FILTER_NEEDS_VERIFICATION intent filter verification
- * broadcast} to respond to the package manager. The response must include
+ * {@link Intent#ACTION_INTENT_FILTER_NEEDS_VERIFICATION} intent filter verification
+ * broadcast to respond to the package manager. The response must include
* the {@code verificationCode} which is one of
* {@link PackageManager#INTENT_FILTER_VERIFICATION_SUCCESS} or
* {@link PackageManager#INTENT_FILTER_VERIFICATION_FAILURE}.
@@ -4721,7 +4743,7 @@
* {@link PackageManager#EXTRA_VERIFICATION_ID} Intent extra.
* @param verificationCode either {@link PackageManager#INTENT_FILTER_VERIFICATION_SUCCESS}
* or {@link PackageManager#INTENT_FILTER_VERIFICATION_FAILURE}.
- * @param outFailedDomains a list of failed domains if the verificationCode is
+ * @param failedDomains a list of failed domains if the verificationCode is
* {@link PackageManager#INTENT_FILTER_VERIFICATION_FAILURE}, otherwise null;
* @throws SecurityException if the caller does not have the
* INTENT_FILTER_VERIFICATION_AGENT permission.
@@ -4730,7 +4752,7 @@
*/
@SystemApi
public abstract void verifyIntentFilter(int verificationId, int verificationCode,
- List<String> outFailedDomains);
+ List<String> failedDomains);
/**
* Get the status of a Domain Verification Result for an IntentFilter. This is
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index 7fe7f84..89f2fc4 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -16,6 +16,8 @@
package android.content.pm;
+import android.content.pm.PackageManager.NameNotFoundException;
+
import java.util.List;
/**
@@ -125,4 +127,17 @@
* @return True a permissions review is required.
*/
public abstract boolean isPermissionsReviewRequired(String packageName, int userId);
+
+ /**
+ * Gets all of the information we know about a particular package.
+ *
+ * @param packageName The package name to find.
+ * @param userId The user under which to check.
+ *
+ * @return An {@link ApplicationInfo} containing information about the
+ * package.
+ * @throws NameNotFoundException if a package with the given name cannot be
+ * found on the system.
+ */
+ public abstract ApplicationInfo getApplicationInfo(String packageName, int userId);
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 0588a9d..bc28ff1 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -121,6 +121,10 @@
private static final int MAX_PACKAGES_PER_APK = 5;
+ public static final int APK_SIGNING_UNKNOWN = 0;
+ public static final int APK_SIGNING_V1 = 1;
+ public static final int APK_SIGNING_V2 = 2;
+
// TODO: switch outError users to PackageParserException
// TODO: refactor "codePath" to "apkPath"
@@ -1058,12 +1062,24 @@
return pkg;
}
+ public static int getApkSigningVersion(Package pkg) {
+ try {
+ if (ApkSignatureSchemeV2Verifier.hasSignature(pkg.baseCodePath)) {
+ return APK_SIGNING_V2;
+ }
+ return APK_SIGNING_V1;
+ } catch (IOException e) {
+ }
+ return APK_SIGNING_UNKNOWN;
+ }
+
/**
* Collect certificates from all the APKs described in the given package,
* populating {@link Package#mSignatures}. Also asserts that all APK
* contents are signed correctly and consistently.
*/
- public static void collectCertificates(Package pkg, int parseFlags) throws PackageParserException {
+ public static void collectCertificates(Package pkg, int parseFlags)
+ throws PackageParserException {
collectCertificatesInternal(pkg, parseFlags);
final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
@@ -1074,7 +1090,8 @@
}
}
- private static void collectCertificatesInternal(Package pkg, int parseFlags) throws PackageParserException {
+ private static void collectCertificatesInternal(Package pkg, int parseFlags)
+ throws PackageParserException {
pkg.mCertificates = null;
pkg.mSignatures = null;
pkg.mSigningKeys = null;
diff --git a/core/java/android/content/res/ConfigurationBoundResourceCache.java b/core/java/android/content/res/ConfigurationBoundResourceCache.java
index fecda87..40d2a82 100644
--- a/core/java/android/content/res/ConfigurationBoundResourceCache.java
+++ b/core/java/android/content/res/ConfigurationBoundResourceCache.java
@@ -23,29 +23,19 @@
* @hide For internal use only.
*/
public class ConfigurationBoundResourceCache<T> extends ThemedResourceCache<ConstantState<T>> {
- private final Resources mResources;
-
- /**
- * Creates a cache for the given Resources instance.
- *
- * @param resources the resources to use when creating new instances
- */
- public ConfigurationBoundResourceCache(Resources resources) {
- mResources = resources;
- }
-
/**
* If the resource is cached, creates and returns a new instance of it.
*
* @param key a key that uniquely identifies the drawable resource
+ * @param resources a Resources object from which to create new instances.
* @param theme the theme where the resource will be used
* @return a new instance of the resource, or {@code null} if not in
* the cache
*/
- public T getInstance(long key, Resources.Theme theme) {
+ public T getInstance(long key, Resources resources, Resources.Theme theme) {
final ConstantState<T> entry = get(key, theme);
if (entry != null) {
- return entry.newInstance(mResources, theme);
+ return entry.newInstance(resources, theme);
}
return null;
diff --git a/core/java/android/content/res/DrawableCache.java b/core/java/android/content/res/DrawableCache.java
index ba00134..7b27fac 100644
--- a/core/java/android/content/res/DrawableCache.java
+++ b/core/java/android/content/res/DrawableCache.java
@@ -22,29 +22,19 @@
* Class which can be used to cache Drawable resources against a theme.
*/
class DrawableCache extends ThemedResourceCache<Drawable.ConstantState> {
- private final Resources mResources;
-
- /**
- * Creates a cache for the given Resources instance.
- *
- * @param resources the resources to use when creating new instances
- */
- public DrawableCache(Resources resources) {
- mResources = resources;
- }
-
/**
* If the resource is cached, creates and returns a new instance of it.
*
* @param key a key that uniquely identifies the drawable resource
+ * @param resources a Resources object from which to create new instances.
* @param theme the theme where the resource will be used
* @return a new instance of the resource, or {@code null} if not in
* the cache
*/
- public Drawable getInstance(long key, Resources.Theme theme) {
+ public Drawable getInstance(long key, Resources resources, Resources.Theme theme) {
final Drawable.ConstantState entry = get(key, theme);
if (entry != null) {
- return entry.newDrawable(mResources, theme);
+ return entry.newDrawable(resources, theme);
}
return null;
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index a54f40f..f337fe6 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -40,23 +40,17 @@
import android.annotation.XmlRes;
import android.content.pm.ActivityInfo;
import android.graphics.Movie;
-import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Drawable.ConstantState;
import android.graphics.drawable.DrawableInflater;
-import android.icu.text.PluralRules;
import android.os.Build;
import android.os.Bundle;
-import android.os.Trace;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
-import android.util.LocaleList;
import android.util.Log;
import android.util.LongSparseArray;
import android.util.Pools.SynchronizedPool;
-import android.util.Slog;
import android.util.TypedValue;
-import android.util.Xml;
import android.view.ViewDebug;
import android.view.ViewHierarchyEncoder;
@@ -68,8 +62,6 @@
import java.io.IOException;
import java.io.InputStream;
-import java.util.Arrays;
-import java.util.Locale;
/**
* Class for accessing an application's resources. This sits on top of the
@@ -99,51 +91,15 @@
public class Resources {
static final String TAG = "Resources";
- private static final boolean DEBUG_LOAD = false;
- private static final boolean DEBUG_CONFIG = false;
- private static final boolean TRACE_FOR_PRELOAD = false;
- private static final boolean TRACE_FOR_MISS_PRELOAD = false;
-
- private static final int LAYOUT_DIR_CONFIG = ActivityInfo.activityInfoConfigToNative(
- ActivityInfo.CONFIG_LAYOUT_DIRECTION);
-
- private static final int ID_OTHER = 0x01000004;
-
private static final Object sSync = new Object();
- // Information about preloaded resources. Note that they are not
- // protected by a lock, because while preloading in zygote we are all
- // single-threaded, and after that these are immutable.
- private static final LongSparseArray<ConstantState>[] sPreloadedDrawables;
- private static final LongSparseArray<ConstantState> sPreloadedColorDrawables
- = new LongSparseArray<>();
- private static final LongSparseArray<android.content.res.ConstantState<ComplexColor>>
- sPreloadedComplexColors = new LongSparseArray<>();
-
- /** Size of the cyclical cache used to map XML files to blocks. */
- private static final int XML_BLOCK_CACHE_SIZE = 4;
-
- // Pool of TypedArrays targeted to this Resources object.
- final SynchronizedPool<TypedArray> mTypedArrayPool = new SynchronizedPool<>(5);
-
// Used by BridgeResources in layoutlib
static Resources mSystem = null;
- private static boolean sPreloaded;
+ private ResourcesImpl mResourcesImpl;
- /** Lock object used to protect access to caches and configuration. */
- private final Object mAccessLock = new Object();
-
- // These are protected by mAccessLock.
- private final Configuration mTmpConfig = new Configuration();
- private final DrawableCache mDrawableCache = new DrawableCache(this);
- private final DrawableCache mColorDrawableCache = new DrawableCache(this);
- private final ConfigurationBoundResourceCache<ComplexColor> mComplexColorCache =
- new ConfigurationBoundResourceCache<>(this);
- private final ConfigurationBoundResourceCache<Animator> mAnimatorCache =
- new ConfigurationBoundResourceCache<>(this);
- private final ConfigurationBoundResourceCache<StateListAnimator> mStateListAnimatorCache =
- new ConfigurationBoundResourceCache<>(this);
+ // Pool of TypedArrays targeted to this Resources object.
+ final SynchronizedPool<TypedArray> mTypedArrayPool = new SynchronizedPool<>(5);
/** Used to inflate drawable objects from XML. */
private DrawableInflater mDrawableInflater;
@@ -154,29 +110,7 @@
/** Single-item pool used to minimize TypedValue allocations. */
private TypedValue mTmpValue = new TypedValue();
- private boolean mPreloading;
-
- // Cyclical cache used for recently-accessed XML files.
- private int mLastCachedXmlBlockIndex = -1;
- private final int[] mCachedXmlBlockCookies = new int[XML_BLOCK_CACHE_SIZE];
- private final String[] mCachedXmlBlockFiles = new String[XML_BLOCK_CACHE_SIZE];
- private final XmlBlock[] mCachedXmlBlocks = new XmlBlock[XML_BLOCK_CACHE_SIZE];
-
- final AssetManager mAssets;
final ClassLoader mClassLoader;
- final DisplayMetrics mMetrics = new DisplayMetrics();
-
- private final Configuration mConfiguration = new Configuration();
-
- private PluralRules mPluralRule;
-
- private CompatibilityInfo mCompatibilityInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
-
- static {
- sPreloadedDrawables = new LongSparseArray[2];
- sPreloadedDrawables[0] = new LongSparseArray<>();
- sPreloadedDrawables[1] = new LongSparseArray<>();
- }
/**
* Returns the most appropriate default theme for the specified target SDK version.
@@ -219,32 +153,20 @@
}
/**
- * @return the inflater used to create drawable objects
- * @hide Pending API finalization.
+ * Return a global shared Resources object that provides access to only
+ * system resources (no application resources), and is not configured for
+ * the current screen (can not use dimension units, does not change based
+ * on orientation, etc).
*/
- public final DrawableInflater getDrawableInflater() {
- if (mDrawableInflater == null) {
- mDrawableInflater = new DrawableInflater(this, mClassLoader);
+ public static Resources getSystem() {
+ synchronized (sSync) {
+ Resources ret = mSystem;
+ if (ret == null) {
+ ret = new Resources();
+ mSystem = ret;
+ }
+ return ret;
}
- return mDrawableInflater;
- }
-
- /**
- * Used by AnimatorInflater.
- *
- * @hide
- */
- public ConfigurationBoundResourceCache<Animator> getAnimatorCache() {
- return mAnimatorCache;
- }
-
- /**
- * Used by AnimatorInflater.
- *
- * @hide
- */
- public ConfigurationBoundResourceCache<StateListAnimator> getStateListAnimatorCache() {
- return mStateListAnimatorCache;
}
/**
@@ -275,51 +197,73 @@
* selecting/computing resource values (optional).
*/
public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config) {
- this(assets, metrics, config, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
+ this(null);
+ mResourcesImpl = new ResourcesImpl(assets, metrics, config,
+ CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO);
}
/**
* Creates a new Resources object with CompatibilityInfo.
*
- * @param assets Previously created AssetManager.
- * @param metrics Current display metrics to consider when
- * selecting/computing resource values.
- * @param config Desired device configuration to consider when
- * selecting/computing resource values (optional).
- * @param compatInfo this resource's compatibility info. Must not be null.
* @param classLoader class loader for the package used to load custom
* resource classes, may be {@code null} to use system
* class loader
* @hide
*/
- public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config,
- CompatibilityInfo compatInfo, @Nullable ClassLoader classLoader) {
- mAssets = assets;
+ public Resources(@Nullable ClassLoader classLoader) {
mClassLoader = classLoader == null ? ClassLoader.getSystemClassLoader() : classLoader;
- mMetrics.setToDefaults();
- if (compatInfo != null) {
- mCompatibilityInfo = compatInfo;
- }
- updateConfiguration(config, metrics);
- assets.ensureStringBlocks();
}
/**
- * Return a global shared Resources object that provides access to only
- * system resources (no application resources), and is not configured for
- * the current screen (can not use dimension units, does not change based
- * on orientation, etc).
+ * Only for creating the System resources.
*/
- public static Resources getSystem() {
- synchronized (sSync) {
- Resources ret = mSystem;
- if (ret == null) {
- ret = new Resources();
- mSystem = ret;
- }
+ private Resources() {
+ this(null);
- return ret;
+ final DisplayMetrics metrics = new DisplayMetrics();
+ metrics.setToDefaults();
+
+ final Configuration config = new Configuration();
+ config.setToDefaults();
+
+ mResourcesImpl = new ResourcesImpl(AssetManager.getSystem(), metrics, config,
+ CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO);
+ }
+
+ /**
+ * @hide
+ */
+ public void setImpl(ResourcesImpl impl) {
+ mResourcesImpl = impl;
+ }
+
+ /**
+ * @return the inflater used to create drawable objects
+ * @hide Pending API finalization.
+ */
+ public final DrawableInflater getDrawableInflater() {
+ if (mDrawableInflater == null) {
+ mDrawableInflater = new DrawableInflater(this, mClassLoader);
}
+ return mDrawableInflater;
+ }
+
+ /**
+ * Used by AnimatorInflater.
+ *
+ * @hide
+ */
+ public ConfigurationBoundResourceCache<Animator> getAnimatorCache() {
+ return mResourcesImpl.getAnimatorCache();
+ }
+
+ /**
+ * Used by AnimatorInflater.
+ *
+ * @hide
+ */
+ public ConfigurationBoundResourceCache<StateListAnimator> getStateListAnimatorCache() {
+ return mResourcesImpl.getStateListAnimatorCache();
}
/**
@@ -337,8 +281,8 @@
* @return CharSequence The string data associated with the resource, plus
* possibly styled text information.
*/
- public CharSequence getText(@StringRes int id) throws NotFoundException {
- CharSequence res = mAssets.getResourceText(id);
+ @NonNull public CharSequence getText(@StringRes int id) throws NotFoundException {
+ CharSequence res = mResourcesImpl.getAssets().getResourceText(id);
if (res != null) {
return res;
}
@@ -366,41 +310,10 @@
* @return CharSequence The string data associated with the resource, plus
* possibly styled text information.
*/
+ @NonNull
public CharSequence getQuantityText(@PluralsRes int id, int quantity)
throws NotFoundException {
- PluralRules rule = getPluralRule();
- CharSequence res = mAssets.getResourceBagText(id,
- attrForQuantityCode(rule.select(quantity)));
- if (res != null) {
- return res;
- }
- res = mAssets.getResourceBagText(id, ID_OTHER);
- if (res != null) {
- return res;
- }
- throw new NotFoundException("Plural resource ID #0x" + Integer.toHexString(id)
- + " quantity=" + quantity
- + " item=" + rule.select(quantity));
- }
-
- private PluralRules getPluralRule() {
- synchronized (sSync) {
- if (mPluralRule == null) {
- mPluralRule = PluralRules.forLocale(mConfiguration.getLocales().get(0));
- }
- return mPluralRule;
- }
- }
-
- private static int attrForQuantityCode(String quantityCode) {
- switch (quantityCode) {
- case PluralRules.KEYWORD_ZERO: return 0x01000005;
- case PluralRules.KEYWORD_ONE: return 0x01000006;
- case PluralRules.KEYWORD_TWO: return 0x01000007;
- case PluralRules.KEYWORD_FEW: return 0x01000008;
- case PluralRules.KEYWORD_MANY: return 0x01000009;
- default: return ID_OTHER;
- }
+ return mResourcesImpl.getQuantityText(id, quantity);
}
/**
@@ -419,12 +332,7 @@
*/
@NonNull
public String getString(@StringRes int id) throws NotFoundException {
- final CharSequence res = getText(id);
- if (res != null) {
- return res.toString();
- }
- throw new NotFoundException("String resource ID #0x"
- + Integer.toHexString(id));
+ return getText(id).toString();
}
@@ -449,7 +357,8 @@
@NonNull
public String getString(@StringRes int id, Object... formatArgs) throws NotFoundException {
final String raw = getString(id);
- return String.format(mConfiguration.getLocales().get(0), raw, formatArgs);
+ return String.format(mResourcesImpl.getConfiguration().getLocales().get(0), raw,
+ formatArgs);
}
/**
@@ -477,10 +386,12 @@
* @return String The string data associated with the resource,
* stripped of styled text information.
*/
+ @NonNull
public String getQuantityString(@PluralsRes int id, int quantity, Object... formatArgs)
throws NotFoundException {
String raw = getQuantityText(id, quantity).toString();
- return String.format(mConfiguration.getLocales().get(0), raw, formatArgs);
+ return String.format(mResourcesImpl.getConfiguration().getLocales().get(0), raw,
+ formatArgs);
}
/**
@@ -503,8 +414,8 @@
* @return String The string data associated with the resource,
* stripped of styled text information.
*/
- public String getQuantityString(@PluralsRes int id, int quantity)
- throws NotFoundException {
+ @NonNull
+ public String getQuantityString(@PluralsRes int id, int quantity) throws NotFoundException {
return getQuantityText(id, quantity).toString();
}
@@ -523,7 +434,7 @@
* possibly styled text information, or def if id is 0 or not found.
*/
public CharSequence getText(@StringRes int id, CharSequence def) {
- CharSequence res = id != 0 ? mAssets.getResourceText(id) : null;
+ CharSequence res = id != 0 ? mResourcesImpl.getAssets().getResourceText(id) : null;
return res != null ? res : def;
}
@@ -538,13 +449,13 @@
*
* @return The styled text array associated with the resource.
*/
+ @NonNull
public CharSequence[] getTextArray(@ArrayRes int id) throws NotFoundException {
- CharSequence[] res = mAssets.getResourceTextArray(id);
+ CharSequence[] res = mResourcesImpl.getAssets().getResourceTextArray(id);
if (res != null) {
return res;
}
- throw new NotFoundException("Text array resource ID #0x"
- + Integer.toHexString(id));
+ throw new NotFoundException("Text array resource ID #0x" + Integer.toHexString(id));
}
/**
@@ -558,14 +469,14 @@
*
* @return The string array associated with the resource.
*/
+ @NonNull
public String[] getStringArray(@ArrayRes int id)
throws NotFoundException {
- String[] res = mAssets.getResourceStringArray(id);
+ String[] res = mResourcesImpl.getAssets().getResourceStringArray(id);
if (res != null) {
return res;
}
- throw new NotFoundException("String array resource ID #0x"
- + Integer.toHexString(id));
+ throw new NotFoundException("String array resource ID #0x" + Integer.toHexString(id));
}
/**
@@ -579,13 +490,13 @@
*
* @return The int array associated with the resource.
*/
+ @NonNull
public int[] getIntArray(@ArrayRes int id) throws NotFoundException {
- int[] res = mAssets.getArrayIntResource(id);
+ int[] res = mResourcesImpl.getAssets().getArrayIntResource(id);
if (res != null) {
return res;
}
- throw new NotFoundException("Int array resource ID #0x"
- + Integer.toHexString(id));
+ throw new NotFoundException("Int array resource ID #0x" + Integer.toHexString(id));
}
/**
@@ -601,16 +512,16 @@
* Be sure to call {@link TypedArray#recycle() TypedArray.recycle()}
* when done with it.
*/
- public TypedArray obtainTypedArray(@ArrayRes int id)
- throws NotFoundException {
- int len = mAssets.getArraySize(id);
+ @NonNull
+ public TypedArray obtainTypedArray(@ArrayRes int id) throws NotFoundException {
+ final ResourcesImpl impl = mResourcesImpl;
+ int len = impl.getAssets().getArraySize(id);
if (len < 0) {
- throw new NotFoundException("Array resource ID #0x"
- + Integer.toHexString(id));
+ throw new NotFoundException("Array resource ID #0x" + Integer.toHexString(id));
}
TypedArray array = TypedArray.obtain(this, len);
- array.mLength = mAssets.retrieveArray(id, array.mData);
+ array.mLength = impl.getAssets().retrieveArray(id, array.mData);
array.mIndices[0] = 0;
return array;
@@ -634,10 +545,12 @@
* @see #getDimensionPixelSize
*/
public float getDimension(@DimenRes int id) throws NotFoundException {
- final TypedValue value = obtainTempTypedValue(id);
+ final TypedValue value = obtainTempTypedValue();
try {
+ final ResourcesImpl impl = mResourcesImpl;
+ impl.getValue(id, value, true);
if (value.type == TypedValue.TYPE_DIMENSION) {
- return TypedValue.complexToDimension(value.data, mMetrics);
+ return TypedValue.complexToDimension(value.data, impl.getDisplayMetrics());
}
throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)
+ " type #0x" + Integer.toHexString(value.type) + " is not valid");
@@ -666,10 +579,13 @@
* @see #getDimensionPixelSize
*/
public int getDimensionPixelOffset(@DimenRes int id) throws NotFoundException {
- final TypedValue value = obtainTempTypedValue(id);
+ final TypedValue value = obtainTempTypedValue();
try {
+ final ResourcesImpl impl = mResourcesImpl;
+ impl.getValue(id, value, true);
if (value.type == TypedValue.TYPE_DIMENSION) {
- return TypedValue.complexToDimensionPixelOffset(value.data, mMetrics);
+ return TypedValue.complexToDimensionPixelOffset(value.data,
+ impl.getDisplayMetrics());
}
throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)
+ " type #0x" + Integer.toHexString(value.type) + " is not valid");
@@ -699,10 +615,12 @@
* @see #getDimensionPixelOffset
*/
public int getDimensionPixelSize(@DimenRes int id) throws NotFoundException {
- final TypedValue value = obtainTempTypedValue(id);
+ final TypedValue value = obtainTempTypedValue();
try {
+ final ResourcesImpl impl = mResourcesImpl;
+ impl.getValue(id, value, true);
if (value.type == TypedValue.TYPE_DIMENSION) {
- return TypedValue.complexToDimensionPixelSize(value.data, mMetrics);
+ return TypedValue.complexToDimensionPixelSize(value.data, impl.getDisplayMetrics());
}
throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)
+ " type #0x" + Integer.toHexString(value.type) + " is not valid");
@@ -729,8 +647,9 @@
* @throws NotFoundException Throws NotFoundException if the given ID does not exist.
*/
public float getFraction(@FractionRes int id, int base, int pbase) {
- final TypedValue value = obtainTempTypedValue(id);
+ final TypedValue value = obtainTempTypedValue();
try {
+ mResourcesImpl.getValue(id, value, true);
if (value.type == TypedValue.TYPE_FRACTION) {
return TypedValue.complexToFraction(value.data, base, pbase);
}
@@ -800,9 +719,11 @@
*/
public Drawable getDrawable(@DrawableRes int id, @Nullable Theme theme)
throws NotFoundException {
- final TypedValue value = obtainTempTypedValue(id);
+ final TypedValue value = obtainTempTypedValue();
try {
- return loadDrawable(value, id, theme);
+ final ResourcesImpl impl = mResourcesImpl;
+ impl.getValue(id, value, true);
+ return impl.loadDrawable(this, value, id, theme, true);
} finally {
releaseTempTypedValue(value);
}
@@ -855,14 +776,16 @@
* not exist.
*/
public Drawable getDrawableForDensity(@DrawableRes int id, int density, @Nullable Theme theme) {
- final TypedValue value = obtainTempTypedValue(id);
+ final TypedValue value = obtainTempTypedValue();
try {
- getValueForDensity(id, density, value, true);
+ final ResourcesImpl impl = mResourcesImpl;
+ impl.getValueForDensity(id, density, value, true);
// If the drawable's XML lives in our current density qualifier,
// it's okay to use a scaled version from the cache. Otherwise, we
// need to actually load the drawable from XML.
- final boolean useCache = value.density == mMetrics.densityDpi;
+ final DisplayMetrics metrics = impl.getDisplayMetrics();
+ final boolean useCache = value.density == metrics.densityDpi;
/*
* Pretend the requested density is actually the display density. If
@@ -873,18 +796,23 @@
*/
if (value.density > 0 && value.density != TypedValue.DENSITY_NONE) {
if (value.density == density) {
- value.density = mMetrics.densityDpi;
+ value.density = metrics.densityDpi;
} else {
- value.density = (value.density * mMetrics.densityDpi) / density;
+ value.density = (value.density * metrics.densityDpi) / density;
}
}
-
- return loadDrawable(value, id, theme, useCache);
+ return impl.loadDrawable(this, value, id, theme, useCache);
} finally {
releaseTempTypedValue(value);
}
}
+ @NonNull
+ Drawable loadDrawable(@NonNull TypedValue value, int id, @Nullable Theme theme)
+ throws NotFoundException {
+ return mResourcesImpl.loadDrawable(this, value, id, theme, true);
+ }
+
/**
* Return a movie object associated with the particular resource ID.
* @param id The desired resource identifier, as generated by the aapt
@@ -894,13 +822,12 @@
*
*/
public Movie getMovie(@RawRes int id) throws NotFoundException {
- InputStream is = openRawResource(id);
- Movie movie = Movie.decodeStream(is);
+ final InputStream is = openRawResource(id);
+ final Movie movie = Movie.decodeStream(is);
try {
is.close();
- }
- catch (java.io.IOException e) {
- // don't care, since the return value is valid
+ } catch (IOException e) {
+ // No one cares.
}
return movie;
}
@@ -944,8 +871,10 @@
*/
@ColorInt
public int getColor(@ColorRes int id, @Nullable Theme theme) throws NotFoundException {
- final TypedValue value = obtainTempTypedValue(id);
+ final TypedValue value = obtainTempTypedValue();
try {
+ final ResourcesImpl impl = mResourcesImpl;
+ impl.getValue(id, value, true);
if (value.type >= TypedValue.TYPE_FIRST_INT
&& value.type <= TypedValue.TYPE_LAST_INT) {
return value.data;
@@ -954,7 +883,7 @@
+ " type #0x" + Integer.toHexString(value.type) + " is not valid");
}
- final ColorStateList csl = loadColorStateList(value, id, theme);
+ final ColorStateList csl = impl.loadColorStateList(this, value, id, theme);
return csl.getDefaultColor();
} finally {
releaseTempTypedValue(value);
@@ -1012,14 +941,27 @@
@Nullable
public ColorStateList getColorStateList(@ColorRes int id, @Nullable Theme theme)
throws NotFoundException {
- final TypedValue value = obtainTempTypedValue(id);
+ final TypedValue value = obtainTempTypedValue();
try {
- return loadColorStateList(value, id, theme);
+ final ResourcesImpl impl = mResourcesImpl;
+ impl.getValue(id, value, true);
+ return impl.loadColorStateList(this, value, id, theme);
} finally {
releaseTempTypedValue(value);
}
}
+ @Nullable
+ ColorStateList loadColorStateList(@NonNull TypedValue value, int id, @Nullable Theme theme)
+ throws NotFoundException {
+ return mResourcesImpl.loadColorStateList(this, value, id, theme);
+ }
+
+ @Nullable
+ public ComplexColor loadComplexColor(@NonNull TypedValue value, int id, @Nullable Theme theme) {
+ return mResourcesImpl.loadComplexColor(this, value, id, theme);
+ }
+
/**
* Return a boolean associated with a particular resource ID. This can be
* used with any integral resource value, and will return true if it is
@@ -1034,8 +976,9 @@
* @return Returns the boolean value contained in the resource.
*/
public boolean getBoolean(@BoolRes int id) throws NotFoundException {
- final TypedValue value = obtainTempTypedValue(id);
+ final TypedValue value = obtainTempTypedValue();
try {
+ mResourcesImpl.getValue(id, value, true);
if (value.type >= TypedValue.TYPE_FIRST_INT
&& value.type <= TypedValue.TYPE_LAST_INT) {
return value.data != 0;
@@ -1059,8 +1002,9 @@
* @return Returns the integer value contained in the resource.
*/
public int getInteger(@IntegerRes int id) throws NotFoundException {
- final TypedValue value = obtainTempTypedValue(id);
+ final TypedValue value = obtainTempTypedValue();
try {
+ mResourcesImpl.getValue(id, value, true);
if (value.type >= TypedValue.TYPE_FIRST_INT
&& value.type <= TypedValue.TYPE_LAST_INT) {
return value.data;
@@ -1086,8 +1030,9 @@
* @hide Pending API council approval.
*/
public float getFloat(int id) {
- final TypedValue value = obtainTempTypedValue(id);
+ final TypedValue value = obtainTempTypedValue();
try {
+ mResourcesImpl.getValue(id, value, true);
if (value.type == TypedValue.TYPE_FLOAT) {
return value.getFloat();
}
@@ -1195,20 +1140,6 @@
}
/**
- * Returns a TypedValue populated with data for the specified resource ID
- * that's suitable for temporary use. The obtained TypedValue should be
- * released using {@link #releaseTempTypedValue(TypedValue)}.
- *
- * @param id the resource ID for which data should be obtained
- * @return a populated typed value suitable for temporary use
- */
- private TypedValue obtainTempTypedValue(@AnyRes int id) {
- final TypedValue value = obtainTempTypedValue();
- getValue(id, value, true);
- return value;
- }
-
- /**
* Returns a TypedValue suitable for temporary use. The obtained TypedValue
* should be released using {@link #releaseTempTypedValue(TypedValue)}.
*
@@ -1257,17 +1188,7 @@
*/
public InputStream openRawResource(@RawRes int id, TypedValue value)
throws NotFoundException {
- getValue(id, value, true);
-
- try {
- return mAssets.openNonAsset(value.assetCookie, value.string.toString(),
- AssetManager.ACCESS_STREAMING);
- } catch (Exception e) {
- NotFoundException rnf = new NotFoundException("File " + value.string.toString() +
- " from drawable resource ID #0x" + Integer.toHexString(id));
- rnf.initCause(e);
- throw rnf;
- }
+ return mResourcesImpl.openRawResource(id, value);
}
/**
@@ -1293,12 +1214,9 @@
*/
public AssetFileDescriptor openRawResourceFd(@RawRes int id)
throws NotFoundException {
- final TypedValue value = obtainTempTypedValue(id);
+ final TypedValue value = obtainTempTypedValue();
try {
- return mAssets.openNonAssetFd(value.assetCookie, value.string.toString());
- } catch (Exception e) {
- throw new NotFoundException("File " + value.string.toString() + " from drawable "
- + "resource ID #0x" + Integer.toHexString(id), e);
+ return mResourcesImpl.openRawResourceFd(id, value);
} finally {
releaseTempTypedValue(value);
}
@@ -1321,12 +1239,7 @@
*/
public void getValue(@AnyRes int id, TypedValue outValue, boolean resolveRefs)
throws NotFoundException {
- boolean found = mAssets.getResourceValue(id, 0, outValue, resolveRefs);
- if (found) {
- return;
- }
- throw new NotFoundException("Resource ID #0x"
- + Integer.toHexString(id));
+ mResourcesImpl.getValue(id, outValue, resolveRefs);
}
/**
@@ -1344,11 +1257,7 @@
*/
public void getValueForDensity(@AnyRes int id, int density, TypedValue outValue,
boolean resolveRefs) throws NotFoundException {
- boolean found = mAssets.getResourceValue(id, density, outValue, resolveRefs);
- if (found) {
- return;
- }
- throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id));
+ mResourcesImpl.getValueForDensity(id, density, outValue, resolveRefs);
}
/**
@@ -1373,12 +1282,7 @@
*/
public void getValue(String name, TypedValue outValue, boolean resolveRefs)
throws NotFoundException {
- int id = getIdentifier(name, "string", null);
- if (id != 0) {
- getValue(id, outValue, resolveRefs);
- return;
- }
- throw new NotFoundException("String resource name " + name);
+ mResourcesImpl.getValue(name, outValue, resolveRefs);
}
/**
@@ -1397,6 +1301,15 @@
* retrieve XML attributes with style and theme information applied.
*/
public final class Theme {
+ private ResourcesImpl.ThemeImpl mThemeImpl;
+
+ private Theme() {
+ }
+
+ void setImpl(ResourcesImpl.ThemeImpl impl) {
+ mThemeImpl = impl;
+ }
+
/**
* Place new attribute values into the theme. The style resource
* specified by <var>resid</var> will be retrieved from this Theme's
@@ -1415,12 +1328,7 @@
* if not already defined in the theme.
*/
public void applyStyle(int resId, boolean force) {
- synchronized (mKey) {
- AssetManager.applyThemeStyle(mTheme, resId, force);
-
- mThemeResId = resId;
- mKey.append(resId, force);
- }
+ mThemeImpl.applyStyle(resId, force);
}
/**
@@ -1433,14 +1341,7 @@
* @param other The existing Theme to copy from.
*/
public void setTo(Theme other) {
- synchronized (mKey) {
- synchronized (other.mKey) {
- AssetManager.copyTheme(mTheme, other.mTheme);
-
- mThemeResId = other.mThemeResId;
- mKey.setTo(other.getKey());
- }
- }
+ mThemeImpl.setTo(other.mThemeImpl);
}
/**
@@ -1463,13 +1364,7 @@
* @see #obtainStyledAttributes(AttributeSet, int[], int, int)
*/
public TypedArray obtainStyledAttributes(@StyleableRes int[] attrs) {
- synchronized (mKey) {
- final int len = attrs.length;
- final TypedArray array = TypedArray.obtain(Resources.this, len);
- array.mTheme = this;
- AssetManager.applyStyle(mTheme, 0, 0, 0, attrs, array.mData, array.mIndices);
- return array;
- }
+ return mThemeImpl.obtainStyledAttributes(this, null, attrs, 0, 0);
}
/**
@@ -1494,13 +1389,7 @@
*/
public TypedArray obtainStyledAttributes(@StyleRes int resId, @StyleableRes int[] attrs)
throws NotFoundException {
- synchronized (mKey) {
- final int len = attrs.length;
- final TypedArray array = TypedArray.obtain(Resources.this, len);
- array.mTheme = this;
- AssetManager.applyStyle(mTheme, 0, resId, 0, attrs, array.mData, array.mIndices);
- return array;
- }
+ return mThemeImpl.obtainStyledAttributes(this, null, attrs, 0, resId);
}
/**
@@ -1553,23 +1442,7 @@
*/
public TypedArray obtainStyledAttributes(AttributeSet set,
@StyleableRes int[] attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
- synchronized (mKey) {
- final int len = attrs.length;
- final TypedArray array = TypedArray.obtain(Resources.this, len);
-
- // XXX note that for now we only work with compiled XML files.
- // To support generic XML files we will need to manually parse
- // out the attributes from the XML file (applying type information
- // contained in the resources and such).
- final XmlBlock.Parser parser = (XmlBlock.Parser) set;
- AssetManager.applyStyle(mTheme, defStyleAttr, defStyleRes,
- parser != null ? parser.mParseState : 0,
- attrs, array.mData, array.mIndices);
- array.mTheme = this;
- array.mXml = parser;
-
- return array;
- }
+ return mThemeImpl.obtainStyledAttributes(this, set, attrs, defStyleAttr, defStyleRes);
}
/**
@@ -1588,20 +1461,7 @@
*/
@NonNull
public TypedArray resolveAttributes(@NonNull int[] values, @NonNull int[] attrs) {
- synchronized (mKey) {
- final int len = attrs.length;
- if (values == null || len != values.length) {
- throw new IllegalArgumentException(
- "Base attribute values must the same length as attrs");
- }
-
- final TypedArray array = TypedArray.obtain(Resources.this, len);
- AssetManager.resolveAttrs(mTheme, 0, 0, values, attrs, array.mData, array.mIndices);
- array.mTheme = this;
- array.mXml = null;
-
- return array;
- }
+ return mThemeImpl.resolveAttributes(this, values, attrs);
}
/**
@@ -1622,9 +1482,7 @@
* <var>outValue</var> is valid, else false.
*/
public boolean resolveAttribute(int resid, TypedValue outValue, boolean resolveRefs) {
- synchronized (mKey) {
- return mAssets.getThemeValue(mTheme, resid, outValue, resolveRefs);
- }
+ return mThemeImpl.resolveAttribute(resid, outValue, resolveRefs);
}
/**
@@ -1634,7 +1492,7 @@
* @hide
*/
public int[] getAllAttributes() {
- return mAssets.getStyleAttributes(getAppliedStyleResId());
+ return mThemeImpl.getAllAttributes();
}
/**
@@ -1670,11 +1528,7 @@
* @see ActivityInfo
*/
public int getChangingConfigurations() {
- synchronized (mKey) {
- final int nativeChangingConfig =
- AssetManager.getThemeChangingConfigurations(mTheme);
- return ActivityInfo.activityInfoConfigNativeToJava(nativeChangingConfig);
- }
+ return mThemeImpl.getChangingConfigurations();
}
/**
@@ -1685,43 +1539,23 @@
* @param prefix Text to prefix each line printed.
*/
public void dump(int priority, String tag, String prefix) {
- synchronized (mKey) {
- AssetManager.dumpTheme(mTheme, priority, tag, prefix);
- }
+ mThemeImpl.dump(priority, tag, prefix);
}
- @Override
- protected void finalize() throws Throwable {
- super.finalize();
- mAssets.releaseTheme(mTheme);
- }
-
- /*package*/ Theme() {
- mAssets = Resources.this.mAssets;
- mTheme = mAssets.createTheme();
- }
-
- /** Unique key for the series of styles applied to this theme. */
- private final ThemeKey mKey = new ThemeKey();
-
- @SuppressWarnings("hiding")
- private final AssetManager mAssets;
- private final long mTheme;
-
- /** Resource identifier for the theme. */
- private int mThemeResId = 0;
-
// Needed by layoutlib.
/*package*/ long getNativeTheme() {
- return mTheme;
+ return mThemeImpl.getNativeTheme();
}
/*package*/ int getAppliedStyleResId() {
- return mThemeResId;
+ return mThemeImpl.getAppliedStyleResId();
}
- /*package*/ ThemeKey getKey() {
- return mKey;
+ /**
+ * @hide
+ */
+ public ThemeKey getKey() {
+ return mThemeImpl.getKey();
}
private String getResourceNameFromHexString(String hexString) {
@@ -1729,7 +1563,7 @@
}
/**
- * Parses {@link #mKey} and returns a String array that holds pairs of
+ * Parses {@link #getKey()} and returns a String array that holds pairs of
* adjacent Theme data: resource name followed by whether or not it was
* forced, as specified by {@link #applyStyle(int, boolean)}.
*
@@ -1737,21 +1571,7 @@
*/
@ViewDebug.ExportedProperty(category = "theme", hasAdjacentMapping = true)
public String[] getTheme() {
- synchronized (mKey) {
- final int N = mKey.mCount;
- final String[] themes = new String[N * 2];
- for (int i = 0, j = N - 1; i < themes.length; i += 2, --j) {
- final int resId = mKey.mResId[j];
- final boolean forced = mKey.mForce[j];
- try {
- themes[i] = getResourceName(resId);
- } catch (NotFoundException e) {
- themes[i] = Integer.toHexString(i);
- }
- themes[i + 1] = forced ? "forced" : "not forced";
- }
- return themes;
- }
+ return mThemeImpl.getTheme();
}
/** @hide */
@@ -1772,16 +1592,7 @@
* @hide
*/
public void rebase() {
- synchronized (mKey) {
- AssetManager.clearTheme(mTheme);
-
- // Reapply the same styles in the same order.
- for (int i = 0; i < mKey.mCount; i++) {
- final int resId = mKey.mResId[i];
- final boolean force = mKey.mForce[i];
- AssetManager.applyThemeStyle(mTheme, resId, force);
- }
- }
+ mThemeImpl.rebase();
}
}
@@ -1870,7 +1681,9 @@
* @return Theme The newly created Theme container.
*/
public final Theme newTheme() {
- return new Theme();
+ Theme theme = new Theme();
+ theme.setImpl(mResourcesImpl.newThemeImpl());
+ return theme;
}
/**
@@ -1894,7 +1707,7 @@
// out the attributes from the XML file (applying type information
// contained in the resources and such).
XmlBlock.Parser parser = (XmlBlock.Parser)set;
- mAssets.retrieveAttributes(parser.mParseState, attrs,
+ mResourcesImpl.getAssets().retrieveAttributes(parser.mParseState, attrs,
array.mData, array.mIndices);
array.mXml = parser;
@@ -1905,151 +1718,16 @@
/**
* Store the newly updated configuration.
*/
- public void updateConfiguration(Configuration config,
- DisplayMetrics metrics) {
+ public void updateConfiguration(Configuration config, DisplayMetrics metrics) {
updateConfiguration(config, metrics, null);
}
/**
* @hide
*/
- public void updateConfiguration(Configuration config,
- DisplayMetrics metrics, CompatibilityInfo compat) {
- synchronized (mAccessLock) {
- if (false) {
- Slog.i(TAG, "**** Updating config of " + this + ": old config is "
- + mConfiguration + " old compat is " + mCompatibilityInfo);
- Slog.i(TAG, "**** Updating config of " + this + ": new config is "
- + config + " new compat is " + compat);
- }
- if (compat != null) {
- mCompatibilityInfo = compat;
- }
- if (metrics != null) {
- mMetrics.setTo(metrics);
- }
- // NOTE: We should re-arrange this code to create a Display
- // with the CompatibilityInfo that is used everywhere we deal
- // with the display in relation to this app, rather than
- // doing the conversion here. This impl should be okay because
- // we make sure to return a compatible display in the places
- // where there are public APIs to retrieve the display... but
- // it would be cleaner and more maintainble to just be
- // consistently dealing with a compatible display everywhere in
- // the framework.
- mCompatibilityInfo.applyToDisplayMetrics(mMetrics);
-
- final int configChanges = calcConfigChanges(config);
-
- LocaleList locales = mConfiguration.getLocales();
- if (locales.isEmpty()) {
- locales = LocaleList.getAdjustedDefault();
- mConfiguration.setLocales(locales);
- }
- if (mConfiguration.densityDpi != Configuration.DENSITY_DPI_UNDEFINED) {
- mMetrics.densityDpi = mConfiguration.densityDpi;
- mMetrics.density = mConfiguration.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
- }
- mMetrics.scaledDensity = mMetrics.density * mConfiguration.fontScale;
-
- final int width, height;
- if (mMetrics.widthPixels >= mMetrics.heightPixels) {
- width = mMetrics.widthPixels;
- height = mMetrics.heightPixels;
- } else {
- //noinspection SuspiciousNameCombination
- width = mMetrics.heightPixels;
- //noinspection SuspiciousNameCombination
- height = mMetrics.widthPixels;
- }
-
- final int keyboardHidden;
- if (mConfiguration.keyboardHidden == Configuration.KEYBOARDHIDDEN_NO
- && mConfiguration.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES) {
- keyboardHidden = Configuration.KEYBOARDHIDDEN_SOFT;
- } else {
- keyboardHidden = mConfiguration.keyboardHidden;
- }
-
- mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc,
- adjustLanguageTag(locales.get(0).toLanguageTag()),
- mConfiguration.orientation,
- mConfiguration.touchscreen,
- mConfiguration.densityDpi, mConfiguration.keyboard,
- keyboardHidden, mConfiguration.navigation, width, height,
- mConfiguration.smallestScreenWidthDp,
- mConfiguration.screenWidthDp, mConfiguration.screenHeightDp,
- mConfiguration.screenLayout, mConfiguration.uiMode,
- Build.VERSION.RESOURCES_SDK_INT);
-
- if (DEBUG_CONFIG) {
- Slog.i(TAG, "**** Updating config of " + this + ": final config is "
- + mConfiguration + " final compat is " + mCompatibilityInfo);
- }
-
- mDrawableCache.onConfigurationChange(configChanges);
- mColorDrawableCache.onConfigurationChange(configChanges);
- mComplexColorCache.onConfigurationChange(configChanges);
- mAnimatorCache.onConfigurationChange(configChanges);
- mStateListAnimatorCache.onConfigurationChange(configChanges);
-
- flushLayoutCache();
- }
- synchronized (sSync) {
- if (mPluralRule != null) {
- mPluralRule = PluralRules.forLocale(mConfiguration.getLocales().get(0));
- }
- }
- }
-
- /**
- * Called by ConfigurationBoundResourceCacheTest via reflection.
- */
- private int calcConfigChanges(Configuration config) {
- int configChanges = 0xfffffff;
- if (config != null) {
- mTmpConfig.setTo(config);
- int density = config.densityDpi;
- if (density == Configuration.DENSITY_DPI_UNDEFINED) {
- density = mMetrics.noncompatDensityDpi;
- }
-
- mCompatibilityInfo.applyToConfiguration(density, mTmpConfig);
-
- if (mTmpConfig.getLocales().isEmpty()) {
- mTmpConfig.setLocales(LocaleList.getDefault());
- }
- configChanges = mConfiguration.updateFrom(mTmpConfig);
- configChanges = ActivityInfo.activityInfoConfigToNative(configChanges);
- }
- return configChanges;
- }
-
- /**
- * {@code Locale.toLanguageTag} will transform the obsolete (and deprecated)
- * language codes "in", "ji" and "iw" to "id", "yi" and "he" respectively.
- *
- * All released versions of android prior to "L" used the deprecated language
- * tags, so we will need to support them for backwards compatibility.
- *
- * Note that this conversion needs to take place *after* the call to
- * {@code toLanguageTag} because that will convert all the deprecated codes to
- * the new ones, even if they're set manually.
- */
- private static String adjustLanguageTag(String languageTag) {
- final int separator = languageTag.indexOf('-');
- final String language;
- final String remainder;
-
- if (separator == -1) {
- language = languageTag;
- remainder = "";
- } else {
- language = languageTag.substring(0, separator);
- remainder = languageTag.substring(separator);
- }
-
- return Locale.adjustLanguageCode(language) + remainder;
+ public void updateConfiguration(Configuration config, DisplayMetrics metrics,
+ CompatibilityInfo compat) {
+ mResourcesImpl.updateConfiguration(config, metrics, compat);
}
/**
@@ -2074,9 +1752,7 @@
* @return The resource's current display metrics.
*/
public DisplayMetrics getDisplayMetrics() {
- if (DEBUG_CONFIG) Slog.v(TAG, "Returning DisplayMetrics: " + mMetrics.widthPixels
- + "x" + mMetrics.heightPixels + " " + mMetrics.density);
- return mMetrics;
+ return mResourcesImpl.getDisplayMetrics();
}
/**
@@ -2086,13 +1762,13 @@
* @return The resource's current configuration.
*/
public Configuration getConfiguration() {
- return mConfiguration;
+ return mResourcesImpl.getConfiguration();
}
/** @hide */
public Configuration[] getSizeConfigurations() {
- return mAssets.getSizeConfigurations();
- };
+ return mResourcesImpl.getSizeConfigurations();
+ }
/**
* Return the compatibility mode information for the application.
@@ -2102,7 +1778,7 @@
* @hide
*/
public CompatibilityInfo getCompatibilityInfo() {
- return mCompatibilityInfo;
+ return mResourcesImpl.getCompatibilityInfo();
}
/**
@@ -2111,8 +1787,7 @@
*/
public void setCompatibilityInfo(CompatibilityInfo ci) {
if (ci != null) {
- mCompatibilityInfo = ci;
- updateConfiguration(mConfiguration, mMetrics);
+ mResourcesImpl.updateConfiguration(null, null, ci);
}
}
@@ -2137,15 +1812,7 @@
* resource was found. (0 is not a valid resource ID.)
*/
public int getIdentifier(String name, String defType, String defPackage) {
- if (name == null) {
- throw new NullPointerException("name is null");
- }
- try {
- return Integer.parseInt(name);
- } catch (Exception e) {
- // Ignore
- }
- return mAssets.getResourceIdentifier(name, defType, defPackage);
+ return mResourcesImpl.getIdentifier(name, defType, defPackage);
}
/**
@@ -2172,10 +1839,7 @@
* @see #getResourceEntryName
*/
public String getResourceName(@AnyRes int resid) throws NotFoundException {
- String str = mAssets.getResourceName(resid);
- if (str != null) return str;
- throw new NotFoundException("Unable to find resource ID #0x"
- + Integer.toHexString(resid));
+ return mResourcesImpl.getResourceName(resid);
}
/**
@@ -2191,10 +1855,7 @@
* @see #getResourceName
*/
public String getResourcePackageName(@AnyRes int resid) throws NotFoundException {
- String str = mAssets.getResourcePackageName(resid);
- if (str != null) return str;
- throw new NotFoundException("Unable to find resource ID #0x"
- + Integer.toHexString(resid));
+ return mResourcesImpl.getResourcePackageName(resid);
}
/**
@@ -2210,10 +1871,7 @@
* @see #getResourceName
*/
public String getResourceTypeName(@AnyRes int resid) throws NotFoundException {
- String str = mAssets.getResourceTypeName(resid);
- if (str != null) return str;
- throw new NotFoundException("Unable to find resource ID #0x"
- + Integer.toHexString(resid));
+ return mResourcesImpl.getResourceTypeName(resid);
}
/**
@@ -2229,10 +1887,7 @@
* @see #getResourceName
*/
public String getResourceEntryName(@AnyRes int resid) throws NotFoundException {
- String str = mAssets.getResourceEntryName(resid);
- if (str != null) return str;
- throw new NotFoundException("Unable to find resource ID #0x"
- + Integer.toHexString(resid));
+ return mResourcesImpl.getResourceEntryName(resid);
}
/**
@@ -2335,7 +1990,7 @@
* Retrieve underlying AssetManager storage for these resources.
*/
public final AssetManager getAssets() {
- return mAssets;
+ return mResourcesImpl.getAssets();
}
/**
@@ -2344,19 +1999,7 @@
* tools.
*/
public final void flushLayoutCache() {
- synchronized (mCachedXmlBlocks) {
- Arrays.fill(mCachedXmlBlockCookies, 0);
- Arrays.fill(mCachedXmlBlockFiles, null);
-
- final XmlBlock[] cachedXmlBlocks = mCachedXmlBlocks;
- for (int i = 0; i < XML_BLOCK_CACHE_SIZE; i++) {
- final XmlBlock oldBlock = cachedXmlBlocks[i];
- if (oldBlock != null) {
- oldBlock.close();
- }
- }
- Arrays.fill(cachedXmlBlocks, null);
- }
+ mResourcesImpl.flushLayoutCache();
}
/**
@@ -2365,15 +2008,7 @@
* {@hide}
*/
public final void startPreloading() {
- synchronized (sSync) {
- if (sPreloaded) {
- throw new IllegalStateException("Resources already preloaded");
- }
- sPreloaded = true;
- mPreloading = true;
- mConfiguration.densityDpi = DisplayMetrics.DENSITY_DEVICE;
- updateConfiguration(null, null);
- }
+ mResourcesImpl.startPreloading();
}
/**
@@ -2381,441 +2016,14 @@
* to normal Resources operation.
*/
public final void finishPreloading() {
- if (mPreloading) {
- mPreloading = false;
- flushLayoutCache();
- }
+ mResourcesImpl.finishPreloading();
}
/**
* @hide
*/
public LongSparseArray<ConstantState> getPreloadedDrawables() {
- return sPreloadedDrawables[0];
- }
-
- private boolean verifyPreloadConfig(int changingConfigurations, int allowVarying,
- int resourceId, String name) {
- // We allow preloading of resources even if they vary by font scale (which
- // doesn't impact resource selection) or density (which we handle specially by
- // simply turning off all preloading), as well as any other configs specified
- // by the caller.
- if (((changingConfigurations&~(ActivityInfo.CONFIG_FONT_SCALE |
- ActivityInfo.CONFIG_DENSITY)) & ~allowVarying) != 0) {
- String resName;
- try {
- resName = getResourceName(resourceId);
- } catch (NotFoundException e) {
- resName = "?";
- }
- // This should never happen in production, so we should log a
- // warning even if we're not debugging.
- Log.w(TAG, "Preloaded " + name + " resource #0x"
- + Integer.toHexString(resourceId)
- + " (" + resName + ") that varies with configuration!!");
- return false;
- }
- if (TRACE_FOR_PRELOAD) {
- String resName;
- try {
- resName = getResourceName(resourceId);
- } catch (NotFoundException e) {
- resName = "?";
- }
- Log.w(TAG, "Preloading " + name + " resource #0x"
- + Integer.toHexString(resourceId)
- + " (" + resName + ")");
- }
- return true;
- }
-
- @Nullable
- Drawable loadDrawable(TypedValue value, int id, Theme theme) throws NotFoundException {
- return loadDrawable(value, id, theme, true);
- }
-
- @Nullable
- Drawable loadDrawable(TypedValue value, int id, Theme theme, boolean useCache)
- throws NotFoundException {
- try {
- if (TRACE_FOR_PRELOAD) {
- // Log only framework resources
- if ((id >>> 24) == 0x1) {
- final String name = getResourceName(id);
- if (name != null) {
- Log.d("PreloadDrawable", name);
- }
- }
- }
-
- final boolean isColorDrawable;
- final DrawableCache caches;
- final long key;
- if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT
- && value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
- isColorDrawable = true;
- caches = mColorDrawableCache;
- key = value.data;
- } else {
- isColorDrawable = false;
- caches = mDrawableCache;
- key = (((long) value.assetCookie) << 32) | value.data;
- }
-
- // First, check whether we have a cached version of this drawable
- // that was inflated against the specified theme. Skip the cache if
- // we're currently preloading or we're not using the cache.
- if (!mPreloading && useCache) {
- final Drawable cachedDrawable = caches.getInstance(key, theme);
- if (cachedDrawable != null) {
- return cachedDrawable;
- }
- }
-
- // Next, check preloaded drawables. Preloaded drawables may contain
- // unresolved theme attributes.
- final ConstantState cs;
- if (isColorDrawable) {
- cs = sPreloadedColorDrawables.get(key);
- } else {
- cs = sPreloadedDrawables[mConfiguration.getLayoutDirection()].get(key);
- }
-
- Drawable dr;
- if (cs != null) {
- dr = cs.newDrawable(this);
- } else if (isColorDrawable) {
- dr = new ColorDrawable(value.data);
- } else {
- dr = loadDrawableForCookie(value, id, null);
- }
-
- // Determine if the drawable has unresolved theme attributes. If it
- // does, we'll need to apply a theme and store it in a theme-specific
- // cache.
- final boolean canApplyTheme = dr != null && dr.canApplyTheme();
- if (canApplyTheme && theme != null) {
- dr = dr.mutate();
- dr.applyTheme(theme);
- dr.clearMutated();
- }
-
- // If we were able to obtain a drawable, store it in the appropriate
- // cache: preload, not themed, null theme, or theme-specific. Don't
- // pollute the cache with drawables loaded from a foreign density.
- if (dr != null && useCache) {
- dr.setChangingConfigurations(value.changingConfigurations);
- cacheDrawable(value, isColorDrawable, caches, theme, canApplyTheme, key, dr);
- }
-
- return dr;
- } catch (Exception e) {
- String name;
- try {
- name = getResourceName(id);
- } catch (NotFoundException e2) {
- name = "(missing name)";
- }
-
- // The target drawable might fail to load for any number of
- // reasons, but we always want to include the resource name.
- // Since the client already expects this method to throw a
- // NotFoundException, just throw one of those.
- final NotFoundException nfe = new NotFoundException("Drawable " + name
- + " with resource ID #0x" + Integer.toHexString(id), e);
- nfe.setStackTrace(new StackTraceElement[0]);
- throw nfe;
- }
- }
-
- private void cacheDrawable(TypedValue value, boolean isColorDrawable, DrawableCache caches,
- Theme theme, boolean usesTheme, long key, Drawable dr) {
- final ConstantState cs = dr.getConstantState();
- if (cs == null) {
- return;
- }
-
- if (mPreloading) {
- final int changingConfigs = cs.getChangingConfigurations();
- if (isColorDrawable) {
- if (verifyPreloadConfig(changingConfigs, 0, value.resourceId, "drawable")) {
- sPreloadedColorDrawables.put(key, cs);
- }
- } else {
- if (verifyPreloadConfig(
- changingConfigs, LAYOUT_DIR_CONFIG, value.resourceId, "drawable")) {
- if ((changingConfigs & LAYOUT_DIR_CONFIG) == 0) {
- // If this resource does not vary based on layout direction,
- // we can put it in all of the preload maps.
- sPreloadedDrawables[0].put(key, cs);
- sPreloadedDrawables[1].put(key, cs);
- } else {
- // Otherwise, only in the layout dir we loaded it for.
- sPreloadedDrawables[mConfiguration.getLayoutDirection()].put(key, cs);
- }
- }
- }
- } else {
- synchronized (mAccessLock) {
- caches.put(key, theme, cs, usesTheme);
- }
- }
- }
-
- /**
- * Loads a drawable from XML or resources stream.
- */
- private Drawable loadDrawableForCookie(TypedValue value, int id, Theme theme) {
- if (value.string == null) {
- throw new NotFoundException("Resource \"" + getResourceName(id) + "\" ("
- + Integer.toHexString(id) + ") is not a Drawable (color or path): " + value);
- }
-
- final String file = value.string.toString();
-
- if (TRACE_FOR_MISS_PRELOAD) {
- // Log only framework resources
- if ((id >>> 24) == 0x1) {
- final String name = getResourceName(id);
- if (name != null) {
- Log.d(TAG, "Loading framework drawable #" + Integer.toHexString(id)
- + ": " + name + " at " + file);
- }
- }
- }
-
- if (DEBUG_LOAD) {
- Log.v(TAG, "Loading drawable for cookie " + value.assetCookie + ": " + file);
- }
-
- final Drawable dr;
-
- Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
- try {
- if (file.endsWith(".xml")) {
- final XmlResourceParser rp = loadXmlResourceParser(
- file, id, value.assetCookie, "drawable");
- dr = Drawable.createFromXml(this, rp, theme);
- rp.close();
- } else {
- final InputStream is = mAssets.openNonAsset(
- value.assetCookie, file, AssetManager.ACCESS_STREAMING);
- dr = Drawable.createFromResourceStream(this, value, is, file, null);
- is.close();
- }
- } catch (Exception e) {
- Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
- final NotFoundException rnf = new NotFoundException(
- "File " + file + " from drawable resource ID #0x" + Integer.toHexString(id));
- rnf.initCause(e);
- throw rnf;
- }
- Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
-
- return dr;
- }
-
- /**
- * Given the value and id, we can get the XML filename as in value.data, based on that, we
- * first try to load CSL from the cache. If not found, try to get from the constant state.
- * Last, parse the XML and generate the CSL.
- */
- private ComplexColor loadComplexColorFromName(Theme theme, TypedValue value, int id) {
- final long key = (((long) value.assetCookie) << 32) | value.data;
- final ConfigurationBoundResourceCache<ComplexColor> cache = mComplexColorCache;
- ComplexColor complexColor = cache.getInstance(key, theme);
- if (complexColor != null) {
- return complexColor;
- }
-
- final android.content.res.ConstantState<ComplexColor> factory =
- sPreloadedComplexColors.get(key);
-
- if (factory != null) {
- complexColor = factory.newInstance(this, theme);
- }
- if (complexColor == null) {
- complexColor = loadComplexColorForCookie(value, id, theme);
- }
-
- if (complexColor != null) {
- if (mPreloading) {
- if (verifyPreloadConfig(value.changingConfigurations, 0, value.resourceId,
- "color")) {
- sPreloadedComplexColors.put(key, complexColor.getConstantState());
- }
- } else {
- cache.put(key, theme, complexColor.getConstantState());
- }
- }
- return complexColor;
- }
-
- @Nullable
- public ComplexColor loadComplexColor(@NonNull TypedValue value, int id, Theme theme) {
- if (TRACE_FOR_PRELOAD) {
- // Log only framework resources
- if ((id >>> 24) == 0x1) {
- final String name = getResourceName(id);
- if (name != null) android.util.Log.d("loadComplexColor", name);
- }
- }
-
- final long key = (((long) value.assetCookie) << 32) | value.data;
-
- // Handle inline color definitions.
- if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT
- && value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
- return getColorStateListFromInt(value, key);
- }
-
- final String file = value.string.toString();
-
- ComplexColor complexColor;
- if (file.endsWith(".xml")) {
- try {
- complexColor = loadComplexColorFromName(theme, value, id);
- } catch (Exception e) {
- final NotFoundException rnf = new NotFoundException(
- "File " + file + " from complex color resource ID #0x"
- + Integer.toHexString(id));
- rnf.initCause(e);
- throw rnf;
- }
- } else {
- throw new NotFoundException(
- "File " + file + " from drawable resource ID #0x"
- + Integer.toHexString(id) + ": .xml extension required");
- }
-
- return complexColor;
- }
-
- @Nullable
- ColorStateList loadColorStateList(TypedValue value, int id, Theme theme)
- throws NotFoundException {
- if (TRACE_FOR_PRELOAD) {
- // Log only framework resources
- if ((id >>> 24) == 0x1) {
- final String name = getResourceName(id);
- if (name != null) android.util.Log.d("PreloadColorStateList", name);
- }
- }
-
- final long key = (((long) value.assetCookie) << 32) | value.data;
-
- // Handle inline color definitions.
- if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT
- && value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
- return getColorStateListFromInt(value, key);
- }
-
- ComplexColor complexColor = loadComplexColorFromName(theme, value, id);
- if (complexColor != null && complexColor instanceof ColorStateList) {
- return (ColorStateList) complexColor;
- }
-
- throw new NotFoundException(
- "Can't find ColorStateList from drawable resource ID #0x"
- + Integer.toHexString(id));
- }
-
- @NonNull
- private ColorStateList getColorStateListFromInt(@NonNull TypedValue value, long key) {
- ColorStateList csl;
- final android.content.res.ConstantState<ComplexColor> factory =
- sPreloadedComplexColors.get(key);
- if (factory != null) {
- return (ColorStateList) factory.newInstance();
- }
-
- csl = ColorStateList.valueOf(value.data);
-
- if (mPreloading) {
- if (verifyPreloadConfig(value.changingConfigurations, 0, value.resourceId,
- "color")) {
- sPreloadedComplexColors.put(key, csl.getConstantState());
- }
- }
-
- return csl;
- }
-
- /**
- * Load a ComplexColor based on the XML file content. The result can be a GradientColor or
- * ColorStateList. Note that pure color will be wrapped into a ColorStateList.
- *
- * We deferred the parser creation to this function b/c we need to differentiate b/t gradient
- * and selector tag.
- *
- * @return a ComplexColor (GradientColor or ColorStateList) based on the XML file content.
- */
- @Nullable
- private ComplexColor loadComplexColorForCookie(TypedValue value, int id, Theme theme) {
- if (value.string == null) {
- throw new UnsupportedOperationException(
- "Can't convert to ComplexColor: type=0x" + value.type);
- }
-
- final String file = value.string.toString();
-
- if (TRACE_FOR_MISS_PRELOAD) {
- // Log only framework resources
- if ((id >>> 24) == 0x1) {
- final String name = getResourceName(id);
- if (name != null) {
- Log.d(TAG, "Loading framework ComplexColor #" + Integer.toHexString(id)
- + ": " + name + " at " + file);
- }
- }
- }
-
- if (DEBUG_LOAD) {
- Log.v(TAG, "Loading ComplexColor for cookie " + value.assetCookie + ": " + file);
- }
-
- ComplexColor complexColor = null;
-
- Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
- if (file.endsWith(".xml")) {
- try {
- final XmlResourceParser parser = loadXmlResourceParser(
- file, id, value.assetCookie, "ComplexColor");
-
- final AttributeSet attrs = Xml.asAttributeSet(parser);
- int type;
- while ((type = parser.next()) != XmlPullParser.START_TAG
- && type != XmlPullParser.END_DOCUMENT) {
- // Seek parser to start tag.
- }
- if (type != XmlPullParser.START_TAG) {
- throw new XmlPullParserException("No start tag found");
- }
-
- final String name = parser.getName();
- if (name.equals("gradient")) {
- complexColor = GradientColor.createFromXmlInner(this, parser, attrs, theme);
- } else if (name.equals("selector")) {
- complexColor = ColorStateList.createFromXmlInner(this, parser, attrs, theme);
- }
- parser.close();
- } catch (Exception e) {
- Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
- final NotFoundException rnf = new NotFoundException(
- "File " + file + " from ComplexColor resource ID #0x"
- + Integer.toHexString(id));
- rnf.initCause(e);
- throw rnf;
- }
- } else {
- Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
- throw new NotFoundException(
- "File " + file + " from drawable resource ID #0x"
- + Integer.toHexString(id) + ": .xml extension required");
- }
- Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
-
- return complexColor;
+ return mResourcesImpl.getPreloadedDrawables();
}
/**
@@ -2829,10 +2037,12 @@
@NonNull
XmlResourceParser loadXmlResourceParser(@AnyRes int id, @NonNull String type)
throws NotFoundException {
- final TypedValue value = obtainTempTypedValue(id);
+ final TypedValue value = obtainTempTypedValue();
try {
+ final ResourcesImpl impl = mResourcesImpl;
+ impl.getValue(id, value, true);
if (value.type == TypedValue.TYPE_STRING) {
- return loadXmlResourceParser(value.string.toString(), id,
+ return impl.loadXmlResourceParser(value.string.toString(), id,
value.assetCookie, type);
}
throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)
@@ -2853,49 +2063,9 @@
* @throws NotFoundException if the file could not be loaded
*/
@NonNull
- XmlResourceParser loadXmlResourceParser(@NonNull String file, @AnyRes int id,
- int assetCookie, @NonNull String type) throws NotFoundException {
- if (id != 0) {
- try {
- synchronized (mCachedXmlBlocks) {
- final int[] cachedXmlBlockCookies = mCachedXmlBlockCookies;
- final String[] cachedXmlBlockFiles = mCachedXmlBlockFiles;
- final XmlBlock[] cachedXmlBlocks = mCachedXmlBlocks;
- // First see if this block is in our cache.
- final int num = cachedXmlBlockFiles.length;
- for (int i = 0; i < num; i++) {
- if (cachedXmlBlockCookies[i] == assetCookie && cachedXmlBlockFiles[i] != null
- && cachedXmlBlockFiles[i].equals(file)) {
- return cachedXmlBlocks[i].newParser();
- }
- }
-
- // Not in the cache, create a new block and put it at
- // the next slot in the cache.
- final XmlBlock block = mAssets.openXmlBlockAsset(assetCookie, file);
- if (block != null) {
- final int pos = (mLastCachedXmlBlockIndex + 1) % num;
- mLastCachedXmlBlockIndex = pos;
- final XmlBlock oldBlock = cachedXmlBlocks[pos];
- if (oldBlock != null) {
- oldBlock.close();
- }
- cachedXmlBlockCookies[pos] = assetCookie;
- cachedXmlBlockFiles[pos] = file;
- cachedXmlBlocks[pos] = block;
- return block.newParser();
- }
- }
- } catch (Exception e) {
- final NotFoundException rnf = new NotFoundException("File " + file
- + " from xml type " + type + " resource ID #0x" + Integer.toHexString(id));
- rnf.initCause(e);
- throw rnf;
- }
- }
-
- throw new NotFoundException("File " + file + " from xml type " + type + " resource ID #0x"
- + Integer.toHexString(id));
+ XmlResourceParser loadXmlResourceParser(String file, int id, int assetCookie,
+ String type) throws NotFoundException {
+ return mResourcesImpl.loadXmlResourceParser(file, id, assetCookie, type);
}
/**
@@ -2911,16 +2081,4 @@
}
return theme.obtainStyledAttributes(set, attrs, 0, 0);
}
-
- private Resources() {
- mAssets = AssetManager.getSystem();
- mClassLoader = ClassLoader.getSystemClassLoader();
- // NOTE: Intentionally leaving this uninitialized (all values set
- // to zero), so that anyone who tries to do something that requires
- // metrics will get a very wrong value.
- mConfiguration.setToDefaults();
- mMetrics.setToDefaults();
- updateConfiguration(null, null);
- mAssets.ensureStringBlocks();
- }
}
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
new file mode 100644
index 0000000..2ffd372
--- /dev/null
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -0,0 +1,1140 @@
+/*
+ * 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.content.res;
+
+import android.animation.Animator;
+import android.animation.StateListAnimator;
+import android.annotation.AnyRes;
+import android.annotation.AttrRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.PluralsRes;
+import android.annotation.RawRes;
+import android.annotation.StyleRes;
+import android.annotation.StyleableRes;
+import android.content.pm.ActivityInfo;
+import android.content.res.Resources.NotFoundException;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.icu.text.PluralRules;
+import android.os.Build;
+import android.os.Trace;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.LocaleList;
+import android.util.Log;
+import android.util.LongSparseArray;
+import android.util.Slog;
+import android.util.TypedValue;
+import android.util.Xml;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Locale;
+
+/**
+ * @hide
+ */
+public class ResourcesImpl {
+ static final String TAG = "Resources";
+
+ private static final boolean DEBUG_LOAD = false;
+ private static final boolean DEBUG_CONFIG = false;
+ private static final boolean TRACE_FOR_PRELOAD = false;
+ private static final boolean TRACE_FOR_MISS_PRELOAD = false;
+
+ private static final int LAYOUT_DIR_CONFIG = ActivityInfo.activityInfoConfigToNative(
+ ActivityInfo.CONFIG_LAYOUT_DIRECTION);
+
+ private static final int ID_OTHER = 0x01000004;
+
+ private static final Object sSync = new Object();
+
+ private static boolean sPreloaded;
+ private boolean mPreloading;
+
+ // Information about preloaded resources. Note that they are not
+ // protected by a lock, because while preloading in zygote we are all
+ // single-threaded, and after that these are immutable.
+ private static final LongSparseArray<Drawable.ConstantState>[] sPreloadedDrawables;
+ private static final LongSparseArray<Drawable.ConstantState> sPreloadedColorDrawables
+ = new LongSparseArray<>();
+ private static final LongSparseArray<android.content.res.ConstantState<ComplexColor>>
+ sPreloadedComplexColors = new LongSparseArray<>();
+
+ /** Lock object used to protect access to caches and configuration. */
+ private final Object mAccessLock = new Object();
+
+ // These are protected by mAccessLock.
+ private final Configuration mTmpConfig = new Configuration();
+ private final DrawableCache mDrawableCache = new DrawableCache();
+ private final DrawableCache mColorDrawableCache = new DrawableCache();
+ private final ConfigurationBoundResourceCache<ComplexColor> mComplexColorCache =
+ new ConfigurationBoundResourceCache<>();
+ private final ConfigurationBoundResourceCache<Animator> mAnimatorCache =
+ new ConfigurationBoundResourceCache<>();
+ private final ConfigurationBoundResourceCache<StateListAnimator> mStateListAnimatorCache =
+ new ConfigurationBoundResourceCache<>();
+
+ /** Size of the cyclical cache used to map XML files to blocks. */
+ private static final int XML_BLOCK_CACHE_SIZE = 4;
+
+ // Cyclical cache used for recently-accessed XML files.
+ private int mLastCachedXmlBlockIndex = -1;
+ private final int[] mCachedXmlBlockCookies = new int[XML_BLOCK_CACHE_SIZE];
+ private final String[] mCachedXmlBlockFiles = new String[XML_BLOCK_CACHE_SIZE];
+ private final XmlBlock[] mCachedXmlBlocks = new XmlBlock[XML_BLOCK_CACHE_SIZE];
+
+
+ final AssetManager mAssets;
+ final DisplayMetrics mMetrics = new DisplayMetrics();
+
+ private PluralRules mPluralRule;
+
+ private final Configuration mConfiguration = new Configuration();
+ private CompatibilityInfo mCompatibilityInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
+
+ static {
+ sPreloadedDrawables = new LongSparseArray[2];
+ sPreloadedDrawables[0] = new LongSparseArray<>();
+ sPreloadedDrawables[1] = new LongSparseArray<>();
+ }
+
+ /**
+ * Creates a new ResourcesImpl object with CompatibilityInfo.
+ *
+ * @param assets Previously created AssetManager.
+ * @param metrics Current display metrics to consider when
+ * selecting/computing resource values.
+ * @param config Desired device configuration to consider when
+ * selecting/computing resource values (optional).
+ * @param compatInfo this resource's compatibility info. Must not be null.
+ */
+ public ResourcesImpl(AssetManager assets, DisplayMetrics metrics, Configuration config,
+ CompatibilityInfo compatInfo) {
+ mAssets = assets;
+ mMetrics.setToDefaults();
+ updateConfiguration(config, metrics, compatInfo);
+ mAssets.ensureStringBlocks();
+ }
+
+ AssetManager getAssets() {
+ return mAssets;
+ }
+
+ DisplayMetrics getDisplayMetrics() {
+ if (DEBUG_CONFIG) Slog.v(TAG, "Returning DisplayMetrics: " + mMetrics.widthPixels
+ + "x" + mMetrics.heightPixels + " " + mMetrics.density);
+ return mMetrics;
+ }
+
+ Configuration getConfiguration() {
+ return mConfiguration;
+ }
+
+ Configuration[] getSizeConfigurations() {
+ return mAssets.getSizeConfigurations();
+ }
+
+ CompatibilityInfo getCompatibilityInfo() {
+ return mCompatibilityInfo;
+ }
+
+ private PluralRules getPluralRule() {
+ synchronized (sSync) {
+ if (mPluralRule == null) {
+ mPluralRule = PluralRules.forLocale(mConfiguration.getLocales().get(0));
+ }
+ return mPluralRule;
+ }
+ }
+
+ void getValue(@AnyRes int id, TypedValue outValue, boolean resolveRefs)
+ throws NotFoundException {
+ boolean found = mAssets.getResourceValue(id, 0, outValue, resolveRefs);
+ if (found) {
+ return;
+ }
+ throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id));
+ }
+
+ void getValueForDensity(@AnyRes int id, int density, TypedValue outValue,
+ boolean resolveRefs) throws NotFoundException {
+ boolean found = mAssets.getResourceValue(id, density, outValue, resolveRefs);
+ if (found) {
+ return;
+ }
+ throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id));
+ }
+
+ void getValue(String name, TypedValue outValue, boolean resolveRefs)
+ throws NotFoundException {
+ int id = getIdentifier(name, "string", null);
+ if (id != 0) {
+ getValue(id, outValue, resolveRefs);
+ return;
+ }
+ throw new NotFoundException("String resource name " + name);
+ }
+
+ int getIdentifier(String name, String defType, String defPackage) {
+ if (name == null) {
+ throw new NullPointerException("name is null");
+ }
+ try {
+ return Integer.parseInt(name);
+ } catch (Exception e) {
+ // Ignore
+ }
+ return mAssets.getResourceIdentifier(name, defType, defPackage);
+ }
+
+ @NonNull
+ String getResourceName(@AnyRes int resid) throws NotFoundException {
+ String str = mAssets.getResourceName(resid);
+ if (str != null) return str;
+ throw new NotFoundException("Unable to find resource ID #0x"
+ + Integer.toHexString(resid));
+ }
+
+ @NonNull
+ String getResourcePackageName(@AnyRes int resid) throws NotFoundException {
+ String str = mAssets.getResourcePackageName(resid);
+ if (str != null) return str;
+ throw new NotFoundException("Unable to find resource ID #0x"
+ + Integer.toHexString(resid));
+ }
+
+ @NonNull
+ String getResourceTypeName(@AnyRes int resid) throws NotFoundException {
+ String str = mAssets.getResourceTypeName(resid);
+ if (str != null) return str;
+ throw new NotFoundException("Unable to find resource ID #0x"
+ + Integer.toHexString(resid));
+ }
+
+ @NonNull
+ String getResourceEntryName(@AnyRes int resid) throws NotFoundException {
+ String str = mAssets.getResourceEntryName(resid);
+ if (str != null) return str;
+ throw new NotFoundException("Unable to find resource ID #0x"
+ + Integer.toHexString(resid));
+ }
+
+ @NonNull
+ CharSequence getQuantityText(@PluralsRes int id, int quantity) throws NotFoundException {
+ PluralRules rule = getPluralRule();
+ CharSequence res = mAssets.getResourceBagText(id,
+ attrForQuantityCode(rule.select(quantity)));
+ if (res != null) {
+ return res;
+ }
+ res = mAssets.getResourceBagText(id, ID_OTHER);
+ if (res != null) {
+ return res;
+ }
+ throw new NotFoundException("Plural resource ID #0x" + Integer.toHexString(id)
+ + " quantity=" + quantity
+ + " item=" + rule.select(quantity));
+ }
+
+ private static int attrForQuantityCode(String quantityCode) {
+ switch (quantityCode) {
+ case PluralRules.KEYWORD_ZERO: return 0x01000005;
+ case PluralRules.KEYWORD_ONE: return 0x01000006;
+ case PluralRules.KEYWORD_TWO: return 0x01000007;
+ case PluralRules.KEYWORD_FEW: return 0x01000008;
+ case PluralRules.KEYWORD_MANY: return 0x01000009;
+ default: return ID_OTHER;
+ }
+ }
+
+ @NonNull
+ AssetFileDescriptor openRawResourceFd(@RawRes int id, TypedValue tempValue)
+ throws NotFoundException {
+ getValue(id, tempValue, true);
+ try {
+ return mAssets.openNonAssetFd(tempValue.assetCookie, tempValue.string.toString());
+ } catch (Exception e) {
+ throw new NotFoundException("File " + tempValue.string.toString() + " from drawable "
+ + "resource ID #0x" + Integer.toHexString(id), e);
+ }
+ }
+
+ @NonNull
+ InputStream openRawResource(@RawRes int id, TypedValue value) throws NotFoundException {
+ getValue(id, value, true);
+ try {
+ return mAssets.openNonAsset(value.assetCookie, value.string.toString(),
+ AssetManager.ACCESS_STREAMING);
+ } catch (Exception e) {
+ NotFoundException rnf = new NotFoundException("File " + value.string.toString() +
+ " from drawable resource ID #0x" + Integer.toHexString(id));
+ rnf.initCause(e);
+ throw rnf;
+ }
+ }
+
+ ConfigurationBoundResourceCache<Animator> getAnimatorCache() {
+ return mAnimatorCache;
+ }
+
+ ConfigurationBoundResourceCache<StateListAnimator> getStateListAnimatorCache() {
+ return mStateListAnimatorCache;
+ }
+
+ void updateConfiguration(Configuration config, DisplayMetrics metrics,
+ CompatibilityInfo compat) {
+ synchronized (mAccessLock) {
+ if (false) {
+ Slog.i(TAG, "**** Updating config of " + this + ": old config is "
+ + mConfiguration + " old compat is " + mCompatibilityInfo);
+ Slog.i(TAG, "**** Updating config of " + this + ": new config is "
+ + config + " new compat is " + compat);
+ }
+ if (compat != null) {
+ mCompatibilityInfo = compat;
+ }
+ if (metrics != null) {
+ mMetrics.setTo(metrics);
+ }
+ // NOTE: We should re-arrange this code to create a Display
+ // with the CompatibilityInfo that is used everywhere we deal
+ // with the display in relation to this app, rather than
+ // doing the conversion here. This impl should be okay because
+ // we make sure to return a compatible display in the places
+ // where there are public APIs to retrieve the display... but
+ // it would be cleaner and more maintainble to just be
+ // consistently dealing with a compatible display everywhere in
+ // the framework.
+ mCompatibilityInfo.applyToDisplayMetrics(mMetrics);
+
+ final int configChanges = calcConfigChanges(config);
+
+ LocaleList locales = mConfiguration.getLocales();
+ if (locales.isEmpty()) {
+ locales = LocaleList.getAdjustedDefault();
+ mConfiguration.setLocales(locales);
+ }
+ if (mConfiguration.densityDpi != Configuration.DENSITY_DPI_UNDEFINED) {
+ mMetrics.densityDpi = mConfiguration.densityDpi;
+ mMetrics.density = mConfiguration.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
+ }
+ mMetrics.scaledDensity = mMetrics.density * mConfiguration.fontScale;
+
+ final int width, height;
+ if (mMetrics.widthPixels >= mMetrics.heightPixels) {
+ width = mMetrics.widthPixels;
+ height = mMetrics.heightPixels;
+ } else {
+ //noinspection SuspiciousNameCombination
+ width = mMetrics.heightPixels;
+ //noinspection SuspiciousNameCombination
+ height = mMetrics.widthPixels;
+ }
+
+ final int keyboardHidden;
+ if (mConfiguration.keyboardHidden == Configuration.KEYBOARDHIDDEN_NO
+ && mConfiguration.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES) {
+ keyboardHidden = Configuration.KEYBOARDHIDDEN_SOFT;
+ } else {
+ keyboardHidden = mConfiguration.keyboardHidden;
+ }
+
+ mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc,
+ adjustLanguageTag(locales.get(0).toLanguageTag()),
+ mConfiguration.orientation,
+ mConfiguration.touchscreen,
+ mConfiguration.densityDpi, mConfiguration.keyboard,
+ keyboardHidden, mConfiguration.navigation, width, height,
+ mConfiguration.smallestScreenWidthDp,
+ mConfiguration.screenWidthDp, mConfiguration.screenHeightDp,
+ mConfiguration.screenLayout, mConfiguration.uiMode,
+ Build.VERSION.RESOURCES_SDK_INT);
+
+ if (DEBUG_CONFIG) {
+ Slog.i(TAG, "**** Updating config of " + this + ": final config is "
+ + mConfiguration + " final compat is " + mCompatibilityInfo);
+ }
+
+ mDrawableCache.onConfigurationChange(configChanges);
+ mColorDrawableCache.onConfigurationChange(configChanges);
+ mComplexColorCache.onConfigurationChange(configChanges);
+ mAnimatorCache.onConfigurationChange(configChanges);
+ mStateListAnimatorCache.onConfigurationChange(configChanges);
+
+ flushLayoutCache();
+ }
+ synchronized (sSync) {
+ if (mPluralRule != null) {
+ mPluralRule = PluralRules.forLocale(mConfiguration.getLocales().get(0));
+ }
+ }
+ }
+
+ /**
+ * Called by ConfigurationBoundResourceCacheTest via reflection.
+ */
+ private int calcConfigChanges(Configuration config) {
+ int configChanges = 0xfffffff;
+ if (config != null) {
+ mTmpConfig.setTo(config);
+ int density = config.densityDpi;
+ if (density == Configuration.DENSITY_DPI_UNDEFINED) {
+ density = mMetrics.noncompatDensityDpi;
+ }
+
+ mCompatibilityInfo.applyToConfiguration(density, mTmpConfig);
+
+ if (mTmpConfig.getLocales().isEmpty()) {
+ mTmpConfig.setLocales(LocaleList.getDefault());
+ }
+ configChanges = mConfiguration.updateFrom(mTmpConfig);
+ configChanges = ActivityInfo.activityInfoConfigToNative(configChanges);
+ }
+ return configChanges;
+ }
+
+ /**
+ * {@code Locale.toLanguageTag} will transform the obsolete (and deprecated)
+ * language codes "in", "ji" and "iw" to "id", "yi" and "he" respectively.
+ *
+ * All released versions of android prior to "L" used the deprecated language
+ * tags, so we will need to support them for backwards compatibility.
+ *
+ * Note that this conversion needs to take place *after* the call to
+ * {@code toLanguageTag} because that will convert all the deprecated codes to
+ * the new ones, even if they're set manually.
+ */
+ private static String adjustLanguageTag(String languageTag) {
+ final int separator = languageTag.indexOf('-');
+ final String language;
+ final String remainder;
+
+ if (separator == -1) {
+ language = languageTag;
+ remainder = "";
+ } else {
+ language = languageTag.substring(0, separator);
+ remainder = languageTag.substring(separator);
+ }
+
+ return Locale.adjustLanguageCode(language) + remainder;
+ }
+
+ /**
+ * Call this to remove all cached loaded layout resources from the
+ * Resources object. Only intended for use with performance testing
+ * tools.
+ */
+ public void flushLayoutCache() {
+ synchronized (mCachedXmlBlocks) {
+ Arrays.fill(mCachedXmlBlockCookies, 0);
+ Arrays.fill(mCachedXmlBlockFiles, null);
+
+ final XmlBlock[] cachedXmlBlocks = mCachedXmlBlocks;
+ for (int i = 0; i < XML_BLOCK_CACHE_SIZE; i++) {
+ final XmlBlock oldBlock = cachedXmlBlocks[i];
+ if (oldBlock != null) {
+ oldBlock.close();
+ }
+ }
+ Arrays.fill(cachedXmlBlocks, null);
+ }
+ }
+
+ @Nullable
+ Drawable loadDrawable(Resources wrapper, TypedValue value, int id, Resources.Theme theme,
+ boolean useCache) throws NotFoundException {
+ try {
+ if (TRACE_FOR_PRELOAD) {
+ // Log only framework resources
+ if ((id >>> 24) == 0x1) {
+ final String name = getResourceName(id);
+ if (name != null) {
+ Log.d("PreloadDrawable", name);
+ }
+ }
+ }
+
+ final boolean isColorDrawable;
+ final DrawableCache caches;
+ final long key;
+ if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT
+ && value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
+ isColorDrawable = true;
+ caches = mColorDrawableCache;
+ key = value.data;
+ } else {
+ isColorDrawable = false;
+ caches = mDrawableCache;
+ key = (((long) value.assetCookie) << 32) | value.data;
+ }
+
+ // First, check whether we have a cached version of this drawable
+ // that was inflated against the specified theme. Skip the cache if
+ // we're currently preloading or we're not using the cache.
+ if (!mPreloading && useCache) {
+ final Drawable cachedDrawable = caches.getInstance(key, wrapper, theme);
+ if (cachedDrawable != null) {
+ return cachedDrawable;
+ }
+ }
+
+ // Next, check preloaded drawables. Preloaded drawables may contain
+ // unresolved theme attributes.
+ final Drawable.ConstantState cs;
+ if (isColorDrawable) {
+ cs = sPreloadedColorDrawables.get(key);
+ } else {
+ cs = sPreloadedDrawables[mConfiguration.getLayoutDirection()].get(key);
+ }
+
+ Drawable dr;
+ if (cs != null) {
+ dr = cs.newDrawable(wrapper);
+ } else if (isColorDrawable) {
+ dr = new ColorDrawable(value.data);
+ } else {
+ dr = loadDrawableForCookie(wrapper, value, id, null);
+ }
+
+ // Determine if the drawable has unresolved theme attributes. If it
+ // does, we'll need to apply a theme and store it in a theme-specific
+ // cache.
+ final boolean canApplyTheme = dr != null && dr.canApplyTheme();
+ if (canApplyTheme && theme != null) {
+ dr = dr.mutate();
+ dr.applyTheme(theme);
+ dr.clearMutated();
+ }
+
+ // If we were able to obtain a drawable, store it in the appropriate
+ // cache: preload, not themed, null theme, or theme-specific. Don't
+ // pollute the cache with drawables loaded from a foreign density.
+ if (dr != null && useCache) {
+ dr.setChangingConfigurations(value.changingConfigurations);
+ cacheDrawable(value, isColorDrawable, caches, theme, canApplyTheme, key, dr);
+ }
+
+ return dr;
+ } catch (Exception e) {
+ String name;
+ try {
+ name = getResourceName(id);
+ } catch (NotFoundException e2) {
+ name = "(missing name)";
+ }
+
+ // The target drawable might fail to load for any number of
+ // reasons, but we always want to include the resource name.
+ // Since the client already expects this method to throw a
+ // NotFoundException, just throw one of those.
+ final NotFoundException nfe = new NotFoundException("Drawable " + name
+ + " with resource ID #0x" + Integer.toHexString(id), e);
+ nfe.setStackTrace(new StackTraceElement[0]);
+ throw nfe;
+ }
+ }
+
+ private void cacheDrawable(TypedValue value, boolean isColorDrawable, DrawableCache caches,
+ Resources.Theme theme, boolean usesTheme, long key, Drawable dr) {
+ final Drawable.ConstantState cs = dr.getConstantState();
+ if (cs == null) {
+ return;
+ }
+
+ if (mPreloading) {
+ final int changingConfigs = cs.getChangingConfigurations();
+ if (isColorDrawable) {
+ if (verifyPreloadConfig(changingConfigs, 0, value.resourceId, "drawable")) {
+ sPreloadedColorDrawables.put(key, cs);
+ }
+ } else {
+ if (verifyPreloadConfig(
+ changingConfigs, LAYOUT_DIR_CONFIG, value.resourceId, "drawable")) {
+ if ((changingConfigs & LAYOUT_DIR_CONFIG) == 0) {
+ // If this resource does not vary based on layout direction,
+ // we can put it in all of the preload maps.
+ sPreloadedDrawables[0].put(key, cs);
+ sPreloadedDrawables[1].put(key, cs);
+ } else {
+ // Otherwise, only in the layout dir we loaded it for.
+ sPreloadedDrawables[mConfiguration.getLayoutDirection()].put(key, cs);
+ }
+ }
+ }
+ } else {
+ synchronized (mAccessLock) {
+ caches.put(key, theme, cs, usesTheme);
+ }
+ }
+ }
+
+ private boolean verifyPreloadConfig(int changingConfigurations, int allowVarying,
+ int resourceId, String name) {
+ // We allow preloading of resources even if they vary by font scale (which
+ // doesn't impact resource selection) or density (which we handle specially by
+ // simply turning off all preloading), as well as any other configs specified
+ // by the caller.
+ if (((changingConfigurations&~(ActivityInfo.CONFIG_FONT_SCALE |
+ ActivityInfo.CONFIG_DENSITY)) & ~allowVarying) != 0) {
+ String resName;
+ try {
+ resName = getResourceName(resourceId);
+ } catch (NotFoundException e) {
+ resName = "?";
+ }
+ // This should never happen in production, so we should log a
+ // warning even if we're not debugging.
+ Log.w(TAG, "Preloaded " + name + " resource #0x"
+ + Integer.toHexString(resourceId)
+ + " (" + resName + ") that varies with configuration!!");
+ return false;
+ }
+ if (TRACE_FOR_PRELOAD) {
+ String resName;
+ try {
+ resName = getResourceName(resourceId);
+ } catch (NotFoundException e) {
+ resName = "?";
+ }
+ Log.w(TAG, "Preloading " + name + " resource #0x"
+ + Integer.toHexString(resourceId)
+ + " (" + resName + ")");
+ }
+ return true;
+ }
+
+ /**
+ * Loads a drawable from XML or resources stream.
+ */
+ private Drawable loadDrawableForCookie(Resources wrapper, TypedValue value, int id,
+ Resources.Theme theme) {
+ if (value.string == null) {
+ throw new NotFoundException("Resource \"" + getResourceName(id) + "\" ("
+ + Integer.toHexString(id) + ") is not a Drawable (color or path): " + value);
+ }
+
+ final String file = value.string.toString();
+
+ if (TRACE_FOR_MISS_PRELOAD) {
+ // Log only framework resources
+ if ((id >>> 24) == 0x1) {
+ final String name = getResourceName(id);
+ if (name != null) {
+ Log.d(TAG, "Loading framework drawable #" + Integer.toHexString(id)
+ + ": " + name + " at " + file);
+ }
+ }
+ }
+
+ if (DEBUG_LOAD) {
+ Log.v(TAG, "Loading drawable for cookie " + value.assetCookie + ": " + file);
+ }
+
+ final Drawable dr;
+
+ Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
+ try {
+ if (file.endsWith(".xml")) {
+ final XmlResourceParser rp = loadXmlResourceParser(
+ file, id, value.assetCookie, "drawable");
+ dr = Drawable.createFromXml(wrapper, rp, theme);
+ rp.close();
+ } else {
+ final InputStream is = mAssets.openNonAsset(
+ value.assetCookie, file, AssetManager.ACCESS_STREAMING);
+ dr = Drawable.createFromResourceStream(wrapper, value, is, file, null);
+ is.close();
+ }
+ } catch (Exception e) {
+ Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+ final NotFoundException rnf = new NotFoundException(
+ "File " + file + " from drawable resource ID #0x" + Integer.toHexString(id));
+ rnf.initCause(e);
+ throw rnf;
+ }
+ Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+
+ return dr;
+ }
+
+ /**
+ * Given the value and id, we can get the XML filename as in value.data, based on that, we
+ * first try to load CSL from the cache. If not found, try to get from the constant state.
+ * Last, parse the XML and generate the CSL.
+ */
+ private ComplexColor loadComplexColorFromName(Resources wrapper, Resources.Theme theme,
+ TypedValue value, int id) {
+ final long key = (((long) value.assetCookie) << 32) | value.data;
+ final ConfigurationBoundResourceCache<ComplexColor> cache = mComplexColorCache;
+ ComplexColor complexColor = cache.getInstance(key, wrapper, theme);
+ if (complexColor != null) {
+ return complexColor;
+ }
+
+ final android.content.res.ConstantState<ComplexColor> factory =
+ sPreloadedComplexColors.get(key);
+
+ if (factory != null) {
+ complexColor = factory.newInstance(wrapper, theme);
+ }
+ if (complexColor == null) {
+ complexColor = loadComplexColorForCookie(wrapper, value, id, theme);
+ }
+
+ if (complexColor != null) {
+ if (mPreloading) {
+ if (verifyPreloadConfig(value.changingConfigurations, 0, value.resourceId,
+ "color")) {
+ sPreloadedComplexColors.put(key, complexColor.getConstantState());
+ }
+ } else {
+ cache.put(key, theme, complexColor.getConstantState());
+ }
+ }
+ return complexColor;
+ }
+
+ @Nullable
+ ComplexColor loadComplexColor(Resources wrapper, @NonNull TypedValue value, int id,
+ Resources.Theme theme) {
+ if (TRACE_FOR_PRELOAD) {
+ // Log only framework resources
+ if ((id >>> 24) == 0x1) {
+ final String name = getResourceName(id);
+ if (name != null) android.util.Log.d("loadComplexColor", name);
+ }
+ }
+
+ final long key = (((long) value.assetCookie) << 32) | value.data;
+
+ // Handle inline color definitions.
+ if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT
+ && value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
+ return getColorStateListFromInt(value, key);
+ }
+
+ final String file = value.string.toString();
+
+ ComplexColor complexColor;
+ if (file.endsWith(".xml")) {
+ try {
+ complexColor = loadComplexColorFromName(wrapper, theme, value, id);
+ } catch (Exception e) {
+ final NotFoundException rnf = new NotFoundException(
+ "File " + file + " from complex color resource ID #0x"
+ + Integer.toHexString(id));
+ rnf.initCause(e);
+ throw rnf;
+ }
+ } else {
+ throw new NotFoundException(
+ "File " + file + " from drawable resource ID #0x"
+ + Integer.toHexString(id) + ": .xml extension required");
+ }
+
+ return complexColor;
+ }
+
+ @Nullable
+ ColorStateList loadColorStateList(Resources wrapper, TypedValue value, int id,
+ Resources.Theme theme)
+ throws NotFoundException {
+ if (TRACE_FOR_PRELOAD) {
+ // Log only framework resources
+ if ((id >>> 24) == 0x1) {
+ final String name = getResourceName(id);
+ if (name != null) android.util.Log.d("PreloadColorStateList", name);
+ }
+ }
+
+ final long key = (((long) value.assetCookie) << 32) | value.data;
+
+ // Handle inline color definitions.
+ if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT
+ && value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
+ return getColorStateListFromInt(value, key);
+ }
+
+ ComplexColor complexColor = loadComplexColorFromName(wrapper, theme, value, id);
+ if (complexColor != null && complexColor instanceof ColorStateList) {
+ return (ColorStateList) complexColor;
+ }
+
+ throw new NotFoundException(
+ "Can't find ColorStateList from drawable resource ID #0x"
+ + Integer.toHexString(id));
+ }
+
+ @NonNull
+ private ColorStateList getColorStateListFromInt(@NonNull TypedValue value, long key) {
+ ColorStateList csl;
+ final android.content.res.ConstantState<ComplexColor> factory =
+ sPreloadedComplexColors.get(key);
+ if (factory != null) {
+ return (ColorStateList) factory.newInstance();
+ }
+
+ csl = ColorStateList.valueOf(value.data);
+
+ if (mPreloading) {
+ if (verifyPreloadConfig(value.changingConfigurations, 0, value.resourceId,
+ "color")) {
+ sPreloadedComplexColors.put(key, csl.getConstantState());
+ }
+ }
+
+ return csl;
+ }
+
+ /**
+ * Load a ComplexColor based on the XML file content. The result can be a GradientColor or
+ * ColorStateList. Note that pure color will be wrapped into a ColorStateList.
+ *
+ * We deferred the parser creation to this function b/c we need to differentiate b/t gradient
+ * and selector tag.
+ *
+ * @return a ComplexColor (GradientColor or ColorStateList) based on the XML file content.
+ */
+ @Nullable
+ private ComplexColor loadComplexColorForCookie(Resources wrapper, TypedValue value, int id,
+ Resources.Theme theme) {
+ if (value.string == null) {
+ throw new UnsupportedOperationException(
+ "Can't convert to ComplexColor: type=0x" + value.type);
+ }
+
+ final String file = value.string.toString();
+
+ if (TRACE_FOR_MISS_PRELOAD) {
+ // Log only framework resources
+ if ((id >>> 24) == 0x1) {
+ final String name = getResourceName(id);
+ if (name != null) {
+ Log.d(TAG, "Loading framework ComplexColor #" + Integer.toHexString(id)
+ + ": " + name + " at " + file);
+ }
+ }
+ }
+
+ if (DEBUG_LOAD) {
+ Log.v(TAG, "Loading ComplexColor for cookie " + value.assetCookie + ": " + file);
+ }
+
+ ComplexColor complexColor = null;
+
+ Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
+ if (file.endsWith(".xml")) {
+ try {
+ final XmlResourceParser parser = loadXmlResourceParser(
+ file, id, value.assetCookie, "ComplexColor");
+
+ final AttributeSet attrs = Xml.asAttributeSet(parser);
+ int type;
+ while ((type = parser.next()) != XmlPullParser.START_TAG
+ && type != XmlPullParser.END_DOCUMENT) {
+ // Seek parser to start tag.
+ }
+ if (type != XmlPullParser.START_TAG) {
+ throw new XmlPullParserException("No start tag found");
+ }
+
+ final String name = parser.getName();
+ if (name.equals("gradient")) {
+ complexColor = GradientColor.createFromXmlInner(wrapper, parser, attrs, theme);
+ } else if (name.equals("selector")) {
+ complexColor = ColorStateList.createFromXmlInner(wrapper, parser, attrs, theme);
+ }
+ parser.close();
+ } catch (Exception e) {
+ Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+ final NotFoundException rnf = new NotFoundException(
+ "File " + file + " from ComplexColor resource ID #0x"
+ + Integer.toHexString(id));
+ rnf.initCause(e);
+ throw rnf;
+ }
+ } else {
+ Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+ throw new NotFoundException(
+ "File " + file + " from drawable resource ID #0x"
+ + Integer.toHexString(id) + ": .xml extension required");
+ }
+ Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+
+ return complexColor;
+ }
+
+ /**
+ * Loads an XML parser for the specified file.
+ *
+ * @param file the path for the XML file to parse
+ * @param id the resource identifier for the file
+ * @param assetCookie the asset cookie for the file
+ * @param type the type of resource (used for logging)
+ * @return a parser for the specified XML file
+ * @throws NotFoundException if the file could not be loaded
+ */
+ @NonNull
+ XmlResourceParser loadXmlResourceParser(@NonNull String file, @AnyRes int id,
+ int assetCookie, @NonNull String type)
+ throws NotFoundException {
+ if (id != 0) {
+ try {
+ synchronized (mCachedXmlBlocks) {
+ final int[] cachedXmlBlockCookies = mCachedXmlBlockCookies;
+ final String[] cachedXmlBlockFiles = mCachedXmlBlockFiles;
+ final XmlBlock[] cachedXmlBlocks = mCachedXmlBlocks;
+ // First see if this block is in our cache.
+ final int num = cachedXmlBlockFiles.length;
+ for (int i = 0; i < num; i++) {
+ if (cachedXmlBlockCookies[i] == assetCookie && cachedXmlBlockFiles[i] != null
+ && cachedXmlBlockFiles[i].equals(file)) {
+ return cachedXmlBlocks[i].newParser();
+ }
+ }
+
+ // Not in the cache, create a new block and put it at
+ // the next slot in the cache.
+ final XmlBlock block = mAssets.openXmlBlockAsset(assetCookie, file);
+ if (block != null) {
+ final int pos = (mLastCachedXmlBlockIndex + 1) % num;
+ mLastCachedXmlBlockIndex = pos;
+ final XmlBlock oldBlock = cachedXmlBlocks[pos];
+ if (oldBlock != null) {
+ oldBlock.close();
+ }
+ cachedXmlBlockCookies[pos] = assetCookie;
+ cachedXmlBlockFiles[pos] = file;
+ cachedXmlBlocks[pos] = block;
+ return block.newParser();
+ }
+ }
+ } catch (Exception e) {
+ final NotFoundException rnf = new NotFoundException("File " + file
+ + " from xml type " + type + " resource ID #0x" + Integer.toHexString(id));
+ rnf.initCause(e);
+ throw rnf;
+ }
+ }
+
+ throw new NotFoundException("File " + file + " from xml type " + type + " resource ID #0x"
+ + Integer.toHexString(id));
+ }
+
+ /**
+ * Start preloading of resource data using this Resources object. Only
+ * for use by the zygote process for loading common system resources.
+ * {@hide}
+ */
+ public final void startPreloading() {
+ synchronized (sSync) {
+ if (sPreloaded) {
+ throw new IllegalStateException("Resources already preloaded");
+ }
+ sPreloaded = true;
+ mPreloading = true;
+ mConfiguration.densityDpi = DisplayMetrics.DENSITY_DEVICE;
+ updateConfiguration(null, null, null);
+ }
+ }
+
+ /**
+ * Called by zygote when it is done preloading resources, to change back
+ * to normal Resources operation.
+ */
+ void finishPreloading() {
+ if (mPreloading) {
+ mPreloading = false;
+ flushLayoutCache();
+ }
+ }
+
+ LongSparseArray<Drawable.ConstantState> getPreloadedDrawables() {
+ return sPreloadedDrawables[0];
+ }
+
+ ThemeImpl newThemeImpl() {
+ return new ThemeImpl();
+ }
+
+ public class ThemeImpl {
+ /**
+ * Unique key for the series of styles applied to this theme.
+ */
+ private final Resources.ThemeKey mKey = new Resources.ThemeKey();
+
+ @SuppressWarnings("hiding")
+ private final AssetManager mAssets;
+ private final long mTheme;
+
+ /**
+ * Resource identifier for the theme.
+ */
+ private int mThemeResId = 0;
+
+ /*package*/ ThemeImpl() {
+ mAssets = ResourcesImpl.this.mAssets;
+ mTheme = mAssets.createTheme();
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ super.finalize();
+ mAssets.releaseTheme(mTheme);
+ }
+
+ /*package*/ Resources.ThemeKey getKey() {
+ return mKey;
+ }
+
+ /*package*/ long getNativeTheme() {
+ return mTheme;
+ }
+
+ /*package*/ int getAppliedStyleResId() {
+ return mThemeResId;
+ }
+
+ void applyStyle(int resId, boolean force) {
+ synchronized (mKey) {
+ AssetManager.applyThemeStyle(mTheme, resId, force);
+
+ mThemeResId = resId;
+ mKey.append(resId, force);
+ }
+ }
+
+ void setTo(ThemeImpl other) {
+ synchronized (mKey) {
+ synchronized (other.mKey) {
+ AssetManager.copyTheme(mTheme, other.mTheme);
+
+ mThemeResId = other.mThemeResId;
+ mKey.setTo(other.getKey());
+ }
+ }
+ }
+
+ @NonNull
+ TypedArray obtainStyledAttributes(@NonNull Resources.Theme wrapper,
+ AttributeSet set,
+ @StyleableRes int[] attrs,
+ @AttrRes int defStyleAttr,
+ @StyleRes int defStyleRes) {
+ synchronized (mKey) {
+ final int len = attrs.length;
+ final TypedArray array = TypedArray.obtain(wrapper.getResources(), len);
+
+ // XXX note that for now we only work with compiled XML files.
+ // To support generic XML files we will need to manually parse
+ // out the attributes from the XML file (applying type information
+ // contained in the resources and such).
+ final XmlBlock.Parser parser = (XmlBlock.Parser) set;
+ AssetManager.applyStyle(mTheme, defStyleAttr, defStyleRes,
+ parser != null ? parser.mParseState : 0,
+ attrs, array.mData, array.mIndices);
+ array.mTheme = wrapper;
+ array.mXml = parser;
+
+ return array;
+ }
+ }
+
+ @NonNull
+ TypedArray resolveAttributes(@NonNull Resources.Theme wrapper,
+ @NonNull int[] values,
+ @NonNull int[] attrs) {
+ synchronized (mKey) {
+ final int len = attrs.length;
+ if (values == null || len != values.length) {
+ throw new IllegalArgumentException(
+ "Base attribute values must the same length as attrs");
+ }
+
+ final TypedArray array = TypedArray.obtain(wrapper.getResources(), len);
+ AssetManager.resolveAttrs(mTheme, 0, 0, values, attrs, array.mData, array.mIndices);
+ array.mTheme = wrapper;
+ array.mXml = null;
+ return array;
+ }
+ }
+
+ boolean resolveAttribute(int resid, TypedValue outValue, boolean resolveRefs) {
+ synchronized (mKey) {
+ return mAssets.getThemeValue(mTheme, resid, outValue, resolveRefs);
+ }
+ }
+
+ int[] getAllAttributes() {
+ return mAssets.getStyleAttributes(getAppliedStyleResId());
+ }
+
+ int getChangingConfigurations() {
+ synchronized (mKey) {
+ final int nativeChangingConfig =
+ AssetManager.getThemeChangingConfigurations(mTheme);
+ return ActivityInfo.activityInfoConfigNativeToJava(nativeChangingConfig);
+ }
+ }
+
+ public void dump(int priority, String tag, String prefix) {
+ synchronized (mKey) {
+ AssetManager.dumpTheme(mTheme, priority, tag, prefix);
+ }
+ }
+
+ String[] getTheme() {
+ synchronized (mKey) {
+ final int N = mKey.mCount;
+ final String[] themes = new String[N * 2];
+ for (int i = 0, j = N - 1; i < themes.length; i += 2, --j) {
+ final int resId = mKey.mResId[j];
+ final boolean forced = mKey.mForce[j];
+ try {
+ themes[i] = getResourceName(resId);
+ } catch (NotFoundException e) {
+ themes[i] = Integer.toHexString(i);
+ }
+ themes[i + 1] = forced ? "forced" : "not forced";
+ }
+ return themes;
+ }
+ }
+
+ /**
+ * Rebases the theme against the parent Resource object's current
+ * configuration by re-applying the styles passed to
+ * {@link #applyStyle(int, boolean)}.
+ */
+ void rebase() {
+ synchronized (mKey) {
+ AssetManager.clearTheme(mTheme);
+
+ // Reapply the same styles in the same order.
+ for (int i = 0; i < mKey.mCount; i++) {
+ final int resId = mKey.mResId[i];
+ final boolean force = mKey.mForce[i];
+ AssetManager.applyThemeStyle(mTheme, resId, force);
+ }
+ }
+ }
+ }
+}
diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java
index da49b64..022bdfb 100644
--- a/core/java/android/content/res/TypedArray.java
+++ b/core/java/android/content/res/TypedArray.java
@@ -1206,8 +1206,8 @@
/*package*/ TypedArray(Resources resources, int[] data, int[] indices, int len) {
mResources = resources;
- mMetrics = mResources.mMetrics;
- mAssets = mResources.mAssets;
+ mMetrics = mResources.getDisplayMetrics();
+ mAssets = mResources.getAssets();
mData = data;
mIndices = indices;
mLength = len;
diff --git a/core/java/android/hardware/camera2/DngCreator.java b/core/java/android/hardware/camera2/DngCreator.java
index 57a080b..9478dc0 100644
--- a/core/java/android/hardware/camera2/DngCreator.java
+++ b/core/java/android/hardware/camera2/DngCreator.java
@@ -137,6 +137,11 @@
throw new IllegalArgumentException("Orientation " + orientation +
" is not a valid EXIF orientation value");
}
+ // ExifInterface and TIFF/EP spec differ on definition of
+ // "Unknown" orientation; other values map directly
+ if (orientation == ExifInterface.ORIENTATION_UNDEFINED) {
+ orientation = TAG_ORIENTATION_UNKNOWN;
+ }
nativeSetOrientation(orientation);
return this;
}
@@ -443,7 +448,7 @@
private static final String GPS_LONG_REF_WEST = "W";
private static final String GPS_DATE_FORMAT_STR = "yyyy:MM:dd";
- private static final String TIFF_DATETIME_FORMAT = "yyyy:MM:dd kk:mm:ss";
+ private static final String TIFF_DATETIME_FORMAT = "yyyy:MM:dd HH:mm:ss";
private static final DateFormat sExifGPSDateStamp = new SimpleDateFormat(GPS_DATE_FORMAT_STR);
private static final DateFormat sDateTimeStampFormat =
new SimpleDateFormat(TIFF_DATETIME_FORMAT);
@@ -458,6 +463,9 @@
private static final int DEFAULT_PIXEL_STRIDE = 2; // bytes per sample
private static final int BYTES_PER_RGB_PIX = 3; // byts per pixel
+ // TIFF tag values needed to map between public API and TIFF spec
+ private static final int TAG_ORIENTATION_UNKNOWN = 9;
+
/**
* Offset, rowStride, and pixelStride are given in bytes. Height and width are given in pixels.
*/
diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
index e62df3c..4c4adea 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
@@ -614,14 +614,16 @@
return LegacyExceptionUtils.throwOnError(nativeDetectSurfaceDataspace(surface));
}
- static void configureSurface(Surface surface, int width, int height,
- int pixelFormat) throws BufferQueueAbandonedException {
+ static void connectSurface(Surface surface) throws BufferQueueAbandonedException {
checkNotNull(surface);
- checkArgumentPositive(width, "width must be positive.");
- checkArgumentPositive(height, "height must be positive.");
- LegacyExceptionUtils.throwOnError(nativeConfigureSurface(surface, width, height,
- pixelFormat));
+ LegacyExceptionUtils.throwOnError(nativeConnectSurface(surface));
+ }
+
+ static void disconnectSurface(Surface surface) throws BufferQueueAbandonedException {
+ if (surface == null) return;
+
+ LegacyExceptionUtils.throwOnError(nativeDisconnectSurface(surface));
}
static void produceFrame(Surface surface, byte[] pixelBuffer, int width,
@@ -716,8 +718,7 @@
private static native int nativeDetectSurfaceDimens(Surface surface,
/*out*/int[/*2*/] dimens);
- private static native int nativeConfigureSurface(Surface surface, int width, int height,
- int pixelFormat);
+ private static native int nativeConnectSurface(Surface surface);
private static native int nativeProduceFrame(Surface surface, byte[] pixelBuffer, int width,
int height, int pixelFormat);
@@ -740,5 +741,7 @@
private static native int nativeSetScalingMode(Surface surface, int scalingMode);
+ private static native int nativeDisconnectSurface(Surface surface);
+
static native int nativeGetJpegFooterSize();
}
diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
index 1ca7ddf..e8ce3ec 100644
--- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
+++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
@@ -365,6 +365,14 @@
mGLThreadManager.waitUntilIdle();
}
resetJpegSurfaceFormats(mCallbackOutputs);
+
+ for (Surface s : mCallbackOutputs) {
+ try {
+ LegacyCameraDevice.disconnectSurface(s);
+ } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
+ Log.w(TAG, "Surface abandoned, skipping...", e);
+ }
+ }
mPreviewOutputs.clear();
mCallbackOutputs.clear();
mJpegSurfaceIds.clear();
@@ -392,6 +400,10 @@
mJpegSurfaceIds.add(LegacyCameraDevice.getSurfaceId(s));
mCallbackOutputs.add(s);
callbackOutputSizes.add(outSize);
+
+ // LegacyCameraDevice is the producer of JPEG output surfaces
+ // so LegacyCameraDevice needs to connect to the surfaces.
+ LegacyCameraDevice.connectSurface(s);
break;
default:
LegacyCameraDevice.setScalingMode(s, LegacyCameraDevice.
diff --git a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
index bc80fc1..70bc2fd 100644
--- a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
+++ b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
@@ -401,6 +401,13 @@
private void clearState() {
mSurfaces.clear();
+ for (EGLSurfaceHolder holder : mConversionSurfaces) {
+ try {
+ LegacyCameraDevice.disconnectSurface(holder.surface);
+ } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
+ Log.w(TAG, "Surface abandoned, skipping...", e);
+ }
+ }
mConversionSurfaces.clear();
mPBufferPixels = null;
if (mSurfaceTexture != null) {
@@ -631,6 +638,9 @@
holder.height = surfaceSize.getHeight();
if (LegacyCameraDevice.needsConversion(s)) {
mConversionSurfaces.add(holder);
+ // LegacyCameraDevice is the producer of surfaces if it's not handled by EGL,
+ // so LegacyCameraDevice needs to connect to the surfaces.
+ LegacyCameraDevice.connectSurface(s);
} else {
mSurfaces.add(holder);
}
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index 20c2168..9e360e1 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -200,6 +200,14 @@
*/
public static final int CMD_PREVENT_AUTOMATIC_RECONNECT = BASE + 15;
+ /**
+ * Sent by ConnectivityService to the NetworkAgent to install an APF program in the network
+ * chipset for use to filter packets.
+ *
+ * obj = byte[] containing the APF program bytecode.
+ */
+ public static final int CMD_PUSH_APF_PROGRAM = BASE + 16;
+
public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
NetworkCapabilities nc, LinkProperties lp, int score) {
this(looper, context, logTag, ni, nc, lp, score, null);
@@ -319,6 +327,10 @@
preventAutomaticReconnect();
break;
}
+ case CMD_PUSH_APF_PROGRAM: {
+ installPacketFilter((byte[]) msg.obj);
+ break;
+ }
}
}
@@ -494,6 +506,15 @@
protected void preventAutomaticReconnect() {
}
+ /**
+ * Install a packet filter.
+ * @param filter an APF program to filter incoming packets.
+ * @return {@code true} if filter successfully installed, {@code false} otherwise.
+ */
+ protected boolean installPacketFilter(byte[] filter) {
+ return false;
+ }
+
protected void log(String s) {
Log.d(LOG_TAG, "NetworkAgent: " + s);
}
diff --git a/core/java/android/net/NetworkMisc.java b/core/java/android/net/NetworkMisc.java
index 5511a24..748699e 100644
--- a/core/java/android/net/NetworkMisc.java
+++ b/core/java/android/net/NetworkMisc.java
@@ -56,6 +56,22 @@
*/
public String subscriberId;
+ /**
+ * Version of APF instruction set supported for packet filtering. 0 indicates no support for
+ * packet filtering using APF programs.
+ */
+ public int apfVersionSupported;
+
+ /**
+ * Maximum size of APF program allowed.
+ */
+ public int maximumApfProgramSize;
+
+ /**
+ * Format of packets passed to APF filter. Should be one of ARPHRD_*
+ */
+ public int apfPacketFormat;
+
public NetworkMisc() {
}
@@ -65,6 +81,9 @@
explicitlySelected = nm.explicitlySelected;
acceptUnvalidated = nm.acceptUnvalidated;
subscriberId = nm.subscriberId;
+ apfVersionSupported = nm.apfVersionSupported;
+ maximumApfProgramSize = nm.maximumApfProgramSize;
+ apfPacketFormat = nm.apfPacketFormat;
}
}
@@ -79,6 +98,9 @@
out.writeInt(explicitlySelected ? 1 : 0);
out.writeInt(acceptUnvalidated ? 1 : 0);
out.writeString(subscriberId);
+ out.writeInt(apfVersionSupported);
+ out.writeInt(maximumApfProgramSize);
+ out.writeInt(apfPacketFormat);
}
public static final Creator<NetworkMisc> CREATOR = new Creator<NetworkMisc>() {
@@ -89,6 +111,9 @@
networkMisc.explicitlySelected = in.readInt() != 0;
networkMisc.acceptUnvalidated = in.readInt() != 0;
networkMisc.subscriberId = in.readString();
+ networkMisc.apfVersionSupported = in.readInt();
+ networkMisc.maximumApfProgramSize = in.readInt();
+ networkMisc.apfPacketFormat = in.readInt();
return networkMisc;
}
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 94de933..8738424 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -68,10 +68,12 @@
public static final int FIREWALL_CHAIN_NONE = 0;
public static final int FIREWALL_CHAIN_DOZABLE = 1;
public static final int FIREWALL_CHAIN_STANDBY = 2;
+ public static final int FIREWALL_CHAIN_POWERSAVE = 3;
public static final String FIREWALL_CHAIN_NAME_NONE = "none";
public static final String FIREWALL_CHAIN_NAME_DOZABLE = "dozable";
public static final String FIREWALL_CHAIN_NAME_STANDBY = "standby";
+ public static final String FIREWALL_CHAIN_NAME_POWERSAVE = "powersave";
private static final boolean ALLOW_PLATFORM_APP_POLICY = true;
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index c6d919f..555032d 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -62,6 +62,13 @@
public native static void attachDhcpFilter(FileDescriptor fd) throws SocketException;
/**
+ * Attaches a socket filter that accepts ICMP6 router advertisement packets to the given socket.
+ * @param fd the socket's {@link FileDescriptor}.
+ * @param packetType the hardware address type, one of ARPHRD_*.
+ */
+ public native static void attachRaFilter(FileDescriptor fd, int packetType) throws SocketException;
+
+ /**
* Binds the current process to the network designated by {@code netId}. All sockets created
* in the future (and not explicitly bound via a bound {@link SocketFactory} (see
* {@link Network#getSocketFactory}) will be bound to this network. Note that if this
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 9984755..8fd3b0c 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -383,6 +383,9 @@
public static final int SIGNAL_KILL = 9;
public static final int SIGNAL_USR1 = 10;
+ private static long sStartElapsedRealtime;
+ private static long sStartUptimeMillis;
+
/**
* State for communicating with the zygote process.
*
@@ -772,6 +775,26 @@
public static final native long getElapsedCpuTime();
/**
+ * Return the {@link SystemClock#elapsedRealtime()} at which this process was started.
+ */
+ public static final long getStartElapsedRealtime() {
+ return sStartElapsedRealtime;
+ }
+
+ /**
+ * Return the {@link SystemClock#uptimeMillis()} at which this process was started.
+ */
+ public static final long getStartUptimeMillis() {
+ return sStartUptimeMillis;
+ }
+
+ /** @hide */
+ public static final void setStartTimes(long elapsedRealtime, long uptimeMillis) {
+ sStartElapsedRealtime = elapsedRealtime;
+ sStartUptimeMillis = uptimeMillis;
+ }
+
+ /**
* Returns true if the current process is a 64-bit runtime.
*/
public static final boolean is64Bit() {
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 3788c74..0065cd9 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -478,7 +478,6 @@
/**
* Capacity of a root in bytes. This column is optional, and may be
* {@code null} if unknown or unbounded.
- * {@hide}
* <p>
* Type: INTEGER (long)
*/
@@ -568,6 +567,22 @@
* @hide
*/
public static final int FLAG_HAS_SETTINGS = 1 << 17;
+
+ /**
+ * Flag indicating that this root is on removable SD card storage.
+ *
+ * @see #COLUMN_FLAGS
+ * @hide
+ */
+ public static final int FLAG_REMOVABLE_SD = 1 << 18;
+
+ /**
+ * Flag indicating that this root is on removable USB storage.
+ *
+ * @see #COLUMN_FLAGS
+ * @hide
+ */
+ public static final int FLAG_REMOVABLE_USB = 1 << 19;
}
/**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index f13f242..a40cf96 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1241,6 +1241,19 @@
public static final String ACTION_SHOW_ADMIN_SUPPORT_DETAILS
= "android.settings.SHOW_ADMIN_SUPPORT_DETAILS";
+ /**
+ * Activity Action: Show a dialog for remote bugreport flow.
+ * <p>
+ * Input: Nothing.
+ * <p>
+ * Output: Nothing.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_SHOW_REMOTE_BUGREPORT_DIALOG
+ = "android.settings.SHOW_REMOTE_BUGREPORT_DIALOG";
+
// End of Intent actions for Settings
/**
diff --git a/core/java/android/service/carrier/CarrierMessagingService.java b/core/java/android/service/carrier/CarrierMessagingService.java
index f5396a3..140341c 100644
--- a/core/java/android/service/carrier/CarrierMessagingService.java
+++ b/core/java/android/service/carrier/CarrierMessagingService.java
@@ -51,6 +51,30 @@
= "android.service.carrier.CarrierMessagingService";
/**
+ * The default bitmask value passed to the callback of {@link #onReceiveTextSms} with all
+ * {@code RECEIVE_OPTIONS_x} flags cleared to indicate that the message should be kept and a
+ * new message notification should be shown.
+ *
+ * @see #RECEIVE_OPTIONS_DROP
+ * @see #RECEIVE_OPTIONS_SKIP_NOTIFY_WHEN_CREDENTIAL_ENCRYPTED_STORAGE_UNAVAILABLE
+ */
+ public static final int RECEIVE_OPTIONS_DEFAULT = 0;
+
+ /**
+ * Used to set the flag in the bitmask passed to the callback of {@link #onReceiveTextSms} to
+ * indicate that the inbound SMS should be dropped.
+ */
+ public static final int RECEIVE_OPTIONS_DROP = 0x1;
+
+ /**
+ * Used to set the flag in the bitmask passed to the callback of {@link #onReceiveTextSms} to
+ * indicate that a new message notification should not be shown to the user when the
+ * credential-encrypted storage of the device is not available before the user unlocks the
+ * phone. It is only applicable to devices that support file-based encryption.
+ */
+ public static final int RECEIVE_OPTIONS_SKIP_NOTIFY_WHEN_CREDENTIAL_ENCRYPTED_STORAGE_UNAVAILABLE = 0x2;
+
+ /**
* Indicates that an SMS or MMS message was successfully sent.
*/
public static final int SEND_STATUS_OK = 0;
@@ -96,7 +120,9 @@
* @param subId SMS subscription ID of the SIM
* @param callback result callback. Call with {@code true} to keep an inbound SMS message and
* deliver to SMS apps, and {@code false} to drop the message.
+ * @deprecated Use {@link #onReceiveTextSms} instead.
*/
+ @Deprecated
public void onFilterSms(@NonNull MessagePdu pdu, @NonNull String format, int destPort,
int subId, @NonNull ResultCallback<Boolean> callback) {
// optional
@@ -107,6 +133,36 @@
}
/**
+ * Override this method to filter inbound SMS messages.
+ *
+ * <p>This method will be called once for every incoming text SMS. You can invoke the callback
+ * with a bitmask to tell the platform how to handle the SMS. For a SMS received on a
+ * file-based encryption capable device while the credential-encrypted storage is not available,
+ * this method will be called for the second time when the credential-encrypted storage becomes
+ * available after the user unlocks the phone, if the bit {@link #RECEIVE_OPTIONS_DROP} is not
+ * set when invoking the callback.
+ *
+ * @param pdu the PDUs of the message
+ * @param format the format of the PDUs, typically "3gpp" or "3gpp2"
+ * @param destPort the destination port of a binary SMS, this will be -1 for text SMS
+ * @param subId SMS subscription ID of the SIM
+ * @param callback result callback. Call with a bitmask integer to indicate how the incoming
+ * text SMS should be handled by the platform. Use {@link #RECEIVE_OPTIONS_DROP} and
+ * {@link #RECEIVE_OPTIONS_SKIP_NOTIFY_WHEN_CREDENTIAL_ENCRYPTED_STORAGE_UNAVAILABLE}
+ * to set the flags in the bitmask.
+ */
+ public void onReceiveTextSms(@NonNull MessagePdu pdu, @NonNull String format,
+ int destPort, int subId, @NonNull final ResultCallback<Integer> callback) {
+ onFilterSms(pdu, format, destPort, subId, new ResultCallback<Boolean>() {
+ @Override
+ public void onReceiveResult(Boolean result) throws RemoteException {
+ callback.onReceiveResult(result ? RECEIVE_OPTIONS_DEFAULT : RECEIVE_OPTIONS_DROP
+ | RECEIVE_OPTIONS_SKIP_NOTIFY_WHEN_CREDENTIAL_ENCRYPTED_STORAGE_UNAVAILABLE);
+ }
+ });
+ }
+
+ /**
* Override this method to intercept text SMSs sent from the device.
* @deprecated Override {@link #onSendTextSms} below instead.
*
@@ -408,10 +464,11 @@
@Override
public void filterSms(MessagePdu pdu, String format, int destPort,
int subId, final ICarrierMessagingCallback callback) {
- onFilterSms(pdu, format, destPort, subId, new ResultCallback<Boolean>() {
+ onReceiveTextSms(pdu, format, destPort, subId,
+ new ResultCallback<Integer>() {
@Override
- public void onReceiveResult(final Boolean result) throws RemoteException {
- callback.onFilterComplete(result);
+ public void onReceiveResult(Integer options) throws RemoteException {
+ callback.onFilterComplete(options);
}
});
}
diff --git a/core/java/android/service/carrier/ICarrierMessagingCallback.aidl b/core/java/android/service/carrier/ICarrierMessagingCallback.aidl
index 6118a20..2753669 100644
--- a/core/java/android/service/carrier/ICarrierMessagingCallback.aidl
+++ b/core/java/android/service/carrier/ICarrierMessagingCallback.aidl
@@ -22,7 +22,7 @@
* @hide
*/
oneway interface ICarrierMessagingCallback {
- void onFilterComplete(boolean keepMessage);
+ void onFilterComplete(int result);
void onSendSmsComplete(int result, int messageRef);
void onSendMultipartSmsComplete(int result, in int[] messageRefs);
void onSendMmsComplete(int result, in byte[] sendConfPdu);
diff --git a/core/java/android/text/util/Linkify.java b/core/java/android/text/util/Linkify.java
index fbd9924..bd376ea 100644
--- a/core/java/android/text/util/Linkify.java
+++ b/core/java/android/text/util/Linkify.java
@@ -224,7 +224,7 @@
}
if ((mask & EMAIL_ADDRESSES) != 0) {
- gatherLinks(links, text, Patterns.EMAIL_ADDRESS,
+ gatherLinks(links, text, Patterns.AUTOLINK_EMAIL_ADDRESS,
new String[] { "mailto:" },
null, null);
}
diff --git a/core/java/android/util/LocaleList.java b/core/java/android/util/LocaleList.java
index fc39004..fa3921c 100644
--- a/core/java/android/util/LocaleList.java
+++ b/core/java/android/util/LocaleList.java
@@ -478,8 +478,6 @@
/**
* Returns the default locale list, adjusted by moving the default locale to its first
* position.
- *
- * {@hide}
*/
@NonNull @Size(min=1)
public static LocaleList getAdjustedDefault() {
diff --git a/core/java/android/util/Patterns.java b/core/java/android/util/Patterns.java
index 9f2bcfd..9ed4850 100644
--- a/core/java/android/util/Patterns.java
+++ b/core/java/android/util/Patterns.java
@@ -394,6 +394,36 @@
public static final Pattern AUTOLINK_WEB_URL = Pattern.compile(
"(" + WEB_URL_WITH_PROTOCOL + "|" + WEB_URL_WITHOUT_PROTOCOL + ")");
+ /**
+ * Regular expression for valid email characters. Does not include some of the valid characters
+ * defined in RFC5321: #&~!^`{}/=$*?|
+ */
+ private static final String EMAIL_CHAR = LABEL_CHAR + "\\+\\-_%'";
+
+ /**
+ * Regular expression for local part of an email address. RFC5321 section 4.5.3.1.1 limits
+ * the local part to be at most 64 octets.
+ */
+ private static final String EMAIL_ADDRESS_LOCAL_PART =
+ "[" + EMAIL_CHAR + "]" + "(?:[" + EMAIL_CHAR + "\\.]{1,62}[" + EMAIL_CHAR + "])?";
+
+ /**
+ * Regular expression for the domain part of an email address. RFC5321 section 4.5.3.1.2 limits
+ * the domain to be at most 255 octets.
+ */
+ private static final String EMAIL_ADDRESS_DOMAIN =
+ "(?=.{1,255}(?:\\s|$|^))" + HOST_NAME;
+
+ /**
+ * Regular expression pattern to match email addresses. It excludes double quoted local parts
+ * and the special characters #&~!^`{}/=$*?| that are included in RFC5321.
+ * @hide
+ */
+ public static final Pattern AUTOLINK_EMAIL_ADDRESS = Pattern.compile("(" + WORD_BOUNDARY +
+ "(?:" + EMAIL_ADDRESS_LOCAL_PART + "@" + EMAIL_ADDRESS_DOMAIN + ")" +
+ WORD_BOUNDARY + ")"
+ );
+
public static final Pattern EMAIL_ADDRESS
= Pattern.compile(
"[a-zA-Z0-9\\+\\.\\_\\%\\-\\+]{1,256}" +
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
index 728f723..60c7270 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
@@ -75,6 +75,36 @@
public static final int SF_ATTRIBUTE_ANDROID_APK_SIGNED_ID = 2;
/**
+ * Returns {@code true} if the provided APK contains an APK Signature Scheme V2
+ * signature. The signature will not be verified.
+ */
+ public static boolean hasSignature(String apkFile) throws IOException {
+ try (RandomAccessFile apk = new RandomAccessFile(apkFile, "r")) {
+ long fileSize = apk.length();
+ if (fileSize > Integer.MAX_VALUE) {
+ return false;
+ }
+ MappedByteBuffer apkContents =
+ apk.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, fileSize);
+ // ZipUtils and APK Signature Scheme v2 verifier expect little-endian byte order.
+ apkContents.order(ByteOrder.LITTLE_ENDIAN);
+
+ final int centralDirOffset =
+ (int) getCentralDirOffset(apkContents, getEocdOffset(apkContents));
+ // Find the APK Signing Block.
+ int apkSigningBlockOffset = findApkSigningBlock(apkContents, centralDirOffset);
+ ByteBuffer apkSigningBlock =
+ sliceFromTo(apkContents, apkSigningBlockOffset, centralDirOffset);
+
+ // Find the APK Signature Scheme v2 Block inside the APK Signing Block.
+ findApkSignatureSchemeV2Block(apkSigningBlock);
+ return true;
+ } catch (SignatureNotFoundException e) {
+ }
+ return false;
+ }
+
+ /**
* Verifies APK Signature Scheme v2 signatures of the provided APK and returns the certificates
* associated with each signer.
*
@@ -130,31 +160,8 @@
// ZipUtils and APK Signature Scheme v2 verifier expect little-endian byte order.
apkContents.order(ByteOrder.LITTLE_ENDIAN);
- // Find the offset of ZIP End of Central Directory (EoCD)
- int eocdOffset = ZipUtils.findZipEndOfCentralDirectoryRecord(apkContents);
- if (eocdOffset == -1) {
- throw new SignatureNotFoundException(
- "Not an APK file: ZIP End of Central Directory record not found");
- }
- if (ZipUtils.isZip64EndOfCentralDirectoryLocatorPresent(apkContents, eocdOffset)) {
- throw new SignatureNotFoundException("ZIP64 APK not supported");
- }
- ByteBuffer eocd = sliceFromTo(apkContents, eocdOffset, apkContents.capacity());
-
- // Look up the offset of ZIP Central Directory.
- long centralDirOffsetLong = ZipUtils.getZipEocdCentralDirectoryOffset(eocd);
- if (centralDirOffsetLong >= eocdOffset) {
- throw new SignatureNotFoundException(
- "ZIP Central Directory offset out of range: " + centralDirOffsetLong
- + ". ZIP End of Central Directory offset: " + eocdOffset);
- }
- long centralDirSizeLong = ZipUtils.getZipEocdCentralDirectorySizeBytes(eocd);
- if (centralDirOffsetLong + centralDirSizeLong != eocdOffset) {
- throw new SignatureNotFoundException(
- "ZIP Central Directory is not immediately followed by End of Central"
- + " Directory");
- }
- int centralDirOffset = (int) centralDirOffsetLong;
+ final int eocdOffset = getEocdOffset(apkContents);
+ final int centralDirOffset = (int) getCentralDirOffset(apkContents, eocdOffset);
// Find the APK Signing Block.
int apkSigningBlockOffset = findApkSigningBlock(apkContents, centralDirOffset);
@@ -499,6 +506,43 @@
return result;
}
+ /**
+ * Finds the offset of ZIP End of Central Directory (EoCD).
+ *
+ * @throws SignatureNotFoundException If the EoCD could not be found
+ */
+ private static int getEocdOffset(ByteBuffer apkContents) throws SignatureNotFoundException {
+ int eocdOffset = ZipUtils.findZipEndOfCentralDirectoryRecord(apkContents);
+ if (eocdOffset == -1) {
+ throw new SignatureNotFoundException(
+ "Not an APK file: ZIP End of Central Directory record not found");
+ }
+ return eocdOffset;
+ }
+
+ private static long getCentralDirOffset(ByteBuffer apkContents, int eocdOffset)
+ throws SignatureNotFoundException {
+ if (ZipUtils.isZip64EndOfCentralDirectoryLocatorPresent(apkContents, eocdOffset)) {
+ throw new SignatureNotFoundException("ZIP64 APK not supported");
+ }
+ ByteBuffer eocd = sliceFromTo(apkContents, eocdOffset, apkContents.capacity());
+
+ // Look up the offset of ZIP Central Directory.
+ long centralDirOffsetLong = ZipUtils.getZipEocdCentralDirectoryOffset(eocd);
+ if (centralDirOffsetLong >= eocdOffset) {
+ throw new SignatureNotFoundException(
+ "ZIP Central Directory offset out of range: " + centralDirOffsetLong
+ + ". ZIP End of Central Directory offset: " + eocdOffset);
+ }
+ long centralDirSizeLong = ZipUtils.getZipEocdCentralDirectorySizeBytes(eocd);
+ if (centralDirOffsetLong + centralDirSizeLong != eocdOffset) {
+ throw new SignatureNotFoundException(
+ "ZIP Central Directory is not immediately followed by End of Central"
+ + " Directory");
+ }
+ return centralDirOffsetLong;
+ }
+
private static final int getChunkCount(int inputSizeBytes) {
return (inputSizeBytes + CHUNK_SIZE_BYTES - 1) / CHUNK_SIZE_BYTES;
}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 9543acf..8048301 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -167,7 +167,7 @@
in CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
int icon, int logo, int windowFlags, IBinder transferFrom, boolean createIfNeeded);
void setAppVisibility(IBinder token, boolean visible);
- void notifyAppStopped(IBinder token);
+ void notifyAppStopped(IBinder token, boolean stopped);
void startAppFreezingScreen(IBinder token, int configChanges);
void stopAppFreezingScreen(IBinder token, boolean force);
void removeAppToken(IBinder token);
diff --git a/core/java/android/widget/EditText.java b/core/java/android/widget/EditText.java
index 1d242d3..434e3eb 100644
--- a/core/java/android/widget/EditText.java
+++ b/core/java/android/widget/EditText.java
@@ -117,6 +117,16 @@
Selection.extendSelection(getText(), index);
}
+ /**
+ * Causes words in the text that are longer than the view's width to be ellipsized instead of
+ * broken in the middle. {@link TextUtils.TruncateAt#MARQUEE
+ * TextUtils.TruncateAt#MARQUEE} is not supported.
+ *
+ * @param ellipsis Type of ellipsis to be applied.
+ * @throws IllegalArgumentException When the value of <code>ellipsis</code> parameter is
+ * {@link TextUtils.TruncateAt#MARQUEE}.
+ * @see TextView#setEllipsize(TextUtils.TruncateAt)
+ */
@Override
public void setEllipsize(TextUtils.TruncateAt ellipsis) {
if (ellipsis == TextUtils.TruncateAt.MARQUEE) {
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 881e5cd..a7b12c1 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -215,7 +215,7 @@
boolean mInBatchEditControllers;
boolean mShowSoftInputOnFocus = true;
- boolean mPreserveDetachedSelection;
+ private boolean mPreserveDetachedSelection;
boolean mTemporaryDetach;
boolean mIsBeingLongClicked;
@@ -352,7 +352,6 @@
void replace() {
int middle = (mTextView.getSelectionStart() + mTextView.getSelectionEnd()) / 2;
- stopTextActionMode();
Selection.setSelection((Spannable) mTextView.getText(), middle);
showSuggestions();
}
@@ -429,10 +428,8 @@
mSpellChecker = null;
}
- mPreserveDetachedSelection = true;
hideCursorAndSpanControllers();
- stopTextActionMode();
- mPreserveDetachedSelection = false;
+ stopTextActionModeWithPreservingSelection();
mTemporaryDetach = false;
}
@@ -1104,7 +1101,6 @@
mInsertionControllerEnabled) {
final int offset = mTextView.getOffsetForPosition(mLastDownPositionX,
mLastDownPositionY);
- stopTextActionMode();
Selection.setSelection((Spannable) mTextView.getText(), offset);
getInsertionController().show();
mIsInsertionActionModeStartPending = true;
@@ -1208,18 +1204,15 @@
mTextView.onEndBatchEdit();
if (mTextView.isInExtractedMode()) {
- // terminateTextSelectionMode removes selection, which we want to keep when
- // ExtractEditText goes out of focus.
- final int selStart = mTextView.getSelectionStart();
- final int selEnd = mTextView.getSelectionEnd();
hideCursorAndSpanControllers();
- stopTextActionMode();
- Selection.setSelection((Spannable) mTextView.getText(), selStart, selEnd);
+ stopTextActionModeWithPreservingSelection();
} else {
- if (mTemporaryDetach) mPreserveDetachedSelection = true;
hideCursorAndSpanControllers();
- stopTextActionMode();
- if (mTemporaryDetach) mPreserveDetachedSelection = false;
+ if (mTemporaryDetach) {
+ stopTextActionModeWithPreservingSelection();
+ } else {
+ stopTextActionMode();
+ }
downgradeEasyCorrectionSpans();
}
// No need to create the controller
@@ -1290,10 +1283,8 @@
makeBlink();
}
final InputMethodManager imm = InputMethodManager.peekInstance();
- final boolean immFullScreen = (imm != null && imm.isFullscreenMode());
- if (mSelectionModifierCursorController != null && mTextView.hasSelection()
- && !immFullScreen && mTextActionMode != null) {
- mSelectionModifierCursorController.show();
+ if (mTextView.hasSelection() && !extractedTextModeWillBeStarted()) {
+ startSelectionActionMode();
}
} else {
if (mBlink != null) {
@@ -1304,9 +1295,7 @@
}
// Order matters! Must be done before onParentLostFocus to rely on isShowingUp
hideCursorAndSpanControllers();
- if (mSelectionModifierCursorController != null) {
- mSelectionModifierCursorController.hide();
- }
+ stopTextActionModeWithPreservingSelection();
if (mSuggestionsPopupWindow != null) {
mSuggestionsPopupWindow.onParentLostFocus();
}
@@ -1856,6 +1845,38 @@
}
}
+ void refreshTextActionMode() {
+ if (extractedTextModeWillBeStarted()) {
+ return;
+ }
+ final boolean hasSelection = mTextView.hasSelection();
+ final SelectionModifierCursorController selectionController = getSelectionController();
+ final InsertionPointCursorController insertionController = getInsertionController();
+ if ((selectionController != null && selectionController.isCursorBeingModified())
+ || (insertionController != null && insertionController.isCursorBeingModified())) {
+ // ActionMode should be managed by the currently active cursor controller.
+ return;
+ }
+ if (hasSelection) {
+ if (mTextActionMode == null || selectionController == null
+ || !selectionController.isActive()) {
+ // Avoid dismissing the selection if it exists.
+ stopTextActionModeWithPreservingSelection();
+ startSelectionActionMode();
+ } else {
+ mTextActionMode.invalidateContentRect();
+ }
+ } else {
+ // Insertion action mode is started only when insertion controller is explicitly
+ // activated.
+ if (insertionController == null || !insertionController.isActive()) {
+ stopTextActionMode();
+ } else if (mTextActionMode != null) {
+ mTextActionMode.invalidateContentRect();
+ }
+ }
+ }
+
/**
* Start an Insertion action mode.
*/
@@ -1879,17 +1900,15 @@
/**
* Starts a Selection Action Mode with the current selection and ensures the selection handles
- * are shown if there is a selection, otherwise the insertion handle is shown. This should be
- * used when the mode is started from a non-touch event.
+ * are shown if there is a selection. This should be used when the mode is started from a
+ * non-touch event.
*
* @return true if the selection mode was actually started.
*/
- boolean startSelectionActionMode() {
+ private boolean startSelectionActionMode() {
boolean selectionStarted = startSelectionActionModeInternal();
if (selectionStarted) {
getSelectionController().show();
- } else if (getInsertionController() != null) {
- getInsertionController().show();
}
return selectionStarted;
}
@@ -1907,66 +1926,52 @@
if (extractedTextModeWillBeStarted()) {
return false;
}
- if (mTextActionMode != null) {
- mTextActionMode.finish();
- }
- if (!checkFieldAndSelectCurrentWord()) {
+ if (!checkField()) {
return false;
}
-
- // Avoid dismissing the selection if it exists.
- mPreserveDetachedSelection = true;
- stopTextActionMode();
- mPreserveDetachedSelection = false;
-
+ if (!mTextView.hasSelection() && !selectCurrentWord()) {
+ // No selection and cannot select a word.
+ return false;
+ }
+ stopTextActionModeWithPreservingSelection();
getSelectionController().enterDrag(
SelectionModifierCursorController.DRAG_ACCELERATOR_MODE_WORD);
return true;
}
/**
- * Checks whether a selection can be performed on the current TextView and if so selects
- * the current word.
+ * Checks whether a selection can be performed on the current TextView.
*
- * @return true if there already was a selection or if the current word was selected.
+ * @return true if a selection can be performed
*/
- boolean checkFieldAndSelectCurrentWord() {
+ boolean checkField() {
if (!mTextView.canSelectText() || !mTextView.requestFocus()) {
Log.w(TextView.LOG_TAG,
"TextView does not support text selection. Selection cancelled.");
return false;
}
-
- if (!mTextView.hasSelection()) {
- // There may already be a selection on device rotation
- return selectCurrentWord();
- }
return true;
}
private boolean startSelectionActionModeInternal() {
+ if (extractedTextModeWillBeStarted()) {
+ return false;
+ }
if (mTextActionMode != null) {
// Text action mode is already started
mTextActionMode.invalidate();
return false;
}
- if (!checkFieldAndSelectCurrentWord()) {
+ if (!checkField() || !mTextView.hasSelection()) {
return false;
}
- boolean willExtract = extractedTextModeWillBeStarted();
+ ActionMode.Callback actionModeCallback =
+ new TextActionModeCallback(true /* hasSelection */);
+ mTextActionMode = mTextView.startActionMode(actionModeCallback, ActionMode.TYPE_FLOATING);
- // Do not start the action mode when extracted text will show up full screen, which would
- // immediately hide the newly created action bar and would be visually distracting.
- if (!willExtract) {
- ActionMode.Callback actionModeCallback =
- new TextActionModeCallback(true /* hasSelection */);
- mTextActionMode = mTextView.startActionMode(
- actionModeCallback, ActionMode.TYPE_FLOATING);
- }
-
- final boolean selectionStarted = mTextActionMode != null || willExtract;
+ final boolean selectionStarted = mTextActionMode != null;
if (selectionStarted && !mTextView.isTextSelectable() && mShowSoftInputOnFocus) {
// Show the IME to be able to replace text, except when selecting non editable text.
final InputMethodManager imm = InputMethodManager.peekInstance();
@@ -2107,6 +2112,12 @@
}
}
+ private void stopTextActionModeWithPreservingSelection() {
+ mPreserveDetachedSelection = true;
+ stopTextActionMode();
+ mPreserveDetachedSelection = false;
+ }
+
/**
* @return True if this view supports insertion handles.
*/
@@ -2436,16 +2447,14 @@
if (offset == -1) {
return;
}
- mPreserveDetachedSelection = true;
- stopTextActionMode();
- mPreserveDetachedSelection = false;
+ stopTextActionModeWithPreservingSelection();
final boolean isOnSelection = mTextView.hasSelection()
&& offset >= mTextView.getSelectionStart() && offset <= mTextView.getSelectionEnd();
if (!isOnSelection) {
// Right clicked position is not on the selection. Remove the selection and move the
// cursor to the right clicked position.
- stopTextActionMode();
Selection.setSelection((Spannable) mTextView.getText(), offset);
+ stopTextActionMode();
}
if (shouldOfferToShowSuggestions()) {
@@ -3160,6 +3169,7 @@
mTextView.getContext(), mTextView.mTextEditSuggestionHighlightStyle);
private TextView mAddToDictionaryButton;
private TextView mDeleteButton;
+ private ListView mSuggestionListView;
private SuggestionSpan mMisspelledSpan;
private int mContainerMarginWidth;
private int mContainerMarginTop;
@@ -3177,7 +3187,7 @@
((Spannable) mTextView.getText()).removeSpan(mSuggestionRangeSpan);
mTextView.setCursorVisible(mCursorWasVisibleBeforeSuggestions);
- if (hasInsertionController()) {
+ if (hasInsertionController() && !extractedTextModeWillBeStarted()) {
getInsertionController().show();
}
}
@@ -3213,12 +3223,12 @@
mClippingLimitLeft = lp.leftMargin;
mClippingLimitRight = lp.rightMargin;
- final ListView suggestionListView = (ListView) relativeLayout.findViewById(
+ mSuggestionListView = (ListView) relativeLayout.findViewById(
com.android.internal.R.id.suggestionContainer);
mSuggestionsAdapter = new SuggestionAdapter();
- suggestionListView.setAdapter(mSuggestionsAdapter);
- suggestionListView.setOnItemClickListener(this);
+ mSuggestionListView.setAdapter(mSuggestionsAdapter);
+ mSuggestionListView.setOnItemClickListener(this);
// Inflate the suggestion items once and for all.
mSuggestionInfos = new SuggestionInfo[MAX_NUMBER_SUGGESTIONS];
@@ -3327,6 +3337,9 @@
@Override
public void show() {
if (!(mTextView.getText() instanceof Editable)) return;
+ if (extractedTextModeWillBeStarted()) {
+ return;
+ }
if (updateSuggestions()) {
mCursorWasVisibleBeforeSuggestions = mCursorVisible;
@@ -3374,6 +3387,7 @@
popupBackground.getPadding(mTempRect);
width += mTempRect.left + mTempRect.right;
}
+ mSuggestionListView.getLayoutParams().width = width;
mPopupWindow.setWidth(width);
}
@@ -3483,7 +3497,6 @@
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Editable editable = (Editable) mTextView.getText();
SuggestionInfo suggestionInfo = mSuggestionInfos[position];
-
final int spanStart = editable.getSpanStart(suggestionInfo.mSuggestionSpan);
final int spanEnd = editable.getSpanEnd(suggestionInfo.mSuggestionSpan);
if (spanStart < 0 || spanEnd <= spanStart) {
@@ -4386,7 +4399,7 @@
if (distanceSquared < touchSlop * touchSlop) {
// Tapping on the handle toggles the insertion action mode.
if (mTextActionMode != null) {
- mTextActionMode.finish();
+ stopTextActionMode();
} else {
startInsertionActionMode();
}
@@ -4803,6 +4816,10 @@
* preventing the activity from being recycled.
*/
public void onDetached();
+
+ public boolean isCursorBeingModified();
+
+ public boolean isActive();
}
private class InsertionPointCursorController implements CursorController {
@@ -4846,6 +4863,16 @@
if (mHandle != null) mHandle.onDetached();
}
+
+ @Override
+ public boolean isCursorBeingModified() {
+ return mHandle != null && mHandle.isDragging();
+ }
+
+ @Override
+ public boolean isActive() {
+ return mHandle != null && mHandle.isShowing();
+ }
}
class SelectionModifierCursorController implements CursorController {
@@ -5035,9 +5062,7 @@
if (mStartOffset != offset) {
// Start character based drag accelerator.
- if (mTextActionMode != null) {
- mTextActionMode.finish();
- }
+ stopTextActionMode();
enterDrag(DRAG_ACCELERATOR_MODE_CHARACTER);
mDiscardNextActionUp = true;
mHaventMovedEnoughToStartDrag = false;
@@ -5111,9 +5136,7 @@
if (mInsertionActionModeRunnable != null) {
mTextView.removeCallbacks(mInsertionActionModeRunnable);
}
- if (mTextActionMode != null) {
- mTextActionMode.finish();
- }
+ stopTextActionMode();
if (!selectCurrentParagraph()) {
return false;
}
@@ -5222,6 +5245,12 @@
mStartOffset = -1;
mDragAcceleratorMode = DRAG_ACCELERATOR_MODE_INACTIVE;
mSwitchedLines = false;
+ final int selectionStart = mTextView.getSelectionStart();
+ final int selectionEnd = mTextView.getSelectionEnd();
+ if (selectionStart > selectionEnd) {
+ Selection.setSelection((Spannable) mTextView.getText(),
+ selectionEnd, selectionStart);
+ }
}
/**
@@ -5231,6 +5260,12 @@
return mStartHandle != null && mStartHandle.isDragging();
}
+ @Override
+ public boolean isCursorBeingModified() {
+ return isDragAcceleratorActive() || isSelectionStartDragged()
+ || (mEndHandle != null && mEndHandle.isDragging());
+ }
+
/**
* @return true if the user is selecting text using the drag accelerator.
*/
@@ -5252,6 +5287,11 @@
if (mStartHandle != null) mStartHandle.onDetached();
if (mEndHandle != null) mEndHandle.onDetached();
}
+
+ @Override
+ public boolean isActive() {
+ return mStartHandle != null && mStartHandle.isShowing();
+ }
}
private class CorrectionHighlighter {
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 2099b04..72a50ec1 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -17,21 +17,15 @@
package android.widget;
import android.animation.ObjectAnimator;
+import android.annotation.InterpolatorRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.graphics.PorterDuff;
-
-import android.util.FloatProperty;
-import android.util.IntProperty;
-import android.view.accessibility.AccessibilityNodeInfo;
-import com.android.internal.R;
-
-import android.annotation.InterpolatorRes;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.Shader;
import android.graphics.drawable.Animatable;
@@ -46,6 +40,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
+import android.util.FloatProperty;
import android.util.MathUtils;
import android.util.Pools.SynchronizedPool;
import android.view.Gravity;
@@ -55,6 +50,7 @@
import android.view.ViewHierarchyEncoder;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
@@ -63,6 +59,7 @@
import android.view.animation.LinearInterpolator;
import android.view.animation.Transformation;
import android.widget.RemoteViews.RemoteView;
+import com.android.internal.R;
import java.util.ArrayList;
@@ -606,15 +603,30 @@
if (indeterminate) {
// swap between indeterminate and regular backgrounds
- mCurrentDrawable = mIndeterminateDrawable;
+ swapCurrentDrawable(mIndeterminateDrawable);
startAnimation();
} else {
- mCurrentDrawable = mProgressDrawable;
+ swapCurrentDrawable(mProgressDrawable);
stopAnimation();
}
}
}
+ private void swapCurrentDrawable(Drawable newDrawable) {
+ final Drawable oldDrawable = mCurrentDrawable;
+ mCurrentDrawable = newDrawable;
+ if (oldDrawable != mCurrentDrawable) {
+ if (oldDrawable != null) {
+ oldDrawable.setVisible(false, false);
+ }
+ if (mCurrentDrawable != null) {
+ mCurrentDrawable.setVisible(
+ getVisibility() == VISIBLE && getWindowVisibility() == VISIBLE,
+ false);
+ }
+ }
+ }
+
/**
* <p>Get the drawable used to draw the progress bar in
* indeterminate mode.</p>
@@ -654,7 +666,7 @@
}
if (mIndeterminate) {
- mCurrentDrawable = d;
+ swapCurrentDrawable(d);
postInvalidate();
}
}
@@ -820,7 +832,7 @@
}
if (!mIndeterminate) {
- mCurrentDrawable = d;
+ swapCurrentDrawable(d);
postInvalidate();
}
@@ -1555,7 +1567,7 @@
* <p>Start the indeterminate progress animation.</p>
*/
void startAnimation() {
- if (getVisibility() != VISIBLE) {
+ if (getVisibility() != VISIBLE || getWindowVisibility() != VISIBLE) {
return;
}
@@ -1653,14 +1665,30 @@
protected void onVisibilityChanged(View changedView, int visibility) {
super.onVisibilityChanged(changedView, visibility);
+ updateVisibility();
+ }
+
+ @Override
+ protected void onWindowVisibilityChanged(@Visibility int visibility) {
+ super.onWindowVisibilityChanged(visibility);
+
+ updateVisibility();
+ }
+
+ private void updateVisibility() {
+ final boolean isVisible = getVisibility() == VISIBLE && getWindowVisibility() == VISIBLE;
if (mIndeterminate) {
// let's be nice with the UI thread
- if (visibility == GONE || visibility == INVISIBLE) {
- stopAnimation();
- } else {
+ if (isVisible) {
startAnimation();
+ } else {
+ stopAnimation();
}
}
+
+ if (mCurrentDrawable != null) {
+ mCurrentDrawable.setVisible(isVisible, false);
+ }
}
@Override
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index f19bf02..73f8fdc 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -20,6 +20,7 @@
import android.R;
import android.annotation.ColorInt;
import android.annotation.DrawableRes;
+import android.annotation.FloatRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Size;
@@ -1510,6 +1511,9 @@
if (result != null) {
if (isTextEditable()) {
replaceSelectionWithText(result);
+ if (mEditor != null) {
+ mEditor.refreshTextActionMode();
+ }
} else {
if (result.length() > 0) {
Toast.makeText(getContext(), String.valueOf(result), Toast.LENGTH_LONG)
@@ -1519,12 +1523,7 @@
}
} else if (mText instanceof Spannable) {
// Reset the selection.
- stopTextActionMode();
- Selection.setSelection((Spannable) mText, getSelectionStart(), getSelectionEnd());
- }
-
- if (mEditor.hasSelectionController()) {
- mEditor.startSelectionActionMode();
+ Selection.setSelection((Spannable) mText, getSelectionEnd());
}
}
}
@@ -5392,11 +5391,7 @@
// - onFocusChanged cannot start it when focus is given to a view with selected text (after
// a screen rotation) since layout is not yet initialized at that point.
if (mEditor != null && mEditor.mCreatedWithASelection) {
- if (mEditor.extractedTextModeWillBeStarted()) {
- mEditor.checkFieldAndSelectCurrentWord();
- } else {
- mEditor.startSelectionActionMode();
- }
+ mEditor.refreshTextActionMode();
mEditor.mCreatedWithASelection = false;
}
@@ -5835,8 +5830,7 @@
final int layoutDirection = getLayoutDirection();
final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection);
- if (mEllipsize == TextUtils.TruncateAt.MARQUEE &&
- mMarqueeFadeMode != MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS) {
+ if (isMarqueeFadeEnabled()) {
if (!mSingleLine && getLineCount() == 1 && canMarquee() &&
(absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) != Gravity.LEFT) {
final int width = mRight - mLeft;
@@ -6594,6 +6588,9 @@
// in the extracted view.
mEditor.hideCursorAndSpanControllers();
stopTextActionMode();
+ if (mEditor.mSelectionModifierCursorController != null) {
+ mEditor.mSelectionModifierCursorController.resetTouchOffsets();
+ }
}
/**
@@ -7888,7 +7885,7 @@
}
/**
- * Causes words in the text that are longer than the view is wide
+ * Causes words in the text that are longer than the view's width
* to be ellipsized instead of broken in the middle. You may also
* want to {@link #setSingleLine} or {@link #setHorizontallyScrolling}
* to constrain the text to a single line. Use <code>null</code>
@@ -8289,6 +8286,9 @@
if (newSelEnd < 0) {
newSelEnd = Selection.getSelectionEnd(buf);
}
+ if (mEditor != null) {
+ mEditor.refreshTextActionMode();
+ }
onSelectionChanged(newSelStart, newSelEnd);
}
}
@@ -8616,78 +8616,59 @@
@Override
protected float getLeftFadingEdgeStrength() {
- if (mEllipsize == TextUtils.TruncateAt.MARQUEE &&
- mMarqueeFadeMode != MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS) {
- if (mMarquee != null && !mMarquee.isStopped()) {
- final Marquee marquee = mMarquee;
- if (marquee.shouldDrawLeftFade()) {
- final float scroll = marquee.getScroll();
- return scroll / getHorizontalFadingEdgeLength();
- } else {
- return 0.0f;
- }
- } else if (getLineCount() == 1) {
- final int layoutDirection = getLayoutDirection();
- final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection);
- switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
- case Gravity.LEFT:
- return 0.0f;
- case Gravity.RIGHT:
- return (mLayout.getLineRight(0) - (mRight - mLeft) -
- getCompoundPaddingLeft() - getCompoundPaddingRight() -
- mLayout.getLineLeft(0)) / getHorizontalFadingEdgeLength();
- case Gravity.CENTER_HORIZONTAL:
- case Gravity.FILL_HORIZONTAL:
- final int textDirection = mLayout.getParagraphDirection(0);
- if (textDirection == Layout.DIR_LEFT_TO_RIGHT) {
- return 0.0f;
- } else {
- return (mLayout.getLineRight(0) - (mRight - mLeft) -
- getCompoundPaddingLeft() - getCompoundPaddingRight() -
- mLayout.getLineLeft(0)) / getHorizontalFadingEdgeLength();
- }
- }
+ if (isMarqueeFadeEnabled() && mMarquee != null && !mMarquee.isStopped()) {
+ final Marquee marquee = mMarquee;
+ if (marquee.shouldDrawLeftFade()) {
+ return getHorizontalFadingEdgeStrength(marquee.getScroll(), 0.0f);
+ } else {
+ return 0.0f;
}
+ } else if (getLineCount() == 1) {
+ final float lineLeft = getLayout().getLineLeft(0);
+ if(lineLeft > mScrollX) return 0.0f;
+ return getHorizontalFadingEdgeStrength(mScrollX, lineLeft);
}
return super.getLeftFadingEdgeStrength();
}
@Override
protected float getRightFadingEdgeStrength() {
- if (mEllipsize == TextUtils.TruncateAt.MARQUEE &&
- mMarqueeFadeMode != MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS) {
- if (mMarquee != null && !mMarquee.isStopped()) {
- final Marquee marquee = mMarquee;
- final float maxFadeScroll = marquee.getMaxFadeScroll();
- final float scroll = marquee.getScroll();
- return (maxFadeScroll - scroll) / getHorizontalFadingEdgeLength();
- } else if (getLineCount() == 1) {
- final int layoutDirection = getLayoutDirection();
- final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection);
- switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
- case Gravity.LEFT:
- final int textWidth = (mRight - mLeft) - getCompoundPaddingLeft() -
- getCompoundPaddingRight();
- final float lineWidth = mLayout.getLineWidth(0);
- return (lineWidth - textWidth) / getHorizontalFadingEdgeLength();
- case Gravity.RIGHT:
- return 0.0f;
- case Gravity.CENTER_HORIZONTAL:
- case Gravity.FILL_HORIZONTAL:
- final int textDirection = mLayout.getParagraphDirection(0);
- if (textDirection == Layout.DIR_RIGHT_TO_LEFT) {
- return 0.0f;
- } else {
- return (mLayout.getLineWidth(0) - ((mRight - mLeft) -
- getCompoundPaddingLeft() - getCompoundPaddingRight())) /
- getHorizontalFadingEdgeLength();
- }
- }
- }
+ if (isMarqueeFadeEnabled() && mMarquee != null && !mMarquee.isStopped()) {
+ final Marquee marquee = mMarquee;
+ return getHorizontalFadingEdgeStrength(marquee.getMaxFadeScroll(), marquee.getScroll());
+ } else if (getLineCount() == 1) {
+ final float rightEdge = mScrollX + (getWidth() - getCompoundPaddingLeft() -
+ getCompoundPaddingRight());
+ final float lineRight = getLayout().getLineRight(0);
+ if(lineRight < rightEdge) return 0.0f;
+ return getHorizontalFadingEdgeStrength(rightEdge, lineRight);
}
return super.getRightFadingEdgeStrength();
}
+ /**
+ * Calculates the fading edge strength as the ratio of the distance between two
+ * horizontal positions to {@link View#getHorizontalFadingEdgeLength()}. Uses the absolute
+ * value for the distance calculation.
+ *
+ * @param position1 A horizontal position.
+ * @param position2 A horizontal position.
+ * @return Fading edge strength between [0.0f, 1.0f].
+ */
+ @FloatRange(from=0.0, to=1.0)
+ private final float getHorizontalFadingEdgeStrength(float position1, float position2) {
+ final int horizontalFadingEdgeLength = getHorizontalFadingEdgeLength();
+ if(horizontalFadingEdgeLength == 0) return 0.0f;
+ final float diff = Math.abs(position1 - position2);
+ if(diff > horizontalFadingEdgeLength) return 1.0f;
+ return diff / horizontalFadingEdgeLength;
+ }
+
+ private final boolean isMarqueeFadeEnabled() {
+ return mEllipsize == TextUtils.TruncateAt.MARQUEE &&
+ mMarqueeFadeMode != MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS;
+ }
+
@Override
protected int computeHorizontalScrollRange() {
if (mLayout != null) {
@@ -9218,10 +9199,6 @@
}
if (start >= 0 && start <= end && end <= text.length()) {
Selection.setSelection((Spannable) text, start, end);
- // Make sure selection mode is engaged.
- if (mEditor != null) {
- mEditor.startSelectionActionMode();
- }
return true;
}
}
@@ -9412,16 +9389,7 @@
switch (id) {
case ID_SELECT_ALL:
- // This starts an action mode if triggered from another action mode. Text is
- // highlighted, so that it can be bulk edited, like selectAllOnFocus does. Returns
- // true even if text is empty.
- boolean shouldRestartActionMode =
- mEditor != null && mEditor.mTextActionMode != null;
- stopTextActionMode();
selectAllText();
- if (shouldRestartActionMode) {
- mEditor.startSelectionActionMode();
- }
return true;
case ID_UNDO:
@@ -9447,7 +9415,6 @@
case ID_CUT:
setPrimaryClip(ClipData.newPlainText(null, getTransformedText(min, max)));
deleteText_internal(min, max);
- stopTextActionMode();
return true;
case ID_COPY:
@@ -9703,12 +9670,6 @@
}
boolean selectAllText() {
- // Need to hide insert point cursor controller before settings selection, otherwise insert
- // point cursor controller obtains cursor update event and update cursor with cancelling
- // selection.
- if (mEditor != null) {
- mEditor.hideInsertionPointCursorController();
- }
final int length = mText.length();
Selection.setSelection((Spannable) mText, 0, length);
return length > 0;
@@ -9747,7 +9708,6 @@
}
}
}
- stopTextActionMode();
sLastCutCopyOrTextChangedTime = 0;
}
}
@@ -9760,7 +9720,7 @@
sharingIntent.removeExtra(android.content.Intent.EXTRA_TEXT);
sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, selectedText);
getContext().startActivity(Intent.createChooser(sharingIntent, null));
- stopTextActionMode();
+ Selection.setSelection((Spannable) mText, getSelectionEnd());
}
}
@@ -10078,6 +10038,12 @@
&& getAccessibilitySelectionEnd() == end) {
return;
}
+ CharSequence text = getIterableTextForAccessibility();
+ if (Math.min(start, end) >= 0 && Math.max(start, end) <= text.length()) {
+ Selection.setSelection((Spannable) text, start, end);
+ } else {
+ Selection.removeSelection((Spannable) text);
+ }
// Hide all selection controllers used for adjusting selection
// since we are doing so explicitlty by other means and these
// controllers interact with how selection behaves.
@@ -10085,12 +10051,6 @@
mEditor.hideCursorAndSpanControllers();
mEditor.stopTextActionMode();
}
- CharSequence text = getIterableTextForAccessibility();
- if (Math.min(start, end) >= 0 && Math.max(start, end) <= text.length()) {
- Selection.setSelection((Spannable) text, start, end);
- } else {
- Selection.removeSelection((Spannable) text);
- }
}
/** @hide */
diff --git a/core/java/com/android/internal/app/LocaleHelper.java b/core/java/com/android/internal/app/LocaleHelper.java
index aca93ab..d8d6e56 100644
--- a/core/java/com/android/internal/app/LocaleHelper.java
+++ b/core/java/com/android/internal/app/LocaleHelper.java
@@ -180,14 +180,16 @@
*/
public static final class LocaleInfoComparator implements Comparator<LocaleStore.LocaleInfo> {
private final Collator mCollator;
+ private final boolean mCountryMode;
/**
* Constructor.
*
* @param sortLocale the locale to be used for sorting.
*/
- public LocaleInfoComparator(Locale sortLocale) {
+ public LocaleInfoComparator(Locale sortLocale, boolean countryMode) {
mCollator = Collator.getInstance(sortLocale);
+ mCountryMode = countryMode;
}
/**
@@ -202,9 +204,9 @@
public int compare(LocaleStore.LocaleInfo lhs, LocaleStore.LocaleInfo rhs) {
// We don't care about the various suggestion types, just "suggested" (!= 0)
// and "all others" (== 0)
- if (lhs.isSuggested() == rhs.isSuggested()) {
+ if (mCountryMode || (lhs.isSuggested() == rhs.isSuggested())) {
// They are in the same "bucket" (suggested / others), so we compare the text
- return mCollator.compare(lhs.getLabel(), rhs.getLabel());
+ return mCollator.compare(lhs.getLabel(mCountryMode), rhs.getLabel(mCountryMode));
} else {
// One locale is suggested and one is not, so we put them in different "buckets"
return lhs.isSuggested() ? -1 : 1;
diff --git a/core/java/com/android/internal/app/LocalePickerWithRegion.java b/core/java/com/android/internal/app/LocalePickerWithRegion.java
index 956ee8c..2ea225f 100644
--- a/core/java/com/android/internal/app/LocalePickerWithRegion.java
+++ b/core/java/com/android/internal/app/LocalePickerWithRegion.java
@@ -50,7 +50,6 @@
private Set<LocaleStore.LocaleInfo> mLocaleList;
private LocaleStore.LocaleInfo mParentLocale;
private boolean mTranslatedOnly = false;
- private boolean mCountryMode = false;
/**
* Other classes can register to be notified when a locale was selected.
@@ -70,15 +69,14 @@
boolean translatedOnly) {
LocalePickerWithRegion localePicker = new LocalePickerWithRegion();
boolean shouldShowTheList = localePicker.setListener(context, listener, parent,
- true /* country mode */, translatedOnly);
+ translatedOnly);
return shouldShowTheList ? localePicker : null;
}
public static LocalePickerWithRegion createLanguagePicker(Context context,
LocaleSelectedListener listener, boolean translatedOnly) {
LocalePickerWithRegion localePicker = new LocalePickerWithRegion();
- localePicker.setListener(context, listener, null,
- false /* language mode */, translatedOnly);
+ localePicker.setListener(context, listener, /* parent */ null, translatedOnly);
return localePicker;
}
@@ -96,14 +94,7 @@
* "pretending" it was selected, and return false.</p>
*/
private boolean setListener(Context context, LocaleSelectedListener listener,
- LocaleStore.LocaleInfo parent, boolean countryMode, boolean translatedOnly) {
- if (countryMode && (parent == null || parent.getLocale() == null)) {
- // The list of countries is determined as all the countries where the parent language
- // is used.
- throw new IllegalArgumentException("The country selection list needs a parent.");
- }
-
- this.mCountryMode = countryMode;
+ LocaleStore.LocaleInfo parent, boolean translatedOnly) {
this.mParentLocale = parent;
this.mListener = listener;
this.mTranslatedOnly = translatedOnly;
@@ -116,7 +107,7 @@
Collections.addAll(langTagsToIgnore, langTags);
}
- if (countryMode) {
+ if (parent != null) {
mLocaleList = LocaleStore.getLevelLocales(context,
langTagsToIgnore, parent, translatedOnly);
if (mLocaleList.size() <= 1) {
@@ -138,13 +129,11 @@
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
- final Locale sortingLocale = (mCountryMode && mParentLocale != null)
- ? mParentLocale.getLocale()
- : Locale.getDefault();
-
- mAdapter = new SuggestedLocaleAdapter(mLocaleList, mCountryMode);
+ final boolean countryMode = mParentLocale != null;
+ final Locale sortingLocale = countryMode ? mParentLocale.getLocale() : Locale.getDefault();
+ mAdapter = new SuggestedLocaleAdapter(mLocaleList, countryMode);
final LocaleHelper.LocaleInfoComparator comp =
- new LocaleHelper.LocaleInfoComparator(sortingLocale);
+ new LocaleHelper.LocaleInfoComparator(sortingLocale, countryMode);
mAdapter.sort(comp);
setListAdapter(mAdapter);
}
@@ -164,12 +153,8 @@
public void onResume() {
super.onResume();
- if (mCountryMode) {
- if (mParentLocale == null) {
- this.getActivity().setTitle(R.string.country_selection_title);
- } else {
- this.getActivity().setTitle(mParentLocale.getFullNameNative());
- }
+ if (mParentLocale != null) {
+ this.getActivity().setTitle(mParentLocale.getFullNameNative());
} else {
this.getActivity().setTitle(R.string.language_selection_title);
}
@@ -182,7 +167,7 @@
final LocaleStore.LocaleInfo locale =
(LocaleStore.LocaleInfo) getListAdapter().getItem(position);
- if (mCountryMode || locale.getParent() != null) {
+ if (locale.getParent() != null) {
if (mListener != null) {
mListener.onLocaleSelected(locale);
}
@@ -205,7 +190,7 @@
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- if (!mCountryMode) {
+ if (mParentLocale == null) {
inflater.inflate(R.menu.language_selection_list, menu);
MenuItem mSearchMenuItem = menu.findItem(R.id.locale_search_menu);
diff --git a/core/java/com/android/internal/app/LocaleStore.java b/core/java/com/android/internal/app/LocaleStore.java
index 465c4d8..c4e6675 100644
--- a/core/java/com/android/internal/app/LocaleStore.java
+++ b/core/java/com/android/internal/app/LocaleStore.java
@@ -145,11 +145,11 @@
return mLangScriptKey;
}
- String getLabel() {
- if (getParent() == null || this.isSuggestionOfType(SUGGESTION_TYPE_SIM)) {
- return getFullNameNative();
- } else {
+ String getLabel(boolean countryMode) {
+ if (countryMode) {
return getFullCountryNameNative();
+ } else {
+ return getFullNameNative();
}
}
@@ -311,9 +311,7 @@
if (level == 2) {
if (parent != null) { // region selection
if (parentId.equals(li.getParent().toLanguageTag())) {
- if (!li.isSuggestionOfType(LocaleInfo.SUGGESTION_TYPE_SIM)) {
- result.add(li);
- }
+ result.add(li);
}
} else { // language selection
if (li.isSuggestionOfType(LocaleInfo.SUGGESTION_TYPE_SIM)) {
diff --git a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
index 0d4a5aa..98102ea 100644
--- a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
+++ b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
@@ -156,7 +156,7 @@
TextView text = (TextView) convertView.findViewById(R.id.locale);
LocaleStore.LocaleInfo item = (LocaleStore.LocaleInfo) getItem(position);
- text.setText(item.getLabel());
+ text.setText(item.getLabel(mCountryMode));
text.setTextLocale(item.getLocale());
if (mCountryMode) {
int layoutDir = TextUtils.getLayoutDirectionFromLocale(item.getParent());
@@ -171,6 +171,9 @@
}
private boolean showHeaders() {
+ if (mCountryMode) { // never show suggestions in country mode
+ return false;
+ }
return mSuggestionCount != 0 && mSuggestionCount != mLocaleOptions.size();
}
diff --git a/core/java/com/android/internal/widget/ImageFloatingTextView.java b/core/java/com/android/internal/widget/ImageFloatingTextView.java
index c4ed2e1..78c5e34 100644
--- a/core/java/com/android/internal/widget/ImageFloatingTextView.java
+++ b/core/java/com/android/internal/widget/ImageFloatingTextView.java
@@ -65,6 +65,8 @@
.setTextDirection(getTextDirectionHeuristic())
.setLineSpacing(getLineSpacingExtra(), getLineSpacingMultiplier())
.setIncludePad(getIncludeFontPadding())
+ .setEllipsize(shouldEllipsize ? effectiveEllipsize : null)
+ .setEllipsizedWidth(ellipsisWidth)
.setBreakStrategy(Layout.BREAK_STRATEGY_HIGH_QUALITY)
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL);
// we set the endmargin on the first 2 lines. this works just in our case but that's
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 816a2c4..623b603 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -7,7 +7,7 @@
LOCAL_CFLAGS += -Wno-unused-parameter
LOCAL_CFLAGS += -Wno-non-virtual-dtor
LOCAL_CFLAGS += -Wno-maybe-uninitialized -Wno-parentheses
-#LOCAL_CFLAGS += -DHWUI_NEW_OPS
+LOCAL_CFLAGS += -DHWUI_NEW_OPS
LOCAL_CPPFLAGS += -Wno-conversion-null
ifeq ($(TARGET_ARCH), arm)
diff --git a/core/jni/android_app_Activity.cpp b/core/jni/android_app_Activity.cpp
index b1d7e82..56f4f01 100644
--- a/core/jni/android_app_Activity.cpp
+++ b/core/jni/android_app_Activity.cpp
@@ -15,16 +15,25 @@
*/
#include <poll.h>
-#include <android/dlext.h>
+
+#include <string>
#include "core_jni_helpers.h"
+extern "C" void android_dlwarning(void*, void (*)(void*, const char*));
+
namespace android
{
static jstring getDlWarning_native(JNIEnv* env, jobject) {
- const char* text = android_dlwarning();
- return text == nullptr ? nullptr : env->NewStringUTF(text);
+ std::string msg;
+ android_dlwarning(&msg, [](void* obj, const char* msg) {
+ if (msg != nullptr) {
+ *reinterpret_cast<std::string*>(obj) = msg;
+ }
+ });
+
+ return msg.empty() ? nullptr : env->NewStringUTF(msg.c_str());
}
static const JNINativeMethod g_methods[] = {
diff --git a/core/jni/android_hardware_camera2_CameraMetadata.cpp b/core/jni/android_hardware_camera2_CameraMetadata.cpp
index afccfcf..f37fd78 100644
--- a/core/jni/android_hardware_camera2_CameraMetadata.cpp
+++ b/core/jni/android_hardware_camera2_CameraMetadata.cpp
@@ -918,15 +918,14 @@
sp<VendorTagDescriptor> desc = new VendorTagDescriptor();
binder::Status res = cameraService->getCameraVendorTagDescriptor(/*out*/desc.get());
- if (res.serviceSpecificErrorCode() == hardware::ICameraService::ERROR_DEPRECATED_HAL) {
- ALOGW("%s: Camera HAL too old; does not support vendor tags", __FUNCTION__);
+ if (res.serviceSpecificErrorCode() == hardware::ICameraService::ERROR_DISCONNECTED) {
+ // No camera module available, not an error on devices with no cameras
VendorTagDescriptor::clearGlobalVendorTagDescriptor();
-
return OK;
} else if (!res.isOk()) {
- ALOGE("%s: Failed to setup vendor tag descriptors: %s: %s",
- __FUNCTION__, res.serviceSpecificErrorCode(),
- res.toString8().string());
+ VendorTagDescriptor::clearGlobalVendorTagDescriptor();
+ ALOGE("%s: Failed to setup vendor tag descriptors: %s",
+ __FUNCTION__, res.toString8().string());
return res.serviceSpecificErrorCode();
}
diff --git a/core/jni/android_hardware_camera2_DngCreator.cpp b/core/jni/android_hardware_camera2_DngCreator.cpp
index cb0abb6..c6baf1c 100644
--- a/core/jni/android_hardware_camera2_DngCreator.cpp
+++ b/core/jni/android_hardware_camera2_DngCreator.cpp
@@ -80,6 +80,13 @@
return nullptr; \
}
+#define BAIL_IF_EXPR_RET_NULL_SP(expr, jnienv, tagId, writer) \
+ if (expr) { \
+ jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
+ "Invalid metadata for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \
+ return nullptr; \
+ }
+
#define ANDROID_DNGCREATOR_CTX_JNI_ID "mNativeContext"
@@ -195,8 +202,8 @@
NativeContext::NativeContext(const CameraMetadata& characteristics, const CameraMetadata& result) :
mCharacteristics(std::make_shared<CameraMetadata>(characteristics)),
mResult(std::make_shared<CameraMetadata>(result)), mThumbnailWidth(0),
- mThumbnailHeight(0), mOrientation(0), mThumbnailSet(false), mGpsSet(false),
- mDescriptionSet(false), mCaptureTimeSet(false) {}
+ mThumbnailHeight(0), mOrientation(TAG_ORIENTATION_UNKNOWN), mThumbnailSet(false),
+ mGpsSet(false), mDescriptionSet(false), mCaptureTimeSet(false) {}
NativeContext::~NativeContext() {}
@@ -1096,7 +1103,7 @@
{
// Set orientation
- uint16_t orientation = 1; // Normal
+ uint16_t orientation = TAG_ORIENTATION_NORMAL;
BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0),
env, TAG_ORIENTATION, writer);
}
@@ -1138,12 +1145,27 @@
}
{
- // Set blacklevel tags
+ // Set blacklevel tags, using dynamic black level if available
camera_metadata_entry entry =
- characteristics.find(ANDROID_SENSOR_BLACK_LEVEL_PATTERN);
- BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_BLACKLEVEL, writer);
- const uint32_t* blackLevel = reinterpret_cast<const uint32_t*>(entry.data.i32);
- BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BLACKLEVEL, entry.count, blackLevel,
+ results.find(ANDROID_SENSOR_DYNAMIC_BLACK_LEVEL);
+ uint32_t blackLevelRational[8] = {0};
+ if (entry.count != 0) {
+ BAIL_IF_EXPR_RET_NULL_SP(entry.count != 4, env, TAG_BLACKLEVEL, writer);
+ for (size_t i = 0; i < entry.count; i++) {
+ blackLevelRational[i * 2] = static_cast<uint32_t>(entry.data.f[i] * 100);
+ blackLevelRational[i * 2 + 1] = 100;
+ }
+ } else {
+ // Fall back to static black level which is guaranteed
+ entry = characteristics.find(ANDROID_SENSOR_BLACK_LEVEL_PATTERN);
+ BAIL_IF_EXPR_RET_NULL_SP(entry.count != 4, env, TAG_BLACKLEVEL, writer);
+ for (size_t i = 0; i < entry.count; i++) {
+ blackLevelRational[i * 2] = static_cast<uint32_t>(entry.data.i32[i]);
+ blackLevelRational[i * 2 + 1] = 1;
+ }
+
+ }
+ BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BLACKLEVEL, 4, blackLevelRational,
TIFF_IFD_0), env, TAG_BLACKLEVEL, writer);
uint16_t repeatDim[2] = {2, 2};
@@ -1913,8 +1935,10 @@
{
// Set bits per sample
- uint16_t bits = BITS_PER_RGB_SAMPLE;
- BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BITSPERSAMPLE, 1, &bits, TIFF_IFD_0),
+ uint16_t bits[SAMPLES_PER_RGB_PIXEL];
+ for (int i = 0; i < SAMPLES_PER_RGB_PIXEL; i++) bits[i] = BITS_PER_RGB_SAMPLE;
+ BAIL_IF_INVALID_RET_NULL_SP(
+ writer->addEntry(TAG_BITSPERSAMPLE, SAMPLES_PER_RGB_PIXEL, bits, TIFF_IFD_0),
env, TAG_BITSPERSAMPLE, writer);
}
diff --git a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
index f1ea7ec..80f9d57 100644
--- a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
+++ b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
@@ -29,6 +29,7 @@
#include <gui/Surface.h>
#include <gui/IGraphicBufferProducer.h>
+#include <gui/IProducerListener.h>
#include <ui/GraphicBuffer.h>
#include <system/window.h>
#include <hardware/camera3.h>
@@ -93,27 +94,17 @@
cStep, yStride, cStride);
}
-static status_t configureSurface(const sp<ANativeWindow>& anw,
- int32_t width,
- int32_t height,
- int32_t pixelFmt,
- int32_t maxBufferSlack) {
+static status_t connectSurface(const sp<Surface>& surface, int32_t maxBufferSlack) {
status_t err = NO_ERROR;
- err = native_window_set_buffers_dimensions(anw.get(), width, height);
- if (err != NO_ERROR) {
- ALOGE("%s: Failed to set native window buffer dimensions, error %s (%d).", __FUNCTION__,
+
+ err = surface->connect(NATIVE_WINDOW_API_CAMERA, /*listener*/NULL);
+ if (err != OK) {
+ ALOGE("%s: Unable to connect to surface, error %s (%d).", __FUNCTION__,
strerror(-err), err);
return err;
}
- err = native_window_set_buffers_format(anw.get(), pixelFmt);
- if (err != NO_ERROR) {
- ALOGE("%s: Failed to set native window buffer format, error %s (%d).", __FUNCTION__,
- strerror(-err), err);
- return err;
- }
-
- err = native_window_set_usage(anw.get(), GRALLOC_USAGE_SW_WRITE_OFTEN);
+ err = native_window_set_usage(surface.get(), GRALLOC_USAGE_SW_WRITE_OFTEN);
if (err != NO_ERROR) {
ALOGE("%s: Failed to set native window usage flag, error %s (%d).", __FUNCTION__,
strerror(-err), err);
@@ -121,19 +112,17 @@
}
int minUndequeuedBuffers;
- err = anw.get()->query(anw.get(),
- NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
- &minUndequeuedBuffers);
+ err = static_cast<ANativeWindow*>(surface.get())->query(surface.get(),
+ NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeuedBuffers);
if (err != NO_ERROR) {
ALOGE("%s: Failed to get native window min undequeued buffers, error %s (%d).",
__FUNCTION__, strerror(-err), err);
return err;
}
- ALOGV("%s: Setting buffer count to %d, size to (%dx%d), fmt (0x%x)", __FUNCTION__,
- maxBufferSlack + 1 + minUndequeuedBuffers,
- width, height, pixelFmt);
- err = native_window_set_buffer_count(anw.get(), maxBufferSlack + 1 + minUndequeuedBuffers);
+ ALOGV("%s: Setting buffer count to %d", __FUNCTION__,
+ maxBufferSlack + 1 + minUndequeuedBuffers);
+ err = native_window_set_buffer_count(surface.get(), maxBufferSlack + 1 + minUndequeuedBuffers);
if (err != NO_ERROR) {
ALOGE("%s: Failed to set native window buffer count, error %s (%d).", __FUNCTION__,
strerror(-err), err);
@@ -509,6 +498,26 @@
return usage;
}
+static jint LegacyCameraDevice_nativeDisconnectSurface(JNIEnv* env, jobject thiz,
+ jobject surface) {
+ ALOGV("nativeDisconnectSurface");
+ if (surface == nullptr) return NO_ERROR;
+
+ sp<ANativeWindow> anw;
+ if ((anw = getNativeWindow(env, surface)) == NULL) {
+ ALOGV("Buffer queue has already been abandoned.");
+ return NO_ERROR;
+ }
+
+ status_t err = native_window_api_disconnect(anw.get(), NATIVE_WINDOW_API_CAMERA);
+ if(err != NO_ERROR) {
+ jniThrowException(env, "Ljava/lang/UnsupportedOperationException;",
+ "Error while disconnecting surface");
+ return err;
+ }
+ return NO_ERROR;
+}
+
static jint LegacyCameraDevice_nativeDetectTextureDimens(JNIEnv* env, jobject thiz,
jobject surfaceTexture, jintArray dimens) {
ALOGV("nativeDetectTextureDimens");
@@ -540,15 +549,14 @@
return NO_ERROR;
}
-static jint LegacyCameraDevice_nativeConfigureSurface(JNIEnv* env, jobject thiz, jobject surface,
- jint width, jint height, jint pixelFormat) {
- ALOGV("nativeConfigureSurface");
- sp<ANativeWindow> anw;
- if ((anw = getNativeWindow(env, surface)) == NULL) {
- ALOGE("%s: Could not retrieve native window from surface.", __FUNCTION__);
+static jint LegacyCameraDevice_nativeConnectSurface(JNIEnv* env, jobject thiz, jobject surface) {
+ ALOGV("nativeConnectSurface");
+ sp<Surface> s;
+ if ((s = getSurface(env, surface)) == NULL) {
+ ALOGE("%s: Could not retrieve surface.", __FUNCTION__);
return BAD_VALUE;
}
- status_t err = configureSurface(anw, width, height, pixelFormat, CAMERA_DEVICE_BUFFER_SLACK);
+ status_t err = connectSurface(s, CAMERA_DEVICE_BUFFER_SLACK);
if (err != NO_ERROR) {
ALOGE("%s: Error while configuring surface %s (%d).", __FUNCTION__, strerror(-err), err);
return err;
@@ -740,9 +748,9 @@
{ "nativeDetectSurfaceDimens",
"(Landroid/view/Surface;[I)I",
(void *)LegacyCameraDevice_nativeDetectSurfaceDimens },
- { "nativeConfigureSurface",
- "(Landroid/view/Surface;III)I",
- (void *)LegacyCameraDevice_nativeConfigureSurface },
+ { "nativeConnectSurface",
+ "(Landroid/view/Surface;)I",
+ (void *)LegacyCameraDevice_nativeConnectSurface },
{ "nativeProduceFrame",
"(Landroid/view/Surface;[BIII)I",
(void *)LegacyCameraDevice_nativeProduceFrame },
@@ -773,6 +781,9 @@
{ "nativeSetScalingMode",
"(Landroid/view/Surface;I)I",
(void *)LegacyCameraDevice_nativeSetScalingMode },
+ { "nativeDisconnectSurface",
+ "(Landroid/view/Surface;)I",
+ (void *)LegacyCameraDevice_nativeDisconnectSurface },
};
// Get all the required offsets in java class and register native functions
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index defb88a..880a79c 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -26,10 +26,13 @@
#include <net/if.h>
#include <linux/filter.h>
#include <linux/if.h>
+#include <linux/if_arp.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <net/if_ether.h>
+#include <netinet/icmp6.h>
#include <netinet/ip.h>
+#include <netinet/ip6.h>
#include <netinet/udp.h>
#include <cutils/properties.h>
@@ -64,10 +67,9 @@
static void android_net_utils_attachDhcpFilter(JNIEnv *env, jobject clazz, jobject javaFd)
{
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
uint32_t ip_offset = sizeof(ether_header);
uint32_t proto_offset = ip_offset + offsetof(iphdr, protocol);
- uint32_t flags_offset = ip_offset + offsetof(iphdr, frag_off);
+ uint32_t flags_offset = ip_offset + offsetof(iphdr, frag_off);
uint32_t dport_indirect_offset = ip_offset + offsetof(udphdr, dest);
struct sock_filter filter_code[] = {
// Check the protocol is UDP.
@@ -94,6 +96,45 @@
filter_code,
};
+ int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) {
+ jniThrowExceptionFmt(env, "java/net/SocketException",
+ "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno));
+ }
+}
+
+static void android_net_utils_attachRaFilter(JNIEnv *env, jobject clazz, jobject javaFd,
+ jint hardwareAddressType)
+{
+ if (hardwareAddressType != ARPHRD_ETHER) {
+ jniThrowExceptionFmt(env, "java/net/SocketException",
+ "attachRaFilter only supports ARPHRD_ETHER");
+ return;
+ }
+
+ uint32_t ipv6_offset = sizeof(ether_header);
+ uint32_t ipv6_next_header_offset = ipv6_offset + offsetof(ip6_hdr, ip6_nxt);
+ uint32_t icmp6_offset = ipv6_offset + sizeof(ip6_hdr);
+ uint32_t icmp6_type_offset = icmp6_offset + offsetof(icmp6_hdr, icmp6_type);
+ struct sock_filter filter_code[] = {
+ // Check IPv6 Next Header is ICMPv6.
+ BPF_STMT(BPF_LD | BPF_B | BPF_ABS, ipv6_next_header_offset),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_ICMPV6, 0, 3),
+
+ // Check ICMPv6 type is Router Advertisement.
+ BPF_STMT(BPF_LD | BPF_B | BPF_ABS, icmp6_type_offset),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ND_ROUTER_ADVERT, 0, 1),
+
+ // Accept or reject.
+ BPF_STMT(BPF_RET | BPF_K, 0xffff),
+ BPF_STMT(BPF_RET | BPF_K, 0)
+ };
+ struct sock_fprog filter = {
+ sizeof(filter_code) / sizeof(filter_code[0]),
+ filter_code,
+ };
+
+ int fd = jniGetFDFromFileDescriptor(env, javaFd);
if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) {
jniThrowExceptionFmt(env, "java/net/SocketException",
"setsockopt(SO_ATTACH_FILTER): %s", strerror(errno));
@@ -148,6 +189,7 @@
{ "protectFromVpn", "(I)Z", (void*)android_net_utils_protectFromVpn },
{ "queryUserAccess", "(II)Z", (void*)android_net_utils_queryUserAccess },
{ "attachDhcpFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDhcpFilter },
+ { "attachRaFilter", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_attachRaFilter },
};
int register_android_net_NetworkUtils(JNIEnv* env)
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 3df0876..f870a89 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -262,7 +262,13 @@
}
name = line + name_pos;
nameLen = strlen(name);
-
+ // Trim the end of the line if it is " (deleted)".
+ const char* deleted_str = " (deleted)";
+ if (nameLen > (int)strlen(deleted_str) &&
+ strcmp(name+nameLen-strlen(deleted_str), deleted_str) == 0) {
+ nameLen -= strlen(deleted_str);
+ name[nameLen] = '\0';
+ }
if ((strstr(name, "[heap]") == name)) {
whichHeap = HEAP_NATIVE;
} else if (strncmp(name, "[anon:libc_malloc]", 18) == 0) {
diff --git a/core/res/res/layout/notification_template_material_big_text.xml b/core/res/res/layout/notification_template_material_big_text.xml
index 9a4b28c..3c59b4e 100644
--- a/core/res/res/layout/notification_template_material_big_text.xml
+++ b/core/res/res/layout/notification_template_material_big_text.xml
@@ -39,7 +39,7 @@
<com.android.internal.widget.ImageFloatingTextView android:id="@+id/big_text"
android:layout_width="match_parent"
android:layout_height="0dp"
- android:layout_marginTop="1.5dp"
+ android:layout_marginTop="1dp"
android:paddingBottom="@dimen/notification_content_margin_bottom"
android:textAppearance="@style/TextAppearance.Material.Notification"
android:singleLine="false"
diff --git a/core/res/res/layout/notification_template_text.xml b/core/res/res/layout/notification_template_text.xml
index 38470cd..47b30ec 100644
--- a/core/res/res/layout/notification_template_text.xml
+++ b/core/res/res/layout/notification_template_text.xml
@@ -14,12 +14,12 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
-<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.internal.widget.ImageFloatingTextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
- android:layout_marginTop="1.5dp"
+ android:layout_marginTop="1dp"
android:ellipsize="marquee"
android:fadingEdge="horizontal"
android:gravity="top"
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 15b18dd..d1c65c0 100644
--- a/core/res/res/layout/text_edit_suggestion_container_material.xml
+++ b/core/res/res/layout/text_edit_suggestion_container_material.xml
@@ -33,7 +33,7 @@
android:id="@+id/suggestionContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingTop="8dp"
+ android:paddingTop="4dp"
android:paddingBottom="0dp"
android:divider="@null" />
<LinearLayout
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 9a2a6eb..7857107 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2440,6 +2440,10 @@
flag). -->
<bool name="config_forceWindowDrawsStatusBarBackground">true</bool>
+ <!-- If set, this will force the navigation bar to always be drawn with an opaque
+ background. -->
+ <bool name="config_forceNavBarAlwaysOpaque">false</bool>
+
<!-- Default bounds [left top right bottom] on screen for picture-in-picture windows. -->
<string translatable="false" name="config_defaultPictureInPictureBounds">"0 0 100 100"</string>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 7b11302..d8efd63 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2917,20 +2917,18 @@
<!-- Message of notification shown when ADB is actively connected to the phone. -->
<string name="adb_active_notification_message">Touch to disable USB debugging.</string>
+ <!-- Title of notification shown to indicate that bug report is being collected. -->
+ <string name="taking_remote_bugreport_notification_title">Taking bug report\u2026</string>
<!-- Title of notification shown to ask for user consent for sharing a bugreport that was requested remotely by the IT administrator. -->
<string name="share_remote_bugreport_notification_title">Share bug report?</string>
<!-- Title of notification shown to indicate that bug report is still being collected after sharing was accepted. -->
<string name="sharing_remote_bugreport_notification_title">Sharing bug report\u2026</string>
- <!-- Message of notification shown to ask for user consent for sharing a bugreport that was requested remotely by the IT administrator. -->
- <string name="share_remote_bugreport_notification_message">Your IT admin requested a bug report to help troubleshoot this device. Apps and data may be shared and your device may temporarily slow down.</string>
- <!-- Message of notification shown to ask for user consent for sharing a bugreport that was requested remotely by the IT administrator. -->
- <string name="share_finished_remote_bugreport_notification_message">Your IT admin requested a bug report to help troubleshoot this device. Apps and data may be shared.</string>
- <!-- Message of notification shown to shown to indicate that bug report is still being collected after sharing was accepted. -->
- <string name="sharing_remote_bugreport_notification_message">This may temporarily slow down your device</string>
+ <!-- Message of a notification shown to ask for user consent for sharing a bugreport that was requested remotely by the IT administrator. -->
+ <string name="share_remote_bugreport_notification_message_finished">Your IT admin requested a bug report to help troubleshoot this device. Apps and data may be shared.</string>
<!-- Acceptance label of notification shown to ask for user consent for sharing the remote bugreport. -->
- <string name="share_remote_bugreport_notification_accept">ACCEPT</string>
+ <string name="share_remote_bugreport_action">SHARE</string>
<!-- Decline label of notification shown to ask for user consent for sharing the remote bugreport. -->
- <string name="share_remote_bugreport_notification_decline">DECLINE</string>
+ <string name="decline_remote_bugreport_action">DECLINE</string>
<!-- Used to replace %s in urls retreived from the signin server with locales. For Some -->
<!-- devices we don't support all the locales we ship to and need to replace the '%s' with a -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 11f42ec..15521e4 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1771,13 +1771,12 @@
<java-symbol type="string" name="accessibility_binding_label" />
<java-symbol type="string" name="adb_active_notification_message" />
<java-symbol type="string" name="adb_active_notification_title" />
+ <java-symbol type="string" name="taking_remote_bugreport_notification_title" />
<java-symbol type="string" name="share_remote_bugreport_notification_title" />
- <java-symbol type="string" name="share_remote_bugreport_notification_message" />
- <java-symbol type="string" name="share_finished_remote_bugreport_notification_message" />
<java-symbol type="string" name="sharing_remote_bugreport_notification_title" />
- <java-symbol type="string" name="sharing_remote_bugreport_notification_message" />
- <java-symbol type="string" name="share_remote_bugreport_notification_accept" />
- <java-symbol type="string" name="share_remote_bugreport_notification_decline" />
+ <java-symbol type="string" name="share_remote_bugreport_notification_message_finished" />
+ <java-symbol type="string" name="share_remote_bugreport_action" />
+ <java-symbol type="string" name="decline_remote_bugreport_action" />
<java-symbol type="string" name="aerr_application" />
<java-symbol type="string" name="aerr_process" />
<java-symbol type="string" name="aerr_application_repeated" />
@@ -2392,6 +2391,7 @@
<java-symbol type="string" name="config_packagedKeyboardName" />
<java-symbol type="bool" name="config_forceWindowDrawsStatusBarBackground" />
+ <java-symbol type="bool" name="config_forceNavBarAlwaysOpaque" />
<java-symbol type="color" name="system_bar_background_semi_transparent" />
<!-- EditText suggestion popup. -->
diff --git a/core/tests/coretests/src/android/animation/ValueAnimatorTests.java b/core/tests/coretests/src/android/animation/ValueAnimatorTests.java
index 998c72a..c92863d 100644
--- a/core/tests/coretests/src/android/animation/ValueAnimatorTests.java
+++ b/core/tests/coretests/src/android/animation/ValueAnimatorTests.java
@@ -328,7 +328,12 @@
// Only a1's pause listener should be called.
assertTrue(l1.pauseCalled);
assertFalse(l1.resumeCalled);
- a1.resume();
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ a1.resume();
+ }
+ });
Thread.sleep(a1.getTotalDuration());
diff --git a/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java b/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java
index e9fd5fb..5d46489 100644
--- a/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java
+++ b/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java
@@ -38,7 +38,7 @@
@Override
protected void setUp() throws Exception {
super.setUp();
- mCache = new ConfigurationBoundResourceCache<Float>(getActivity().getResources());
+ mCache = new ConfigurationBoundResourceCache<>();
}
public void testGetEmpty() {
diff --git a/core/tests/coretests/src/android/util/PatternsTest.java b/core/tests/coretests/src/android/util/PatternsTest.java
index d383775..348f8fd 100644
--- a/core/tests/coretests/src/android/util/PatternsTest.java
+++ b/core/tests/coretests/src/android/util/PatternsTest.java
@@ -25,7 +25,7 @@
public class PatternsTest extends TestCase {
- //Tests for Patterns.TOP_LEVEL_DOMAIN
+ // Tests for Patterns.TOP_LEVEL_DOMAIN
@SmallTest
public void testTldPattern() throws Exception {
@@ -56,7 +56,7 @@
assertFalse("Matched invalid TLD!", t);
}
- //Tests for Patterns.IANA_TOP_LEVEL_DOMAINS
+ // Tests for Patterns.IANA_TOP_LEVEL_DOMAINS
@SmallTest
public void testIanaTopLevelDomains_matchesValidTld() throws Exception {
@@ -94,7 +94,7 @@
assertFalse("Should not match invalid Punycode TLD", pattern.matcher("xn").matches());
}
- //Tests for Patterns.WEB_URL
+ // Tests for Patterns.WEB_URL
@SmallTest
public void testWebUrl_matchesValidUrlWithSchemeAndHostname() throws Exception {
@@ -208,7 +208,7 @@
Patterns.WEB_URL.matcher(url).matches());
}
- //Tests for Patterns.AUTOLINK_WEB_URL
+ // Tests for Patterns.AUTOLINK_WEB_URL
@SmallTest
public void testAutoLinkWebUrl_matchesValidUrlWithSchemeAndHostname() throws Exception {
@@ -419,7 +419,7 @@
Patterns.AUTOLINK_WEB_URL.matcher(url).matches());
}
- //Tests for Patterns.IP_ADDRESS
+ // Tests for Patterns.IP_ADDRESS
@SmallTest
public void testIpPattern() throws Exception {
@@ -432,7 +432,7 @@
assertFalse("Invalid IP", t);
}
- //Tests for Patterns.DOMAIN_NAME
+ // Tests for Patterns.DOMAIN_NAME
@SmallTest
public void testDomain_matchesPunycodeTld() throws Exception {
@@ -508,7 +508,227 @@
Patterns.DOMAIN_NAME.matcher(domain).matches());
}
- //Tests for Patterns.PHONE
+ // Tests for Patterns.AUTOLINK_EMAIL_ADDRESS
+
+ public void testAutoLinkEmailAddress_matchesShortValidEmail() throws Exception {
+ String email = "a@a.co";
+ assertTrue("Should match short valid email: " + email,
+ Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches());
+ }
+
+ public void testAutoLinkEmailAddress_matchesRegularEmail() throws Exception {
+ String email = "email@android.com";
+ assertTrue("Should match email: " + email,
+ Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches());
+ }
+
+ public void testAutoLinkEmailAddress_matchesEmailWithMultipleSubdomains() throws Exception {
+ String email = "email@e.somelongdomainnameforandroid.abc.uk";
+ assertTrue("Should match email: " + email,
+ Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches());
+ }
+
+ public void testAutoLinkEmailAddress_matchesLocalPartWithDot() throws Exception {
+ String email = "e.mail@android.com";
+ assertTrue("Should match email: " + email,
+ Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches());
+ }
+
+ public void testAutoLinkEmailAddress_matchesLocalPartWithPlus() throws Exception {
+ String email = "e+mail@android.com";
+ assertTrue("Should match email: " + email,
+ Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches());
+ }
+
+ public void testAutoLinkEmailAddress_matchesLocalPartWithUnderscore() throws Exception {
+ String email = "e_mail@android.com";
+ assertTrue("Should match email: " + email,
+ Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches());
+ }
+
+ public void testAutoLinkEmailAddress_matchesLocalPartWithDash() throws Exception {
+ String email = "e-mail@android.com";
+ assertTrue("Should match email: " + email,
+ Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches());
+ }
+
+ public void testAutoLinkEmailAddress_matchesLocalPartWithApostrophe() throws Exception {
+ String email = "e'mail@android.com";
+ assertTrue("Should match email: " + email,
+ Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches());
+ }
+
+ public void testAutoLinkEmailAddress_matchesLocalPartWithDigits() throws Exception {
+ String email = "123@android.com";
+ assertTrue("Should match email: " + email,
+ Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches());
+ }
+
+ public void testAutoLinkEmailAddress_matchesUnicodeLocalPart() throws Exception {
+ String email = "\uD604\uAE08\uC601\uC218\uC99D@android.kr";
+ assertTrue("Should match email: " + email,
+ Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches());
+ }
+
+ public void testAutoLinkEmailAddress_matchesLocalPartWithEmoji() throws Exception {
+ String email = "smiley\u263A@android.com";
+ assertTrue("Should match email: " + email,
+ Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches());
+ }
+
+ public void testAutoLinkEmailAddress_matchesLocalPartWithSurrogatePairs() throws Exception {
+ String email = "\uD83C\uDF38@android.com";
+ assertTrue("Should match email: " + email,
+ Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches());
+ }
+
+ public void testAutoLinkEmailAddress_matchesDomainWithDash() throws Exception {
+ String email = "email@an-droid.com";
+ assertTrue("Should match email: " + email,
+ Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches());
+ }
+
+ public void testAutoLinkEmailAddress_matchesUnicodeDomain() throws Exception {
+ String email = "email@\uD604\uAE08\uC601\uC218\uC99D.kr";
+ assertTrue("Should match email: " + email,
+ Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches());
+ }
+
+ public void testAutoLinkEmailAddress_matchesUnicodeLocalPartAndDomain() throws Exception {
+ String email = "\uD604\uAE08\uC601\uC218\uC99D@\uD604\uAE08\uC601\uC218\uC99D.kr";
+ assertTrue("Should match email: " + email,
+ Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches());
+ }
+
+ public void testAutoLinkEmailAddress_matchesDomainWithEmoji() throws Exception {
+ String email = "smiley@\u263Aandroid.com";
+ assertTrue("Should match email: " + email,
+ Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches());
+ }
+
+ public void testAutoLinkEmailAddress_matchesDomainWithSurrogatePairs() throws Exception {
+ String email = "email@\uD83C\uDF38android.com";
+ assertTrue("Should match email: " + email,
+ Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches());
+ }
+
+ public void testAutoLinkEmailAddress_matchesLocalPartAndDomainWithSurrogatePairs()
+ throws Exception {
+ String email = "\uD83C\uDF38@\uD83C\uDF38android.com";
+ assertTrue("Should match email: " + email,
+ Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches());
+ }
+
+ public void testAutoLinkEmailAddress_doesNotMatchStringWithoutAtSign() throws Exception {
+ String email = "android.com";
+ assertFalse("Should not match email: " + email,
+ Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches());
+ }
+
+ public void testAutoLinkEmailAddress_doesNotMatchPlainString() throws Exception {
+ String email = "email";
+ assertFalse("Should not match email: " + email,
+ Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches());
+ }
+
+ public void testAutoLinkEmailAddress_doesNotMatchStringWithMultipleAtSigns() throws Exception {
+ String email = "email@android@android.com";
+ assertFalse("Should not match email: " + email,
+ Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches());
+ }
+
+ public void testAutoLinkEmailAddress_doesNotMatchEmailWithoutTld() throws Exception {
+ String email = "email@android";
+ assertFalse("Should not match email: " + email,
+ Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches());
+ }
+
+ public void testAutoLinkEmailAddress_doesNotMatchLocalPartEndingWithDot() throws Exception {
+ String email = "email.@android.com";
+ assertFalse("Should not match email: " + email,
+ Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches());
+ }
+
+ public void testAutoLinkEmailAddress_doesNotMatchLocalPartStartingWithDot() throws Exception {
+ String email = ".email@android.com";
+ assertFalse("Should not match email: " + email,
+ Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches());
+ }
+
+ public void testAutoLinkEmailAddress_doesNotMatchDomainStartingWithDash() throws Exception {
+ String email = "email@-android.com";
+ assertFalse("Should not match email: " + email,
+ Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches());
+ }
+
+ public void testAutoLinkEmailAddress_doesNotMatchDomainWithConsecutiveDots() throws Exception {
+ String email = "email@android..com";
+ assertFalse("Should not match email: " + email,
+ Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches());
+ }
+
+ public void testAutoLinkEmailAddress_doesNotMatchEmailWithIpAsDomain() throws Exception {
+ String email = "email@127.0.0.1";
+ assertFalse("Should not match email: " + email,
+ Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches());
+ }
+
+ public void testAutoLinkEmailAddress_doesNotMatchEmailWithInvalidTld() throws Exception {
+ String email = "email@android.c";
+ assertFalse("Should not match email: " + email,
+ Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches());
+ }
+
+ public void testAutoLinkEmailAddress_matchesLocalPartUpTo64Chars() throws Exception {
+ String localPart = "";
+ for (int i = 0; i < 64; i++) {
+ localPart += "a";
+ }
+ String email = localPart + "@android.com";
+
+ assertTrue("Should match local part of length: " + localPart.length(),
+ Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches());
+
+ email = localPart + "a@android.com";
+ assertFalse("Should not match local part of length: " + localPart.length(),
+ Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches());
+ }
+
+ public void testAutoLinkEmailAddress_matchesSubdomainUpTo63Chars() throws Exception {
+ String subdomain = "";
+ for (int i = 0; i < 63; i++) {
+ subdomain += "a";
+ }
+ String email = "email@" + subdomain + ".com";
+
+ assertTrue("Should match subdomain of length: " + subdomain.length(),
+ Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches());
+
+ subdomain += "a";
+ email = "email@" + subdomain + ".com";
+ assertFalse("Should not match local part of length: " + subdomain.length(),
+ Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches());
+ }
+
+ public void testAutoLinkEmailAddress_matchesDomainUpTo255Chars() throws Exception {
+ String longDomain = "";
+ while (longDomain.length() <= 250) {
+ longDomain += "d.";
+ }
+ longDomain += "com";
+ assertEquals(255, longDomain.length());
+ String email = "a@" + longDomain;
+
+ assertTrue("Should match domain of length: " + longDomain.length(),
+ Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches());
+
+ email = email + "m";
+ assertEquals(258, email.length());
+ assertFalse("Should not match domain of length: " + longDomain.length(),
+ Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches());
+ }
+
+ // Tests for Patterns.PHONE
@SmallTest
public void testPhonePattern() throws Exception {
diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h
index 16bea79..f23fceb 100644
--- a/include/androidfw/ResourceTypes.h
+++ b/include/androidfw/ResourceTypes.h
@@ -1151,9 +1151,13 @@
uint32_t screenConfig2;
};
- // If true, it means that the script of the locale was explicitly provided.
- // If false, it means that the script was automatically computed.
- bool localeScriptWasProvided;
+ // If false and localeScript is set, it means that the script of the locale
+ // was explicitly provided.
+ //
+ // If true, it means that localeScript was automatically computed.
+ // localeScript may still not be set in this case, which means that we
+ // tried but could not compute a script.
+ bool localeScriptWasComputed;
void copyFromDeviceNoSwap(const ResTable_config& o);
@@ -1233,7 +1237,7 @@
inline void clearLocale() {
locale = 0;
- localeScriptWasProvided = false;
+ localeScriptWasComputed = false;
memset(localeScript, 0, sizeof(localeScript));
memset(localeVariant, 0, sizeof(localeVariant));
}
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index 7adad8a..0886487 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -547,11 +547,8 @@
Intent intent = new Intent(IKeyChainService.class.getName());
ComponentName comp = intent.resolveSystemService(context.getPackageManager(), 0);
intent.setComponent(comp);
- boolean isBound = context.bindServiceAsUser(intent,
- keyChainServiceConnection,
- Context.BIND_AUTO_CREATE,
- user);
- if (!isBound) {
+ if (comp == null || !context.bindServiceAsUser(
+ intent, keyChainServiceConnection, Context.BIND_AUTO_CREATE, user)) {
throw new AssertionError("could not bind to KeyChainService");
}
return new KeyChainConnection(context, keyChainServiceConnection, q.take());
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index c73bb584..49b3a51 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -1870,8 +1870,8 @@
// The language & region are equal, so compare the scripts and variants.
const char emptyScript[sizeof(l.localeScript)] = {'\0', '\0', '\0', '\0'};
- const char *lScript = l.localeScriptWasProvided ? l.localeScript : emptyScript;
- const char *rScript = r.localeScriptWasProvided ? r.localeScript : emptyScript;
+ const char *lScript = l.localeScriptWasComputed ? emptyScript : l.localeScript;
+ const char *rScript = r.localeScriptWasComputed ? emptyScript : r.localeScript;
int script = memcmp(lScript, rScript, sizeof(l.localeScript));
if (script) {
return script;
@@ -2016,11 +2016,11 @@
// scripts since it seems more useful to do so. We will consider
// "en-US-POSIX" to be more specific than "en-Latn-US".
- const int score = (localeScriptWasProvided ? 1 : 0) +
- ((localeVariant[0] != 0) ? 2 : 0);
+ const int score = ((localeScript[0] != '\0' && !localeScriptWasComputed) ? 1 : 0) +
+ ((localeVariant[0] != '\0') ? 2 : 0);
- const int oScore = (o.localeScriptWasProvided ? 1 : 0) +
- ((o.localeVariant[0] != 0) ? 2 : 0);
+ const int oScore = (o.localeScript[0] != '\0' && !o.localeScriptWasComputed ? 1 : 0) +
+ ((o.localeVariant[0] != '\0') ? 2 : 0);
return score - oScore;
@@ -2535,7 +2535,8 @@
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
+ if (localeScript[0] == '\0' && !localeScriptWasComputed) {
+ // script was not provided or computed, so we try to compute it
localeDataComputeScript(computed_script, language, country);
if (computed_script[0] == '\0') { // we could not compute the script
countriesMustMatch = true;
@@ -2684,8 +2685,8 @@
if (!language[0]) {
return;
}
-
- if (!localeScriptWasProvided && !localeVariant[0]) {
+ const bool scriptWasProvided = localeScript[0] != '\0' && !localeScriptWasComputed;
+ if (!scriptWasProvided && !localeVariant[0]) {
// Legacy format.
if (out.size() > 0) {
out.append("-");
@@ -2715,7 +2716,7 @@
size_t len = unpackLanguage(buf);
out.append(buf, len);
- if (localeScriptWasProvided) {
+ if (scriptWasProvided) {
out.append("+");
out.append(localeScript, sizeof(localeScript));
}
@@ -2746,7 +2747,7 @@
charsWritten += unpackLanguage(str);
}
- if (localeScriptWasProvided) {
+ if (localeScript[0] && !localeScriptWasComputed) {
if (charsWritten) {
str[charsWritten++] = '-';
}
@@ -2787,7 +2788,6 @@
for (size_t i = 1; i < 4; ++i) {
config->localeScript[i] = tolower(start[i]);
}
- config->localeScriptWasProvided = true;
break;
}
case 5:
@@ -2807,7 +2807,6 @@
void ResTable_config::setBcp47Locale(const char* in) {
locale = 0;
- localeScriptWasProvided = false;
memset(localeScript, 0, sizeof(localeScript));
memset(localeVariant, 0, sizeof(localeVariant));
@@ -2824,9 +2823,10 @@
const size_t size = in + strlen(in) - start;
assignLocaleComponent(this, start, size);
- if (localeScript[0] == '\0') {
+ localeScriptWasComputed = (localeScript[0] == '\0');
+ if (localeScriptWasComputed) {
computeScript();
- };
+ }
}
String8 ResTable_config::toString() const {
diff --git a/libs/androidfw/tests/ConfigLocale_test.cpp b/libs/androidfw/tests/ConfigLocale_test.cpp
index 4b8d65c..2bf9b12 100644
--- a/libs/androidfw/tests/ConfigLocale_test.cpp
+++ b/libs/androidfw/tests/ConfigLocale_test.cpp
@@ -125,10 +125,10 @@
if (script != NULL) {
memcpy(out->localeScript, script, 4);
- out->localeScriptWasProvided = true;
+ out->localeScriptWasComputed = false;
} else {
out->computeScript();
- out->localeScriptWasProvided = false;
+ out->localeScriptWasComputed = true;
}
if (variant != NULL) {
@@ -182,7 +182,7 @@
EXPECT_EQ('n', test.language[1]);
EXPECT_EQ('U', test.country[0]);
EXPECT_EQ('S', test.country[1]);
- EXPECT_FALSE(test.localeScriptWasProvided);
+ EXPECT_TRUE(test.localeScriptWasComputed);
EXPECT_EQ(0, memcmp("Latn", test.localeScript, 4));
EXPECT_EQ(0, test.localeVariant[0]);
@@ -203,7 +203,7 @@
EXPECT_EQ('e', test.language[0]);
EXPECT_EQ('n', test.language[1]);
EXPECT_EQ(0, memcmp("Latn", test.localeScript, 4));
- EXPECT_TRUE(test.localeScriptWasProvided);
+ EXPECT_FALSE(test.localeScriptWasComputed);
memset(out, 1, 4);
test.unpackRegion(out);
EXPECT_EQ('4', out[0]);
@@ -216,7 +216,7 @@
EXPECT_EQ('d', out[0]);
EXPECT_EQ('e', out[1]);
EXPECT_EQ('\0', out[2]);
- EXPECT_FALSE(test.localeScriptWasProvided);
+ EXPECT_TRUE(test.localeScriptWasComputed);
EXPECT_EQ(0, memcmp("Latn", test.localeScript, 4));
memset(out, 1, 4);
test.unpackRegion(out);
@@ -229,7 +229,7 @@
EXPECT_EQ('d', out[0]);
EXPECT_EQ('e', out[1]);
EXPECT_EQ('\0', out[2]);
- EXPECT_TRUE(test.localeScriptWasProvided);
+ EXPECT_FALSE(test.localeScriptWasComputed);
EXPECT_EQ(0, memcmp("Latn", test.localeScript, 4));
memset(out, 1, 4);
test.unpackRegion(out);
@@ -270,11 +270,11 @@
fillIn("en", NULL, "Latn", NULL, &config);
char out[RESTABLE_MAX_LOCALE_LEN];
- config.localeScriptWasProvided = true;
+ config.localeScriptWasComputed = false;
config.getBcp47Locale(out);
EXPECT_EQ(0, strcmp("en-Latn", out));
- config.localeScriptWasProvided = false;
+ config.localeScriptWasComputed = true;
config.getBcp47Locale(out);
EXPECT_EQ(0, strcmp("en", out));
}
@@ -379,7 +379,7 @@
// emulate packages built with older AAPT
memset(supported.localeScript, '\0', 4);
- supported.localeScriptWasProvided = false;
+ supported.localeScriptWasComputed = false;
EXPECT_TRUE(supported.match(requested));
}
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 54b453d..70995ac 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -2,7 +2,7 @@
include $(CLEAR_VARS)
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-HWUI_NEW_OPS := false
+HWUI_NEW_OPS := true
# Enables fine-grained GLES error checking
# If set to true, every GLES call is wrapped & error checked
diff --git a/libs/hwui/ClipArea.cpp b/libs/hwui/ClipArea.cpp
index 501cbe5..afe9807 100644
--- a/libs/hwui/ClipArea.cpp
+++ b/libs/hwui/ClipArea.cpp
@@ -375,15 +375,13 @@
serialization->rect.set(mClipRegion.getBounds());
break;
}
+ // TODO: this is only done for draw time, should eventually avoid for record time
+ serialization->rect.snapToPixelBoundaries();
mLastSerialization = serialization;
}
return mLastSerialization;
}
-inline static const Rect& getRect(const ClipBase* scb) {
- return reinterpret_cast<const ClipRect*>(scb)->rect;
-}
-
inline static const RectangleList& getRectList(const ClipBase* scb) {
return reinterpret_cast<const ClipRectList*>(scb)->rectList;
}
@@ -425,9 +423,10 @@
&& recordedClip->mode == ClipMode::Rectangle
&& recordedClipTransform.rectToRect())) {
// common case - result is a single rectangle
- auto rectClip = allocator.create<ClipRect>(getRect(recordedClip));
+ auto rectClip = allocator.create<ClipRect>(recordedClip->rect);
recordedClipTransform.mapRect(rectClip->rect);
rectClip->rect.doIntersect(mClipRect);
+ rectClip->rect.snapToPixelBoundaries();
mLastResolutionResult = rectClip;
} else if (CC_UNLIKELY(mMode == ClipMode::Region
|| recordedClip->mode == ClipMode::Region
@@ -438,11 +437,11 @@
case ClipMode::Rectangle:
if (CC_LIKELY(recordedClipTransform.rectToRect())) {
// simple transform, skip creating SkPath
- Rect resultClip(getRect(recordedClip));
+ Rect resultClip(recordedClip->rect);
recordedClipTransform.mapRect(resultClip);
other.setRect(resultClip.toSkIRect());
} else {
- SkPath transformedRect = pathFromTransformedRectangle(getRect(recordedClip),
+ SkPath transformedRect = pathFromTransformedRectangle(recordedClip->rect,
recordedClipTransform);
other.setPath(transformedRect, createViewportRegion());
}
@@ -474,6 +473,7 @@
regionClip->region.op(mClipRegion, other, SkRegion::kIntersect_Op);
break;
}
+ // Don't need to snap, since region's in int bounds
regionClip->rect.set(regionClip->region.getBounds());
mLastResolutionResult = regionClip;
} else {
@@ -484,7 +484,7 @@
}
if (recordedClip->mode == ClipMode::Rectangle) {
- rectList.intersectWith(getRect(recordedClip), recordedClipTransform);
+ rectList.intersectWith(recordedClip->rect, recordedClipTransform);
} else {
const RectangleList& other = getRectList(recordedClip);
for (int i = 0; i < other.getTransformedRectanglesCount(); i++) {
@@ -495,6 +495,7 @@
}
}
rectListClip->rect = rectList.calculateBounds();
+ rectListClip->rect.snapToPixelBoundaries();
mLastResolutionResult = rectListClip;
}
}
@@ -505,7 +506,7 @@
if (!clip) return; // nothing to do
if (CC_LIKELY(clip->mode == ClipMode::Rectangle)) {
- clipRectWithTransform(getRect(clip), &transform, SkRegion::kIntersect_Op);
+ clipRectWithTransform(clip->rect, &transform, SkRegion::kIntersect_Op);
} else if (CC_LIKELY(clip->mode == ClipMode::RectangleList)) {
auto&& rectList = getRectList(clip);
for (int i = 0; i < rectList.getTransformedRectanglesCount(); i++) {
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 9ae2212..ddf0528 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -24,6 +24,14 @@
namespace android {
namespace uirenderer {
+
+static Rect sUnreasonablyLargeBounds(-10000, -10000, 10000, 10000);
+
+static const Rect& getConservativeOpBounds(const ClipBase* clip) {
+ // if op is clipped, that rect can be used, but otherwise just use a conservatively large rect
+ return clip ? clip->rect : sUnreasonablyLargeBounds;
+}
+
RecordingCanvas::RecordingCanvas(size_t width, size_t height)
: mState(*this)
, mResourceCache(ResourceCache::getInstance()) {
@@ -242,10 +250,8 @@
void RecordingCanvas::drawPaint(const SkPaint& paint) {
const ClipBase* clip = getRecordedClip();
- // if there's no current clip, draw a big rect and hope we cover the eventual clip bounds
- Rect bounds = clip ? clip->rect : Rect(-10000, -10000, 10000, 10000);
addOp(alloc().create_trivial<RectOp>(
- bounds,
+ getConservativeOpBounds(clip),
Matrix4::identity(),
clip,
refPaint(&paint)));
@@ -534,10 +540,11 @@
float hOffset, float vOffset, const SkPaint& paint) {
if (!glyphs || glyphCount <= 0 || PaintUtils::paintWillNotDrawText(paint)) return;
glyphs = refBuffer<glyph_t>(glyphs, glyphCount);
+ auto clip = getRecordedClip();
addOp(alloc().create_trivial<TextOnPathOp>(
- mState.getLocalClipBounds(), // TODO: explicitly define bounds
+ getConservativeOpBounds(clip), // TODO: explicitly define bounds
*(mState.currentSnapshot()->transform),
- getRecordedClip(),
+ clip,
refPaint(&paint), glyphs, glyphCount, refPath(&path), hOffset, vOffset));
}
@@ -586,10 +593,11 @@
void RecordingCanvas::callDrawGLFunction(Functor* functor) {
mDisplayList->functors.push_back(functor);
+ auto clip = getRecordedClip();
addOp(alloc().create_trivial<FunctorOp>(
- mState.getLocalClipBounds(), // TODO: explicitly define bounds
+ getConservativeOpBounds(clip), // TODO: explicitly define bounds
*(mState.currentSnapshot()->transform),
- getRecordedClip(),
+ clip,
functor));
}
diff --git a/libs/hwui/tests/unit/ClipAreaTests.cpp b/libs/hwui/tests/unit/ClipAreaTests.cpp
index dc2ea07..822d04f 100644
--- a/libs/hwui/tests/unit/ClipAreaTests.cpp
+++ b/libs/hwui/tests/unit/ClipAreaTests.cpp
@@ -27,7 +27,7 @@
namespace android {
namespace uirenderer {
-static Rect kViewportBounds(0, 0, 2048, 2048);
+static Rect kViewportBounds(2048, 2048);
static ClipArea createClipArea() {
ClipArea area;
@@ -140,17 +140,15 @@
// rect list
Matrix4 rotate;
- rotate.loadRotate(2.0f);
- area.clipRectWithTransform(Rect(200, 200), &rotate, SkRegion::kIntersect_Op);
+ rotate.loadRotate(5.0f);
+ area.clipRectWithTransform(Rect(50, 50, 150, 150), &rotate, SkRegion::kIntersect_Op);
{
auto serializedClip = area.serializeClip(allocator);
ASSERT_NE(nullptr, serializedClip);
ASSERT_EQ(ClipMode::RectangleList, serializedClip->mode);
auto clipRectList = reinterpret_cast<const ClipRectList*>(serializedClip);
EXPECT_EQ(2, clipRectList->rectList.getTransformedRectanglesCount());
- EXPECT_FALSE(clipRectList->rect.isEmpty());
- EXPECT_FLOAT_EQ(199.87817f, clipRectList->rect.right)
- << "Right side should be clipped by rotated rect";
+ EXPECT_EQ(Rect(37, 54, 145, 163), clipRectList->rect);
EXPECT_EQ(serializedClip, area.serializeClip(allocator))
<< "Requery of clip on unmodified ClipArea must return same pointer.";
}
@@ -241,5 +239,28 @@
}
}
+TEST(ClipArea, serializeIntersectedClip_snap) {
+ ClipArea area(createClipArea());
+ area.setClip(100.2, 100.4, 500.6, 500.8);
+ LinearAllocator allocator;
+
+ {
+ // no recorded clip case
+ auto resolvedClip = area.serializeIntersectedClip(allocator, nullptr, Matrix4::identity());
+ EXPECT_EQ(Rect(100, 100, 501, 501), resolvedClip->rect);
+ }
+ {
+ // recorded clip case
+ ClipRect recordedClip(Rect(100.12, 100.74));
+ Matrix4 translateScale;
+ translateScale.loadTranslate(100, 100, 0);
+ translateScale.scale(2, 3, 1); // recorded clip will have non-int coords, even after transform
+ auto resolvedClip = area.serializeIntersectedClip(allocator, &recordedClip, translateScale);
+ ASSERT_NE(nullptr, resolvedClip);
+ EXPECT_EQ(ClipMode::Rectangle, resolvedClip->mode);
+ EXPECT_EQ(Rect(100, 100, 300, 402), resolvedClip->rect);
+ }
+}
+
} // namespace uirenderer
} // namespace android
diff --git a/libs/hwui/tests/unit/RecordingCanvasTests.cpp b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
index c39047c..d35b1f9 100644
--- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp
+++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
@@ -442,9 +442,7 @@
// since the same clip will be computed at draw time. If such a change is made, this
// check could be done at record time by querying the clip, or the clip could be altered
// slightly so that it is serialized.
- EXPECT_RECT_APPROX_EQ(Rect(58.57864, 58.57864, 341.42136, 341.42136),
- (reinterpret_cast<const ClipRect*>(op.localClip))->rect);
-
+ EXPECT_EQ(Rect(59, 59, 341, 341), op.localClip->rect);
EXPECT_EQ(Rect(400, 400), op.unmappedBounds);
expectedMatrix.loadIdentity();
EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 646ab4e..a0e2481 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -644,6 +644,21 @@
/** Full range. Y, Cr and Cb component values range from 0 to 255 for 8-bit content. */
public static final int COLOR_RANGE_FULL = 1;
+ /**
+ * A key describing a unique ID for the content of a media track.
+ *
+ * <p>This key is used by {@link MediaExtractor}. Some extractors provide multiple encodings
+ * of the same track (e.g. float audio tracks for FLAC and WAV may be expressed as two
+ * tracks via MediaExtractor: a normal PCM track for backward compatibility, and a float PCM
+ * track for added fidelity. Similarly, Dolby Vision extractor may provide a baseline SDR
+ * version of a DV track.) This key can be used to identify which MediaExtractor tracks refer
+ * to the same underlying content.
+ * </p>
+ *
+ * The associated value is an integer.
+ */
+ public static final String KEY_TRACK_ID = "track-id";
+
/* package private */ MediaFormat(Map<String, Object> map) {
mMap = map;
}
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index adf8551..b78869e 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -2845,13 +2845,17 @@
MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_UNSUPPORTED, null);
sendMessage(msg2);
}
- if (mOnPreparedListener != null)
- mOnPreparedListener.onPrepared(mMediaPlayer);
+ OnPreparedListener onPreparedListener = mOnPreparedListener;
+ if (onPreparedListener != null)
+ onPreparedListener.onPrepared(mMediaPlayer);
return;
case MEDIA_PLAYBACK_COMPLETE:
- if (mOnCompletionListener != null)
- mOnCompletionListener.onCompletion(mMediaPlayer);
+ {
+ OnCompletionListener onCompletionListener = mOnCompletionListener;
+ if (onCompletionListener != null)
+ onCompletionListener.onCompletion(mMediaPlayer);
+ }
stayAwake(false);
return;
@@ -2875,13 +2879,15 @@
break;
case MEDIA_BUFFERING_UPDATE:
- if (mOnBufferingUpdateListener != null)
- mOnBufferingUpdateListener.onBufferingUpdate(mMediaPlayer, msg.arg1);
+ OnBufferingUpdateListener onBufferingUpdateListener = mOnBufferingUpdateListener;
+ if (onBufferingUpdateListener != null)
+ onBufferingUpdateListener.onBufferingUpdate(mMediaPlayer, msg.arg1);
return;
case MEDIA_SEEK_COMPLETE:
- if (mOnSeekCompleteListener != null) {
- mOnSeekCompleteListener.onSeekComplete(mMediaPlayer);
+ OnSeekCompleteListener onSeekCompleteListener = mOnSeekCompleteListener;
+ if (onSeekCompleteListener != null) {
+ onSeekCompleteListener.onSeekComplete(mMediaPlayer);
}
// fall through
@@ -2895,8 +2901,9 @@
return;
case MEDIA_SET_VIDEO_SIZE:
- if (mOnVideoSizeChangedListener != null) {
- mOnVideoSizeChangedListener.onVideoSizeChanged(
+ OnVideoSizeChangedListener onVideoSizeChangedListener = mOnVideoSizeChangedListener;
+ if (onVideoSizeChangedListener != null) {
+ onVideoSizeChangedListener.onVideoSizeChanged(
mMediaPlayer, msg.arg1, msg.arg2);
}
return;
@@ -2904,11 +2911,15 @@
case MEDIA_ERROR:
Log.e(TAG, "Error (" + msg.arg1 + "," + msg.arg2 + ")");
boolean error_was_handled = false;
- if (mOnErrorListener != null) {
- error_was_handled = mOnErrorListener.onError(mMediaPlayer, msg.arg1, msg.arg2);
+ OnErrorListener onErrorListener = mOnErrorListener;
+ if (onErrorListener != null) {
+ error_was_handled = onErrorListener.onError(mMediaPlayer, msg.arg1, msg.arg2);
}
- if (mOnCompletionListener != null && ! error_was_handled) {
- mOnCompletionListener.onCompletion(mMediaPlayer);
+ {
+ OnCompletionListener onCompletionListener = mOnCompletionListener;
+ if (onCompletionListener != null && ! error_was_handled) {
+ onCompletionListener.onCompletion(mMediaPlayer);
+ }
}
stayAwake(false);
return;
@@ -2944,47 +2955,52 @@
break;
}
- if (mOnInfoListener != null) {
- mOnInfoListener.onInfo(mMediaPlayer, msg.arg1, msg.arg2);
+ OnInfoListener onInfoListener = mOnInfoListener;
+ if (onInfoListener != null) {
+ onInfoListener.onInfo(mMediaPlayer, msg.arg1, msg.arg2);
}
// No real default action so far.
return;
case MEDIA_TIMED_TEXT:
- if (mOnTimedTextListener == null)
+ OnTimedTextListener onTimedTextListener = mOnTimedTextListener;
+ if (onTimedTextListener == null)
return;
if (msg.obj == null) {
- mOnTimedTextListener.onTimedText(mMediaPlayer, null);
+ onTimedTextListener.onTimedText(mMediaPlayer, null);
} else {
if (msg.obj instanceof Parcel) {
Parcel parcel = (Parcel)msg.obj;
TimedText text = new TimedText(parcel);
parcel.recycle();
- mOnTimedTextListener.onTimedText(mMediaPlayer, text);
+ onTimedTextListener.onTimedText(mMediaPlayer, text);
}
}
return;
case MEDIA_SUBTITLE_DATA:
- if (mOnSubtitleDataListener == null) {
+ OnSubtitleDataListener onSubtitleDataListener = mOnSubtitleDataListener;
+ if (onSubtitleDataListener == null) {
return;
}
if (msg.obj instanceof Parcel) {
Parcel parcel = (Parcel) msg.obj;
SubtitleData data = new SubtitleData(parcel);
parcel.recycle();
- mOnSubtitleDataListener.onSubtitleData(mMediaPlayer, data);
+ onSubtitleDataListener.onSubtitleData(mMediaPlayer, data);
}
return;
case MEDIA_META_DATA:
- if (mOnTimedMetaDataAvailableListener == null) {
+ OnTimedMetaDataAvailableListener onTimedMetaDataAvailableListener =
+ mOnTimedMetaDataAvailableListener;
+ if (onTimedMetaDataAvailableListener == null) {
return;
}
if (msg.obj instanceof Parcel) {
Parcel parcel = (Parcel) msg.obj;
TimedMetaData data = TimedMetaData.createTimedMetaDataFromParcel(parcel);
parcel.recycle();
- mOnTimedMetaDataAvailableListener.onTimedMetaDataAvailable(mMediaPlayer, data);
+ onTimedMetaDataAvailableListener.onTimedMetaDataAvailable(mMediaPlayer, data);
}
return;
diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java
index b5ea2a0..f593685 100644
--- a/media/java/android/service/media/MediaBrowserService.java
+++ b/media/java/android/service/media/MediaBrowserService.java
@@ -370,15 +370,14 @@
* called when the loading is complete.
* </p><p>
* In case the media item does not have any children, call {@link Result#sendResult}
- * with an empty list which is not {@code null}. If {@code null} is sent that means
- * the given {@code parentId} is invalid and {@link MediaBrowser.SubscriptionCallback#onError}
- * will be called.
+ * with an empty list. When the given {@code parentId} is invalid, implementations must
+ * call {@link Result#sendResult result.sendResult} with {@code null}, which will invoke
+ * {@link MediaBrowser.SubscriptionCallback#onError}.
* </p>
*
* @param parentId The id of the parent media item whose children are to be
* queried.
- * @param result The Result to send the list of children to. Send null if the
- * id is invalid.
+ * @param result The Result to send the list of children to.
*/
public abstract void onLoadChildren(@NonNull String parentId,
@NonNull Result<List<MediaBrowser.MediaItem>> result);
@@ -394,15 +393,14 @@
* called when the loading is complete.
* </p><p>
* In case the media item does not have any children, call {@link Result#sendResult}
- * with an empty list which is not {@code null}. If {@code null} is sent that means
- * the given {@code parentId} is invalid and {@link MediaBrowser.SubscriptionCallback#onError}
- * will be called.
+ * with an empty list. When the given {@code parentId} is invalid, implementations must
+ * call {@link Result#sendResult result.sendResult} with {@code null}, which will invoke
+ * {@link MediaBrowser.SubscriptionCallback#onError}.
* </p>
*
* @param parentId The id of the parent media item whose children are to be
* queried.
- * @param result The Result to send the list of children to. Send null if the
- * id is invalid.
+ * @param result The Result to send the list of children to.
* @param options A bundle of service-specific arguments sent from the media
* browse. The information returned through the result should be
* affected by the contents of this bundle.
@@ -424,13 +422,18 @@
* result.detach} may be called before returning from this function, and
* then {@link Result#sendResult result.sendResult} called when the item has
* been loaded.
- * <p>
- * The default implementation sends a null result.
+ * </p><p>
+ * When the given {@code itemId} is invalid, implementations must call
+ * {@link Result#sendResult result.sendResult} with {@code null}, which will
+ * invoke {@link MediaBrowser.ItemCallback#onError}.
+ * </p><p>
+ * The default implementation calls {@link Result#sendResult result.sendResult}
+ * with {@code null}.
+ * </p>
*
* @param itemId The id for the specific
* {@link android.media.browse.MediaBrowser.MediaItem}.
- * @param result The Result to send the item to. Send null if the id is
- * invalid.
+ * @param result The Result to send the item to.
*/
public void onLoadItem(String itemId, Result<MediaBrowser.MediaItem> result) {
result.sendResult(null);
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 2004a3a..d6994b3 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -1302,7 +1302,10 @@
jobject patternObj = env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoPatternID);
CryptoPlugin::Pattern pattern;
- if (patternObj != NULL) {
+ if (patternObj == NULL) {
+ pattern.mEncryptBlocks = 0;
+ pattern.mSkipBlocks = 0;
+ } else {
pattern.mEncryptBlocks = env->GetIntField(patternObj, gFields.patternEncryptBlocksID);
pattern.mSkipBlocks = env->GetIntField(patternObj, gFields.patternSkipBlocksID);
}
diff --git a/media/jni/android_media_MediaDataSource.cpp b/media/jni/android_media_MediaDataSource.cpp
index 3b892cb..537b56d 100644
--- a/media/jni/android_media_MediaDataSource.cpp
+++ b/media/jni/android_media_MediaDataSource.cpp
@@ -116,7 +116,8 @@
return UNKNOWN_ERROR;
}
if (mSizeIsCached) {
- return mCachedSize;
+ *size = mCachedSize;
+ return OK;
}
JNIEnv* env = AndroidRuntime::getJNIEnv();
diff --git a/packages/DocumentsUI/perf-tests/src/com/android/documentsui/FilesActivityPerfTest.java b/packages/DocumentsUI/perf-tests/src/com/android/documentsui/FilesActivityPerfTest.java
index 05fd2f7..bf056f1 100644
--- a/packages/DocumentsUI/perf-tests/src/com/android/documentsui/FilesActivityPerfTest.java
+++ b/packages/DocumentsUI/perf-tests/src/com/android/documentsui/FilesActivityPerfTest.java
@@ -121,7 +121,7 @@
activity.removeEventListener(listener);
}
- assertEquals(i, measurements.size());
+ assertEquals(i + 1, measurements.size());
// Go back to the empty root.
bots.roots.openRoot(STRESS_ROOT_0_ID);
diff --git a/packages/DocumentsUI/res/drawable/ic_sd_storage.xml b/packages/DocumentsUI/res/drawable/ic_sd_storage.xml
index b0f3cc3..5aeebbb 100644
--- a/packages/DocumentsUI/res/drawable/ic_sd_storage.xml
+++ b/packages/DocumentsUI/res/drawable/ic_sd_storage.xml
@@ -14,8 +14,8 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
+ android:width="24dp"
+ android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
diff --git a/packages/DocumentsUI/res/drawable/ic_usb_storage.xml b/packages/DocumentsUI/res/drawable/ic_usb_storage.xml
new file mode 100644
index 0000000..2a8d024
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable/ic_usb_storage.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M15 7v4h1v2h-3V5h2l-3,-4,-3 4h2v8H8v-2.07c.7,-.37 1.2,-1.08 1.2,-1.93 0,-1.21,-.99,-2.2,-2.2,-2.2,-1.21 0,-2.2.99,-2.2 2.2 0 .85.5 1.56 1.2 1.93V13c0 1.11.89 2 2 2h3v3.05c-.71.37,-1.2 1.1,-1.2 1.95 0 1.22.99 2.2 2.2 2.2 1.21 0 2.2,-.98 2.2,-2.2 0,-.85,-.49,-1.58,-1.2,-1.95V15h3c1.11 0 2,-.89 2,-2v-2h1V7h-4z"/>
+</vector>
diff --git a/packages/DocumentsUI/res/layout/drawer_layout.xml b/packages/DocumentsUI/res/layout/drawer_layout.xml
index 065102b..09f4401 100644
--- a/packages/DocumentsUI/res/layout/drawer_layout.xml
+++ b/packages/DocumentsUI/res/layout/drawer_layout.xml
@@ -46,6 +46,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
+ android:popupTheme="?actionBarPopupTheme"
android:overlapAnchor="true" />
</com.android.documentsui.DocumentsToolbar>
diff --git a/packages/DocumentsUI/res/layout/fixed_layout.xml b/packages/DocumentsUI/res/layout/fixed_layout.xml
index 84a928d..9f7296b 100644
--- a/packages/DocumentsUI/res/layout/fixed_layout.xml
+++ b/packages/DocumentsUI/res/layout/fixed_layout.xml
@@ -44,6 +44,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
+ android:popupTheme="?actionBarPopupTheme"
android:overlapAnchor="true" />
</com.android.documentsui.DocumentsToolbar>
diff --git a/packages/DocumentsUI/res/layout/item_subdir.xml b/packages/DocumentsUI/res/layout/item_subdir.xml
index b8251d1..ffe4afe 100644
--- a/packages/DocumentsUI/res/layout/item_subdir.xml
+++ b/packages/DocumentsUI/res/layout/item_subdir.xml
@@ -24,16 +24,6 @@
android:orientation="horizontal"
android:baselineAligned="false">
- <ImageView
- android:id="@+id/subdir"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:paddingEnd="8dp"
- android:scaleType="centerInside"
- android:visibility="gone"
- android:src="@drawable/ic_subdirectory_arrow"
- android:contentDescription="@null" />
-
<TextView
android:id="@android:id/title"
android:layout_width="0dp"
diff --git a/packages/DocumentsUI/res/layout/single_pane_layout.xml b/packages/DocumentsUI/res/layout/single_pane_layout.xml
index 235d22d..8bf023f 100644
--- a/packages/DocumentsUI/res/layout/single_pane_layout.xml
+++ b/packages/DocumentsUI/res/layout/single_pane_layout.xml
@@ -43,6 +43,7 @@
android:id="@+id/stack"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:popupTheme="?actionBarPopupTheme"
android:layout_marginStart="4dp"
android:overlapAnchor="true" />
diff --git a/packages/DocumentsUI/src/com/android/documentsui/NavigationView.java b/packages/DocumentsUI/src/com/android/documentsui/NavigationView.java
index 4cba135..c520204 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/NavigationView.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/NavigationView.java
@@ -220,17 +220,14 @@
.inflate(R.layout.item_subdir, parent, false);
}
- final ImageView subdir = (ImageView) convertView.findViewById(R.id.subdir);
final TextView title = (TextView) convertView.findViewById(android.R.id.title);
final DocumentInfo doc = getItem(position);
if (position == 0) {
final RootInfo root = mEnv.getCurrentRoot();
title.setText(root.title);
- subdir.setVisibility(View.GONE);
} else {
title.setText(doc.displayName);
- subdir.setVisibility(View.VISIBLE);
}
return convertView;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/ListDocumentHolder.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/ListDocumentHolder.java
index 8b619b6..0831dbf 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/ListDocumentHolder.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/ListDocumentHolder.java
@@ -52,13 +52,14 @@
super(context, parent, R.layout.item_doc_list);
mTitle = (TextView) itemView.findViewById(android.R.id.title);
- mDetails = (LinearLayout) itemView.findViewById(R.id.line2);
mDate = (TextView) itemView.findViewById(R.id.date);
mSize = (TextView) itemView.findViewById(R.id.size);
mSummary = (TextView) itemView.findViewById(android.R.id.summary);
mIconMime = (ImageView) itemView.findViewById(R.id.icon_mime);
mIconThumb = (ImageView) itemView.findViewById(R.id.icon_thumb);
mIconCheck = (ImageView) itemView.findViewById(R.id.icon_check);
+ // Warning: mDetails view doesn't exists in layout-sw720dp-land layout
+ mDetails = (LinearLayout) itemView.findViewById(R.id.line2);
mIconHelper = iconHelper;
}
@@ -110,11 +111,11 @@
mTitle.setVisibility(View.VISIBLE);
- // Note, we don't show any details for any directory...ever.
+ boolean hasDetails = false;
if (isDirectory) {
- mDetails.setVisibility(View.GONE);
+ // Note, we don't show any details for any directory...ever.
+ hasDetails = false;
} else {
- boolean hasDetails = false;
if (docSummary != null) {
hasDetails = true;
mSummary.setText(docSummary);
@@ -136,8 +137,11 @@
mSize.setText(Formatter.formatFileSize(mContext, docSize));
} else {
mSize.setVisibility(View.GONE);
- mDetails.setVisibility(View.GONE);
}
+ }
+
+ // mDetails view doesn't exists in layout-sw720dp-land layout
+ if (mDetails != null) {
mDetails.setVisibility(hasDetails ? View.VISIBLE : View.GONE);
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
index 29273a3..3eaf10a 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
@@ -16,6 +16,7 @@
package com.android.documentsui.model;
+import static com.android.documentsui.Shared.DEBUG;
import static com.android.documentsui.Shared.compareToIgnoreCaseNullable;
import static com.android.documentsui.model.DocumentInfo.getCursorInt;
import static com.android.documentsui.model.DocumentInfo.getCursorLong;
@@ -31,6 +32,7 @@
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Root;
import android.text.TextUtils;
+import android.util.Log;
import com.android.documentsui.IconUtils;
import com.android.documentsui.R;
@@ -47,6 +49,8 @@
* Representation of a {@link Root}.
*/
public class RootInfo implements Durable, Parcelable, Comparable<RootInfo> {
+
+ private static final String TAG = "RootInfo";
private static final int VERSION_INIT = 1;
private static final int VERSION_DROP_TYPE = 2;
@@ -59,6 +63,8 @@
TYPE_DOWNLOADS,
TYPE_LOCAL,
TYPE_MTP,
+ TYPE_SD,
+ TYPE_USB,
TYPE_OTHER
})
@Retention(RetentionPolicy.SOURCE)
@@ -70,7 +76,9 @@
public static final int TYPE_DOWNLOADS = 5;
public static final int TYPE_LOCAL = 6;
public static final int TYPE_MTP = 7;
- public static final int TYPE_OTHER = 8;
+ public static final int TYPE_SD = 8;
+ public static final int TYPE_USB = 9;
+ public static final int TYPE_OTHER = 10;
public String authority;
public String rootId;
@@ -185,33 +193,40 @@
private void deriveFields() {
derivedMimeTypes = (mimeTypes != null) ? mimeTypes.split("\n") : null;
- // TODO: remove these special case icons
if (isHome()) {
+ derivedType = TYPE_LOCAL;
derivedIcon = R.drawable.ic_root_documents;
- derivedType = TYPE_LOCAL;
- } else if (isExternalStorage()) {
- derivedIcon = R.drawable.ic_root_smartphone;
- derivedType = TYPE_LOCAL;
- // TODO: Apply SD card icon to SD devices.
- } else if (isDownloads()) {
- derivedIcon = R.drawable.ic_root_download;
- derivedType = TYPE_DOWNLOADS;
- } else if (isImages()) {
- derivedIcon = R.drawable.ic_doc_image;
- derivedType = TYPE_IMAGES;
- } else if (isVideos()) {
- derivedIcon = R.drawable.ic_doc_video;
- derivedType = TYPE_VIDEO;
- } else if (isAudio()) {
- derivedIcon = R.drawable.ic_doc_audio;
- derivedType = TYPE_AUDIO;
- } else if (isRecents()) {
- derivedType = TYPE_RECENTS;
} else if (isMtp()) {
derivedType = TYPE_MTP;
+ derivedIcon = R.drawable.ic_usb_storage;
+ } else if (isUsb()) {
+ derivedType = TYPE_USB;
+ derivedIcon = R.drawable.ic_usb_storage;
+ } else if (isSd()) {
+ derivedType = TYPE_SD;
+ derivedIcon = R.drawable.ic_sd_storage;
+ } else if (isExternalStorage()) {
+ derivedType = TYPE_LOCAL;
+ derivedIcon = R.drawable.ic_root_smartphone;
+ } else if (isDownloads()) {
+ derivedType = TYPE_DOWNLOADS;
+ derivedIcon = R.drawable.ic_root_download;
+ } else if (isImages()) {
+ derivedType = TYPE_IMAGES;
+ derivedIcon = R.drawable.ic_doc_image;
+ } else if (isVideos()) {
+ derivedType = TYPE_VIDEO;
+ derivedIcon = R.drawable.ic_doc_video;
+ } else if (isAudio()) {
+ derivedType = TYPE_AUDIO;
+ derivedIcon = R.drawable.ic_doc_audio;
+ } else if (isRecents()) {
+ derivedType = TYPE_RECENTS;
} else {
derivedType = TYPE_OTHER;
}
+
+ if (DEBUG) Log.d(TAG, "Finished deriving fields: " + this);
}
public Uri getUri() {
@@ -291,6 +306,14 @@
return (flags & Root.FLAG_EMPTY) != 0;
}
+ public boolean isSd() {
+ return (flags & Root.FLAG_REMOVABLE_SD) != 0;
+ }
+
+ public boolean isUsb() {
+ return (flags & Root.FLAG_REMOVABLE_USB) != 0;
+ }
+
public Drawable loadIcon(Context context) {
if (derivedIcon != 0) {
return context.getDrawable(derivedIcon);
@@ -358,7 +381,14 @@
@Override
public String toString() {
- return "Root{authority=" + authority + ", rootId=" + rootId + ", title=" + title + "}";
+ return "Root{"
+ + "authority=" + authority
+ + ", rootId=" + rootId
+ + ", title=" + title
+ + ", isUsb=" + isUsb()
+ + ", isSd=" + isSd()
+ + ", isMtp=" + isMtp()
+ + "}";
}
public String getDirectoryString() {
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 3eda8ec..9a51b05 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -34,6 +34,7 @@
import android.os.ParcelFileDescriptor;
import android.os.ParcelFileDescriptor.OnCloseListener;
import android.os.UserHandle;
+import android.os.storage.DiskInfo;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.provider.DocumentsContract;
@@ -184,6 +185,14 @@
root.flags = Root.FLAG_LOCAL_ONLY
| Root.FLAG_SUPPORTS_SEARCH | Root.FLAG_SUPPORTS_IS_CHILD;
+ final DiskInfo disk = volume.getDisk();
+ if (DEBUG) Log.d(TAG, "Disk for root " + rootId + " is " + disk);
+ if (disk != null && disk.isSd()) {
+ root.flags |= Root.FLAG_REMOVABLE_SD;
+ } else if (disk != null && disk.isUsb()) {
+ root.flags |= Root.FLAG_REMOVABLE_USB;
+ }
+
if (volume.isPrimary()) {
// save off the primary volume for subsequent "Home" dir initialization.
primaryVolume = volume;
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index 64f5cc6..4b62f24 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -731,7 +731,10 @@
// The activity is a component name, therefore it is one or none.
if (resolvedActivities.get(0).activityInfo.exported) {
- intent.putExtra(PrintService.EXTRA_PRINT_JOB_INFO, mPrintJob);
+ PrintJobInfo.Builder printJobBuilder = new PrintJobInfo.Builder(mPrintJob);
+ printJobBuilder.setPages(mSelectedPages);
+
+ intent.putExtra(PrintService.EXTRA_PRINT_JOB_INFO, printJobBuilder.build());
intent.putExtra(PrintService.EXTRA_PRINTER_INFO, printer);
intent.putExtra(PrintService.EXTRA_PRINT_DOCUMENT_INFO,
mPrintedDocument.getDocumentInfo().info);
@@ -759,10 +762,14 @@
// Take the advanced options without interpretation.
mPrintJob.setAdvancedOptions(printJobInfo.getAdvancedOptions());
- // Take copies without interpretation as the advanced print dialog
- // cannot create a print job info with invalid copies.
- mCopiesEditText.setText(String.valueOf(printJobInfo.getCopies()));
- mPrintJob.setCopies(printJobInfo.getCopies());
+ if (printJobInfo.getCopies() < 1) {
+ Log.w(LOG_TAG, "Cannot apply return value from advanced options activity. Copies " +
+ "must be 1 or more. Actual value is: " + printJobInfo.getCopies() + ". " +
+ "Ignoring.");
+ } else {
+ mCopiesEditText.setText(String.valueOf(printJobInfo.getCopies()));
+ mPrintJob.setCopies(printJobInfo.getCopies());
+ }
PrintAttributes currAttributes = mPrintJob.getAttributes();
PrintAttributes newAttributes = printJobInfo.getAttributes();
@@ -771,7 +778,7 @@
// Take the media size only if the current printer supports is.
MediaSize oldMediaSize = currAttributes.getMediaSize();
MediaSize newMediaSize = newAttributes.getMediaSize();
- if (!oldMediaSize.equals(newMediaSize)) {
+ if (newMediaSize != null && !oldMediaSize.equals(newMediaSize)) {
final int mediaSizeCount = mMediaSizeSpinnerAdapter.getCount();
MediaSize newMediaSizePortrait = newAttributes.getMediaSize().asPortrait();
for (int i = 0; i < mediaSizeCount; i++) {
diff --git a/packages/SettingsLib/res/drawable/ic_info.xml b/packages/SettingsLib/res/drawable/ic_info.xml
new file mode 100644
index 0000000..afe7e6b
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_info.xml
@@ -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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?android:attr/colorAccent">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_settings_lock_outline.xml b/packages/SettingsLib/res/drawable/ic_settings_lock_outline.xml
deleted file mode 100644
index b3d7cf9..0000000
--- a/packages/SettingsLib/res/drawable/ic_settings_lock_outline.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- 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.
--->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="21dp"
- android:height="21dp"
- android:viewportWidth="21.0"
- android:viewportHeight="21.0"
- android:tint="?android:attr/colorAccent">
- <path
- android:fillColor="@android:color/white"
- android:pathData="M8,16c1.1,0,2-0.9,2-2s-0.9-2-2-2s-2,0.9-2,2S6.9,16,8,16zM14,7h-1V5c0-2.8-2.2-5-5-5S3,2.2,3,5v2H2C0.9,7,0,7.9,0,9v10c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2V9C16,7.9,15.1,7,14,7z M4.9,5c0-1.7,1.4-3.1,3.1-3.1s3.1,1.4,3.1,3.1v2H4.9V5z M14,19H2V9h12V19z" />
-</vector>
diff --git a/packages/SettingsLib/res/layout/restricted_icon.xml b/packages/SettingsLib/res/layout/restricted_icon.xml
new file mode 100644
index 0000000..d57fb80
--- /dev/null
+++ b/packages/SettingsLib/res/layout/restricted_icon.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/restricted_icon"
+ android:layout_width="@dimen/restricted_icon_size"
+ android:layout_height="@dimen/restricted_icon_size"
+ android:src="@drawable/ic_info"
+ android:gravity="end|center_vertical" />
\ No newline at end of file
diff --git a/packages/SettingsLib/res/layout/restricted_switch_widget.xml b/packages/SettingsLib/res/layout/restricted_switch_widget.xml
new file mode 100644
index 0000000..6183812
--- /dev/null
+++ b/packages/SettingsLib/res/layout/restricted_switch_widget.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+ <ImageView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/restricted_icon"
+ android:layout_width="@dimen/restricted_icon_size"
+ android:layout_height="@dimen/restricted_icon_size"
+ android:src="@drawable/ic_info"
+ android:gravity="end|center_vertical" />
+ <!-- Based off frameworks/base/core/res/res/layout/preference_widget_switch.xml -->
+ <Switch xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+android:id/switch_widget"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:focusable="false"
+ android:clickable="false"
+ android:background="@null" />
+</merge>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml
index 9f78e87..c3b3cfc 100644
--- a/packages/SettingsLib/res/values/dimens.xml
+++ b/packages/SettingsLib/res/values/dimens.xml
@@ -33,8 +33,8 @@
<dimen name="user_spinner_item_height">56dp</dimen>
<!-- Lock icon for preferences locked by admin -->
- <dimen name="restricted_lock_icon_size">16dp</dimen>
- <dimen name="restricted_lock_icon_padding">4dp</dimen>
+ <dimen name="restricted_icon_size">16dp</dimen>
+ <dimen name="restricted_icon_padding">4dp</dimen>
<dimen name="wifi_preference_badge_padding">8dip</dimen>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 72fa939..ae2c6e7 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -773,6 +773,11 @@
<!-- Summary for settings preference disabled by administrator [CHAR LIMIT=50] -->
<string name="disabled_by_admin_summary_text">Controlled by admin</string>
+ <!-- Summary for switch preference to denote it is switched on [CHAR LIMIT=50] -->
+ <string name="enabled_by_admin">Enabled by administrator</string>
+ <!-- Summary for switch preference to denote it is switched on [CHAR LIMIT=50] -->
+ <string name="disabled_by_admin">Disabled by administrator</string>
+
<!-- Option in navigation drawer that leads to Settings main screen [CHAR LIMIT=30] -->
<string name="home">Home</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java
index c2f885d..4c0450e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java
@@ -49,7 +49,7 @@
mRestrictedPadlock = RestrictedLockUtils.getRestrictedPadlock(context);
mRestrictedPadlockPadding = context.getResources().getDimensionPixelSize(
- R.dimen.restricted_lock_icon_padding);
+ R.dimen.restricted_icon_padding);
}
private final OnItemSelectedListener mItemSelectedListener = new OnItemSelectedListener() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockImageSpan.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockImageSpan.java
index e63130d..360a34c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockImageSpan.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockImageSpan.java
@@ -36,7 +36,7 @@
mContext = context;
mExtraPadding = mContext.getResources().getDimensionPixelSize(
- R.dimen.restricted_lock_icon_padding);
+ R.dimen.restricted_icon_padding);
mRestrictedPadlock = RestrictedLockUtils.getRestrictedPadlock(mContext);
}
@@ -53,7 +53,7 @@
// Add extra padding before the padlock.
float transX = x + mExtraPadding;
- float transY = bottom - drawable.getBounds().bottom - paint.getFontMetricsInt().descent;
+ float transY = (bottom - drawable.getBounds().bottom) / 2.0f;
canvas.translate(transX, transY);
drawable.draw(canvas);
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
index 6d29c5f..d0c249f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
@@ -49,9 +49,9 @@
* @return drawables for displaying with settings that are locked by a device admin.
*/
public static Drawable getRestrictedPadlock(Context context) {
- Drawable restrictedPadlock = context.getDrawable(R.drawable.ic_settings_lock_outline);
+ Drawable restrictedPadlock = context.getDrawable(R.drawable.ic_info);
final int iconSize = context.getResources().getDimensionPixelSize(
- R.dimen.restricted_lock_icon_size);
+ R.dimen.restricted_icon_size);
restrictedPadlock.setBounds(0, 0, iconSize, iconSize);
return restrictedPadlock;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
index 810f6eb..e69497a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
@@ -23,6 +23,7 @@
import android.support.v7.preference.PreferenceManager;
import android.support.v7.preference.PreferenceViewHolder;
import android.util.AttributeSet;
+import android.view.View;
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
@@ -36,6 +37,7 @@
public RestrictedPreference(Context context, AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ setWidgetLayoutResource(R.layout.restricted_icon);
mHelper = new RestrictedPreferenceHelper(context, this, attrs);
}
@@ -56,6 +58,10 @@
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
mHelper.onBindViewHolder(holder);
+ final View restrictedIcon = holder.findViewById(R.id.restricted_icon);
+ if (restrictedIcon != null) {
+ restrictedIcon.setVisibility(isDisabledByAdmin() ? View.VISIBLE : View.GONE);
+ }
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
index 227b1e8..0c0af24 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
@@ -54,7 +54,7 @@
mRestrictedPadlock = RestrictedLockUtils.getRestrictedPadlock(mContext);
mRestrictedPadlockPadding = mContext.getResources().getDimensionPixelSize(
- R.dimen.restricted_lock_icon_padding);
+ R.dimen.restricted_icon_padding);
if (attrs != null) {
final TypedArray attributes = context.obtainStyledAttributes(attrs,
@@ -91,12 +91,8 @@
* Modify PreferenceViewHolder to add padlock if restriction is disabled.
*/
public void onBindViewHolder(PreferenceViewHolder holder) {
- final TextView titleView = (TextView) holder.findViewById(android.R.id.title);
- if (titleView != null) {
- RestrictedLockUtils.setTextViewPadlock(mContext, titleView, mDisabledByAdmin);
- if (mDisabledByAdmin) {
- holder.itemView.setEnabled(true);
- }
+ if (mDisabledByAdmin) {
+ holder.itemView.setEnabled(true);
}
if (mUseAdminDisabledSummary) {
final TextView summaryView = (TextView) holder.findViewById(android.R.id.summary);
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
index 6cae8aa..f381286 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
@@ -23,6 +23,8 @@
import android.support.v7.preference.PreferenceViewHolder;
import android.support.v14.preference.SwitchPreference;
import android.util.AttributeSet;
+import android.view.View;
+import android.widget.TextView;
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
@@ -36,6 +38,7 @@
public RestrictedSwitchPreference(Context context, AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ setWidgetLayoutResource(R.layout.restricted_switch_widget);
mHelper = new RestrictedPreferenceHelper(context, this, attrs);
}
@@ -56,6 +59,20 @@
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
mHelper.onBindViewHolder(holder);
+ final View restrictedIcon = holder.findViewById(R.id.restricted_icon);
+ final View switchWidget = holder.findViewById(android.R.id.switch_widget);
+ if (restrictedIcon != null) {
+ restrictedIcon.setVisibility(isDisabledByAdmin() ? View.VISIBLE : View.GONE);
+ }
+ if (switchWidget != null) {
+ switchWidget.setVisibility(isDisabledByAdmin() ? View.GONE : View.VISIBLE);
+ }
+ final TextView summaryView = (TextView) holder.findViewById(android.R.id.summary);
+ if (summaryView != null && isDisabledByAdmin()) {
+ summaryView.setText(
+ isChecked() ? R.string.enabled_by_admin : R.string.disabled_by_admin);
+ summaryView.setVisibility(View.VISIBLE);
+ }
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
index a578055..7a1c741 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
@@ -69,16 +69,18 @@
long startTime = System.currentTimeMillis();
- getWindow().addFlags(LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
- getWindow().addFlags(LayoutParams.FLAG_TRANSLUCENT_STATUS);
- requestWindowFeature(Window.FEATURE_NO_TITLE);
+ TypedArray theme = getTheme().obtainStyledAttributes(android.R.styleable.Theme);
+ if (!theme.getBoolean(android.R.styleable.Theme_windowNoTitle, false)) {
+ getWindow().addFlags(LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
+ getWindow().addFlags(LayoutParams.FLAG_TRANSLUCENT_STATUS);
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
+ }
super.setContentView(R.layout.settings_with_drawer);
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
if (mDrawerLayout == null) {
return;
}
Toolbar toolbar = (Toolbar) findViewById(R.id.action_bar);
- TypedArray theme = getTheme().obtainStyledAttributes(android.R.styleable.Theme);
if (theme.getBoolean(android.R.styleable.Theme_windowNoTitle, false)) {
toolbar.setVisibility(View.GONE);
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
index 2dfdfda..418b138 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
@@ -41,7 +41,7 @@
public class TileUtils {
private static final boolean DEBUG = false;
- private static final boolean DEBUG_TIMING = true;
+ private static final boolean DEBUG_TIMING = false;
private static final String LOG_TAG = "TileUtils";
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index b5b7bcd..637551c 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -245,7 +245,7 @@
android:stateNotNeeded="true"
android:resumeWhilePausing="true"
android:screenOrientation="behind"
- android:theme="@style/RecentsTheme.Wallpaper">
+ android:theme="@style/RecentsTvTheme.Wallpaper">
<intent-filter>
<action android:name="com.android.systemui.recents.TOGGLE_RECENTS" />
</intent-filter>
diff --git a/packages/SystemUI/res/color/notification_guts_buttons.xml b/packages/SystemUI/res/color/notification_guts_buttons.xml
new file mode 100644
index 0000000..f7a4ee9
--- /dev/null
+++ b/packages/SystemUI/res/color/notification_guts_buttons.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_checked="true"
+ android:color="@*android:color/material_deep_teal_500" />
+ <item android:color="@android:color/black"
+ android:alpha=".87" />
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_info.xml b/packages/SystemUI/res/drawable/ic_info.xml
deleted file mode 100644
index 65e7bf5..0000000
--- a/packages/SystemUI/res/drawable/ic_info.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<!--
-Copyright (C) 2014 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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24.0dp"
- android:height="24.0dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0">
- <path
- android:pathData="M12.000000,2.000000C6.500000,2.000000 2.000000,6.500000 2.000000,12.000000s4.500000,10.000000 10.000000,10.000000c5.500000,0.000000 10.000000,-4.500000 10.000000,-10.000000S17.500000,2.000000 12.000000,2.000000zM13.000000,17.000000l-2.000000,0.000000l0.000000,-6.000000l2.000000,0.000000L13.000000,17.000000zM13.000000,9.000000l-2.000000,0.000000L11.000000,7.000000l2.000000,0.000000L13.000000,9.000000z"
- android:fillColor="#FFFFFF"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/recents_tv_background_gradient.xml b/packages/SystemUI/res/drawable/recents_tv_background_gradient.xml
new file mode 100644
index 0000000..e98d43f
--- /dev/null
+++ b/packages/SystemUI/res/drawable/recents_tv_background_gradient.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <gradient
+ android:startColor="#99000000"
+ android:endColor="#E6000000"
+ android:angle="90"/>
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/notification_guts.xml b/packages/SystemUI/res/layout/notification_guts.xml
index 4b0c834..1ab6bf9 100644
--- a/packages/SystemUI/res/layout/notification_guts.xml
+++ b/packages/SystemUI/res/layout/notification_guts.xml
@@ -63,29 +63,32 @@
android:id="@+id/importance_buttons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingTop="8dp"
- android:paddingBottom="8dip"
+ android:paddingTop="4dp"
+ android:paddingBottom="16dip"
android:paddingEnd="8dp" >
<RadioButton
android:id="@+id/silent_importance"
android:layout_width="wrap_content"
android:layout_height="48dp"
+ android:paddingStart="32dp"
android:text="@string/show_silently"
- style="@style/TextAppearance.NotificationGuts.Primary"
- android:buttonTint="#858383" />
+ style="@style/TextAppearance.NotificationGuts.Radio"
+ android:buttonTint="@color/notification_guts_buttons" />
<RadioButton
android:id="@+id/block_importance"
android:layout_width="wrap_content"
android:layout_height="48dp"
+ android:paddingStart="32dp"
android:text="@string/block"
- style="@style/TextAppearance.NotificationGuts.Primary"
- android:buttonTint="#858383" />
+ style="@style/TextAppearance.NotificationGuts.Radio"
+ android:buttonTint="@color/notification_guts_buttons" />
<RadioButton
android:id="@+id/reset_importance"
android:layout_width="wrap_content"
android:layout_height="48dp"
- style="@style/TextAppearance.NotificationGuts.Primary"
- android:buttonTint="#858383" />
+ android:paddingStart="32dp"
+ style="@style/TextAppearance.NotificationGuts.Radio"
+ android:buttonTint="@color/notification_guts_buttons" />
</RadioGroup>
<!-- Importance slider -->
<LinearLayout
diff --git a/packages/SystemUI/res/layout/qs_tile_label.xml b/packages/SystemUI/res/layout/qs_tile_label.xml
index 603ebbf..b0dca9a 100644
--- a/packages/SystemUI/res/layout/qs_tile_label.xml
+++ b/packages/SystemUI/res/layout/qs_tile_label.xml
@@ -20,22 +20,22 @@
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView android:id="@+id/tile_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textColor="@color/qs_tile_text"
- android:gravity="center_horizontal"
- android:minLines="2"
- android:padding="0dp"
- android:fontFamily="sans-serif-condensed"
- android:textStyle="normal"
- android:textSize="@dimen/qs_tile_text_size"
- android:clickable="false" />
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="@color/qs_tile_text"
+ android:gravity="center_horizontal"
+ android:minLines="2"
+ android:padding="0dp"
+ android:fontFamily="sans-serif-condensed"
+ android:textStyle="normal"
+ android:textSize="@dimen/qs_tile_text_size"
+ android:clickable="false" />
<ImageView android:id="@+id/restricted_padlock"
- android:layout_width="@dimen/qs_tile_text_size"
- android:layout_height="@dimen/qs_tile_text_size"
- android:src="@drawable/ic_settings_lock_outline"
- android:layout_marginLeft="@dimen/restricted_padlock_pading"
- android:baselineAlignBottom="true"
- android:scaleType="centerInside"
- android:visibility="gone" />
+ android:layout_width="@dimen/qs_tile_text_size"
+ android:layout_height="match_parent"
+ android:paddingBottom="@dimen/qs_tile_text_size"
+ android:src="@drawable/ic_info"
+ android:layout_marginLeft="@dimen/restricted_padlock_pading"
+ android:scaleType="centerInside"
+ android:visibility="gone" />
</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_user_detail_item.xml b/packages/SystemUI/res/layout/qs_user_detail_item.xml
index a22c360..661d74a 100644
--- a/packages/SystemUI/res/layout/qs_user_detail_item.xml
+++ b/packages/SystemUI/res/layout/qs_user_detail_item.xml
@@ -53,10 +53,10 @@
<ImageView
android:id="@+id/restricted_padlock"
android:layout_width="@dimen/qs_detail_item_secondary_text_size"
- android:layout_height="@dimen/qs_detail_item_secondary_text_size"
- android:src="@drawable/ic_settings_lock_outline"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:src="@drawable/ic_info"
android:layout_marginLeft="@dimen/restricted_padlock_pading"
- android:baselineAlignBottom="true"
android:scaleType="centerInside"
android:visibility="gone" />
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/recents_on_tv.xml b/packages/SystemUI/res/layout/recents_on_tv.xml
index 94b099e..3a7c1d1 100644
--- a/packages/SystemUI/res/layout/recents_on_tv.xml
+++ b/packages/SystemUI/res/layout/recents_on_tv.xml
@@ -18,9 +18,10 @@
android:id="@+id/recents_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:background="@drawable/recents_tv_background_gradient"
android:clipChildren="false"
- android:clipToPadding="false" >
-
+ android:clipToPadding="false"
+ android:layoutDirection="rtl">
<com.android.systemui.recents.tv.views.TaskStackHorizontalGridView
android:id="@+id/task_list"
android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/layout/recents_task_card_view.xml b/packages/SystemUI/res/layout/recents_tv_task_card_view.xml
similarity index 65%
rename from packages/SystemUI/res/layout/recents_task_card_view.xml
rename to packages/SystemUI/res/layout/recents_tv_task_card_view.xml
index fa1daad..c5b1a7a 100644
--- a/packages/SystemUI/res/layout/recents_task_card_view.xml
+++ b/packages/SystemUI/res/layout/recents_tv_task_card_view.xml
@@ -20,28 +20,28 @@
android:focusable="true"
android:focusableInTouchMode="true"
android:layout_gravity="center"
- android:layout_centerInParent="true">
+ android:layout_centerInParent="true"
+ android:layoutDirection="ltr">
- <RelativeLayout
+ <LinearLayout
android:layout_width="@dimen/recents_tv_card_width"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
- android:layout_gravity="center">
- <ImageView
- android:id="@+id/card_view_thumbnail"
- android:layout_width="match_parent"
- android:layout_height="@dimen/recents_tv_card_height"
- android:scaleType="centerCrop"
- android:gravity="center"
- android:layout_alignParentTop="true"
- android:layout_centerHorizontal="true"/>
-
- <RelativeLayout
+ android:layout_gravity="center"
+ android:orientation="vertical" >
+ <LinearLayout
android:id="@+id/card_info_field"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@id/card_view_thumbnail"
- android:background="@color/recents_tv_card_background_color" >
+ android:layout_height="wrap_content">
+ <ImageView
+ android:id="@+id/card_extra_badge"
+ android:layout_width="@dimen/recents_tv_card_extra_badge_size"
+ android:layout_height="@dimen/recents_tv_card_extra_badge_size"
+ android:layout_marginBottom="@dimen/recents_tv_icon_padding_bottom"
+ android:layout_marginEnd="@dimen/recents_tv_icon_padding_end"
+ android:scaleType="fitCenter"
+ android:layout_centerVertical="true"
+ android:layout_alignParentRight="true" />
<TextView
android:id="@+id/card_title_text"
android:layout_width="match_parent"
@@ -49,29 +49,21 @@
android:layout_alignParentTop="false"
android:includeFontPadding="true"
android:minLines="1"
- android:maxLines="2"
+ android:maxLines="1"
android:textColor="@color/recents_tv_card_title_text_color"
- android:ellipsize="end" />
- <TextView
- android:id="@+id/card_content_text"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentStart="true"
- android:layout_below="@id/card_title_text"
- android:includeFontPadding="true"
- android:minLines="1"
- android:maxLines="2"
- android:textColor="@color/recents_tv_card_content_text_color"
- android:ellipsize="end" />
- <ImageView
- android:id="@+id/card_extra_badge"
- android:layout_width="@dimen/recents_tv_card_extra_badge_size"
- android:layout_height="@dimen/recents_tv_card_extra_badge_size"
- android:scaleType="fitCenter"
- android:background="@android:color/transparent"
- android:contentDescription="@null"
- android:layout_centerVertical="true"
- android:layout_alignParentRight="true"/>
- </RelativeLayout>
- </RelativeLayout>
+ android:fontFamily="@string/font_roboto_regular"
+ android:textSize="@dimen/recents_tv_title_text_size"
+ android:layout_marginBottom="@dimen/recents_tv_text_padding_bottom"
+ android:ellipsize="end"/>
+ </LinearLayout>
+ <ImageView
+ android:id="@+id/card_view_thumbnail"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/recents_tv_card_height"
+ android:scaleType="centerCrop"
+ android:gravity="center"
+ android:layout_alignParentTop="true"
+ android:layout_centerHorizontal="true"
+ android:layout_below="@id/card_title_text" />
+ </LinearLayout>
</com.android.systemui.recents.tv.views.TaskCardView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/remote_input.xml b/packages/SystemUI/res/layout/remote_input.xml
index 818df3b..75195c4 100644
--- a/packages/SystemUI/res/layout/remote_input.xml
+++ b/packages/SystemUI/res/layout/remote_input.xml
@@ -43,7 +43,7 @@
android:singleLine="true"
android:ellipsize="start"
android:inputType="textShortMessage|textAutoCorrect|textCapSentences"
- android:imeOptions="actionSend" />
+ android:imeOptions="actionSend|flagNoExtractUi" />
<FrameLayout
android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/values/colors_tv.xml b/packages/SystemUI/res/values/colors_tv.xml
index 6f4c983..af99aae 100644
--- a/packages/SystemUI/res/values/colors_tv.xml
+++ b/packages/SystemUI/res/values/colors_tv.xml
@@ -18,7 +18,5 @@
-->
<resources>
<color name="recents_tv_card_background_color">#FF37474F</color>
- <color name="recents_tv_card_title_text_color">#FFEEEEEE</color>
- <color name="recents_tv_card_content_text_color">#99EEEEEE</color>
- <color name="recents_tv_card_source_text_color">#99EEEEEE</color>
+ <color name="recents_tv_card_title_text_color">#CCEEEEEE</color>
</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 8a7f90b..12c3a5d 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -292,6 +292,9 @@
<!-- The amount to allow the stack to overscroll. -->
<dimen name="recents_stack_overscroll">24dp</dimen>
+ <!-- The size of the initial peek area at the top of the stack (below the status bar). -->
+ <dimen name="recents_initial_top_peek_size">8dp</dimen>
+
<!-- The size of the peek area at the top of the stack (below the status bar). -->
<dimen name="recents_layout_focused_top_peek_size">@dimen/recents_history_button_height</dimen>
diff --git a/packages/SystemUI/res/values/dimens_tv.xml b/packages/SystemUI/res/values/dimens_tv.xml
index bf32cc7..b589110 100644
--- a/packages/SystemUI/res/values/dimens_tv.xml
+++ b/packages/SystemUI/res/values/dimens_tv.xml
@@ -18,15 +18,21 @@
-->
<resources>
<!-- Dimens for recents card in the recents view on tv -->
- <dimen name="recents_tv_card_width">150dip</dimen>
- <dimen name="recents_tv_card_height">85dip</dimen>
- <dimen name="recents_tv_card_extra_badge_size">16dip</dimen>
+ <dimen name="recents_tv_card_width">268dip</dimen>
+ <dimen name="recents_tv_card_height">151dip</dimen>
+ <dimen name="recents_tv_card_extra_badge_size">20dip</dimen>
+ <dimen name="recents_tv_banner_width">114dip</dimen>
+ <dimen name="recents_tv_banner_height">64dip</dimen>
+ <dimen name="recents_tv_banner_margin_top">16dip</dimen>
+ <dimen name="recents_tv_icon_padding_bottom">8dip</dimen>
+ <dimen name="recents_tv_icon_padding_end">12dip</dimen>
+ <dimen name="recents_tv_text_padding_bottom">12dip</dimen>
<!-- Padding for grid view in recents view on tv -->
<dimen name="recents_tv_grid_row_padding">56dip</dimen>
<dimen name="recents_tv_gird_row_top_padding">57dip</dimen>
- <dimen name="recents_tv_grid_max_row_height">200dip</dimen>
- <dimen name="recents_tv_gird_card_spacing">8dip</dimen>
+ <dimen name="recents_tv_grid_max_row_height">268dip</dimen>
+ <dimen name="recents_tv_gird_card_spacing">20dip</dimen>
<!-- Values for focus animation -->
<dimen name="recents_tv_unselected_item_z">6dp</dimen>
@@ -34,4 +40,7 @@
<!-- Extra space around the PIP and its outline in PIP onboarding activity -->
<dimen name="tv_pip_bounds_space">3dp</dimen>
+
+ <!-- Values for text on recents cards on tv -->
+ <dimen name="recents_tv_title_text_size">12sp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/strings_tv.xml b/packages/SystemUI/res/values/strings_tv.xml
index 4f382ea..59cfb1e 100644
--- a/packages/SystemUI/res/values/strings_tv.xml
+++ b/packages/SystemUI/res/values/strings_tv.xml
@@ -31,11 +31,13 @@
<string name="pip_cancel" translatable="false">Cancel</string>
<!-- Overlay text on PIP -->
<string name="pip_hold_home" translatable="false">Hold HOME to control PIP</string>
-
<!-- Picture-in-Picture onboarding screen -->
<eat-comment />
<!-- Description for onboarding screen. -->
<string name="pip_onboarding_description" translatable="false">Press and hold the HOME\nbutton to close or control it</string>
<!-- Button to close onboarding screen. -->
<string name="pip_onboarding_button" translatable="false">Got it</string>
+ <!-- Font for Recents -->
+ <!-- DO NOT TRANSLATE -->
+ <string name="font_roboto_regular" translatable="false">sans-serif</string>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 11df681..89890d6 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -323,6 +323,10 @@
<item name="android:textSize">16sp</item>
</style>
+ <style name="TextAppearance.NotificationGuts.Radio">
+ <item name="android:alpha">.87</item>
+ </style>
+
<style name="TextAppearance.NotificationGuts.Button">
<item name="android:textSize">14sp</item>
<item name="android:textAllCaps">true</item>
diff --git a/packages/SystemUI/res/values/styles_tv.xml b/packages/SystemUI/res/values/styles_tv.xml
index 3f0caab..263e1a4 100644
--- a/packages/SystemUI/res/values/styles_tv.xml
+++ b/packages/SystemUI/res/values/styles_tv.xml
@@ -22,4 +22,11 @@
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:backgroundDimEnabled">false</item>
</style>
+
+ <style name="RecentsTvTheme.Wallpaper" parent="@android:style/Theme.Material.NoActionBar.Overscan">
+ <item name="android:windowBackground">@android:color/transparent</item>
+ <item name="android:backgroundDimEnabled">false</item>
+ <item name="android:colorBackgroundCacheHint">@null</item>
+ <item name="android:windowIsTranslucent">true</item>
+ </style>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 33f3c30..f6dcc11 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -144,7 +144,9 @@
protected Animator getViewTranslationAnimator(View v, float target,
AnimatorUpdateListener listener) {
ObjectAnimator anim = createTranslationAnimation(v, target);
- anim.addUpdateListener(listener);
+ if (listener != null) {
+ anim.addUpdateListener(listener);
+ }
return anim;
}
@@ -370,6 +372,9 @@
};
Animator anim = getViewTranslationAnimator(animView, newPos, updateListener);
+ if (anim == null) {
+ return;
+ }
if (useAccelerateInterpolator) {
anim.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
} else {
@@ -411,6 +416,9 @@
};
Animator anim = getViewTranslationAnimator(animView, targetLeft, updateListener);
+ if (anim == null) {
+ return;
+ }
int duration = SNAP_ANIM_LEN;
anim.setDuration(duration);
anim.addListener(new AnimatorListenerAdapter() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 8ccf60d..24b45cc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -167,6 +167,11 @@
}
};
+ public int getColumnCount() {
+ if (mPages.size() == 0) return 0;
+ return mPages.get(0).mColumns;
+ }
+
public static class TilePage extends TileLayout {
private int mMaxRows = 3;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index c643d67..6137349 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -16,6 +16,7 @@
import android.util.Log;
import android.view.View;
+import android.view.View.OnAttachStateChangeListener;
import android.view.View.OnLayoutChangeListener;
import android.view.animation.PathInterpolator;
import android.widget.TextView;
@@ -25,14 +26,20 @@
import com.android.systemui.qs.TouchAnimator.Builder;
import com.android.systemui.qs.TouchAnimator.Listener;
import com.android.systemui.statusbar.phone.QSTileHost;
+import com.android.systemui.tuner.TunerService;
+import com.android.systemui.tuner.TunerService.Tunable;
import java.util.ArrayList;
import java.util.Collection;
-public class QSAnimator implements Callback, PageListener, Listener, OnLayoutChangeListener {
+public class QSAnimator implements Callback, PageListener, Listener, OnLayoutChangeListener,
+ OnAttachStateChangeListener, Tunable {
private static final String TAG = "QSAnimator";
+ private static final String ALLOW_FANCY_ANIMATION = "sysui_qs_fancy_anim";
+ private static final String MOVE_FULL_ROWS = "sysui_qs_move_whole_rows";
+
public static final PathInterpolator TRANSLATION_Y_INTERPOLATOR =
new PathInterpolator(.1f, .3f, 1, 1);
@@ -44,34 +51,77 @@
private final QSPanel mQsPanel;
private final QSContainer mQsContainer;
+ private PagedTileLayout mPagedLayout;
+
private boolean mOnFirstPage = true;
private TouchAnimator mFirstPageAnimator;
private TouchAnimator mFirstPageDelayedAnimator;
private TouchAnimator mTranslationYAnimator;
private TouchAnimator mNonfirstPageAnimator;
+ private boolean mOnKeyguard;
+
+ private boolean mAllowFancy;
+ private boolean mFullRows;
+ private int mNumQuickTiles;
+
public QSAnimator(QSContainer container, QuickQSPanel quickPanel, QSPanel panel) {
mQsContainer = container;
mQuickQsPanel = quickPanel;
mQsPanel = panel;
+ mQsPanel.addOnAttachStateChangeListener(this);
container.addOnLayoutChangeListener(this);
QSTileLayout tileLayout = mQsPanel.getTileLayout();
if (tileLayout instanceof PagedTileLayout) {
- ((PagedTileLayout) tileLayout).setPageListener(this);
+ mPagedLayout = ((PagedTileLayout) tileLayout);
+ mPagedLayout.setPageListener(this);
} else {
Log.w(TAG, "QS Not using page layout");
}
}
+ public void setOnKeyguard(boolean onKeyguard) {
+ mOnKeyguard = onKeyguard;
+ if (mOnKeyguard) {
+ clearAnimationState();
+ }
+ }
+
public void setHost(QSTileHost qsh) {
qsh.addCallback(this);
}
@Override
+ public void onViewAttachedToWindow(View v) {
+ TunerService.get(mQsContainer.getContext()).addTunable(this, ALLOW_FANCY_ANIMATION,
+ MOVE_FULL_ROWS, QuickQSPanel.NUM_QUICK_TILES);
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ TunerService.get(mQsContainer.getContext()).removeTunable(this);
+ }
+
+ @Override
+ public void onTuningChanged(String key, String newValue) {
+ if (ALLOW_FANCY_ANIMATION.equals(key)) {
+ mAllowFancy = newValue == null || Integer.parseInt(newValue) != 0;
+ if (!mAllowFancy) {
+ clearAnimationState();
+ }
+ } else if (MOVE_FULL_ROWS.equals(key)) {
+ mFullRows = newValue != null && Integer.parseInt(newValue) != 0;
+ } else if (QuickQSPanel.NUM_QUICK_TILES.equals(key)) {
+ mNumQuickTiles = QuickQSPanel.getNumQuickTiles(mQsContainer.getContext());
+ clearAnimationState();
+ }
+ updateAnimators();
+ }
+
+ @Override
public void onPageChanged(boolean isFirst) {
if (mOnFirstPage == isFirst) return;
if (!isFirst) {
- setPosition(1);
clearAnimationState();
}
mOnFirstPage = isFirst;
@@ -85,6 +135,7 @@
int count = 0;
int[] loc1 = new int[2];
int[] loc2 = new int[2];
+ int lastYDiff = 0;
firstPageDelayedBuilder.setStartDelay(EXPANDED_TILE_DELAY);
firstPageBuilder.setListener(this);
translationYBuilder.setInterpolator(TRANSLATION_Y_INTERPOLATOR);
@@ -92,18 +143,20 @@
firstPageDelayedBuilder.addFloat(mQsPanel.getTileLayout(), "alpha", 0, 1);
mAllViews.clear();
mTopFiveQs.clear();
+ mAllViews.add((View) mQsPanel.getTileLayout());
for (QSTile<?> tile : tiles) {
QSTileBaseView tileView = mQsPanel.getTileView(tile);
final TextView label = ((QSTileView) tileView).getLabel();
- if (count++ < 5) {
+ final View tileIcon = tileView.getIcon();
+ if (count < mNumQuickTiles && mAllowFancy) {
// Quick tiles.
QSTileBaseView quickTileView = mQuickQsPanel.getTileView(tile);
- final View tileIcon = tileView.getIcon();
getRelativePosition(loc1, quickTileView.getIcon(), mQsContainer);
getRelativePosition(loc2, tileIcon, mQsContainer);
final int xDiff = loc2[0] - loc1[0];
final int yDiff = loc2[1] - loc1[1];
+ lastYDiff = yDiff;
// Move the quick tile right from its location to the new one.
firstPageBuilder.addFloat(quickTileView, "translationX", 0, xDiff);
translationYBuilder.addFloat(quickTileView, "translationY", 0, yDiff);
@@ -119,20 +172,38 @@
mTopFiveQs.add(tileIcon);
mAllViews.add(tileIcon);
+ mAllViews.add(label);
mAllViews.add(quickTileView);
+ } else if (mFullRows && isIconInAnimatedRow(count)) {
+ firstPageBuilder.addFloat(tileView, "translationY", mQsPanel.getHeight(), 0);
+ translationYBuilder.addFloat(label, "translationY", -lastYDiff, 0);
+ translationYBuilder.addFloat(tileIcon, "translationY", -lastYDiff, 0);
+ mAllViews.add(tileIcon);
+ mAllViews.add(label);
}
mAllViews.add(tileView);
mAllViews.add(label);
+ count++;
}
- mFirstPageAnimator = firstPageBuilder.build();
- mFirstPageDelayedAnimator = firstPageDelayedBuilder.build();
- mTranslationYAnimator = translationYBuilder.build();
+ if (mAllowFancy) {
+ mFirstPageAnimator = firstPageBuilder.build();
+ mFirstPageDelayedAnimator = firstPageDelayedBuilder.build();
+ mTranslationYAnimator = translationYBuilder.build();
+ }
mNonfirstPageAnimator = new TouchAnimator.Builder()
.addFloat(mQuickQsPanel, "alpha", 1, 0)
.setEndDelay(.5f)
.build();
}
+ private boolean isIconInAnimatedRow(int count) {
+ if (mPagedLayout == null) {
+ return false;
+ }
+ final int columnCount = mPagedLayout.getColumnCount();
+ return count < ((mNumQuickTiles + columnCount - 1) / columnCount) * columnCount;
+ }
+
private void getRelativePosition(int[] loc1, View view, View parent) {
loc1[0] = 0 + view.getWidth() / 2;
loc1[1] = 0;
@@ -148,7 +219,10 @@
public void setPosition(float position) {
if (mFirstPageAnimator == null) return;
- if (mOnFirstPage) {
+ if (mOnKeyguard) {
+ return;
+ }
+ if (mOnFirstPage && mAllowFancy) {
mQuickQsPanel.setAlpha(1);
mFirstPageAnimator.setPosition(position);
mFirstPageDelayedAnimator.setPosition(position);
@@ -186,12 +260,17 @@
private void clearAnimationState() {
final int N = mAllViews.size();
mQuickQsPanel.setAlpha(0);
+ mQuickQsPanel.setVisibility(View.VISIBLE);
for (int i = 0; i < N; i++) {
View v = mAllViews.get(i);
v.setAlpha(1);
v.setTranslationX(1);
v.setTranslationY(1);
}
+ final int N2 = mTopFiveQs.size();
+ for (int i = 0; i < N2; i++) {
+ mTopFiveQs.get(i).setVisibility(View.VISIBLE);
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java
index c59da8d..c0c1e4d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java
@@ -156,6 +156,7 @@
public void setKeyguardShowing(boolean keyguardShowing) {
if (DEBUG) Log.d(TAG, "setKeyguardShowing " + keyguardShowing);
mKeyguardShowing = keyguardShowing;
+ mQSAnimator.setOnKeyguard(keyguardShowing);
updateQsState();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index 8b826ee..f8a57d0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -26,6 +26,8 @@
import com.android.systemui.R;
import com.android.systemui.qs.QSTile.SignalState;
import com.android.systemui.qs.QSTile.State;
+import com.android.systemui.tuner.TunerService;
+import com.android.systemui.tuner.TunerService.Tunable;
import java.util.ArrayList;
import java.util.Collection;
@@ -35,6 +37,8 @@
*/
public class QuickQSPanel extends QSPanel {
+ public static final String NUM_QUICK_TILES = "sysui_qqs_count";
+
private int mMaxTiles;
private QSPanel mFullPanel;
private View mHeader;
@@ -52,6 +56,18 @@
}
@Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ TunerService.get(mContext).addTunable(mNumTiles, NUM_QUICK_TILES);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ TunerService.get(mContext).removeTunable(mNumTiles);
+ }
+
+ @Override
protected void createCustomizePanel() {
// No customizing from the header.
}
@@ -86,6 +102,7 @@
public void setMaxTiles(int maxTiles) {
mMaxTiles = maxTiles;
+ setTiles(mHost.getTiles());
}
@Override
@@ -114,6 +131,17 @@
super.setTiles(quickTiles);
}
+ private final Tunable mNumTiles = new Tunable() {
+ @Override
+ public void onTuningChanged(String key, String newValue) {
+ setMaxTiles(getNumQuickTiles(mContext));
+ }
+ };
+
+ public static int getNumQuickTiles(Context context) {
+ return TunerService.get(context).getValue(NUM_QUICK_TILES, 5);
+ }
+
private static class HeaderTileLayout extends LinearLayout implements QSTileLayout {
private final Space mEndSpacer;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index aa85f78..d95d3ef 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -28,8 +28,6 @@
import android.os.Handler;
import android.os.Looper;
import android.service.quicksettings.TileService;
-import com.android.systemui.Prefs;
-import com.android.systemui.Prefs.Key;
import com.android.systemui.R;
import com.android.systemui.qs.QSTile;
import com.android.systemui.qs.QSTile.DrawableIcon;
@@ -57,10 +55,8 @@
}
private void addSystemTiles(QSTileHost host) {
- boolean hasColorMod = Prefs.getBoolean(host.getContext(), Key.QS_NIGHT_ADDED, false)
- && TunerService.isTunerEnabled(host.getContext());
String possible = mContext.getString(R.string.quick_settings_tiles_default)
- + ",hotspot,inversion,saver,work,cast" + (hasColorMod ? ",night" : "");
+ + ",hotspot,inversion,saver,work,cast,night";
String[] possibleTiles = possible.split(",");
final Handler qsHandler = new Handler(host.getLooper());
final Handler mainHandler = new Handler(Looper.getMainLooper());
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index d7c12ba..a2934d7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -43,6 +43,7 @@
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.AppWidgetProviderChangedEvent;
import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
+import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
import com.android.systemui.recents.events.activity.DebugFlagsChangedEvent;
import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
@@ -107,7 +108,7 @@
private RecentsAppWidgetHostView mSearchWidgetHostView;
// Runnables to finish the Recents activity
- private FinishRecentsRunnable mFinishLaunchHomeRunnable;
+ private Intent mHomeIntent;
// The trigger to automatically launch the current task
private int mFocusTimerDuration;
@@ -119,7 +120,7 @@
* last activity launch state. Generally we always launch home when we exit Recents rather than
* just finishing the activity since we don't know what is behind Recents in the task stack.
*/
- class FinishRecentsRunnable implements Runnable {
+ class LaunchHomeRunnable implements Runnable {
Intent mLaunchIntent;
ActivityOptions mOpts;
@@ -127,7 +128,7 @@
/**
* Creates a finish runnable that starts the specified intent.
*/
- public FinishRecentsRunnable(Intent launchIntent, ActivityOptions opts) {
+ public LaunchHomeRunnable(Intent launchIntent, ActivityOptions opts) {
mLaunchIntent = launchIntent;
mOpts = opts;
}
@@ -215,7 +216,7 @@
MetricsLogger.count(this, "overview_trigger_nav_btn", 1);
}
// Keep track of whether we launched from an app or from home
- if (launchState.launchedFromAppWithThumbnail) {
+ if (launchState.launchedFromApp) {
MetricsLogger.count(this, "overview_source_app", 1);
// If from an app, track the stack index of the app in the stack (for affiliated tasks)
MetricsLogger.histogram(this, "overview_source_app_index", launchTaskIndexInStack);
@@ -294,12 +295,8 @@
void dismissRecentsToHome(boolean animateTaskViews, ActivityOptions overrideAnimation) {
DismissRecentsToHomeAnimationStarted dismissEvent =
new DismissRecentsToHomeAnimationStarted(animateTaskViews);
- if (overrideAnimation != null) {
- dismissEvent.addPostAnimationCallback(new FinishRecentsRunnable(
- mFinishLaunchHomeRunnable.mLaunchIntent, overrideAnimation));
- } else {
- dismissEvent.addPostAnimationCallback(mFinishLaunchHomeRunnable);
- }
+ dismissEvent.addPostAnimationCallback(new LaunchHomeRunnable(mHomeIntent,
+ overrideAnimation));
dismissEvent.addPostAnimationCallback(new Runnable() {
@Override
public void run() {
@@ -365,11 +362,10 @@
});
// Create the home intent runnable
- Intent homeIntent = new Intent(Intent.ACTION_MAIN, null);
- homeIntent.addCategory(Intent.CATEGORY_HOME);
- homeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
+ mHomeIntent = new Intent(Intent.ACTION_MAIN, null);
+ mHomeIntent.addCategory(Intent.CATEGORY_HOME);
+ mHomeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
- mFinishLaunchHomeRunnable = new FinishRecentsRunnable(homeIntent, null);
// Bind the search app widget when we first start up
if (RecentsDebugFlags.Static.EnableSearchBar) {
@@ -404,7 +400,7 @@
RecentsConfiguration config = Recents.getConfiguration();
RecentsActivityLaunchState launchState = config.getLaunchState();
boolean wasLaunchedByAm = !launchState.launchedFromHome &&
- !launchState.launchedFromAppWithThumbnail;
+ !launchState.launchedFromApp;
if (launchState.launchedHasConfigurationChanged || wasLaunchedByAm) {
EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
}
@@ -528,6 +524,7 @@
@Override
public void onMultiWindowChanged(boolean inMultiWindow) {
super.onMultiWindowChanged(inMultiWindow);
+ EventBus.getDefault().send(new ConfigurationChangedEvent());
RecentsTaskLoader loader = Recents.getTaskLoader();
RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
launchOpts.loadIcons = false;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
index aa1437b..ec4820a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
@@ -28,7 +28,8 @@
public class RecentsActivityLaunchState {
public boolean launchedWithAltTab;
- public boolean launchedFromAppWithThumbnail;
+ public boolean launchedFromApp;
+ public boolean launchedFromAppDocked;
public boolean launchedFromHome;
public boolean launchedFromSearchHome;
public boolean launchedReuseTaskStackViews;
@@ -42,7 +43,8 @@
public void reset() {
launchedFromHome = false;
launchedFromSearchHome = false;
- launchedFromAppWithThumbnail = false;
+ launchedFromApp = false;
+ launchedFromAppDocked = false;
launchedToTaskId = -1;
launchedWithAltTab = false;
launchedHasConfigurationChanged = false;
@@ -67,7 +69,7 @@
public int getInitialFocusTaskIndex(int numTasks) {
RecentsDebugFlags debugFlags = Recents.getDebugFlags();
RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
- if (launchedFromAppWithThumbnail) {
+ if (launchedFromApp) {
if (!launchState.launchedWithAltTab && debugFlags.isFastToggleRecentsEnabled()) {
// If fast toggling, focus the front most task so that the next tap will focus the
// N-1 task
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index 9e43bb4..eec0411 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -69,7 +69,6 @@
public final int smallestWidth;
/** Misc **/
- public boolean useHardwareLayers;
public boolean fakeShadows;
public int svelteLevel;
public int searchBarSpaceHeightPx;
@@ -80,7 +79,6 @@
SystemServicesProxy ssp = Recents.getSystemServices();
Context appContext = context.getApplicationContext();
Resources res = appContext.getResources();
- useHardwareLayers = res.getBoolean(R.bool.config_recents_use_hardware_layers);
fakeShadows = res.getBoolean(R.bool.config_recents_fake_shadows);
svelteLevel = res.getInteger(R.integer.recents_svelte_level);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java
index cd64323..6feda81 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java
@@ -39,8 +39,8 @@
public static final boolean EnableAffiliatedTaskGroups = true;
// Enables the history
public static final boolean EnableHistory = false;
- // Overrides the Tuner flags and enables the fast toggle and timeout
- public static final boolean EnableFastToggleTimeoutOverride = true;
+ // Overrides the Tuner flags and enables the timeout
+ private static final boolean EnableFastToggleTimeout = false;
// Enables us to create mock recents tasks
public static final boolean EnableMockTasks = false;
@@ -54,9 +54,9 @@
public static final int MockTaskGroupsTaskCount = 12;
}
- private static final String KEY_DISABLE_FAST_TOGGLE = "overview_disable_fast_toggle_via_button";
+ private static final String KEY_ENABLE_PAGING = "overview_enable_paging";
- private boolean mDisableFastToggleRecents;
+ private boolean mEnablePaging;
/**
* We read the prefs once when we start the activity, then update them as the tuner changes
@@ -65,31 +65,32 @@
public RecentsDebugFlags(Context context) {
// Register all our flags, this will also call onTuningChanged() for each key, which will
// initialize the current state of each flag
- TunerService.get(context).addTunable(this, KEY_DISABLE_FAST_TOGGLE);
+ TunerService.get(context).addTunable(this, KEY_ENABLE_PAGING);
}
/**
* @return whether we are enabling fast toggling.
*/
public boolean isFastToggleRecentsEnabled() {
- // These checks EnableFastToggleTimeoutOverride
SystemServicesProxy ssp = Recents.getSystemServices();
- if (mDisableFastToggleRecents || ssp.hasFreeformWorkspaceSupport() || ssp.hasDockedTask()
- || ssp.isTouchExplorationEnabled()) {
+ if (ssp.hasFreeformWorkspaceSupport() || ssp.isTouchExplorationEnabled()) {
return false;
}
- if (Static.EnableFastToggleTimeoutOverride) {
- return true;
- }
- return true;
+ return Static.EnableFastToggleTimeout;
+ }
+
+ /**
+ * @return whether we are enabling paging.
+ */
+ public boolean isPagingEnabled() {
+ return mEnablePaging;
}
@Override
public void onTuningChanged(String key, String newValue) {
switch (key) {
- case KEY_DISABLE_FAST_TOGGLE:
- mDisableFastToggleRecents = (newValue != null) &&
- (Integer.parseInt(newValue) != 0);
+ case KEY_ENABLE_PAGING:
+ mEnablePaging = (newValue != null) && (Integer.parseInt(newValue) != 0);
break;
}
EventBus.getDefault().send(new DebugFlagsChangedEvent());
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 86b03c8..1458d7b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -16,6 +16,8 @@
package com.android.systemui.recents;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.ITaskStackListener;
@@ -64,8 +66,10 @@
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskGrouping;
import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.recents.tv.views.TaskStackHorizontalGridView;
import com.android.systemui.recents.views.TaskStackLayoutAlgorithm;
import com.android.systemui.recents.views.TaskStackView;
+import com.android.systemui.recents.views.TaskStackViewScroller;
import com.android.systemui.recents.views.TaskViewHeader;
import com.android.systemui.recents.views.TaskViewTransform;
import com.android.systemui.statusbar.BaseStatusBar;
@@ -74,8 +78,6 @@
import java.util.ArrayList;
-import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
-
/**
* An implementation of the Recents component for the current user. For secondary users, this can
* be called remotely from the system user.
@@ -98,6 +100,7 @@
//Used to store tv or non-tv activty for use in creating intents.
private final String mRecentsIntentActivityName;
+
/**
* An implementation of ITaskStackListener, that allows us to listen for changes to the system
* task stacks and update recents accordingly.
@@ -276,28 +279,24 @@
mTriggeredFromAltTab = triggeredFromAltTab;
mDraggingInRecents = draggingInRecents;
mLaunchedWhileDocking = launchedWhileDockingTask;
- if (mFastAltTabTrigger.hasTriggered()) {
- // We are calling this from the doze trigger, so just fall through to show Recents
- mFastAltTabTrigger.resetTrigger();
+ if (mFastAltTabTrigger.isAsleep()) {
+ // Fast alt-tab duration has elapsed, fall through to showing Recents and reset
+ mFastAltTabTrigger.stopDozing();
} else if (mFastAltTabTrigger.isDozing()) {
- // We are dozing but haven't yet triggered, ignore this if this is not another alt-tab,
- // otherwise, this is an additional tab (alt-tab*), which means that we should trigger
- // immediately (fall through and disable the pending trigger)
- // TODO: This is tricky, we need to handle the tab key, but Recents has not yet started
- // so we may actually additional signal to handle multiple quick tab cases. The
- // severity of this is inversely proportional to the FAST_ALT_TAB_DELAY_MS
- // duration though
+ // Fast alt-tab duration has not elapsed. If this is triggered by a different
+ // showRecents() call, then ignore that call for now.
+ // TODO: We can not handle quick tabs that happen between the initial showRecents() call
+ // that started the activity and the activity starting up. The severity of this
+ // is inversely proportional to the FAST_ALT_TAB_DELAY_MS duration though.
if (!triggeredFromAltTab) {
return;
}
mFastAltTabTrigger.stopDozing();
- } else {
- // Otherwise, the doze trigger is not running, and if this is an alt tab, we should
- // start the trigger and then wait for the hide (or for it to elapse)
- if (triggeredFromAltTab) {
- mFastAltTabTrigger.startDozing();
- return;
- }
+ } else if (triggeredFromAltTab) {
+ // The fast alt-tab detector is not yet running, so start the trigger and wait for the
+ // hideRecents() call, or for the fast alt-tab duration to elapse
+ mFastAltTabTrigger.startDozing();
+ return;
}
try {
@@ -321,7 +320,6 @@
// Cancel the fast alt-tab trigger
mFastAltTabTrigger.stopDozing();
- mFastAltTabTrigger.resetTrigger();
return;
}
@@ -348,12 +346,14 @@
long elapsedTime = SystemClock.elapsedRealtime() - mLastToggleTime;
if (topTask != null && ssp.isRecentsTopMost(topTask, isTopTaskHome)) {
+ RecentsDebugFlags debugFlags = Recents.getDebugFlags();
RecentsConfiguration config = Recents.getConfiguration();
RecentsActivityLaunchState launchState = config.getLaunchState();
if (!launchState.launchedWithAltTab) {
// If the user taps quickly
- if (ViewConfiguration.getDoubleTapMinTime() < elapsedTime &&
- elapsedTime < ViewConfiguration.getDoubleTapTimeout()) {
+ if (!debugFlags.isPagingEnabled() ||
+ (ViewConfiguration.getDoubleTapMinTime() < elapsedTime &&
+ elapsedTime < ViewConfiguration.getDoubleTapTimeout())) {
// Launch the next focused task
EventBus.getDefault().post(new LaunchNextTaskRequestEvent());
} else {
@@ -574,7 +574,7 @@
false /* triggeredFromAltTab */,
dragMode == NavigationBarGestureHelper.DRAG_MODE_RECENTS,
false /* animate */,
- true /* reloadTasks*/);
+ true /* launchedWhileDockingTask*/);
}
}
@@ -707,8 +707,7 @@
// Update the destination rect
mDummyStackView.updateLayoutForStack(stack);
final Task toTask = new Task();
- final TaskViewTransform toTransform = getThumbnailTransitionTransform(stack, stackView,
- toTask);
+ final TaskViewTransform toTransform = getThumbnailTransitionTransform(stackView, toTask);
ForegroundThread.getHandler().postAtFrontOfQueue(new Runnable() {
@Override
public void run() {
@@ -754,17 +753,20 @@
* Creates the activity options for an app->recents transition.
*/
private ActivityOptions getThumbnailTransitionActivityOptions(
- ActivityManager.RunningTaskInfo topTask, TaskStack stack, TaskStackView stackView) {
+ ActivityManager.RunningTaskInfo topTask, TaskStackView stackView) {
if (topTask.stackId == FREEFORM_WORKSPACE_STACK_ID) {
ArrayList<AppTransitionAnimationSpec> specs = new ArrayList<>();
- stackView.getScroller().setStackScrollToInitialState();
- ArrayList<Task> tasks = stack.getStackTasks();
+ ArrayList<Task> tasks = stackView.getStack().getStackTasks();
+ TaskStackLayoutAlgorithm stackLayout = stackView.getStackAlgorithm();
+ TaskStackViewScroller stackScroller = stackView.getScroller();
+
+ stackView.updateToInitialState();
+
for (int i = tasks.size() - 1; i >= 0; i--) {
Task task = tasks.get(i);
if (task.isFreeformTask()) {
- mTmpTransform = stackView.getStackAlgorithm()
- .getStackTransformScreenCoordinates(task,
- stackView.getScroller().getStackScroll(), mTmpTransform, null);
+ mTmpTransform = stackLayout.getStackTransformScreenCoordinates(task,
+ stackScroller.getStackScroll(), mTmpTransform, null);
Rect toTaskRect = new Rect();
mTmpTransform.rect.round(toTaskRect);
Bitmap thumbnail = getThumbnailBitmap(topTask, task, mTmpTransform);
@@ -778,8 +780,7 @@
} else {
// Update the destination rect
Task toTask = new Task();
- TaskViewTransform toTransform = getThumbnailTransitionTransform(stack, stackView,
- toTask);
+ TaskViewTransform toTransform = getThumbnailTransitionTransform(stackView, toTask);
RectF toTaskRect = toTransform.rect;
Bitmap thumbnail = getThumbnailBitmap(topTask, toTask, toTransform);
if (thumbnail != null) {
@@ -811,9 +812,10 @@
/**
* Returns the transition rect for the given task id.
*/
- private TaskViewTransform getThumbnailTransitionTransform(TaskStack stack,
- TaskStackView stackView, Task runningTaskOut) {
+ private TaskViewTransform getThumbnailTransitionTransform(TaskStackView stackView,
+ Task runningTaskOut) {
// Find the running task in the TaskStack
+ TaskStack stack = stackView.getStack();
Task launchTask = stack.getLaunchTarget();
if (launchTask != null) {
runningTaskOut.copyFrom(launchTask);
@@ -824,7 +826,7 @@
}
// Get the transform for the running task
- stackView.getScroller().setStackScrollToInitialState();
+ stackView.updateToInitialState();
mTmpTransform = stackView.getStackAlgorithm().getStackTransformScreenCoordinates(launchTask,
stackView.getScroller().getStackScroll(), mTmpTransform, null);
return mTmpTransform;
@@ -852,6 +854,7 @@
c.scale(toTransform.scale, toTransform.scale);
mHeaderBar.rebindToTask(toTask, false /* touchExplorationEnabled */,
disabledInSafeMode);
+ mHeaderBar.setDimAlpha(toTransform.dimAlpha);
mHeaderBar.draw(c);
c.setBitmap(null);
}
@@ -900,8 +903,7 @@
if (useThumbnailTransition) {
// Try starting with a thumbnail transition
- ActivityOptions opts = getThumbnailTransitionActivityOptions(topTask, stack,
- mDummyStackView);
+ ActivityOptions opts = getThumbnailTransitionActivityOptions(topTask, mDummyStackView);
if (opts != null) {
startRecentsActivity(topTask, opts, false /* fromHome */,
false /* fromSearchHome */, true /* fromThumbnail */, stackVr);
@@ -948,14 +950,15 @@
* Starts the recents activity.
*/
private void startRecentsActivity(ActivityManager.RunningTaskInfo topTask,
- ActivityOptions opts, boolean fromHome, boolean fromSearchHome, boolean fromThumbnail,
- TaskStackLayoutAlgorithm.VisibilityReport vr) {
+ ActivityOptions opts, boolean fromHome, boolean fromSearchHome,
+ boolean fromThumbnail, TaskStackLayoutAlgorithm.VisibilityReport vr) {
// Update the configuration based on the launch options
RecentsConfiguration config = Recents.getConfiguration();
RecentsActivityLaunchState launchState = config.getLaunchState();
launchState.launchedFromHome = fromSearchHome || fromHome;
launchState.launchedFromSearchHome = fromSearchHome;
- launchState.launchedFromAppWithThumbnail = fromThumbnail;
+ launchState.launchedFromApp = fromThumbnail || mLaunchedWhileDocking;
+ launchState.launchedFromAppDocked = mLaunchedWhileDocking;
launchState.launchedToTaskId = (topTask != null) ? topTask.id : -1;
launchState.launchedWithAltTab = mTriggeredFromAltTab;
launchState.launchedReuseTaskStackViews = mCanReuseTaskStackViews;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ConfigurationChangedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ConfigurationChangedEvent.java
new file mode 100644
index 0000000..0ad4681
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ConfigurationChangedEvent.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.events.activity;
+
+import com.android.systemui.recents.events.EventBus;
+
+/**
+ * This is sent when the Recents activity configuration has changed.
+ */
+public class ConfigurationChangedEvent extends EventBus.AnimatedEvent {
+ // Simple event
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTvTaskEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTvTaskEvent.java
new file mode 100644
index 0000000..04ca68f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTvTaskEvent.java
@@ -0,0 +1,39 @@
+/*
+ * 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.recents.events.activity;
+
+
+import android.graphics.Rect;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.tv.views.TaskCardView;
+
+public class LaunchTvTaskEvent extends EventBus.Event {
+
+ public final TaskCardView taskView;
+ public final Task task;
+ public final Rect targetTaskBounds;
+ public final int targetTaskStack;
+
+ public LaunchTvTaskEvent(TaskCardView taskView, Task task, Rect targetTaskBounds,
+ int targetTaskStack) {
+ this.taskView = taskView;
+ this.task = task;
+ this.targetTaskBounds = targetTaskBounds;
+ this.targetTaskStack = targetTaskStack;
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTvTaskStartedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTvTaskStartedEvent.java
new file mode 100644
index 0000000..75d3efa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTvTaskStartedEvent.java
@@ -0,0 +1,34 @@
+/*
+ * 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.recents.events.activity;
+
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.tv.views.TaskCardView;
+
+/**
+ * This event is sent following {@link LaunchTvTaskEvent} after the call to the system is made to
+ * start the task, only used on TV.
+ */
+public class LaunchTvTaskStartedEvent extends EventBus.AnimatedEvent {
+
+ public final TaskCardView taskView;
+
+ public LaunchTvTaskStartedEvent(TaskCardView taskView) {
+ this.taskView = taskView;
+ }
+
+}
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 95aa10f..574ea03 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java
@@ -30,7 +30,7 @@
@ViewDebug.ExportedProperty(category="recents")
boolean mIsDozing;
@ViewDebug.ExportedProperty(category="recents")
- boolean mHasTriggered;
+ boolean mIsAsleep;
@ViewDebug.ExportedProperty(category="recents")
int mDozeDurationMilliseconds;
Runnable mOnSleepRunnable;
@@ -40,7 +40,7 @@
@Override
public void run() {
mIsDozing = false;
- mHasTriggered = true;
+ mIsAsleep = true;
mOnSleepRunnable.run();
}
};
@@ -56,7 +56,7 @@
*/
public void startDozing() {
forcePoke();
- mHasTriggered = false;
+ mIsAsleep = false;
}
/**
@@ -65,6 +65,7 @@
public void stopDozing() {
mHandler.removeCallbacks(mDozeRunnable);
mIsDozing = false;
+ mIsAsleep = false;
}
/**
@@ -99,12 +100,7 @@
}
/** Returns whether the trigger has fired at least once. */
- public boolean hasTriggered() {
- return mHasTriggered;
- }
-
- /** Resets the doze trigger state. */
- public void resetTrigger() {
- mHasTriggered = false;
+ public boolean isAsleep() {
+ return mIsAsleep;
}
}
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 7c5a931..532e796 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -695,6 +695,37 @@
}
/**
+ * Returns a banner used on TV for the specified Activity.
+ */
+ public Drawable getActivityBanner(ActivityInfo info) {
+ if (mPm == null) return null;
+
+ // If we are mocking, then return a mock banner
+ if (RecentsDebugFlags.Static.EnableMockTasks) {
+ return new ColorDrawable(0xFF666666);
+ }
+
+ Drawable banner = info.loadBanner(mPm);
+ return banner;
+ }
+
+ /**
+ * Returns a logo used on TV for the specified Activity.
+ */
+ public Drawable getActivityLogo(ActivityInfo info) {
+ if (mPm == null) return null;
+
+ // If we are mocking, then return a mock logo
+ if (RecentsDebugFlags.Static.EnableMockTasks) {
+ return new ColorDrawable(0xFF666666);
+ }
+
+ Drawable logo = info.loadLogo(mPm);
+ return logo;
+ }
+
+
+ /**
* Returns the given label for a user, badging if necessary.
*/
private String getBadgedLabel(String label, int userId) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
index 0c48cf7..02c8d96 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
@@ -40,7 +40,6 @@
import com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent;
import com.android.systemui.recents.events.activity.HideRecentsEvent;
import com.android.systemui.recents.events.activity.LaunchTaskFailedEvent;
-import com.android.systemui.recents.events.activity.TaskStackUpdatedEvent;
import com.android.systemui.recents.events.activity.ToggleRecentsEvent;
import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
@@ -60,6 +59,8 @@
import com.android.systemui.tv.pip.PipManager;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
/**
* The main TV recents activity started by the RecentsImpl.
@@ -157,11 +158,13 @@
mRecentsView.setTaskStack(stack);
+ List stackTasks = stack.getStackTasks();
+ Collections.reverse(stackTasks);
if (mTaskStackViewAdapter == null) {
- mTaskStackViewAdapter = new TaskStackHorizontalViewAdapter(stack.getStackTasks());
+ mTaskStackViewAdapter = new TaskStackHorizontalViewAdapter(stackTasks);
mRecentsView.setTaskStackViewAdapter(mTaskStackViewAdapter);
} else {
- mTaskStackViewAdapter.setNewStackTasks(stack.getStackTasks());
+ mTaskStackViewAdapter.setNewStackTasks(stackTasks);
}
if (launchState.launchedToTaskId != -1) {
@@ -284,7 +287,7 @@
RecentsConfiguration config = Recents.getConfiguration();
RecentsActivityLaunchState launchState = config.getLaunchState();
boolean wasLaunchedByAm = !launchState.launchedFromHome &&
- !launchState.launchedFromAppWithThumbnail;
+ !launchState.launchedFromApp;
if (launchState.launchedHasConfigurationChanged || wasLaunchedByAm) {
EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvTransitionHelper.java
new file mode 100644
index 0000000..ef8d48e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvTransitionHelper.java
@@ -0,0 +1,132 @@
+/*
+ * 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.recents.tv.views;
+
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IRemoteCallback;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.WindowManagerGlobal;
+import com.android.internal.annotations.GuardedBy;
+import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.*;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.model.TaskStack;
+
+
+public class RecentsTvTransitionHelper {
+ private static final String TAG = "RecentsTvTransitionHelper";
+
+ private Context mContext;
+ private Handler mHandler;
+
+ public RecentsTvTransitionHelper(Context context, Handler handler) {
+ mContext = context;
+ mHandler = handler;
+ }
+
+ public void launchTaskFromRecents(final TaskStack stack, @Nullable final Task task,
+ final TaskStackHorizontalGridView stackView, final TaskCardView taskView,
+ final Rect bounds, int destinationStack) {
+ final ActivityOptions opts = ActivityOptions.makeBasic();
+ if (bounds != null) {
+ opts.setLaunchBounds(bounds.isEmpty() ? null : bounds);
+ }
+
+ final ActivityOptions.OnAnimationStartedListener animStartedListener;
+ if (task.thumbnail != null && task.thumbnail.getWidth() > 0 &&
+ task.thumbnail.getHeight() > 0) {
+ animStartedListener = new ActivityOptions.OnAnimationStartedListener() {
+ @Override
+ public void onAnimationStarted() {
+ // If we are launching into another task, cancel the previous task's
+ // window transition
+ EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task));
+ EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent());
+ }
+ };
+ } else {
+ // This is only the case if the task is not on screen (scrolled offscreen for example)
+ animStartedListener = new ActivityOptions.OnAnimationStartedListener() {
+ @Override
+ public void onAnimationStarted() {
+ EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent());
+ }
+ };
+ }
+
+ if (taskView == null) {
+ // If there is no task view, then we do not need to worry about animating out occluding
+ // task views, and we can launch immediately
+ startTaskActivity(stack, task, taskView, opts, animStartedListener);
+ } else {
+ LaunchTvTaskStartedEvent launchStartedEvent = new LaunchTvTaskStartedEvent(taskView);
+ EventBus.getDefault().send(launchStartedEvent);
+ startTaskActivity(stack, task, taskView, opts, animStartedListener);
+ }
+ }
+
+ private void startTaskActivity(TaskStack stack, Task task, @Nullable TaskCardView taskView,
+ ActivityOptions opts,final ActivityOptions.OnAnimationStartedListener animStartedListener) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ if (ssp.startActivityFromRecents(mContext, task.key.id, task.title, opts)) {
+ // Keep track of the index of the task launch
+ int taskIndexFromFront = 0;
+ int taskIndex = stack.indexOfStackTask(task);
+ if (taskIndex > -1) {
+ taskIndexFromFront = stack.getTaskCount() - taskIndex - 1;
+ }
+ EventBus.getDefault().send(new LaunchTaskSucceededEvent(taskIndexFromFront));
+ } else {
+ // Keep track of failed launches
+ EventBus.getDefault().send(new LaunchTaskFailedEvent());
+ }
+
+ IRemoteCallback.Stub callback = null;
+ if (animStartedListener != null) {
+ callback = new IRemoteCallback.Stub() {
+ @Override
+ public void sendResult(Bundle data) throws RemoteException {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (animStartedListener != null) {
+ animStartedListener.onAnimationStarted();
+ }
+ }
+ });
+ }
+ };
+ }
+ try {
+ Rect taskRect = taskView.getGlobalRect();
+ WindowManagerGlobal.getWindowManagerService()
+ .overridePendingAppTransitionThumb(task.thumbnail, taskRect.left,
+ taskRect.top, callback, true);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to override transition: " + e);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java
index 8e768a2..bf6229c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java
@@ -17,6 +17,7 @@
import android.content.Context;
import android.graphics.Rect;
+import android.os.Handler;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
@@ -31,7 +32,7 @@
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
-import com.android.systemui.recents.events.activity.TaskStackUpdatedEvent;
+import com.android.systemui.recents.events.activity.LaunchTvTaskEvent;
import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.model.Task;
@@ -53,7 +54,8 @@
private View mEmptyView;
private boolean mAwaitingFirstLayout = true;
private Rect mSystemInsets = new Rect();
-
+ private RecentsTvTransitionHelper mTransitionHelper;
+ private Handler mHandler;
public RecentsTvView(Context context) {
this(context, null);
@@ -75,6 +77,8 @@
LayoutInflater inflater = LayoutInflater.from(context);
mEmptyView = inflater.inflate(R.layout.recents_empty, this, false);
addView(mEmptyView);
+ mHandler = new Handler();
+ mTransitionHelper = new RecentsTvTransitionHelper(mContext, mHandler);
}
public void setTaskStack(TaskStack stack) {
@@ -209,6 +213,11 @@
/**** EventBus Events ****/
+ public final void onBusEvent(LaunchTvTaskEvent event) {
+ mTransitionHelper.launchTaskFromRecents(mStack, event.task, mTaskStackHorizontalView,
+ event.taskView, event.targetTaskBounds, event.targetTaskStack);
+ }
+
public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) {
// If we are going home, cancel the previous task's window transition
EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(null));
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
index e275f22..7d8a3ce 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
@@ -16,20 +16,20 @@
package com.android.systemui.recents.tv.views;
import android.content.Context;
+import android.graphics.Rect;
import android.util.AttributeSet;
import android.widget.ImageView;
-import android.widget.RelativeLayout;
+import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.systemui.R;
-import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.tv.animations.ViewFocusAnimator;
+import com.android.systemui.recents.model.Task;
-public class TaskCardView extends RelativeLayout {
+public class TaskCardView extends LinearLayout {
private ImageView mThumbnailView;
private TextView mTitleTextView;
- private TextView mContentTextView;
private ImageView mBadgeView;
private Task mTask;
@@ -52,7 +52,6 @@
protected void onFinishInflate() {
mThumbnailView = (ImageView) findViewById(R.id.card_view_thumbnail);
mTitleTextView = (TextView) findViewById(R.id.card_title_text);
- mContentTextView = (TextView) findViewById(R.id.card_content_text);
mBadgeView = (ImageView) findViewById(R.id.card_extra_badge);
}
@@ -60,11 +59,27 @@
mTask = task;
mThumbnailView.setImageBitmap(task.thumbnail);
mTitleTextView.setText(task.title);
- mContentTextView.setText(task.contentDescription);
mBadgeView.setImageDrawable(task.icon);
}
public Task getTask() {
return mTask;
}
+
+ @Override
+ public void getFocusedRect(Rect r) {
+ mThumbnailView.getFocusedRect(r);
+ }
+
+ public Rect getFocusedRect() {
+ Rect r = new Rect();
+ getFocusedRect(r);
+ return r;
+ }
+
+ public Rect getGlobalRect() {
+ Rect r = new Rect();
+ getGlobalVisibleRect(r);
+ return r;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java
index 58ec852..4458639 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java
@@ -15,7 +15,6 @@
*/
package com.android.systemui.recents.tv.views;
-
import android.content.Context;
import android.support.v17.leanback.widget.HorizontalGridView;
import android.util.AttributeSet;
@@ -36,13 +35,17 @@
/**
* Horizontal Grid View Implementation to show the Task Stack for TV.
*/
-public class TaskStackHorizontalGridView extends HorizontalGridView implements TaskStackCallbacks{
+public class TaskStackHorizontalGridView extends HorizontalGridView implements TaskStackCallbacks {
private TaskStack mStack;
private ArrayList<TaskCardView> mTaskViews = new ArrayList<>();
private Task mFocusedTask;
+ public TaskStackHorizontalGridView(Context context) {
+ this(context, null);
+ }
+
public TaskStackHorizontalGridView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@@ -64,8 +67,6 @@
* Resets this view for reuse.
*/
public void reset() {
- // Reset the focused task
- resetFocusedTask(getFocusedTask());
requestLayout();
}
@@ -73,12 +74,6 @@
* @param task - Task to reset
*/
private void resetFocusedTask(Task task) {
- if (task != null) {
- TaskCardView tv = getChildViewForTask(task);
- if (tv != null) {
- tv.requestFocus();
- }
- }
mFocusedTask = null;
}
@@ -107,6 +102,9 @@
* @return - The focused task.
*/
public Task getFocusedTask() {
+ if (findFocus() != null) {
+ mFocusedTask = ((TaskCardView)findFocus()).getTask();
+ }
return mFocusedTask;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java
index f154331..fba424e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java
@@ -16,7 +16,6 @@
package com.android.systemui.recents.tv.views;
import android.app.Activity;
-import android.app.ActivityManagerNative;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
@@ -24,15 +23,20 @@
import android.view.ViewGroup;
import com.android.systemui.R;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.LaunchTvTaskEvent;
import com.android.systemui.recents.model.Task;
import java.util.ArrayList;
import java.util.List;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+
public class TaskStackHorizontalViewAdapter extends
RecyclerView.Adapter<TaskStackHorizontalViewAdapter.ViewHolder> {
- private static final String TAG = "TaskStackHorizontalViewAdapter";
+ //Full class name is 30 characters
+ private static final String TAG = "TaskStackViewAdapter";
private List<Task> mTaskList;
static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
@@ -54,7 +58,8 @@
@Override
public void onClick(View v) {
try {
- ActivityManagerNative.getDefault().startActivityFromRecents(mTask.key.id, null);
+ EventBus.getDefault().send(new LaunchTvTaskEvent(mTaskCardView, mTask,
+ null, INVALID_STACK_ID));
((Activity)(v.getContext())).finish();
} catch (Exception e) {
Log.e(TAG, v.getContext()
@@ -73,11 +78,12 @@
mTaskList.addAll(tasks);
notifyDataSetChanged();
}
+
@Override
public TaskStackHorizontalViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
View view = LayoutInflater.from(parent.getContext())
- .inflate(R.layout.recents_task_card_view, parent, false);
+ .inflate(R.layout.recents_tv_task_card_view, parent, false);
ViewHolder viewHolder = new ViewHolder(view);
return viewHolder;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java
index 4359101..72b914c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java
@@ -37,6 +37,13 @@
private int mTaskPadding;
public FreeformWorkspaceLayoutAlgorithm(Context context) {
+ reloadOnConfigurationChange(context);
+ }
+
+ /**
+ * Reloads the layout for the current configuration.
+ */
+ public void reloadOnConfigurationChange(Context context) {
// This is applied to the edges of each task
mTaskPadding = context.getResources().getDimensionPixelSize(
R.dimen.recents_freeform_workspace_task_padding) / 2;
@@ -72,8 +79,7 @@
}
// Bound the task width to the workspace width so that at the worst case, it will
// fit its own row
- normalizedTaskWidths[i] = Math.min(rowTaskWidth,
- normalizedWorkspaceWidth);
+ normalizedTaskWidths[i] = Math.min(rowTaskWidth, normalizedWorkspaceWidth);
}
// Determine the scale to best fit each of the tasks in the workspace
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
index 37b2859..e5022a4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
@@ -105,9 +105,6 @@
animStartedListener = new ActivityOptions.OnAnimationStartedListener() {
@Override
public void onAnimationStarted() {
- // If we are launching into another task, cancel the previous task's
- // window transition
- EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task));
EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent());
if (screenPinningRequested) {
@@ -149,6 +146,10 @@
animStartedListener);
}
}
+
+ // If we are launching into another task, cancel the previous task's
+ // window transition
+ EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task));
}
/**
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 5dde926..10f491e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -192,7 +192,7 @@
// If we are already occluded by the app, then just set the default background scrim now.
// Otherwise, defer until the enter animation completes to animate the scrim with the
// tasks for the home animation.
- if (launchState.launchedWhileDocking || launchState.launchedFromAppWithThumbnail
+ if (launchState.launchedWhileDocking || launchState.launchedFromApp
|| mStack.getTaskCount() == 0) {
mBackgroundScrim.setAlpha((int) (DEFAULT_SCRIM_ALPHA * 255));
} else {
@@ -671,7 +671,7 @@
public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
- if (!launchState.launchedWhileDocking && !launchState.launchedFromAppWithThumbnail
+ if (!launchState.launchedWhileDocking && !launchState.launchedFromApp
&& mStack.getTaskCount() > 0) {
animateBackgroundScrim(DEFAULT_SCRIM_ALPHA,
TaskStackAnimationHelper.ENTER_FROM_HOME_TRANSLATION_DURATION);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
index d152010..758f4d82 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
@@ -56,8 +56,8 @@
/**
* Callback to start the animation for the launch target {@link TaskView}.
*/
- void onStartLaunchTargetEnterAnimation(int duration, boolean screenPinningEnabled,
- ReferenceCountedTrigger postAnimationTrigger);
+ void onStartLaunchTargetEnterAnimation(TaskViewTransform transform, int duration,
+ boolean screenPinningEnabled, ReferenceCountedTrigger postAnimationTrigger);
/**
* Callback to start the animation for the launch target {@link TaskView} when it is
@@ -141,7 +141,7 @@
tv.setVisibility(View.INVISIBLE);
} else if (launchState.launchedHasConfigurationChanged) {
// Just load the views as-is
- } else if (launchState.launchedFromAppWithThumbnail) {
+ } else if (launchState.launchedFromApp) {
if (task.isLaunchTarget) {
tv.onPrepareLaunchTargetForEnterAnimation();
} else if (currentTaskOccludesLaunchTarget) {
@@ -205,10 +205,11 @@
stackLayout.getStackTransform(task, stackScroller.getStackScroll(), mTmpTransform,
null);
- if (launchState.launchedFromAppWithThumbnail) {
+ if (launchState.launchedFromApp) {
if (task.isLaunchTarget) {
- tv.onStartLaunchTargetEnterAnimation(taskViewEnterFromAppDuration,
- mStackView.mScreenPinningEnabled, postAnimationTrigger);
+ tv.onStartLaunchTargetEnterAnimation(mTmpTransform,
+ taskViewEnterFromAppDuration, mStackView.mScreenPinningEnabled,
+ postAnimationTrigger);
} else {
// Animate the task up if it was occluding the launch target
if (currentTaskOccludesLaunchTarget) {
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 261b6f6..17c3eeb 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
@@ -29,6 +29,7 @@
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsActivityLaunchState;
import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.recents.RecentsDebugFlags;
import com.android.systemui.recents.misc.FreePathInterpolator;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.Utilities;
@@ -36,6 +37,7 @@
import com.android.systemui.recents.model.TaskStack;
import java.util.ArrayList;
+import java.util.List;
/**
* Used to describe a visible range that can be normalized to [0, 1].
@@ -220,6 +222,10 @@
private Range mUnfocusedRange;
private Range mFocusedRange;
+ // The initial offset from the top of the stack
+ @ViewDebug.ExportedProperty(category="recents")
+ private int mInitialTopPeekHeight;
+
// The offset from the top when scrolled to the top of the stack
@ViewDebug.ExportedProperty(category="recents")
private int mFocusedTopPeekHeight;
@@ -231,6 +237,10 @@
@ViewDebug.ExportedProperty(category="recents")
private int mStackTopOffset;
+ // The height of the header bar
+ @ViewDebug.ExportedProperty(category="recents")
+ private int mHeaderBarHeight;
+
// 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")
@@ -249,6 +259,9 @@
private FreePathInterpolator mUnfocusedDimCurveInterpolator;
private FreePathInterpolator mFocusedDimCurveInterpolator;
+ // Indexed from the front of the stack, the normalized x in the unfocused range for each task
+ private float[] mInitialNormX;
+
// 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")
@@ -292,23 +305,32 @@
TaskViewTransform mFrontOfStackTransform = new TaskViewTransform();
public TaskStackLayoutAlgorithm(Context context, TaskStackLayoutAlgorithmCallbacks cb) {
- Resources res = context.getResources();
mContext = context;
mCb = cb;
+ mFreeformLayoutAlgorithm = new FreeformWorkspaceLayoutAlgorithm(context);
+ reloadOnConfigurationChange(context);
+ }
+ /**
+ * Reloads the layout for the current configuration.
+ */
+ public void reloadOnConfigurationChange(Context context) {
+ Resources res = context.getResources();
mFocusedRange = new Range(res.getFloat(R.integer.recents_layout_focused_range_min),
res.getFloat(R.integer.recents_layout_focused_range_max));
mUnfocusedRange = new Range(res.getFloat(R.integer.recents_layout_unfocused_range_min),
res.getFloat(R.integer.recents_layout_unfocused_range_max));
- mFocusState = getDefaultFocusState();
+ mFocusState = getInitialFocusState();
+ mInitialTopPeekHeight = res.getDimensionPixelSize(R.dimen.recents_initial_top_peek_size);
mFocusedTopPeekHeight =
res.getDimensionPixelSize(R.dimen.recents_layout_focused_top_peek_size);
mFocusedBottomTaskPeekHeight =
res.getDimensionPixelSize(R.dimen.recents_layout_focused_bottom_task_peek_size);
+ mHeaderBarHeight = res.getDimensionPixelSize(R.dimen.recents_task_bar_height);
mMinTranslationZ = res.getDimensionPixelSize(R.dimen.recents_task_view_z_min);
mMaxTranslationZ = res.getDimensionPixelSize(R.dimen.recents_task_view_z_max);
- mFreeformLayoutAlgorithm = new FreeformWorkspaceLayoutAlgorithm(context);
+ mFreeformLayoutAlgorithm.reloadOnConfigurationChange(context);
}
/**
@@ -316,7 +338,7 @@
*/
public void reset() {
mTaskIndexOverrideMap.clear();
- setFocusState(getDefaultFocusState());
+ setFocusState(getInitialFocusState());
}
/**
@@ -439,46 +461,87 @@
mTaskIndexMap.put(task.key.id, i);
}
- // Calculate the min/max scroll
- if (getDefaultFocusState() > 0f) {
- mMinScrollP = 0;
- mMaxScrollP = Math.max(mMinScrollP, mNumStackTasks - 1);
- } else {
- if (!ssp.hasFreeformWorkspaceSupport() && mNumStackTasks == 1) {
- mMinScrollP = mMaxScrollP = 0;
- } else {
- float bottomOffsetPct = (float) (mStackBottomOffset + mTaskRect.height()) /
- mStackRect.height();
- float normX = mUnfocusedCurveInterpolator.getX(bottomOffsetPct);
- mMinScrollP = 0;
- mMaxScrollP = Math.max(mMinScrollP, (mNumStackTasks - 1) -
- Math.max(0, mUnfocusedRange.getAbsoluteX(normX)));
- }
- }
-
+ // Update the freeform tasks
if (!freeformTasks.isEmpty()) {
mFreeformLayoutAlgorithm.update(freeformTasks, this);
- mInitialScrollP = mMaxScrollP;
- } else {
- Task launchTask = stack.getLaunchTarget();
- int launchTaskIndex = launchTask != null
- ? stack.indexOfStackTask(launchTask)
- : mNumStackTasks - 1;
- if (!ssp.hasFreeformWorkspaceSupport() && mNumStackTasks == 1) {
- mInitialScrollP = mMinScrollP;
- } else if (getDefaultFocusState() > 0f) {
- if (launchState.launchedFromHome) {
- mInitialScrollP = Utilities.clamp(launchTaskIndex, mMinScrollP, mMaxScrollP);
- } else {
- mInitialScrollP = Utilities.clamp(launchTaskIndex - 1, mMinScrollP,
- mMaxScrollP);
- }
+ }
+
+ // Calculate the min/max/initial scroll
+ Task launchTask = stack.getLaunchTarget();
+ int launchTaskIndex = launchTask != null
+ ? stack.indexOfStackTask(launchTask)
+ : mNumStackTasks - 1;
+ if (getInitialFocusState() == STATE_FOCUSED) {
+ mMinScrollP = 0;
+ mMaxScrollP = Math.max(mMinScrollP, mNumStackTasks - 1);
+ if (launchState.launchedFromHome) {
+ mInitialScrollP = Utilities.clamp(launchTaskIndex, mMinScrollP, mMaxScrollP);
} else {
- float offsetPct = (float) (mTaskRect.height() / 3) / mStackRect.height();
- float normX = mUnfocusedCurveInterpolator.getX(offsetPct);
- mInitialScrollP = Utilities.clamp(launchTaskIndex -
- mUnfocusedRange.getAbsoluteX(normX), mMinScrollP, mMaxScrollP);
+ mInitialScrollP = Utilities.clamp(launchTaskIndex - 1, mMinScrollP, mMaxScrollP);
}
+ } else if (!ssp.hasFreeformWorkspaceSupport() && mNumStackTasks == 1) {
+ // If there is one stack task, ignore the min/max/initial scroll positions
+ mMinScrollP = 0;
+ mMaxScrollP = 0;
+ mInitialScrollP = 0;
+ } else {
+ // Set the max scroll to be the point where the front most task is visible with the
+ // stack bottom offset
+ int maxBottomOffset = mStackBottomOffset + mTaskRect.height();
+ float maxBottomOffsetPct = (float) maxBottomOffset / mStackRect.height();
+ float maxBottomNormX = mUnfocusedCurveInterpolator.getX(maxBottomOffsetPct);
+ mUnfocusedRange.offset(0f);
+ mMinScrollP = 0;
+ mMaxScrollP = Math.max(mMinScrollP, (mNumStackTasks - 1) -
+ Math.max(0, mUnfocusedRange.getAbsoluteX(maxBottomNormX)));
+ boolean scrollToFront = launchState.launchedFromHome ||
+ launchState.launchedFromAppDocked;
+ if (scrollToFront) {
+ mInitialScrollP = Utilities.clamp(launchTaskIndex, mMinScrollP, mMaxScrollP);
+ } else {
+ mInitialScrollP = Utilities.clamp(launchTaskIndex - 1, mMinScrollP, mMaxScrollP);
+ }
+
+ // Set the initial scroll to the predefined state (which differs from the stack)
+ int initialPeekOffset = mStackRect.height() - mInitialTopPeekHeight;
+ float initialPeekOffsetPct = (float) initialPeekOffset / mStackRect.height();
+ float initialPeekOffsetNormX = mUnfocusedCurveInterpolator.getX(initialPeekOffsetPct);
+ float initialFocusedOffset = mStackRect.height() - mInitialTopPeekHeight -
+ (mHeaderBarHeight * 1f) + 1;
+ float initialFocusedOffsetPct = (float) initialFocusedOffset / mStackRect.height();
+ float initialFocusedNormX = mUnfocusedCurveInterpolator.getX(initialFocusedOffsetPct);
+ int initialBottomOffset = mStackBottomOffset + mHeaderBarHeight;
+ float initialBottomOffsetPct = (float) initialBottomOffset / mStackRect.height();
+ float initialBottomNormX = mUnfocusedCurveInterpolator.getX(initialBottomOffsetPct);
+ /*
+ // If we want to offset the top card slightly
+ mInitialNormX = scrollToFront
+ ? new float[] { initialFocusedNormX, initialPeekOffsetNormX, 0f }
+ : new float[] { initialBottomNormX, initialFocusedNormX,
+ initialPeekOffsetNormX, 0f };
+ */
+ mInitialNormX = scrollToFront
+ ? new float[] { initialFocusedNormX, initialPeekOffsetNormX, 0f }
+ : new float[] { initialBottomNormX, 0.5f, 0f };
+ }
+ }
+
+ public void updateToInitialState(List<Task> tasks) {
+ if (mInitialNormX == null) {
+ return;
+ }
+
+ RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
+ mUnfocusedRange.offset(0f);
+ int taskCount = tasks.size();
+ for (int i = taskCount - 1; i >= 0; i--) {
+ int indexFromFront = taskCount - i - 1;
+ if (indexFromFront >= mInitialNormX.length) {
+ break;
+ }
+ float newTaskProgress = mInitialScrollP +
+ mUnfocusedRange.getAbsoluteX(mInitialNormX[indexFromFront]);
+ mTaskIndexOverrideMap.put(tasks.get(i).key.id, newTaskProgress);
}
}
@@ -500,6 +563,10 @@
}
}
+ public void clearUnfocusedTaskOverrides() {
+ mTaskIndexOverrideMap.clear();
+ }
+
/**
* Updates this stack when a scroll happens.
*/
@@ -523,7 +590,7 @@
} else {
// Scrolling override x away from x, we should still move the scroll towards x
float deltaX = overrideX - x;
- newOverrideX = Math.signum(deltaX) * (Math.abs(deltaX) - deltaScroll);
+ newOverrideX = Math.signum(deltaX) * (Math.abs(deltaX) - Math.abs(deltaScroll));
mTaskIndexOverrideMap.put(taskId, x + newOverrideX);
}
}
@@ -532,8 +599,13 @@
/**
* Returns the default focus state.
*/
- public int getDefaultFocusState() {
- return STATE_FOCUSED;
+ public int getInitialFocusState() {
+ RecentsDebugFlags debugFlags = Recents.getDebugFlags();
+ if (debugFlags.isPagingEnabled()) {
+ return STATE_FOCUSED;
+ } else {
+ return STATE_UNFOCUSED;
+ }
}
/**
@@ -577,7 +649,7 @@
// Otherwise, walk backwards in the stack and count the number of tasks and visible
// thumbnails and add that to the total freeform task count
TaskViewTransform tmpTransform = new TaskViewTransform();
- Range currentRange = getDefaultFocusState() > 0f ? mFocusedRange : mUnfocusedRange;
+ Range currentRange = getInitialFocusState() > 0f ? mFocusedRange : mUnfocusedRange;
currentRange.offset(mInitialScrollP);
int taskBarHeight = mContext.getResources().getDimensionPixelSize(
R.dimen.recents_task_bar_height);
@@ -600,8 +672,9 @@
boolean isFrontMostTaskInGroup = task.group == null || task.group.isFrontMostTask(task);
if (isFrontMostTaskInGroup) {
- getStackTransform(taskProgress, mInitialScrollP, mFocusState, tmpTransform, null,
- false /* ignoreSingleTaskCase */, false /* forceUpdate */);
+ getStackTransform(taskProgress, taskProgress, mInitialScrollP, mFocusState,
+ tmpTransform, null, false /* ignoreSingleTaskCase */,
+ false /* forceUpdate */);
float screenY = tmpTransform.rect.top;
boolean hasVisibleThumbnail = (prevScreenY - screenY) > taskBarHeight;
if (hasVisibleThumbnail) {
@@ -635,23 +708,34 @@
public TaskViewTransform getStackTransform(Task task, float stackScroll,
TaskViewTransform transformOut, TaskViewTransform frontTransform) {
return getStackTransform(task, stackScroll, mFocusState, transformOut, frontTransform,
- false /* forceUpdate */);
+ false /* forceUpdate */, false /* ignoreTaskOverrides */);
+ }
+
+ public TaskViewTransform getStackTransform(Task task, float stackScroll,
+ TaskViewTransform transformOut, TaskViewTransform frontTransform,
+ boolean ignoreTaskOverrides) {
+ return getStackTransform(task, stackScroll, mFocusState, transformOut, frontTransform,
+ false /* forceUpdate */, ignoreTaskOverrides);
}
public TaskViewTransform getStackTransform(Task task, float stackScroll, int focusState,
- TaskViewTransform transformOut, TaskViewTransform frontTransform, boolean forceUpdate) {
+ TaskViewTransform transformOut, TaskViewTransform frontTransform, boolean forceUpdate,
+ boolean ignoreTaskOverrides) {
if (mFreeformLayoutAlgorithm.isTransformAvailable(task, this)) {
mFreeformLayoutAlgorithm.getTransform(task, transformOut, this);
return transformOut;
} else {
// Return early if we have an invalid index
- if (task == null || mTaskIndexMap.get(task.key.id, -1) == -1) {
+ int nonOverrideTaskProgress = mTaskIndexMap.get(task.key.id, -1);
+ if (task == null || nonOverrideTaskProgress == -1) {
transformOut.reset();
return transformOut;
}
- float taskProgress = getStackScrollForTask(task);
- getStackTransform(taskProgress, stackScroll, focusState, transformOut,
- frontTransform, false /* ignoreSingleTaskCase */, forceUpdate);
+ float taskProgress = ignoreTaskOverrides
+ ? nonOverrideTaskProgress
+ : getStackScrollForTask(task);
+ getStackTransform(taskProgress, nonOverrideTaskProgress, stackScroll, focusState,
+ transformOut, frontTransform, false /* ignoreSingleTaskCase */, forceUpdate);
return transformOut;
}
}
@@ -676,9 +760,9 @@
* internally to ensure that we can calculate the transform for any
* position in the stack.
*/
- public void getStackTransform(float taskProgress, float stackScroll, int focusState,
- TaskViewTransform transformOut, TaskViewTransform frontTransform,
- boolean ignoreSingleTaskCase, boolean forceUpdate) {
+ public void getStackTransform(float taskProgress, float nonOverrideTaskProgress,
+ float stackScroll, int focusState, TaskViewTransform transformOut,
+ TaskViewTransform frontTransform, boolean ignoreSingleTaskCase, boolean forceUpdate) {
SystemServicesProxy ssp = Recents.getSystemServices();
// Compute the focused and unfocused offset
@@ -687,6 +771,8 @@
mFocusedRange.offset(boundedStackScroll);
float boundedScrollUnfocusedRangeX = mUnfocusedRange.getNormalizedX(taskProgress);
float boundedScrollFocusedRangeX = mFocusedRange.getNormalizedX(taskProgress);
+ float boundedScrollUnfocusedNonOverrideRangeX =
+ mUnfocusedRange.getNormalizedX(nonOverrideTaskProgress);
mUnfocusedRange.offset(stackScroll);
mFocusedRange.offset(stackScroll);
boolean unfocusedVisible = mUnfocusedRange.isInRange(taskProgress);
@@ -730,7 +816,7 @@
y = (mStackRect.top - mTaskRect.top) +
(int) Utilities.mapRange(focusState, unfocusedY, focusedY);
- z = Utilities.mapRange(Utilities.clamp01(boundedScrollUnfocusedRangeX),
+ z = Utilities.mapRange(Utilities.clamp01(boundedScrollUnfocusedNonOverrideRangeX),
mMinTranslationZ, mMaxTranslationZ);
dimAlpha = Utilities.mapRange(focusState, unfocusedDim, focusedDim);
viewOutlineAlpha = Utilities.mapRange(Utilities.clamp01(boundedScrollUnfocusedRangeX),
@@ -851,8 +937,8 @@
// The unfocused dim interpolator starts at max dim, reduces to zero at 0.5 (the focused
// task), then goes back to max dim towards the front of the stack
p.moveTo(0f, MAX_DIM);
- p.cubicTo(0f, 0.1f, 0.4f, 0f, 0.5f, 0f);
- p.cubicTo(0.6f, 0f, 0.9f, MAX_DIM - 0.1f, 1f, MAX_DIM / 2f);
+ p.cubicTo(0.1f, MAX_DIM, 0.4f, 0.0f, 0.5f, 0f);
+ p.cubicTo(0.6f, 0f, 0.9f, MAX_DIM / 2f, 1f, MAX_DIM / 2f);
return p;
}
@@ -870,9 +956,9 @@
mFocusedRange.relativeMin);
float max = Utilities.mapRange(mFocusState, mUnfocusedRange.relativeMax,
mFocusedRange.relativeMax);
- getStackTransform(min, 0f, mFocusState, mBackOfStackTransform, null,
+ getStackTransform(min, min, 0f, mFocusState, mBackOfStackTransform, null,
true /* ignoreSingleTaskCase */, true /* forceUpdate */);
- getStackTransform(max, 0f, mFocusState, mFrontOfStackTransform, null,
+ getStackTransform(max, max, 0f, mFocusState, mFrontOfStackTransform, null,
true /* ignoreSingleTaskCase */, true /* forceUpdate */);
mBackOfStackTransform.visible = true;
mFrontOfStackTransform.visible = true;
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 e1a81c8..c2bfc28 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -35,7 +35,6 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.MutableBoolean;
-import android.util.SparseBooleanArray;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
@@ -56,6 +55,7 @@
import com.android.systemui.recents.RecentsDebugFlags;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
+import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
import com.android.systemui.recents.events.activity.EnterRecentsTaskStackAnimationCompletedEvent;
import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
@@ -73,7 +73,6 @@
import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
-import com.android.systemui.recents.events.ui.StackViewScrolledEvent;
import com.android.systemui.recents.events.ui.TaskViewDismissedEvent;
import com.android.systemui.recents.events.ui.UpdateFreeformTaskViewVisibilityEvent;
import com.android.systemui.recents.events.ui.UserInteractionEvent;
@@ -274,10 +273,18 @@
}
/** Returns the task stack. */
- TaskStack getStack() {
+ public TaskStack getStack() {
return mStack;
}
+ /**
+ * Updates this TaskStackView to the initial state.
+ */
+ public void updateToInitialState() {
+ mStackScroller.setStackScrollToInitialState();
+ mLayoutAlgorithm.updateToInitialState(mStack.getStackTasks());
+ }
+
/** Updates the list of task views */
void updateTaskViewsList() {
mTaskViews.clear();
@@ -355,7 +362,6 @@
mAwaitingFirstLayout = true;
mEnterAnimationComplete = false;
mUIDozeTrigger.stopDozing();
- mUIDozeTrigger.resetTrigger();
mStackScroller.reset();
mLayoutAlgorithm.reset();
readSystemFlags();
@@ -410,7 +416,7 @@
*/
int[] computeVisibleTaskTransforms(ArrayList<TaskViewTransform> taskTransforms,
ArrayList<Task> tasks, float curStackScroll, float targetStackScroll,
- ArraySet<Task.TaskKey> ignoreTasksSet) {
+ ArraySet<Task.TaskKey> ignoreTasksSet, boolean ignoreTaskOverrides) {
int taskCount = tasks.size();
int[] visibleTaskRange = mTmpIntPair;
visibleTaskRange[0] = -1;
@@ -430,7 +436,7 @@
// Calculate the current and (if necessary) the target transform for the task
transform = mLayoutAlgorithm.getStackTransform(task, curStackScroll,
- taskTransforms.get(i), frontTransform);
+ taskTransforms.get(i), frontTransform, ignoreTaskOverrides);
if (useTargetStackScroll && !transform.visible) {
// If we have a target stack scroll and the task is not currently visible, then we
// just update the transform at the new scroll
@@ -468,11 +474,13 @@
/**
* Binds the visible {@link TaskView}s at the given target scroll.
- *
- * @see #bindVisibleTaskViews(float, ArraySet<Task.TaskKey>)
*/
void bindVisibleTaskViews(float targetStackScroll) {
- bindVisibleTaskViews(targetStackScroll, mIgnoreTasks);
+ bindVisibleTaskViews(targetStackScroll, mIgnoreTasks, false /* ignoreTaskOverrides */);
+ }
+
+ void bindVisibleTaskViews(float targetStackScroll, boolean ignoreTaskOverrides) {
+ bindVisibleTaskViews(targetStackScroll, mIgnoreTasks, ignoreTaskOverrides);
}
/**
@@ -487,12 +495,16 @@
* target stack scroll.
* @param ignoreTasksSet The set of tasks to ignore in this rebinding of the visible
* {@link TaskView}s
+ * @param ignoreTaskOverrides If set, the visible task computation will get the transforms for
+ * tasks at their non-overridden task progress
*/
- void bindVisibleTaskViews(float targetStackScroll, ArraySet<Task.TaskKey> ignoreTasksSet) {
+ void bindVisibleTaskViews(float targetStackScroll, ArraySet<Task.TaskKey> ignoreTasksSet,
+ boolean ignoreTaskOverrides) {
// Get all the task transforms
ArrayList<Task> tasks = mStack.getStackTasks();
int[] visibleTaskRange = computeVisibleTaskTransforms(mCurrentTaskTransforms, tasks,
- mStackScroller.getStackScroll(), targetStackScroll, ignoreTasksSet);
+ mStackScroller.getStackScroll(), targetStackScroll, ignoreTasksSet,
+ ignoreTaskOverrides);
// Return all the invisible children to the pool
mTmpTaskViewMap.clear();
@@ -605,7 +617,8 @@
cancelAllTaskViewAnimations();
// Synchronize the current set of TaskViews
- bindVisibleTaskViews(mStackScroller.getStackScroll(), ignoreTasksSet);
+ bindVisibleTaskViews(mStackScroller.getStackScroll(), ignoreTasksSet,
+ false /* ignoreTaskOverrides */);
// Animate them to their final transforms with the given animation
List<TaskView> taskViews = getTaskViews();
@@ -657,7 +670,8 @@
transform.fillIn(tv);
} else {
mLayoutAlgorithm.getStackTransform(task, mStackScroller.getStackScroll(),
- focusState, transform, null, true /* forceUpdate */);
+ focusState, transform, null, true /* forceUpdate */,
+ false /* ignoreTaskOverrides */);
}
transform.visible = true;
}
@@ -674,7 +688,7 @@
Task task = tasks.get(i);
TaskViewTransform transform = transformsOut.get(i);
mLayoutAlgorithm.getStackTransform(task, stackScroll, focusState, transform, null,
- true /* forceUpdate */);
+ true /* forceUpdate */, true /* ignoreTaskOverrides */);
transform.visible = true;
}
}
@@ -759,9 +773,7 @@
}
}
tv.getViewBounds().setClipBottom(clipBottom);
- if (!config.useHardwareLayers) {
- tv.mThumbnailView.updateThumbnailVisibility(clipBottom - tv.getPaddingBottom());
- }
+ tv.mThumbnailView.updateThumbnailVisibility(clipBottom - tv.getPaddingBottom());
prevVisibleTv = tv;
}
mTaskViewsClipDirty = false;
@@ -860,6 +872,7 @@
cancelAllTaskViewAnimations();
}
+ mLayoutAlgorithm.clearUnfocusedTaskOverrides();
willScroll = mAnimationHelper.startScrollToFocusedTaskAnimation(newFocusedTask,
requestViewFocus);
} else {
@@ -1162,11 +1175,16 @@
// If this is the first layout, then scroll to the front of the stack, then update the
// TaskViews with the stack so that we can lay them out
- if (mAwaitingFirstLayout) {
- mStackScroller.setStackScrollToInitialState();
+ // TODO: The second check is a workaround for wacky layouts that we get while docking via
+ // long pressing the recents button
+ if (mAwaitingFirstLayout ||
+ (mStackScroller.getStackScroll() == mLayoutAlgorithm.mInitialScrollP)) {
+ updateToInitialState();
}
+
// Rebind all the views, including the ignore ones
- bindVisibleTaskViews(mStackScroller.getStackScroll(), EMPTY_TASK_SET);
+ bindVisibleTaskViews(mStackScroller.getStackScroll(), EMPTY_TASK_SET,
+ false /* ignoreTaskOverrides */);
// Measure each of the TaskViews
mTmpTaskViews.clear();
@@ -1458,7 +1476,7 @@
Recents.getTaskLoader().loadTaskData(task, true /* fetchAndInvalidateThumbnails */);
// If the doze trigger has already fired, then update the state for this task view
- if (mUIDozeTrigger.hasTriggered()) {
+ if (mUIDozeTrigger.isAsleep()) {
tv.setNoUserInteractionState();
}
@@ -1859,6 +1877,12 @@
});
}
+ public final void onBusEvent(ConfigurationChangedEvent event) {
+ mLayoutAlgorithm.reloadOnConfigurationChange(getContext());
+ mLayoutAlgorithm.initialize(mStackBounds,
+ TaskStackLayoutAlgorithm.StackState.getStackStateForStack(mStack));
+ }
+
/**
* Removes the task from the stack, and updates the focus to the next task in the stack if the
* removed TaskView was focused.
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 333df9d..ad46abd 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
@@ -115,13 +115,8 @@
* @return whether the stack progress changed.
*/
public boolean setStackScrollToInitialState() {
- SystemServicesProxy ssp = Recents.getSystemServices();
float prevStackScrollP = mStackScrollP;
- if (ssp.hasDockedTask()) {
- setStackScroll(mLayoutAlgorithm.mMaxScrollP);
- } else {
- setStackScroll(mLayoutAlgorithm.mInitialScrollP);
- }
+ setStackScroll(mLayoutAlgorithm.mInitialScrollP);
return Float.compare(prevStackScrollP, mStackScrollP) != 0;
}
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 20933ee..52f8fc8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -446,7 +446,7 @@
}
// Pick up the newly visible views, not including the deleting tasks
- mSv.bindVisibleTaskViews(newStackScroll);
+ mSv.bindVisibleTaskViews(newStackScroll, true /* ignoreTaskOverrides */);
// Get the final set of task transforms (with task removed)
mSv.getLayoutTaskTransforms(newStackScroll, TaskStackLayoutAlgorithm.STATE_UNFOCUSED,
@@ -486,6 +486,7 @@
mSv.getScroller().setStackScroll(mTargetStackScroll, null);
// Update the focus state to the final focus state
mSv.getStackAlgorithm().setFocusState(TaskStackLayoutAlgorithm.STATE_UNFOCUSED);
+ mSv.getStackAlgorithm().clearUnfocusedTaskOverrides();
// Remove the task view from the stack
EventBus.getDefault().send(new TaskViewDismissedEvent(tv.getTask(), tv));
// Stop tracking this deletion animation
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 2e7c7f2..e9c7ac6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -77,6 +77,24 @@
/**
* The dim overlay is generally calculated from the task progress, but occasionally (like when
+ * launching) needs to be animated independently of the task progress. This call is only used
+ * when animating the task into Recents, when the header dim is already applied
+ */
+ public static final Property<TaskView, Float> DIM_ALPHA_WITHOUT_HEADER =
+ new FloatProperty<TaskView>("dimAlphaWithoutHeader") {
+ @Override
+ public void setValue(TaskView tv, float dimAlpha) {
+ tv.setDimAlphaWithoutHeader(dimAlpha);
+ }
+
+ @Override
+ public Float get(TaskView tv) {
+ return tv.getDimAlpha();
+ }
+ };
+
+ /**
+ * The dim overlay is generally calculated from the task progress, but occasionally (like when
* launching) needs to be animated independently of the task progress.
*/
public static final Property<TaskView, Float> DIM_ALPHA =
@@ -388,21 +406,17 @@
* Sets the current dim.
*/
public void setDimAlpha(float dimAlpha) {
- RecentsConfiguration config = Recents.getConfiguration();
-
- int dimAlphaInt = (int) (dimAlpha * 255);
mDimAlpha = dimAlpha;
- if (config.useHardwareLayers) {
- // Defer setting hardware layers if we have not yet measured, or there is no dim to draw
- if (getMeasuredWidth() > 0 && getMeasuredHeight() > 0) {
- mDimColorFilter.setColor(Color.argb(dimAlphaInt, 0, 0, 0));
- mDimLayerPaint.setColorFilter(mDimColorFilter);
- mContent.setLayerType(LAYER_TYPE_HARDWARE, mDimLayerPaint);
- }
- } else {
- mThumbnailView.setDimAlpha(dimAlpha);
- mHeaderView.setDimAlpha(dimAlpha);
- }
+ mThumbnailView.setDimAlpha(dimAlpha);
+ mHeaderView.setDimAlpha(dimAlpha);
+ }
+
+ /**
+ * Sets the current dim without updating the header's dim.
+ */
+ public void setDimAlphaWithoutHeader(float dimAlpha) {
+ mDimAlpha = dimAlpha;
+ mThumbnailView.setDimAlpha(dimAlpha);
}
/**
@@ -413,25 +427,6 @@
}
/**
- * Animates the dim to the given value.
- */
- void animateDimAlpha(float toDimAlpha, AnimationProps animation) {
- // Animate the dim into view as well
- if (Float.compare(toDimAlpha, getDimAlpha()) != 0) {
- Animator anim = animation.apply(AnimationProps.DIM_ALPHA, ObjectAnimator.ofFloat(this,
- DIM_ALPHA, getDimAlpha(), toDimAlpha));
- if (animation.getListener() != null) {
- anim.addListener(animation.getListener());
- }
- anim.start();
- } else {
- if (animation.getListener() != null) {
- animation.getListener().onAnimationEnd(null);
- }
- }
- }
-
- /**
* Explicitly sets the focused state of this task.
*/
public void setFocusedState(boolean isFocused, boolean requestViewFocus) {
@@ -517,18 +512,20 @@
@Override
public void onPrepareLaunchTargetForEnterAnimation() {
// These values will be animated in when onStartLaunchTargetEnterAnimation() is called
- setDimAlpha(0);
+ setDimAlphaWithoutHeader(0);
mActionButtonView.setAlpha(0f);
}
@Override
- public void onStartLaunchTargetEnterAnimation(int duration, boolean screenPinningEnabled,
- ReferenceCountedTrigger postAnimationTrigger) {
- // Un-dim the view before/while launching the target
- AnimationProps animation = new AnimationProps(duration, Interpolators.ALPHA_OUT)
- .setListener(postAnimationTrigger.decrementOnAnimationEnd());
+ public void onStartLaunchTargetEnterAnimation(TaskViewTransform transform, int duration,
+ boolean screenPinningEnabled, ReferenceCountedTrigger postAnimationTrigger) {
+ // Dim the view after the app window transitions down into recents
postAnimationTrigger.increment();
- animateDimAlpha(0, animation);
+ AnimationProps animation = new AnimationProps(duration, Interpolators.ALPHA_OUT);
+ Animator anim = animation.apply(AnimationProps.DIM_ALPHA, ObjectAnimator.ofFloat(this,
+ DIM_ALPHA_WITHOUT_HEADER, getDimAlpha(), transform.dimAlpha));
+ anim.addListener(postAnimationTrigger.decrementOnAnimationEnd());
+ anim.start();
if (screenPinningEnabled) {
showActionButton(true /* fadeIn */, duration /* fadeInDuration */);
@@ -540,7 +537,9 @@
ReferenceCountedTrigger postAnimationTrigger) {
// Un-dim the view before/while launching the target
AnimationProps animation = new AnimationProps(duration, Interpolators.ALPHA_OUT);
- animateDimAlpha(0, animation);
+ Animator anim = animation.apply(AnimationProps.DIM_ALPHA, ObjectAnimator.ofFloat(this,
+ DIM_ALPHA, getDimAlpha(), 0));
+ anim.start();
postAnimationTrigger.increment();
hideActionButton(true /* fadeOut */, duration,
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 05a8527..b2a7d90 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -347,9 +347,11 @@
* Sets the dim alpha, only used when we are not using hardware layers.
* (see RecentsConfiguration.useHardwareLayers)
*/
- void setDimAlpha(float dimAlpha) {
- mDimAlpha = dimAlpha;
- updateBackgroundColor(mBackground.getColor(), dimAlpha);
+ public void setDimAlpha(float dimAlpha) {
+ if (Float.compare(mDimAlpha, dimAlpha) != 0) {
+ mDimAlpha = dimAlpha;
+ updateBackgroundColor(mBackground.getColor(), dimAlpha);
+ }
}
/**
@@ -377,7 +379,9 @@
int primaryColor = disabledInSafeMode
? mDisabledTaskBarBackgroundColor
: t.colorPrimary;
- updateBackgroundColor(primaryColor, mDimAlpha);
+ if (mBackground.getColor() != primaryColor) {
+ updateBackgroundColor(primaryColor, mDimAlpha);
+ }
if (t.icon != null) {
mIconView.setImageDrawable(t.icon);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index f63e97a..143f160 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -1090,6 +1090,8 @@
@Override
public void onGearDisplayed(ExpandableNotificationRow row) {
+ MetricsLogger.action(mContext, MetricsEvent.ACTION_REVEAL_GEAR,
+ row.getStatusBarNotification().getPackageName());
mNotificationGearDisplayed = row;
}
@@ -1889,7 +1891,7 @@
}
}
- protected abstract boolean isPanelFullyCollapsed();
+ public abstract boolean isPanelFullyCollapsed();
/**
* Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index f446593..12a83fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -105,7 +105,6 @@
private boolean mClearable;
private ExpansionLogger mLogger;
private String mLoggingKey;
- private boolean mWasReset;
private NotificationSettingsIconRow mSettingsIconRow;
private NotificationGuts mGuts;
private NotificationData.Entry mEntry;
@@ -615,20 +614,16 @@
mShowingPublicInitialized = false;
mIsSystemExpanded = false;
mOnKeyguard = false;
- mPublicLayout.reset(mIsHeadsUp);
- mPrivateLayout.reset(mIsHeadsUp);
+ mPublicLayout.reset();
+ mPrivateLayout.reset();
resetHeight();
resetTranslation();
logExpansionEvent(false, wasExpanded);
}
public void resetHeight() {
- if (mIsHeadsUp) {
- resetActualHeight();
- }
mMaxExpandHeight = 0;
mHeadsUpHeight = 0;
- mWasReset = true;
onHeightReset();
requestLayout();
}
@@ -684,7 +679,7 @@
mTranslateableViews.remove(mGutsStub);
}
- public void setTranslationForOutline(float translationX) {
+ private void setTranslationForOutline(float translationX) {
setOutlineRect(false, translationX, getTop(), getRight() + translationX, getBottom());
}
@@ -704,6 +699,46 @@
if (mTranslateAnim != null) {
mTranslateAnim.cancel();
}
+ mTranslateAnim = (AnimatorSet) getTranslateViewAnimator(leftTarget,
+ null /* updateListener */);
+ if (mTranslateAnim != null) {
+ mTranslateAnim.start();
+ }
+ }
+
+ @Override
+ public void setTranslation(float translationX) {
+ if (areGutsExposed()) {
+ // Don't translate if guts are showing.
+ return;
+ }
+ // Translate the group of views
+ for (int i = 0; i < mTranslateableViews.size(); i++) {
+ if (mTranslateableViews.get(i) != null) {
+ mTranslateableViews.get(i).setTranslationX(translationX);
+ }
+ }
+ setTranslationForOutline(translationX);
+ if (mSettingsIconRow != null) {
+ mSettingsIconRow.updateSettingsIcons(translationX, getMeasuredWidth());
+ }
+ }
+
+ @Override
+ public float getTranslation() {
+ if (mTranslateableViews != null && mTranslateableViews.size() > 0) {
+ // All of the views in the list should have same translation, just use first one.
+ return mTranslateableViews.get(0).getTranslationX();
+ }
+ return 0;
+ }
+
+ public Animator getTranslateViewAnimator(final float leftTarget,
+ AnimatorUpdateListener listener) {
+ if (areGutsExposed()) {
+ // No translation if guts are exposed.
+ return null;
+ }
AnimatorSet set = new AnimatorSet();
if (mTranslateableViews != null) {
for (int i = 0; i < mTranslateableViews.size(); i++) {
@@ -715,8 +750,15 @@
@Override
public void onAnimationUpdate(ValueAnimator animation) {
setTranslationForOutline((float) animation.getAnimatedValue());
+ if (mSettingsIconRow != null) {
+ mSettingsIconRow.updateSettingsIcons(
+ (float) animation.getAnimatedValue(), getMeasuredWidth());
+ }
}
});
+ if (listener != null) {
+ translateAnim.addUpdateListener(listener);
+ }
}
translateAnim.addListener(new AnimatorListenerAdapter() {
@Override
@@ -730,8 +772,7 @@
set.play(translateAnim);
}
}
- mTranslateAnim = set;
- set.start();
+ return set;
}
public float getSpaceForGear() {
@@ -748,10 +789,6 @@
return mSettingsIconRow;
}
- public ArrayList<View> getContentViews() {
- return mTranslateableViews;
- }
-
public void inflateGuts() {
if (mGuts == null) {
mGutsStub.inflate();
@@ -922,18 +959,6 @@
return mStatusBarNotification != null && mStatusBarNotification.isClearable();
}
- /**
- * Apply an expansion state to the layout.
- */
- public void applyExpansionToLayout() {
- boolean expand = isExpanded();
- if (expand && mExpandable) {
- setActualHeight(mMaxExpandHeight);
- } else {
- setActualHeight(getMinHeight());
- }
- }
-
@Override
public int getIntrinsicHeight() {
if (isUserLocked()) {
@@ -1015,12 +1040,7 @@
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
- boolean updateExpandHeight = mMaxExpandHeight == 0 && !mWasReset;
updateMaxHeights();
- if (updateExpandHeight) {
- applyExpansionToLayout();
- }
- mWasReset = false;
}
private void updateMaxHeights() {
@@ -1169,6 +1189,10 @@
return mMaxExpandHeight;
}
+ public boolean areGutsExposed() {
+ return (mGuts != null && mGuts.areGutsExposed());
+ }
+
@Override
public boolean isContentExpandable() {
NotificationContentView showingLayout = getShowingLayout();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
index 1ff87f5..c0e4340 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -36,7 +36,6 @@
protected OnHeightChangedListener mOnHeightChangedListener;
private int mActualHeight;
protected int mClipTopAmount;
- private boolean mActualHeightInitialized;
private boolean mDark;
private ArrayList<View> mMatchParentViews = new ArrayList<View>();
private int mClipTopOptimization;
@@ -99,28 +98,9 @@
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
- if (!mActualHeightInitialized && mActualHeight == 0) {
- int initialHeight = getInitialHeight();
- if (initialHeight != 0) {
- setActualHeight(initialHeight);
- }
- }
updateClipping();
}
- /**
- * Resets the height of the view on the next layout pass
- */
- protected void resetActualHeight() {
- mActualHeight = 0;
- mActualHeightInitialized = false;
- requestLayout();
- }
-
- protected int getInitialHeight() {
- return getHeight();
- }
-
@Override
public boolean pointInView(float localX, float localY, float slop) {
float top = mClipTopAmount;
@@ -137,7 +117,6 @@
* @param notifyListeners Whether the listener should be informed about the change.
*/
public void setActualHeight(int actualHeight, boolean notifyListeners) {
- mActualHeightInitialized = true;
mActualHeight = actualHeight;
updateClipping();
if (notifyListeners) {
@@ -283,6 +262,20 @@
public void setBelowSpeedBump(boolean below) {
}
+ /**
+ * Sets the translation of the view.
+ */
+ public void setTranslation(float translation) {
+ setTranslationX(translation);
+ }
+
+ /**
+ * Gets the translation of the view.
+ */
+ public float getTranslation() {
+ return getTranslationX();
+ }
+
public void onHeightReset() {
if (mOnHeightChangedListener != null) {
mOnHeightChangedListener.onReset(this);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 61105f8..b94c15b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -120,7 +120,7 @@
R.dimen.min_notification_layout_height);
mNotificationContentMarginEnd = getResources().getDimensionPixelSize(
com.android.internal.R.dimen.notification_content_margin_end);
- reset(true);
+ reset();
}
public void setHeights(int smallHeight, int headsUpMaxHeight, int maxHeight) {
@@ -255,7 +255,7 @@
updateVisibility();
}
- public void reset(boolean resetActualHeight) {
+ public void reset() {
if (mContractedChild != null) {
mContractedChild.animate().cancel();
removeView(mContractedChild);
@@ -271,10 +271,6 @@
mContractedChild = null;
mExpandedChild = null;
mHeadsUpChild = null;
- mVisibleType = VISIBLE_TYPE_CONTRACTED;
- if (resetActualHeight) {
- mContentHeight = mSmallHeight;
- }
}
public View getContractedChild() {
@@ -484,12 +480,18 @@
private void animateToVisibleType(int visibleType) {
final TransformableView shownView = getTransformableViewForVisibleType(visibleType);
final TransformableView hiddenView = getTransformableViewForVisibleType(mVisibleType);
+ if (shownView == hiddenView) {
+ shownView.setVisible(true);
+ return;
+ }
shownView.transformFrom(hiddenView);
getViewForVisibleType(visibleType).setVisibility(View.VISIBLE);
hiddenView.transformTo(shownView, new Runnable() {
@Override
public void run() {
- hiddenView.setVisible(false);
+ if (hiddenView != getTransformableViewForVisibleType(mVisibleType)) {
+ hiddenView.setVisible(false);
+ }
}
});
}
@@ -550,6 +552,9 @@
|| mContainingNotification.isExpanded()
? mContainingNotification.getMaxContentHeight()
: mContainingNotification.getShowingLayout().getMinHeight();
+ if (height == 0) {
+ height = mContentHeight;
+ }
int expandedVisualType = getVisualTypeForHeight(height);
int collapsedVisualType = getVisualTypeForHeight(
mContainingNotification.getMinExpandHeight());
@@ -557,7 +562,12 @@
? expandedVisualType
: collapsedVisualType;
}
- int viewHeight = Math.min(mContentHeight, mContainingNotification.getIntrinsicHeight());
+ int intrinsicHeight = mContainingNotification.getIntrinsicHeight();
+ int viewHeight = mContentHeight;
+ if (intrinsicHeight != 0) {
+ // the intrinsicHeight might be 0 because it was just reset.
+ viewHeight = Math.min(mContentHeight, intrinsicHeight);
+ }
return getVisualTypeForHeight(viewHeight);
}
@@ -638,7 +648,6 @@
mBeforeN = entry.targetSdk < Build.VERSION_CODES.N;
updateSingleLineView();
applyRemoteInput(entry);
- selectLayout(false /* animate */, true /* force */);
if (mContractedChild != null) {
mContractedWrapper.notifyContentUpdated(entry.notification);
}
@@ -648,6 +657,7 @@
if (mHeadsUpChild != null) {
mHeadsUpWrapper.notifyContentUpdated(entry.notification);
}
+ selectLayout(false /* animate */, true /* force */);
setDark(mDark, false /* animate */, 0 /* delay */);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
index 1c16bdc..45a24a0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
@@ -130,7 +130,12 @@
importanceSlider.setVisibility(View.VISIBLE);
importanceButtons.setVisibility(View.GONE);
} else {
- bindToggles(importanceButtons, sbn, systemApp);
+ int userImportance = NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED;
+ try {
+ userImportance =
+ mINotificationManager.getImportance(sbn.getPackageName(), sbn.getUid());
+ } catch (RemoteException e) {}
+ bindToggles(importanceButtons, userImportance, systemApp);
importanceButtons.setVisibility(View.VISIBLE);
importanceSlider.setVisibility(View.GONE);
}
@@ -144,7 +149,7 @@
if (mBlock.isChecked()) {
progress = NotificationListenerService.Ranking.IMPORTANCE_NONE;
} else if (mSilent.isChecked()) {
- progress = NotificationListenerService.Ranking.IMPORTANCE_DEFAULT;
+ progress = NotificationListenerService.Ranking.IMPORTANCE_LOW;
} else {
progress = NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED;
}
@@ -158,7 +163,7 @@
}
}
- private void bindToggles(final View importanceButtons, final StatusBarNotification sbn,
+ private void bindToggles(final View importanceButtons, final int importance,
final boolean systemApp) {
mBlock = (RadioButton) importanceButtons.findViewById(R.id.block_importance);
mSilent = (RadioButton) importanceButtons.findViewById(R.id.silent_importance);
@@ -169,7 +174,11 @@
} else {
mReset.setText(mContext.getString(R.string.do_not_silence_block));
}
- mReset.setChecked(true);
+ if (importance == NotificationListenerService.Ranking.IMPORTANCE_LOW) {
+ mSilent.setChecked(true);
+ } else {
+ mReset.setChecked(true);
+ }
}
private void bindSlider(final View importanceSlider, final StatusBarNotification sbn,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
index 960e4cf..988d537 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
@@ -221,6 +221,7 @@
apply();
applyIconTint();
+ mNC.addSignalCallback(this);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java
index bf05d1d..66f945e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java
@@ -76,26 +76,26 @@
});
mViewTransformationAnimation.setInterpolator(Interpolators.LINEAR);
mViewTransformationAnimation.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
- if (endRunnable != null) {
- mViewTransformationAnimation.addListener(new AnimatorListenerAdapter() {
- public boolean mCancelled;
+ mViewTransformationAnimation.addListener(new AnimatorListenerAdapter() {
+ public boolean mCancelled;
- @Override
- public void onAnimationEnd(Animator animation) {
- endRunnable.run();
- if (!mCancelled) {
- setVisible(false);
- } else {
- abortTransformations();
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (!mCancelled) {
+ if (endRunnable != null) {
+ endRunnable.run();
}
+ setVisible(false);
+ } else {
+ abortTransformations();
}
+ }
- @Override
- public void onAnimationCancel(Animator animation) {
- mCancelled = true;
- }
- });
- }
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mCancelled = true;
+ }
+ });
mViewTransformationAnimation.start();
}
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 c152171..073a848 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -995,7 +995,6 @@
SignalClusterView signalCluster =
(SignalClusterView) containerView.findViewById(R.id.signal_cluster);
if (signalCluster != null) {
- mNetworkController.addSignalCallback(signalCluster);
signalCluster.setSecurityController(mSecurityController);
signalCluster.setNetworkController(mNetworkController);
}
@@ -3555,7 +3554,7 @@
}
@Override
- protected boolean isPanelFullyCollapsed() {
+ public boolean isPanelFullyCollapsed() {
return mNotificationPanel.isFullyCollapsed();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
index 256cc6b..326ca2b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
@@ -296,7 +296,6 @@
mHost = host;
host.setHeaderView(this);
mHeaderQsPanel.setQSPanelAndHeader(mQsPanel, this);
- mHeaderQsPanel.setMaxTiles(5);
mHeaderQsPanel.setHost(host);
setUserInfoController(host.getUserInfoController());
setBatteryController(host.getBatteryController());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java
index bd36462..159bd41 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java
@@ -19,6 +19,7 @@
import android.net.NetworkCapabilities;
import com.android.systemui.statusbar.policy.NetworkController.IconState;
+import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
import java.util.BitSet;
@@ -46,12 +47,12 @@
}
@Override
- public void notifyListeners() {
+ public void notifyListeners(SignalCallback callback) {
boolean ethernetVisible = mCurrentState.connected;
String contentDescription = getStringIfExists(getContentDescription());
// TODO: wire up data transfer using WifiSignalPoller.
- mCallbackHandler.setEthernetIndicators(new IconState(ethernetVisible, getCurrentIconId(),
+ callback.setEthernetIndicators(new IconState(ethernetVisible, getCurrentIconId(),
contentDescription));
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index 8fd4d9c..80dcfb6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -34,6 +34,7 @@
import com.android.internal.telephony.cdma.EriInfo;
import com.android.systemui.R;
import com.android.systemui.statusbar.policy.NetworkController.IconState;
+import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
import com.android.systemui.statusbar.policy.NetworkControllerImpl.Config;
import com.android.systemui.statusbar.policy.NetworkControllerImpl.SubscriptionDefaults;
@@ -198,7 +199,7 @@
}
@Override
- public void notifyListeners() {
+ public void notifyListeners(SignalCallback callback) {
MobileIconGroup icons = getIcons();
String contentDescription = getStringIfExists(getContentDescription());
@@ -231,7 +232,7 @@
|| mCurrentState.iconGroup == TelephonyIcons.ROAMING
|| mCurrentState.iconGroup == TelephonyIcons.DATA_DISABLED;
int typeIcon = showDataIcon ? icons.mDataType : 0;
- mCallbackHandler.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon,
+ callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon,
activityIn, activityOut, dataContentDescription, description, icons.mIsWide,
mSubscriptionInfo.getSubscriptionId());
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 8193b52..40eb71d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -322,16 +322,16 @@
}
public void addSignalCallback(SignalCallback cb) {
- mCallbackHandler.setListening(cb, true);
- mCallbackHandler.setSubs(mCurrentSubscriptions);
- mCallbackHandler.setIsAirplaneMode(new IconState(mAirplaneMode,
+ cb.setSubs(mCurrentSubscriptions);
+ cb.setIsAirplaneMode(new IconState(mAirplaneMode,
TelephonyIcons.FLIGHT_MODE_ICON, R.string.accessibility_airplane_mode, mContext));
- mCallbackHandler.setNoSims(mHasNoSims);
- mWifiSignalController.notifyListeners();
- mEthernetSignalController.notifyListeners();
+ cb.setNoSims(mHasNoSims);
+ mWifiSignalController.notifyListeners(cb);
+ mEthernetSignalController.notifyListeners(cb);
for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
- mobileSignalController.notifyListeners();
+ mobileSignalController.notifyListeners(cb);
}
+ mCallbackHandler.setListening(cb, true);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java
index c954d08..4cfd1c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.text.format.DateFormat;
import android.util.Log;
+import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
import java.io.PrintWriter;
import java.util.BitSet;
@@ -48,7 +49,7 @@
// is aware of current state.
protected final NetworkControllerImpl mNetworkController;
- protected final CallbackHandler mCallbackHandler;
+ private final CallbackHandler mCallbackHandler;
// Save the previous HISTORY_SIZE states for logging.
private final State[] mHistory;
@@ -198,12 +199,16 @@
}
}
+ public final void notifyListeners() {
+ notifyListeners(mCallbackHandler);
+ }
+
/**
* Trigger callbacks based on current state. The callbacks should be completely
* based on current state, and only need to be called in the scenario where
* mCurrentState != mLastState.
*/
- public abstract void notifyListeners();
+ public abstract void notifyListeners(SignalCallback callback);
/**
* Generate a blank T.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index cc98eb6..a6ed04f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -28,6 +28,7 @@
import com.android.internal.util.AsyncChannel;
import com.android.settingslib.wifi.WifiStatusTracker;
import com.android.systemui.statusbar.policy.NetworkController.IconState;
+import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
import java.util.Objects;
@@ -72,7 +73,7 @@
}
@Override
- public void notifyListeners() {
+ public void notifyListeners(SignalCallback callback) {
// only show wifi in the cluster if connected or if wifi-only
boolean wifiVisible = mCurrentState.enabled
&& (mCurrentState.connected || !mHasMobileData);
@@ -83,7 +84,7 @@
IconState statusIcon = new IconState(wifiVisible, getCurrentIconId(), contentDescription);
IconState qsIcon = new IconState(mCurrentState.connected, getQsCurrentIconId(),
contentDescription);
- mCallbackHandler.setWifiIndicators(mCurrentState.enabled, statusIcon, qsIcon,
+ callback.setWifiIndicators(mCurrentState.enabled, statusIcon, qsIcon,
ssidPresent && mCurrentState.activityIn, ssidPresent && mCurrentState.activityOut,
wifiDesc);
}
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 e8dad92..aa444f5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -48,6 +48,8 @@
import android.view.animation.Interpolator;
import android.widget.OverScroller;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.MetricsProto.MetricsEvent;
import com.android.systemui.ExpandHelper;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
@@ -363,6 +365,8 @@
@Override
public void onGearTouched(ExpandableNotificationRow row, int x, int y) {
if (mLongPressListener != null) {
+ MetricsLogger.action(mContext, MetricsEvent.ACTION_TOUCH_GEAR,
+ row.getStatusBarNotification().getPackageName());
mLongPressListener.onLongPress(row, x, y);
}
}
@@ -710,6 +714,7 @@
if (targetLeft == 0 && mCurrIconRow != null) {
mCurrIconRow.resetState();
+ mCurrIconRow = null;
if (mGearExposedView != null && mGearExposedView == mTranslatingParentView) {
mGearExposedView = null;
}
@@ -3363,7 +3368,6 @@
private static final long GEAR_SHOW_DELAY = 60;
- private ArrayList<View> mTranslatingViews = new ArrayList<>();
private CheckForDrag mCheckForDrag;
private Handler mHandler;
private int mMoveState = MOVE_STATE_UNDEFINED;
@@ -3380,6 +3384,7 @@
// Reset check for drag gesture
mCheckForDrag = null;
+ mCurrIconRow = null;
// Slide back any notifications that might be showing a gear
resetExposedGearView();
@@ -3388,9 +3393,6 @@
// Set the listener for the current row's gear
mCurrIconRow = ((ExpandableNotificationRow) currView).getSettingsRow();
mCurrIconRow.setGearListener(NotificationStackScrollLayout.this);
-
- // And the translating children
- mTranslatingViews = ((ExpandableNotificationRow) currView).getContentViews();
}
mMoveState = MOVE_STATE_UNDEFINED;
}
@@ -3404,15 +3406,12 @@
}
mMoveState = newMoveState;
- if (view instanceof ExpandableNotificationRow) {
- ((ExpandableNotificationRow) view).setTranslationForOutline(translation);
- if (!isPinnedHeadsUp(view)) {
- // Only show the gear if we're not a heads up view.
- checkForDrag();
- if (mCurrIconRow != null) {
- mCurrIconRow.updateSettingsIcons(translation, getSize(view));
- }
- }
+ final boolean gutsExposed = (view instanceof ExpandableNotificationRow)
+ && ((ExpandableNotificationRow) view).areGutsExposed();
+
+ if (!isPinnedHeadsUp(view) && !gutsExposed) {
+ // Only show the gear if we're not a heads up view and guts aren't exposed.
+ checkForDrag();
}
}
@@ -3435,12 +3434,12 @@
(!fromLeft && absTrans >= snapBackThreshold * 0.4f
&& absTrans <= notiThreshold);
- if (pastGear && !isPinnedHeadsUp(animView)) {
+ if (pastGear && !isPinnedHeadsUp(animView)
+ && (animView instanceof ExpandableNotificationRow)) {
// bouncity
final float target = fromLeft ? snapBackThreshold : -snapBackThreshold;
mGearExposedView = mTranslatingParentView;
- if (mGearDisplayedListener != null
- && (animView instanceof ExpandableNotificationRow)) {
+ if (mGearDisplayedListener != null) {
mGearDisplayedListener.onGearDisplayed((ExpandableNotificationRow) animView);
}
super.snapChild(animView, target, velocity);
@@ -3450,38 +3449,16 @@
}
@Override
- public void onTranslationUpdate(View animView, float value, boolean canBeDismissed) {
- if (mDismissAllInProgress) {
- // When dismissing all, we translate the entire view instead.
- super.onTranslationUpdate(animView, value, canBeDismissed);
- return;
- }
- if (animView instanceof ExpandableNotificationRow) {
- ((ExpandableNotificationRow) animView).setTranslationForOutline(value);
- }
- if (mCurrIconRow != null) {
- mCurrIconRow.updateSettingsIcons(value, getSize(animView));
- }
- }
-
- @Override
public Animator getViewTranslationAnimator(View v, float target,
AnimatorUpdateListener listener) {
if (mDismissAllInProgress) {
// When dismissing all, we translate the entire view instead.
return super.getViewTranslationAnimator(v, target, listener);
+ } else if (v instanceof ExpandableNotificationRow) {
+ return ((ExpandableNotificationRow) v).getTranslateViewAnimator(target, listener);
+ } else {
+ return super.getViewTranslationAnimator(v, target, listener);
}
- ArrayList<Animator> animators = new ArrayList<Animator>();
- for (int i = 0; i < mTranslatingViews.size(); i++) {
- ObjectAnimator anim = createTranslationAnimation(mTranslatingViews.get(i), target);
- animators.add(anim);
- if (i == 0 && listener != null) {
- anim.addUpdateListener(listener);
- }
- }
- AnimatorSet set = new AnimatorSet();
- set.playTogether(animators);
- return set;
}
@Override
@@ -3489,13 +3466,8 @@
if (mDismissAllInProgress) {
// When dismissing all, we translate the entire view instead.
super.setTranslation(v, translate);
- return;
- }
- // Translate the group of views
- for (int i = 0; i < mTranslatingViews.size(); i++) {
- if (mTranslatingViews.get(i) != null) {
- super.setTranslation(mTranslatingViews.get(i), translate);
- }
+ } else {
+ ((ExpandableView) v).setTranslation(translate);
}
}
@@ -3504,15 +3476,11 @@
if (mDismissAllInProgress) {
// When dismissing all, we translate the entire view instead.
return super.getTranslation(v);
+ } else {
+ return ((ExpandableView) v).getTranslation();
}
- // All of the views in the list should have same translation, just use first one.
- if (mTranslatingViews.size() > 0) {
- return super.getTranslation(mTranslatingViews.get(0));
- }
- return 0;
}
-
/**
* Returns the horizontal space in pixels required to display the gear behind a
* notification.
@@ -3567,26 +3535,11 @@
final View prevGearExposedView = mGearExposedView;
mGearExposedView = null;
- AnimatorListenerAdapter listener = new AnimatorListenerAdapter() {
- public void onAnimationEnd(Animator animator) {
- if (prevGearExposedView instanceof ExpandableNotificationRow) {
- ((ExpandableNotificationRow) prevGearExposedView).getSettingsRow()
- .resetState();
- }
- }
- };
- AnimatorUpdateListener updateListener = new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- if (prevGearExposedView instanceof ExpandableNotificationRow) {
- ((ExpandableNotificationRow) prevGearExposedView)
- .setTranslationForOutline((float) animation.getAnimatedValue());
- }
- }
- };
- Animator set = getViewTranslationAnimator(prevGearExposedView, 0, updateListener);
- set.addListener(listener);
- set.start();
+ Animator anim = getViewTranslationAnimator(prevGearExposedView,
+ 0 /* leftTarget */, null /* updateListener */);
+ if (anim != null) {
+ anim.start();
+ }
}
}
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 0ed6ef8..2524e1a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -125,7 +125,7 @@
}
@Override
- protected boolean isPanelFullyCollapsed() {
+ public boolean isPanelFullyCollapsed() {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/NightModeTile.java b/packages/SystemUI/src/com/android/systemui/tuner/NightModeTile.java
index 61135bd..26e1d46 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/NightModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/NightModeTile.java
@@ -20,6 +20,8 @@
import android.provider.Settings;
import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.systemui.Prefs;
+import com.android.systemui.Prefs.Key;
import com.android.systemui.R;
import com.android.systemui.qs.QSTile;
import com.android.systemui.statusbar.policy.NightModeController;
@@ -46,6 +48,12 @@
}
@Override
+ public boolean isAvailable() {
+ return Prefs.getBoolean(mContext, Key.QS_NIGHT_ADDED, false)
+ && TunerService.isTunerEnabled(mContext);
+ }
+
+ @Override
public void setListening(boolean listening) {
if (listening) {
mNightModeController.addListener(this);
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java
index fd753e9..285dfd1 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java
@@ -50,6 +50,7 @@
private TextView mPlayPauseDescriptionTextView;
private View mCloseButtonView;
private View mCloseDescriptionView;
+ private boolean mPipMovedToFullscreen;
private MediaController.Callback mMediaControllerCallback = new MediaController.Callback() {
@Override
@@ -69,6 +70,7 @@
@Override
public void onClick(View v) {
mPipManager.movePipToFullscreen();
+ mPipMovedToFullscreen = true;
finish();
}
});
@@ -167,7 +169,9 @@
}
private void restorePipAndFinish() {
- mPipManager.resizePinnedStack(PipManager.STATE_PIP_OVERLAY);
+ if (!mPipMovedToFullscreen) {
+ mPipManager.resizePinnedStack(PipManager.STATE_PIP_OVERLAY);
+ }
finish();
}
@@ -221,7 +225,7 @@
@Override
public void finish() {
super.finish();
- if (mPipManager.isRecentsShown()) {
+ if (mPipManager.isRecentsShown() && !mPipMovedToFullscreen) {
SystemUI[] services = ((SystemUIApplication) getApplication()).getServices();
for (int i = services.length - 1; i >= 0; i--) {
if (services[i] instanceof Recents) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index ebd5384..60d33fa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -32,6 +32,7 @@
import com.android.settingslib.net.DataUsageController;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.policy.NetworkController.IconState;
+import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
import com.android.systemui.statusbar.policy.NetworkControllerImpl.Config;
import com.android.systemui.statusbar.policy.NetworkControllerImpl.SubscriptionDefaults;
import org.mockito.ArgumentCaptor;
@@ -100,7 +101,7 @@
// Trigger blank callbacks to always get the current state (some tests don't trigger
// changes from default state).
- mNetworkController.addSignalCallback(null);
+ mNetworkController.addSignalCallback(mock(SignalCallback.class));
mNetworkController.addEmergencyListener(null);
}
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 6d9018d..f3140d2 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -1945,6 +1945,14 @@
// access before; action pass package name of calling package.
ACTION_SCOPED_DIRECTORY_ACCESS_ALREADY_GRANTED_BY_PACKAGE = 331;
+ // Logged when the user slides a notification and
+ // reveals the gear beneath it.
+ ACTION_REVEAL_GEAR = 332;
+
+ // Logged when the user taps on the gear beneath
+ // a notification.
+ ACTION_TOUCH_GEAR = 333;
+
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
diff --git a/rs/java/android/renderscript/Allocation.java b/rs/java/android/renderscript/Allocation.java
index 8c78a3a..fc92966 100644
--- a/rs/java/android/renderscript/Allocation.java
+++ b/rs/java/android/renderscript/Allocation.java
@@ -115,7 +115,7 @@
if (cmp == Short.TYPE) {
if (checkType) {
- validateIsInt16();
+ validateIsInt16OrFloat16();
return mType.mElement.mType;
}
return Element.DataType.SIGNED_16;
@@ -402,9 +402,10 @@
"32 bit integer source does not match allocation type " + mType.mElement.mType);
}
- private void validateIsInt16() {
+ private void validateIsInt16OrFloat16() {
if ((mType.mElement.mType == Element.DataType.SIGNED_16) ||
- (mType.mElement.mType == Element.DataType.UNSIGNED_16)) {
+ (mType.mElement.mType == Element.DataType.UNSIGNED_16) ||
+ (mType.mElement.mType == Element.DataType.FLOAT_16)) {
return;
}
throw new RSIllegalArgumentException(
@@ -751,7 +752,7 @@
* @param d the source data array
*/
public void copyFrom(short[] d) {
- validateIsInt16();
+ validateIsInt16OrFloat16();
copyFromUnchecked(d, Element.DataType.SIGNED_16, d.length);
}
@@ -1060,7 +1061,7 @@
* @param d the source data array
*/
public void copy1DRangeFrom(int off, int count, short[] d) {
- validateIsInt16();
+ validateIsInt16OrFloat16();
copy1DRangeFromUnchecked(off, count, d, Element.DataType.SIGNED_16, d.length);
}
@@ -1204,7 +1205,7 @@
* @param data to be placed into the Allocation
*/
public void copy2DRangeFrom(int xoff, int yoff, int w, int h, short[] data) {
- validateIsInt16();
+ validateIsInt16OrFloat16();
copy2DRangeFromUnchecked(xoff, yoff, w, h, data,
Element.DataType.SIGNED_16, data.length);
}
@@ -1473,7 +1474,7 @@
* @param d The array to be set from the Allocation.
*/
public void copyTo(short[] d) {
- validateIsInt16();
+ validateIsInt16OrFloat16();
copyTo(d, Element.DataType.SIGNED_16, d.length);
}
@@ -1693,7 +1694,7 @@
* @param d the source data array
*/
public void copy1DRangeTo(int off, int count, short[] d) {
- validateIsInt16();
+ validateIsInt16OrFloat16();
copy1DRangeToUnchecked(off, count, d, Element.DataType.SIGNED_16, d.length);
}
@@ -1794,7 +1795,7 @@
* @param data Dest Array to be copied into
*/
public void copy2DRangeTo(int xoff, int yoff, int w, int h, short[] data) {
- validateIsInt16();
+ validateIsInt16OrFloat16();
copy2DRangeToUnchecked(xoff, yoff, w, h, data,
Element.DataType.SIGNED_16, data.length);
}
diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp
index 3bef19e..4877a378 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -151,6 +151,7 @@
return; \
case RS_TYPE_SIGNED_16: \
case RS_TYPE_UNSIGNED_16: \
+ case RS_TYPE_FLOAT_16: \
len = _env->GetArrayLength((jshortArray)data); \
ptr = _env->GetShortArrayElements((jshortArray)data, flag); \
if (ptr == nullptr) { \
@@ -1061,7 +1062,7 @@
type, kind, norm, size);
}
return (jlong)(uintptr_t)rsElementCreate((RsContext)con, (RsDataType)type, (RsDataKind)kind,
- norm, size);
+ norm, size, true);
}
static jlong
@@ -1100,7 +1101,7 @@
jlong id = (jlong)(uintptr_t)rsElementCreate2((RsContext)con,
(const RsElement *)ids, fieldCount,
nameArray, fieldCount * sizeof(size_t), sizeArray,
- (const uint32_t *)arraySizes, fieldCount);
+ (const uint32_t *)arraySizes, fieldCount, true);
free(ids);
free(arraySizes);
@@ -1174,7 +1175,7 @@
}
return (jlong)(uintptr_t)rsTypeCreate((RsContext)con, (RsElement)eid, dimx, dimy, dimz, mips,
- faces, yuv);
+ faces, yuv, true);
}
static void
@@ -1210,7 +1211,7 @@
}
return (jlong)(uintptr_t) rsAllocationCreateTyped((RsContext)con, (RsType)type,
(RsAllocationMipmapControl)mips,
- (uint32_t)usage, (uintptr_t)pointer);
+ (uint32_t)usage, (uintptr_t)pointer, true);
}
static void
@@ -1315,7 +1316,7 @@
const void* ptr = bitmap.getPixels();
jlong id = (jlong)(uintptr_t)rsAllocationCreateFromBitmap((RsContext)con,
(RsType)type, (RsAllocationMipmapControl)mip,
- ptr, bitmap.getSize(), usage);
+ ptr, bitmap.getSize(), usage, true);
bitmap.unlockPixels();
return id;
}
@@ -1331,7 +1332,7 @@
const void* ptr = bitmap.getPixels();
jlong id = (jlong)(uintptr_t)rsAllocationCreateTyped((RsContext)con,
(RsType)type, (RsAllocationMipmapControl)mip,
- (uint32_t)usage, (uintptr_t)ptr);
+ (uint32_t)usage, (uintptr_t)ptr, true);
bitmap.unlockPixels();
return id;
}
@@ -1347,7 +1348,7 @@
const void* ptr = bitmap.getPixels();
jlong id = (jlong)(uintptr_t)rsAllocationCubeCreateFromBitmap((RsContext)con,
(RsType)type, (RsAllocationMipmapControl)mip,
- ptr, bitmap.getSize(), usage);
+ ptr, bitmap.getSize(), usage, true);
bitmap.unlockPixels();
return id;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index acd57b17..9e6c21c 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -655,10 +655,9 @@
userState.mUiAutomationServiceOwner = owner;
userState.mUiAutomationServiceClient = serviceClient;
userState.mUiAutomationFlags = flags;
- userState.mIsAccessibilityEnabled = true;
userState.mInstalledServices.add(accessibilityServiceInfo);
if ((flags & UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES) == 0) {
- // Set the temporary state.
+ // Set the temporary state, and use it instead of settings
userState.mIsTouchExplorationEnabled = false;
userState.mIsEnhancedWebAccessibilityEnabled = false;
userState.mIsDisplayMagnificationEnabled = false;
@@ -709,7 +708,6 @@
return;
}
- userState.mIsAccessibilityEnabled = true;
userState.mIsTouchExplorationEnabled = touchExplorationEnabled;
userState.mIsEnhancedWebAccessibilityEnabled = false;
userState.mIsDisplayMagnificationEnabled = false;
@@ -1245,17 +1243,17 @@
}
}
- private void manageServicesLocked(UserState userState) {
+ private void updateServicesLocked(UserState userState) {
Map<ComponentName, Service> componentNameToServiceMap =
userState.mComponentNameToServiceMap;
boolean isUnlocked = mContext.getSystemService(UserManager.class)
.isUserUnlocked(userState.mUserId);
- boolean isEnabled = userState.mIsAccessibilityEnabled;
for (int i = 0, count = userState.mInstalledServices.size(); i < count; i++) {
AccessibilityServiceInfo installedService = userState.mInstalledServices.get(i);
ComponentName componentName = ComponentName.unflattenFromString(
installedService.getId());
+
Service service = componentNameToServiceMap.get(componentName);
// Ignore non-encryption-aware services until user is unlocked
@@ -1264,45 +1262,25 @@
continue;
}
- if (isEnabled) {
- // Wait for the binding if it is in process.
- if (userState.mBindingServices.contains(componentName)) {
+ // Wait for the binding if it is in process.
+ if (userState.mBindingServices.contains(componentName)) {
+ continue;
+ }
+ if (userState.mEnabledServices.contains(componentName)) {
+ if (service == null) {
+ service = new Service(userState.mUserId, componentName, installedService);
+ } else if (userState.mBoundServices.contains(service)) {
continue;
}
- if (userState.mEnabledServices.contains(componentName)) {
- if (service == null) {
- service = new Service(userState.mUserId, componentName, installedService);
- } else if (userState.mBoundServices.contains(service)) {
- continue;
- }
- service.bindLocked();
- } else {
- if (service != null) {
- service.unbindLocked();
- }
- }
+ service.bindLocked();
} else {
if (service != null) {
service.unbindLocked();
- } else {
- userState.mBindingServices.remove(componentName);
}
}
}
- // No enabled installed services => disable accessibility to avoid
- // sending accessibility events with no recipient across processes.
- if (isEnabled && isUnlocked && userState.mBoundServices.isEmpty()
- && userState.mBindingServices.isEmpty()) {
- userState.mIsAccessibilityEnabled = false;
- final long identity = Binder.clearCallingIdentity();
- try {
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_ENABLED, 0, userState.mUserId);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
+ updateAccessibilityEnabledSetting(userState);
}
private void scheduleUpdateClientsIfNeededLocked(UserState userState) {
@@ -1329,7 +1307,8 @@
flags |= AccessibilityInputFilter.FLAG_FEATURE_SCREEN_MAGNIFIER;
}
// Touch exploration without accessibility makes no sense.
- if (userState.mIsAccessibilityEnabled && userState.mIsTouchExplorationEnabled) {
+ if (userState.isHandlingAccessibilityEvents()
+ && userState.mIsTouchExplorationEnabled) {
flags |= AccessibilityInputFilter.FLAG_FEATURE_TOUCH_EXPLORATION;
}
if (userState.mIsFilterKeyEventsEnabled) {
@@ -1468,25 +1447,17 @@
}
private void updateWindowsForAccessibilityCallbackLocked(UserState userState) {
- if (userState.mIsAccessibilityEnabled) {
- // We observe windows for accessibility only if there is at least
- // one bound service that can retrieve window content that specified
- // it is interested in accessing such windows. For services that are
- // binding we do an update pass after each bind event, so we run this
- // code and register the callback if needed.
- boolean boundServiceCanRetrieveInteractiveWindows = false;
+ // We observe windows for accessibility only if there is at least
+ // one bound service that can retrieve window content that specified
+ // it is interested in accessing such windows. For services that are
+ // binding we do an update pass after each bind event, so we run this
+ // code and register the callback if needed.
- List<Service> boundServices = userState.mBoundServices;
- final int boundServiceCount = boundServices.size();
- for (int i = 0; i < boundServiceCount; i++) {
- Service boundService = boundServices.get(i);
- if (boundService.canRetrieveInteractiveWindowsLocked()) {
- boundServiceCanRetrieveInteractiveWindows = true;
- break;
- }
- }
-
- if (boundServiceCanRetrieveInteractiveWindows) {
+ List<Service> boundServices = userState.mBoundServices;
+ final int boundServiceCount = boundServices.size();
+ for (int i = 0; i < boundServiceCount; i++) {
+ Service boundService = boundServices.get(i);
+ if (boundService.canRetrieveInteractiveWindowsLocked()) {
if (mWindowsForAccessibilityCallback == null) {
mWindowsForAccessibilityCallback = new WindowsForAccessibilityCallback();
mWindowManagerService.setWindowsForAccessibilityCallback(
@@ -1554,37 +1525,30 @@
userState.mIsFilterKeyEventsEnabled = false;
}
- private void updateServicesLocked(UserState userState) {
- if (userState.mIsAccessibilityEnabled) {
- manageServicesLocked(userState);
- } else {
- unbindAllServicesLocked(userState);
- }
- }
-
private boolean readConfigurationForUserStateLocked(UserState userState) {
- boolean somthingChanged = readAccessibilityEnabledSettingLocked(userState);
- somthingChanged |= readInstalledAccessibilityServiceLocked(userState);
- somthingChanged |= readEnabledAccessibilityServicesLocked(userState);
- somthingChanged |= readTouchExplorationGrantedAccessibilityServicesLocked(userState);
- somthingChanged |= readTouchExplorationEnabledSettingLocked(userState);
- somthingChanged |= readHighTextContrastEnabledSettingLocked(userState);
- somthingChanged |= readEnhancedWebAccessibilityEnabledChangedLocked(userState);
- somthingChanged |= readDisplayMagnificationEnabledSettingLocked(userState);
- somthingChanged |= readAutoclickEnabledSettingLocked(userState);
- somthingChanged |= readDisplayColorAdjustmentSettingsLocked(userState);
- return somthingChanged;
+ boolean somethingChanged = readInstalledAccessibilityServiceLocked(userState);
+ somethingChanged |= readEnabledAccessibilityServicesLocked(userState);
+ somethingChanged |= readTouchExplorationGrantedAccessibilityServicesLocked(userState);
+ somethingChanged |= readTouchExplorationEnabledSettingLocked(userState);
+ somethingChanged |= readHighTextContrastEnabledSettingLocked(userState);
+ somethingChanged |= readEnhancedWebAccessibilityEnabledChangedLocked(userState);
+ somethingChanged |= readDisplayMagnificationEnabledSettingLocked(userState);
+ somethingChanged |= readAutoclickEnabledSettingLocked(userState);
+ somethingChanged |= readDisplayColorAdjustmentSettingsLocked(userState);
+
+ return somethingChanged;
}
- private boolean readAccessibilityEnabledSettingLocked(UserState userState) {
- final boolean accessibilityEnabled = Settings.Secure.getIntForUser(
- mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_ENABLED, 0, userState.mUserId) == 1;
- if (accessibilityEnabled != userState.mIsAccessibilityEnabled) {
- userState.mIsAccessibilityEnabled = accessibilityEnabled;
- return true;
+ private void updateAccessibilityEnabledSetting(UserState userState) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_ENABLED,
+ userState.isHandlingAccessibilityEvents() ? 0 : 1,
+ userState.mUserId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
- return false;
}
private boolean readTouchExplorationEnabledSettingLocked(UserState userState) {
@@ -1809,7 +1773,7 @@
// Check whether any Accessibility Services are still enabled and, if not, remove flag
// requesting no soft keyboard
final boolean accessibilityRequestingNoIme = userState.mSoftKeyboardShowMode == 1;
- if (accessibilityRequestingNoIme && !userState.mIsAccessibilityEnabled) {
+ if (accessibilityRequestingNoIme && !userState.isHandlingAccessibilityEvents()) {
// No active Accessibility Services can be requesting the soft keyboard to be hidden
Settings.Secure.putIntForUser(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
@@ -1853,7 +1817,6 @@
UserState userState = mUserStates.valueAt(i);
pw.append("User state[attributes:{id=" + userState.mUserId);
pw.append(", currentUser=" + (userState.mUserId == mCurrentUserId));
- pw.append(", accessibilityEnabled=" + userState.mIsAccessibilityEnabled);
pw.append(", touchExplorationEnabled=" + userState.mIsTouchExplorationEnabled);
pw.append(", displayMagnificationEnabled="
+ userState.mIsDisplayMagnificationEnabled);
@@ -2003,7 +1966,7 @@
private void announceNewUserIfNeeded() {
synchronized (mLock) {
UserState userState = getCurrentUserStateLocked();
- if (userState.mIsAccessibilityEnabled) {
+ if (userState.isHandlingAccessibilityEvents()) {
UserManager userManager = (UserManager) mContext.getSystemService(
Context.USER_SERVICE);
String message = mContext.getString(R.string.user_switched,
@@ -4061,7 +4024,6 @@
public int mSoftKeyboardShowMode = 0;
- public boolean mIsAccessibilityEnabled;
public boolean mIsTouchExplorationEnabled;
public boolean mIsTextHighContrastEnabled;
public boolean mIsEnhancedWebAccessibilityEnabled;
@@ -4096,11 +4058,11 @@
public int getClientState() {
int clientState = 0;
- if (mIsAccessibilityEnabled) {
+ if (isHandlingAccessibilityEvents()) {
clientState |= AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED;
}
// Touch exploration relies on enabled accessibility.
- if (mIsAccessibilityEnabled && mIsTouchExplorationEnabled) {
+ if (isHandlingAccessibilityEvents() && mIsTouchExplorationEnabled) {
clientState |= AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED;
}
if (mIsTextHighContrastEnabled) {
@@ -4109,6 +4071,10 @@
return clientState;
}
+ public boolean isHandlingAccessibilityEvents() {
+ return !mBoundServices.isEmpty() || !mBoundServices.isEmpty();
+ }
+
public void onSwitchToAnotherUser() {
// Clear UI test automation state.
if (mUiAutomationService != null) {
@@ -4128,7 +4094,6 @@
// Clear state persisted in settings.
mEnabledServices.clear();
mTouchExplorationGrantedServices.clear();
- mIsAccessibilityEnabled = false;
mIsTouchExplorationEnabled = false;
mIsEnhancedWebAccessibilityEnabled = false;
mIsDisplayMagnificationEnabled = false;
@@ -4155,9 +4120,6 @@
private final class AccessibilityContentObserver extends ContentObserver {
- private final Uri mAccessibilityEnabledUri = Settings.Secure.getUriFor(
- Settings.Secure.ACCESSIBILITY_ENABLED);
-
private final Uri mTouchExplorationEnabledUri = Settings.Secure.getUriFor(
Settings.Secure.TOUCH_EXPLORATION_ENABLED);
@@ -4199,8 +4161,6 @@
}
public void register(ContentResolver contentResolver) {
- contentResolver.registerContentObserver(mAccessibilityEnabledUri,
- false, this, UserHandle.USER_ALL);
contentResolver.registerContentObserver(mTouchExplorationEnabledUri,
false, this, UserHandle.USER_ALL);
contentResolver.registerContentObserver(mDisplayMagnificationEnabledUri,
@@ -4240,11 +4200,7 @@
return;
}
- if (mAccessibilityEnabledUri.equals(uri)) {
- if (readAccessibilityEnabledSettingLocked(userState)) {
- onUserStateChangedLocked(userState);
- }
- } else if (mTouchExplorationEnabledUri.equals(uri)) {
+ if (mTouchExplorationEnabledUri.equals(uri)) {
if (readTouchExplorationEnabledSettingLocked(userState)) {
onUserStateChangedLocked(userState);
}
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index f1a9c44..cd4d107d 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -3495,9 +3495,8 @@
// The agent was running with a stub Application object, so shut it down.
// !!! We hardcode the confirmation UI's package name here rather than use a
// manifest flag! TODO something less direct.
- if (app.uid != Process.SYSTEM_UID
- && !app.packageName.equals("com.android.backupconfirm")
- && app.uid != Process.PHONE_UID) {
+ if (app.uid >= Process.FIRST_APPLICATION_UID
+ && !app.packageName.equals("com.android.backupconfirm")) {
if (MORE_DEBUG) Slog.d(TAG, "Killing agent host process");
mActivityManager.killApplicationProcess(app.processName, app.uid);
} else {
@@ -6881,7 +6880,7 @@
// The agent was running with a stub Application object, so shut it down.
// !!! We hardcode the confirmation UI's package name here rather than use a
// manifest flag! TODO something less direct.
- if (app.uid != Process.SYSTEM_UID
+ if (app.uid >= Process.FIRST_APPLICATION_UID
&& !app.packageName.equals("com.android.backupconfirm")) {
if (DEBUG) Slog.d(TAG, "Killing host process");
mActivityManager.killApplicationProcess(app.processName, app.uid);
@@ -8625,13 +8624,15 @@
// it is explicitly not killed following that operation.
//
// We execute this kill when these conditions hold:
- // 1. the app did not request its own restore (mTargetPackage == null), and either
- // 2a. the app is a full-data target (TYPE_FULL_STREAM) or
+ // 1. it's not a system-uid process,
+ // 2. the app did not request its own restore (mTargetPackage == null), and either
+ // 3a. the app is a full-data target (TYPE_FULL_STREAM) or
// b. the app does not state android:killAfterRestore="false" in its manifest
final int appFlags = mCurrentPackage.applicationInfo.flags;
final boolean killAfterRestore =
- (mRestoreDescription.getDataType() == RestoreDescription.TYPE_FULL_STREAM)
- || ((appFlags & ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0);
+ (mCurrentPackage.applicationInfo.uid >= Process.FIRST_APPLICATION_UID)
+ && ((mRestoreDescription.getDataType() == RestoreDescription.TYPE_FULL_STREAM)
+ || ((appFlags & ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0));
if (mTargetPackage == null && killAfterRestore) {
if (DEBUG) Slog.d(TAG, "Restore complete, killing host process of "
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 25b6fdd..86040c2 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -125,6 +125,7 @@
import com.android.server.connectivity.NetworkMonitor;
import com.android.server.connectivity.PacManager;
import com.android.server.connectivity.PermissionMonitor;
+import com.android.server.connectivity.ApfFilter;
import com.android.server.connectivity.Tethering;
import com.android.server.connectivity.Vpn;
import com.android.server.net.BaseNetworkObserver;
@@ -163,7 +164,7 @@
implements PendingIntent.OnFinished {
private static final String TAG = "ConnectivityService";
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
private static final boolean VDBG = false;
private static final boolean LOGD_RULES = false;
@@ -353,6 +354,13 @@
*/
private static final int EVENT_REGISTER_NETWORK_LISTENER_WITH_INTENT = 31;
+ /**
+ * used to push APF program to NetworkAgent
+ * replyTo = NetworkAgent message handler
+ * obj = byte[] of APF program
+ */
+ private static final int EVENT_PUSH_APF_PROGRAM_TO_NETWORK = 32;
+
/** Handler thread used for both of the handlers below. */
@VisibleForTesting
protected final HandlerThread mHandlerThread;
@@ -447,7 +455,7 @@
*/
private class LegacyTypeTracker {
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
private static final boolean VDBG = false;
private static final String TAG = "CSLegacyTypeTracker";
@@ -2190,6 +2198,7 @@
mKeepaliveTracker.handleStopAllKeepalives(nai,
ConnectivityManager.PacketKeepalive.ERROR_INVALID_NETWORK);
nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_DISCONNECTED);
+ if (nai.apfFilter != null) nai.apfFilter.shutdown();
mNetworkAgentInfos.remove(msg.replyTo);
updateClat(null, nai.linkProperties, nai);
synchronized (mNetworkForNetId) {
@@ -2404,6 +2413,13 @@
accept ? 1 : 0, always ? 1: 0, network));
}
+ public void pushApfProgramToNetwork(NetworkAgentInfo nai, byte[] program) {
+ enforceConnectivityInternalPermission();
+ Message msg = mHandler.obtainMessage(EVENT_PUSH_APF_PROGRAM_TO_NETWORK, program);
+ msg.replyTo = nai.messenger;
+ mHandler.sendMessage(msg);
+ }
+
private void handleSetAcceptUnvalidated(Network network, boolean accept, boolean always) {
if (DBG) log("handleSetAcceptUnvalidated network=" + network +
" accept=" + accept + " always=" + always);
@@ -2553,6 +2569,16 @@
handleMobileDataAlwaysOn();
break;
}
+ case EVENT_PUSH_APF_PROGRAM_TO_NETWORK: {
+ NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
+ if (nai == null) {
+ loge("EVENT_PUSH_APF_PROGRAM_TO_NETWORK from unknown NetworkAgent");
+ } else {
+ nai.asyncChannel.sendMessage(NetworkAgent.CMD_PUSH_APF_PROGRAM,
+ (byte[]) msg.obj);
+ }
+ break;
+ }
// Sent by KeepaliveTracker to process an app request on the state machine thread.
case NetworkAgent.CMD_START_PACKET_KEEPALIVE: {
mKeepaliveTracker.handleStartKeepalive(msg);
@@ -4068,6 +4094,9 @@
if (networkAgent.clatd != null) {
networkAgent.clatd.fixupLinkProperties(oldLp);
}
+ if (networkAgent.apfFilter != null) {
+ networkAgent.apfFilter.updateFilter();
+ }
updateInterfaces(newLp, oldLp, netId);
updateMtu(newLp, oldLp);
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 63c9822..3b6c62b 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -1960,14 +1960,6 @@
throw new IllegalArgumentException("Unknown id: " + id);
}
- if (mCurClient != null && mCurAttribute != null) {
- // We have already made sure that the package name belongs to the application's UID.
- // No further UID check is required.
- if (SystemConfig.getInstance().getFixedImeApps().contains(mCurAttribute.packageName)) {
- return;
- }
- }
-
// See if we need to notify a subtype change within the same IME.
if (id.equals(mCurMethodId)) {
final int subtypeCount = info.getSubtypeCount();
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 799d0bd..329f716 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -22,8 +22,10 @@
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_NONE;
+import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_STANDBY;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NONE;
+import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_POWERSAVE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_STANDBY;
import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
import static android.net.NetworkPolicyManager.FIREWALL_TYPE_BLACKLIST;
@@ -43,7 +45,6 @@
import static com.android.server.NetworkManagementService.NetdResponseCode.TetheringStatsListResult;
import static com.android.server.NetworkManagementService.NetdResponseCode.TtyListResult;
import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED;
-
import android.annotation.NonNull;
import android.app.ActivityManagerNative;
import android.content.Context;
@@ -226,6 +227,12 @@
*/
@GuardedBy("mQuotaLock")
private SparseIntArray mUidFirewallDozableRules = new SparseIntArray();
+ /**
+ * Set of UIDs that are to be blocked/allowed by firewall controller. This set of Ids matches
+ * to device on power-save mode.
+ */
+ @GuardedBy("mQuotaLock")
+ private SparseIntArray mUidFirewallPowerSaveRules = new SparseIntArray();
/** Set of states for the child firewall chains. True if the chain is active. */
@GuardedBy("mQuotaLock")
final SparseBooleanArray mFirewallChainStates = new SparseBooleanArray();
@@ -286,8 +293,8 @@
Watchdog.getInstance().addMonitor(this);
}
- static NetworkManagementService create(Context context,
- String socket) throws InterruptedException {
+ static NetworkManagementService create(Context context, String socket)
+ throws InterruptedException {
final NetworkManagementService service = new NetworkManagementService(context, socket);
final CountDownLatch connectedSignal = service.mConnectedSignal;
if (DBG) Slog.d(TAG, "Creating NetworkManagementService");
@@ -303,8 +310,15 @@
}
public void systemReady() {
- prepareNativeDaemon();
- if (DBG) Slog.d(TAG, "Prepared");
+ if (DBG) {
+ final long start = System.currentTimeMillis();
+ prepareNativeDaemon();
+ final long delta = System.currentTimeMillis() - start;
+ Slog.d(TAG, "Prepared in " + delta + "ms");
+ return;
+ } else {
+ prepareNativeDaemon();
+ }
}
private IBatteryStats getBatteryStats() {
@@ -339,8 +353,7 @@
for (int i = 0; i < length; i++) {
try {
mObservers.getBroadcastItem(i).interfaceStatusChanged(iface, up);
- } catch (RemoteException e) {
- } catch (RuntimeException e) {
+ } catch (RemoteException | RuntimeException e) {
}
}
} finally {
@@ -358,8 +371,7 @@
for (int i = 0; i < length; i++) {
try {
mObservers.getBroadcastItem(i).interfaceLinkStateChanged(iface, up);
- } catch (RemoteException e) {
- } catch (RuntimeException e) {
+ } catch (RemoteException | RuntimeException e) {
}
}
} finally {
@@ -376,8 +388,7 @@
for (int i = 0; i < length; i++) {
try {
mObservers.getBroadcastItem(i).interfaceAdded(iface);
- } catch (RemoteException e) {
- } catch (RuntimeException e) {
+ } catch (RemoteException | RuntimeException e) {
}
}
} finally {
@@ -399,8 +410,7 @@
for (int i = 0; i < length; i++) {
try {
mObservers.getBroadcastItem(i).interfaceRemoved(iface);
- } catch (RemoteException e) {
- } catch (RuntimeException e) {
+ } catch (RemoteException | RuntimeException e) {
}
}
} finally {
@@ -417,8 +427,7 @@
for (int i = 0; i < length; i++) {
try {
mObservers.getBroadcastItem(i).limitReached(limitName, iface);
- } catch (RemoteException e) {
- } catch (RuntimeException e) {
+ } catch (RemoteException | RuntimeException e) {
}
}
} finally {
@@ -476,8 +485,7 @@
try {
mObservers.getBroadcastItem(i).interfaceClassDataActivityChanged(
Integer.toString(type), isActive, tsNanos);
- } catch (RemoteException e) {
- } catch (RuntimeException e) {
+ } catch (RemoteException | RuntimeException e) {
}
}
} finally {
@@ -520,7 +528,7 @@
Log.wtf(TAG, "problem enabling bandwidth controls", e);
}
} else {
- Slog.d(TAG, "not enabling bandwidth control");
+ Slog.i(TAG, "not enabling bandwidth control");
}
SystemProperties.set(PROP_QTAGUID_ENABLED, mBandwidthControlEnabled ? "1" : "0");
@@ -543,7 +551,7 @@
synchronized (mQuotaLock) {
int size = mActiveQuotas.size();
if (size > 0) {
- Slog.d(TAG, "Pushing " + size + " active quota rules");
+ if (DBG) Slog.d(TAG, "Pushing " + size + " active quota rules");
final HashMap<String, Long> activeQuotas = mActiveQuotas;
mActiveQuotas = Maps.newHashMap();
for (Map.Entry<String, Long> entry : activeQuotas.entrySet()) {
@@ -553,7 +561,7 @@
size = mActiveAlerts.size();
if (size > 0) {
- Slog.d(TAG, "Pushing " + size + " active alert rules");
+ if (DBG) Slog.d(TAG, "Pushing " + size + " active alert rules");
final HashMap<String, Long> activeAlerts = mActiveAlerts;
mActiveAlerts = Maps.newHashMap();
for (Map.Entry<String, Long> entry : activeAlerts.entrySet()) {
@@ -563,7 +571,7 @@
size = mUidRejectOnQuota.size();
if (size > 0) {
- Slog.d(TAG, "Pushing " + size + " active UID rules");
+ if (DBG) Slog.d(TAG, "Pushing " + size + " active UID rules");
final SparseBooleanArray uidRejectOnQuota = mUidRejectOnQuota;
mUidRejectOnQuota = new SparseBooleanArray();
for (int i = 0; i < uidRejectOnQuota.size(); i++) {
@@ -573,7 +581,7 @@
size = mUidCleartextPolicy.size();
if (size > 0) {
- Slog.d(TAG, "Pushing " + size + " active UID cleartext policies");
+ if (DBG) Slog.d(TAG, "Pushing " + size + " active UID cleartext policies");
final SparseIntArray local = mUidCleartextPolicy;
mUidCleartextPolicy = new SparseIntArray();
for (int i = 0; i < local.size(); i++) {
@@ -585,7 +593,7 @@
size = mUidFirewallRules.size();
if (size > 0) {
- Slog.d(TAG, "Pushing " + size + " active firewall UID rules");
+ if (DBG) Slog.d(TAG, "Pushing " + size + " active firewall UID rules");
final SparseIntArray uidFirewallRules = mUidFirewallRules;
mUidFirewallRules = new SparseIntArray();
for (int i = 0; i < uidFirewallRules.size(); i++) {
@@ -596,7 +604,7 @@
size = mUidFirewallStandbyRules.size();
if (size > 0) {
- Slog.d(TAG, "Pushing " + size + " active firewall standby UID rules");
+ if (DBG) Slog.d(TAG, "Pushing " + size + " active firewall standby UID rules");
final SparseIntArray uidFirewallRules = mUidFirewallStandbyRules;
mUidFirewallStandbyRules = new SparseIntArray();
for (int i = 0; i < uidFirewallRules.size(); i++) {
@@ -610,7 +618,7 @@
size = mUidFirewallDozableRules.size();
if (size > 0) {
- Slog.d(TAG, "Pushing " + size + " active firewall dozable UID rules");
+ if (DBG) Slog.d(TAG, "Pushing " + size + " active firewall dozable UID rules");
final SparseIntArray uidFirewallRules = mUidFirewallDozableRules;
mUidFirewallDozableRules = new SparseIntArray();
for (int i = 0; i < uidFirewallRules.size(); i++) {
@@ -621,6 +629,20 @@
if (mFirewallChainStates.get(FIREWALL_CHAIN_DOZABLE)) {
setFirewallChainEnabled(FIREWALL_CHAIN_DOZABLE, true);
}
+
+ size = mUidFirewallPowerSaveRules.size();
+ if (size > 0) {
+ Slog.d(TAG, "Pushing " + size + " active firewall powersave UID rules");
+ final SparseIntArray uidFirewallRules = mUidFirewallPowerSaveRules;
+ mUidFirewallPowerSaveRules = new SparseIntArray();
+ for (int i = 0; i < uidFirewallRules.size(); i++) {
+ setFirewallUidRuleInternal(FIREWALL_CHAIN_POWERSAVE, uidFirewallRules.keyAt(i),
+ uidFirewallRules.valueAt(i));
+ }
+ }
+ if (mFirewallChainStates.get(FIREWALL_CHAIN_POWERSAVE)) {
+ setFirewallChainEnabled(FIREWALL_CHAIN_POWERSAVE, true);
+ }
}
}
@@ -633,8 +655,7 @@
for (int i = 0; i < length; i++) {
try {
mObservers.getBroadcastItem(i).addressUpdated(iface, address);
- } catch (RemoteException e) {
- } catch (RuntimeException e) {
+ } catch (RemoteException | RuntimeException e) {
}
}
} finally {
@@ -651,8 +672,7 @@
for (int i = 0; i < length; i++) {
try {
mObservers.getBroadcastItem(i).addressRemoved(iface, address);
- } catch (RemoteException e) {
- } catch (RuntimeException e) {
+ } catch (RemoteException | RuntimeException e) {
}
}
} finally {
@@ -670,8 +690,7 @@
try {
mObservers.getBroadcastItem(i).interfaceDnsServerInfo(iface, lifetime,
addresses);
- } catch (RemoteException e) {
- } catch (RuntimeException e) {
+ } catch (RemoteException | RuntimeException e) {
}
}
} finally {
@@ -692,8 +711,7 @@
} else {
mObservers.getBroadcastItem(i).routeRemoved(route);
}
- } catch (RemoteException e) {
- } catch (RuntimeException e) {
+ } catch (RemoteException | RuntimeException e) {
}
}
} finally {
@@ -1210,7 +1228,7 @@
// TODO: remove from aidl if nobody calls externally
mContext.enforceCallingOrSelfPermission(SHUTDOWN, TAG);
- Slog.d(TAG, "Shutting down");
+ Slog.i(TAG, "Shutting down");
}
@Override
@@ -2023,6 +2041,9 @@
case FIREWALL_CHAIN_DOZABLE:
chainName = FIREWALL_CHAIN_NAME_DOZABLE;
break;
+ case FIREWALL_CHAIN_POWERSAVE:
+ chainName = FIREWALL_CHAIN_NAME_POWERSAVE;
+ break;
default:
throw new IllegalArgumentException("Bad child chain: " + chain);
}
@@ -2039,6 +2060,8 @@
return FIREWALL_TYPE_BLACKLIST;
case FIREWALL_CHAIN_DOZABLE:
return FIREWALL_TYPE_WHITELIST;
+ case FIREWALL_CHAIN_POWERSAVE:
+ return FIREWALL_TYPE_WHITELIST;
default:
return isFirewallEnabled() ? FIREWALL_TYPE_WHITELIST : FIREWALL_TYPE_BLACKLIST;
}
@@ -2138,6 +2161,8 @@
return mUidFirewallStandbyRules;
case FIREWALL_CHAIN_DOZABLE:
return mUidFirewallDozableRules;
+ case FIREWALL_CHAIN_POWERSAVE:
+ return mUidFirewallPowerSaveRules;
case FIREWALL_CHAIN_NONE:
return mUidFirewallRules;
default:
@@ -2151,6 +2176,8 @@
return FIREWALL_CHAIN_NAME_STANDBY;
case FIREWALL_CHAIN_DOZABLE:
return FIREWALL_CHAIN_NAME_DOZABLE;
+ case FIREWALL_CHAIN_POWERSAVE:
+ return FIREWALL_CHAIN_NAME_POWERSAVE;
case FIREWALL_CHAIN_NONE:
return FIREWALL_CHAIN_NAME_NONE;
default:
@@ -2225,8 +2252,7 @@
for (int i = 0; i < length; i++) {
try {
mNetworkActivityListeners.getBroadcastItem(i).onNetworkActive();
- } catch (RemoteException e) {
- } catch (RuntimeException e) {
+ } catch (RemoteException | RuntimeException e) {
}
}
} finally {
@@ -2271,43 +2297,25 @@
}
synchronized (mUidFirewallRules) {
- pw.print("UID firewall rule: [");
- final int size = mUidFirewallRules.size();
- for (int i = 0; i < size; i++) {
- pw.print(mUidFirewallRules.keyAt(i));
- pw.print(":");
- pw.print(mUidFirewallRules.valueAt(i));
- if (i < size - 1) pw.print(",");
- }
- pw.println("]");
+ dumpUidFirewallRule(pw, "", mUidFirewallRules);
}
pw.println("UID firewall standby chain enabled: " +
mFirewallChainStates.get(FIREWALL_CHAIN_STANDBY));
synchronized (mUidFirewallStandbyRules) {
- pw.print("UID firewall standby rule: [");
- final int size = mUidFirewallStandbyRules.size();
- for (int i = 0; i < size; i++) {
- pw.print(mUidFirewallStandbyRules.keyAt(i));
- pw.print(":");
- pw.print(mUidFirewallStandbyRules.valueAt(i));
- if (i < size - 1) pw.print(",");
- }
- pw.println("]");
+ dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_STANDBY, mUidFirewallStandbyRules);
}
pw.println("UID firewall dozable chain enabled: " +
mFirewallChainStates.get(FIREWALL_CHAIN_DOZABLE));
synchronized (mUidFirewallDozableRules) {
- pw.print("UID firewall dozable rule: [");
- final int size = mUidFirewallDozableRules.size();
- for (int i = 0; i < size; i++) {
- pw.print(mUidFirewallDozableRules.keyAt(i));
- pw.print(":");
- pw.print(mUidFirewallDozableRules.valueAt(i));
- if (i < size - 1) pw.print(",");
- }
- pw.println("]");
+ dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_DOZABLE, mUidFirewallDozableRules);
+ }
+
+ pw.println("UID firewall powersave chain enabled: " +
+ mFirewallChainStates.get(FIREWALL_CHAIN_POWERSAVE));
+ synchronized (mUidFirewallPowerSaveRules) {
+ dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_POWERSAVE, mUidFirewallPowerSaveRules);
}
synchronized (mIdleTimerLock) {
@@ -2324,6 +2332,20 @@
pw.print("Firewall enabled: "); pw.println(mFirewallEnabled);
}
+ private void dumpUidFirewallRule(PrintWriter pw, String name, SparseIntArray rules) {
+ pw.print("UID firewall");
+ pw.print(name);
+ pw.print(" rule: [");
+ final int size = rules.size();
+ for (int i = 0; i < size; i++) {
+ pw.print(rules.keyAt(i));
+ pw.print(":");
+ pw.print(rules.valueAt(i));
+ if (i < size - 1) pw.print(",");
+ }
+ pw.println("]");
+ }
+
@Override
public void createPhysicalNetwork(int netId, String permission) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
diff --git a/services/core/java/com/android/server/NsdService.java b/services/core/java/com/android/server/NsdService.java
index f4c6225..11aef17 100644
--- a/services/core/java/com/android/server/NsdService.java
+++ b/services/core/java/com/android/server/NsdService.java
@@ -58,7 +58,7 @@
private static final String TAG = "NsdService";
private static final String MDNS_TAG = "mDnsConnector";
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
private Context mContext;
private ContentResolver mContentResolver;
diff --git a/services/core/java/com/android/server/RecoverySystemService.java b/services/core/java/com/android/server/RecoverySystemService.java
index d237fe7..d284d07 100644
--- a/services/core/java/com/android/server/RecoverySystemService.java
+++ b/services/core/java/com/android/server/RecoverySystemService.java
@@ -17,6 +17,8 @@
package com.android.server;
import android.content.Context;
+import android.net.LocalSocket;
+import android.net.LocalSocketAddress;
import android.os.IRecoverySystem;
import android.os.IRecoverySystemProgressListener;
import android.os.RecoverySystem;
@@ -26,9 +28,11 @@
import android.system.Os;
import android.util.Slog;
-import java.io.BufferedReader;
+import libcore.io.IoUtils;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
import java.io.File;
-import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
@@ -43,10 +47,10 @@
private static final String TAG = "RecoverySystemService";
private static final boolean DEBUG = false;
- // A pipe file to monitor the uncrypt progress.
- private static final String UNCRYPT_STATUS_FILE = "/cache/recovery/uncrypt_status";
- // Temporary command file to communicate between the system server and uncrypt.
- private static final String COMMAND_FILE = "/cache/recovery/command";
+ // The socket at /dev/socket/uncrypt to communicate with uncrypt.
+ private static final String UNCRYPT_SOCKET = "uncrypt";
+
+ private static final int SOCKET_CONNECTION_MAX_RETRY = 30;
private Context mContext;
@@ -79,60 +83,63 @@
return false;
}
- // Create the status pipe file to communicate with uncrypt.
- new File(UNCRYPT_STATUS_FILE).delete();
- try {
- Os.mkfifo(UNCRYPT_STATUS_FILE, 0600);
- } catch (ErrnoException e) {
- Slog.e(TAG, "ErrnoException when creating named pipe \"" + UNCRYPT_STATUS_FILE +
- "\": " + e.getMessage());
- return false;
- }
-
// Trigger uncrypt via init.
SystemProperties.set("ctl.start", "uncrypt");
- // Read the status from the pipe.
- try (BufferedReader reader = new BufferedReader(new FileReader(UNCRYPT_STATUS_FILE))) {
+ // Connect to the uncrypt service socket.
+ LocalSocket socket = connectService();
+ if (socket == null) {
+ Slog.e(TAG, "Failed to connect to uncrypt socket");
+ return false;
+ }
+
+ // Read the status from the socket.
+ try (DataInputStream dis = new DataInputStream(socket.getInputStream());
+ DataOutputStream dos = new DataOutputStream(socket.getOutputStream())) {
int lastStatus = Integer.MIN_VALUE;
while (true) {
- String str = reader.readLine();
- try {
- int status = Integer.parseInt(str);
+ int status = dis.readInt();
+ // Avoid flooding the log with the same message.
+ if (status == lastStatus && lastStatus != Integer.MIN_VALUE) {
+ continue;
+ }
+ lastStatus = status;
- // Avoid flooding the log with the same message.
- if (status == lastStatus && lastStatus != Integer.MIN_VALUE) {
- continue;
- }
- lastStatus = status;
-
- if (status >= 0 && status <= 100) {
- // Update status
- Slog.i(TAG, "uncrypt read status: " + status);
- if (listener != null) {
- try {
- listener.onProgress(status);
- } catch (RemoteException unused) {
- Slog.w(TAG, "RemoteException when posting progress");
- }
+ if (status >= 0 && status <= 100) {
+ // Update status
+ Slog.i(TAG, "uncrypt read status: " + status);
+ if (listener != null) {
+ try {
+ listener.onProgress(status);
+ } catch (RemoteException unused) {
+ Slog.w(TAG, "RemoteException when posting progress");
}
- if (status == 100) {
- Slog.i(TAG, "uncrypt successfully finished.");
- break;
- }
- } else {
- // Error in /system/bin/uncrypt.
- Slog.e(TAG, "uncrypt failed with status: " + status);
- return false;
}
- } catch (NumberFormatException unused) {
- Slog.e(TAG, "uncrypt invalid status received: " + str);
+ if (status == 100) {
+ Slog.i(TAG, "uncrypt successfully finished.");
+ // Ack receipt of the final status code. uncrypt
+ // waits for the ack so the socket won't be
+ // destroyed before we receive the code.
+ dos.writeInt(0);
+ dos.flush();
+ break;
+ }
+ } else {
+ // Error in /system/bin/uncrypt.
+ Slog.e(TAG, "uncrypt failed with status: " + status);
+ // Ack receipt of the final status code. uncrypt waits
+ // for the ack so the socket won't be destroyed before
+ // we receive the code.
+ dos.writeInt(0);
+ dos.flush();
return false;
}
}
- } catch (IOException unused) {
- Slog.e(TAG, "IOException when reading \"" + UNCRYPT_STATUS_FILE + "\".");
+ } catch (IOException e) {
+ Slog.e(TAG, "IOException when reading status: " + e);
return false;
+ } finally {
+ IoUtils.closeQuietly(socket);
}
return true;
@@ -150,29 +157,35 @@
return setupOrClearBcb(true, command);
}
- private boolean setupOrClearBcb(boolean isSetup, String command) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
-
- if (isSetup) {
- // Set up the command file to be read by uncrypt.
- try (FileWriter commandFile = new FileWriter(COMMAND_FILE)) {
- commandFile.write(command + "\n");
- } catch (IOException e) {
- Slog.e(TAG, "IOException when writing \"" + COMMAND_FILE +
- "\": " + e.getMessage());
- return false;
+ private LocalSocket connectService() {
+ LocalSocket socket = new LocalSocket();
+ boolean done = false;
+ // The uncrypt socket will be created by init upon receiving the
+ // service request. It may not be ready by this point. So we will
+ // keep retrying until success or reaching timeout.
+ for (int retry = 0; retry < SOCKET_CONNECTION_MAX_RETRY; retry++) {
+ try {
+ socket.connect(new LocalSocketAddress(UNCRYPT_SOCKET,
+ LocalSocketAddress.Namespace.RESERVED));
+ done = true;
+ break;
+ } catch (IOException unused) {
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ Slog.w(TAG, "Interrupted: " + e);
+ }
}
}
-
- // Create the status pipe file to communicate with uncrypt.
- new File(UNCRYPT_STATUS_FILE).delete();
- try {
- Os.mkfifo(UNCRYPT_STATUS_FILE, 0600);
- } catch (ErrnoException e) {
- Slog.e(TAG, "ErrnoException when creating named pipe \"" +
- UNCRYPT_STATUS_FILE + "\": " + e.getMessage());
- return false;
+ if (!done) {
+ Slog.e(TAG, "Timed out connecting to uncrypt socket");
+ return null;
}
+ return socket;
+ }
+
+ private boolean setupOrClearBcb(boolean isSetup, String command) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
if (isSetup) {
SystemProperties.set("ctl.start", "setup-bcb");
@@ -180,34 +193,45 @@
SystemProperties.set("ctl.start", "clear-bcb");
}
- // Read the status from the pipe.
- try (BufferedReader reader = new BufferedReader(new FileReader(UNCRYPT_STATUS_FILE))) {
- while (true) {
- String str = reader.readLine();
- try {
- int status = Integer.parseInt(str);
-
- if (status == 100) {
- Slog.i(TAG, "uncrypt " + (isSetup ? "setup" : "clear") +
- " bcb successfully finished.");
- break;
- } else {
- // Error in /system/bin/uncrypt.
- Slog.e(TAG, "uncrypt failed with status: " + status);
- return false;
- }
- } catch (NumberFormatException unused) {
- Slog.e(TAG, "uncrypt invalid status received: " + str);
- return false;
- }
- }
- } catch (IOException unused) {
- Slog.e(TAG, "IOException when reading \"" + UNCRYPT_STATUS_FILE + "\".");
+ // Connect to the uncrypt service socket.
+ LocalSocket socket = connectService();
+ if (socket == null) {
+ Slog.e(TAG, "Failed to connect to uncrypt socket");
return false;
}
- // Delete the command file as we don't need it anymore.
- new File(COMMAND_FILE).delete();
+ try (DataInputStream dis = new DataInputStream(socket.getInputStream());
+ DataOutputStream dos = new DataOutputStream(socket.getOutputStream())) {
+ // Send the BCB commands if it's to setup BCB.
+ if (isSetup) {
+ dos.writeInt(command.length());
+ dos.writeBytes(command);
+ dos.flush();
+ }
+
+ // Read the status from the socket.
+ int status = dis.readInt();
+
+ // Ack receipt of the status code. uncrypt waits for the ack so
+ // the socket won't be destroyed before we receive the code.
+ dos.writeInt(0);
+ dos.flush();
+
+ if (status == 100) {
+ Slog.i(TAG, "uncrypt " + (isSetup ? "setup" : "clear") +
+ " bcb successfully finished.");
+ } else {
+ // Error in /system/bin/uncrypt.
+ Slog.e(TAG, "uncrypt failed with status: " + status);
+ return false;
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "IOException when getting output stream: " + e);
+ return false;
+ } finally {
+ IoUtils.closeQuietly(socket);
+ }
+
return true;
}
}
diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java
index 1c1784e..73d8bdd 100644
--- a/services/core/java/com/android/server/SystemConfig.java
+++ b/services/core/java/com/android/server/SystemConfig.java
@@ -105,9 +105,6 @@
// background while in data-usage save mode, as read from the configuration files.
final ArraySet<String> mAllowInDataUsageSave = new ArraySet<>();
- // These are the app package names that should not allow IME switching.
- final ArraySet<String> mFixedImeApps = new ArraySet<>();
-
// These are the package names of apps which should be in the 'always'
// URL-handling state upon factory reset.
final ArraySet<String> mLinkedApps = new ArraySet<>();
@@ -159,10 +156,6 @@
return mAllowInDataUsageSave;
}
- public ArraySet<String> getFixedImeApps() {
- return mFixedImeApps;
- }
-
public ArraySet<String> getLinkedApps() {
return mLinkedApps;
}
@@ -411,17 +404,6 @@
XmlUtils.skipCurrentTag(parser);
continue;
- } else if ("fixed-ime-app".equals(name) && allowAll) {
- String pkgname = parser.getAttributeValue(null, "package");
- if (pkgname == null) {
- Slog.w(TAG, "<fixed-ime-app> without package in " + permFile + " at "
- + parser.getPositionDescription());
- } else {
- mFixedImeApps.add(pkgname);
- }
- XmlUtils.skipCurrentTag(parser);
- continue;
-
} else if ("app-link".equals(name) && allowAppConfigs) {
String pkgname = parser.getAttributeValue(null, "package");
if (pkgname == null) {
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 8c0ec78..63a0e87 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -319,7 +319,7 @@
+ " (pid=" + Binder.getCallingPid()
+ ") when starting service " + service);
}
- callerFg = callerApp.setSchedGroup != Process.THREAD_GROUP_BG_NONINTERACTIVE;
+ callerFg = callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND;
} else {
callerFg = true;
}
@@ -831,7 +831,7 @@
"BIND_TREAT_LIKE_ACTIVITY");
}
- final boolean callerFg = callerApp.setSchedGroup != Process.THREAD_GROUP_BG_NONINTERACTIVE;
+ final boolean callerFg = callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND;
final boolean isBindExternal = (flags & Context.BIND_EXTERNAL_SERVICE) != 0;
ServiceLookupResult res =
@@ -1138,7 +1138,7 @@
for (int i=b.apps.size()-1; i>=0; i--) {
ProcessRecord client = b.apps.valueAt(i).client;
if (client != null && client.setSchedGroup
- != Process.THREAD_GROUP_BG_NONINTERACTIVE) {
+ != ProcessList.SCHED_GROUP_BACKGROUND) {
inFg = true;
break;
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
index 4f0d4d9..f2bf4f9 100644
--- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
+++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
@@ -46,6 +46,7 @@
// Available log categories in the activity manager package.
static final boolean DEBUG_ADD_REMOVE = DEBUG_ALL_ACTIVITIES || false;
+ static final boolean DEBUG_ANR = false;
static final boolean DEBUG_APP = DEBUG_ALL_ACTIVITIES || false;
static final boolean DEBUG_BACKUP = DEBUG_ALL || false;
static final boolean DEBUG_BROADCAST = 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 565ec82..037ec59 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -101,6 +101,8 @@
import android.app.PendingIntent;
import android.app.ProfilerInfo;
import android.app.admin.DevicePolicyManager;
+import android.app.admin.DevicePolicyManagerInternal;
+import android.app.admin.IDevicePolicyManager;
import android.app.assist.AssistContent;
import android.app.assist.AssistStructure;
import android.app.backup.IBackupManager;
@@ -284,6 +286,7 @@
import static com.android.internal.util.XmlUtils.writeIntAttribute;
import static com.android.internal.util.XmlUtils.writeLongAttribute;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ANR;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKUP;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_BACKGROUND;
@@ -5091,8 +5094,13 @@
int num = firstPids.size();
for (int i = 0; i < num; i++) {
synchronized (observer) {
+ if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for pid "
+ + firstPids.get(i));
+ final long sime = SystemClock.elapsedRealtime();
Process.sendSignal(firstPids.get(i), Process.SIGNAL_QUIT);
- observer.wait(200); // Wait for write-close, give up after 200msec
+ observer.wait(1000); // Wait for write-close, give up after 1 sec
+ if (DEBUG_ANR) Slog.d(TAG, "Done with pid " + firstPids.get(i)
+ + " in " + (SystemClock.elapsedRealtime()-sime) + "ms");
}
}
} catch (InterruptedException e) {
@@ -5105,7 +5113,11 @@
int[] pids = Process.getPidsForCommands(nativeProcs);
if (pids != null) {
for (int pid : pids) {
+ if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for native pid " + pid);
+ final long sime = SystemClock.elapsedRealtime();
Debug.dumpNativeBacktraceToFile(pid, tracesPath);
+ if (DEBUG_ANR) Slog.d(TAG, "Done with native pid " + pid
+ + " in " + (SystemClock.elapsedRealtime()-sime) + "ms");
}
}
}
@@ -5132,13 +5144,20 @@
numProcs++;
try {
synchronized (observer) {
+ if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for extra pid "
+ + stats.pid);
+ final long stime = SystemClock.elapsedRealtime();
Process.sendSignal(stats.pid, Process.SIGNAL_QUIT);
- observer.wait(200); // Wait for write-close, give up after 200msec
+ observer.wait(1000); // Wait for write-close, give up after 1 sec
+ if (DEBUG_ANR) Slog.d(TAG, "Done with extra pid " + stats.pid
+ + " in " + (SystemClock.elapsedRealtime()-stime) + "ms");
}
} catch (InterruptedException e) {
Slog.wtf(TAG, e);
}
-
+ } else if (DEBUG_ANR) {
+ Slog.d(TAG, "Skipping next CPU consuming process, not a java proc: "
+ + stats.pid);
}
}
}
@@ -5244,9 +5263,13 @@
public boolean clearApplicationUserData(final String packageName,
final IPackageDataObserver observer, int userId) {
enforceNotIsolatedCaller("clearApplicationUserData");
- if (packageName != null && packageName.equals(mDeviceOwnerName)) {
- throw new SecurityException("Clearing DeviceOwner data is forbidden.");
+
+ final DevicePolicyManagerInternal dpmi = LocalServices
+ .getService(DevicePolicyManagerInternal.class);
+ if (dpmi != null && dpmi.hasDeviceOwnerOrProfileOwner(packageName, userId)) {
+ throw new SecurityException("Cannot clear data for a device owner or a profile owner");
}
+
int uid = Binder.getCallingUid();
int pid = Binder.getCallingPid();
userId = mUserController.handleIncomingUser(pid, uid, userId, false,
@@ -6212,7 +6235,7 @@
app.makeActive(thread, mProcessStats);
app.curAdj = app.setAdj = ProcessList.INVALID_ADJ;
- app.curSchedGroup = app.setSchedGroup = Process.THREAD_GROUP_DEFAULT;
+ app.curSchedGroup = app.setSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
app.forcingToForeground = null;
updateProcessForegroundLocked(app, false, false);
app.hasShownUi = false;
@@ -6270,9 +6293,10 @@
// If the app is being launched for restore or full backup, set it up specially
boolean isRestrictedBackupMode = false;
if (mBackupTarget != null && mBackupAppName.equals(processName)) {
- isRestrictedBackupMode = (mBackupTarget.backupMode == BackupRecord.RESTORE)
- || (mBackupTarget.backupMode == BackupRecord.RESTORE_FULL)
- || (mBackupTarget.backupMode == BackupRecord.BACKUP_FULL);
+ isRestrictedBackupMode = mBackupTarget.appInfo.uid >= Process.FIRST_APPLICATION_UID
+ && ((mBackupTarget.backupMode == BackupRecord.RESTORE)
+ || (mBackupTarget.backupMode == BackupRecord.RESTORE_FULL)
+ || (mBackupTarget.backupMode == BackupRecord.BACKUP_FULL));
}
notifyPackageUse(app.instrumentationInfo != null
@@ -9203,7 +9227,7 @@
// Kill the running processes.
for (int i = 0; i < procsToKill.size(); i++) {
ProcessRecord pr = procsToKill.get(i);
- if (pr.setSchedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE
+ if (pr.setSchedGroup == ProcessList.SCHED_GROUP_BACKGROUND
&& pr.curReceiver == null) {
pr.kill("remove task", true);
} else {
@@ -9617,6 +9641,20 @@
}
@Override
+ public void resizePinnedStack(Rect pinnedBounds, Rect tempPinnedTaskBounds) {
+ enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
+ "resizePinnedStack()");
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (this) {
+ mStackSupervisor.resizePinnedStackLocked(pinnedBounds, tempPinnedTaskBounds);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
public void positionTaskInStack(int taskId, int stackId, int position) {
enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "positionTaskInStack()");
if (stackId == HOME_STACK_ID) {
@@ -10464,7 +10502,7 @@
cpi.packageName, r.userId)) {
final boolean callerForeground = r != null ? r.setSchedGroup
- != Process.THREAD_GROUP_BG_NONINTERACTIVE : true;
+ != ProcessList.SCHED_GROUP_BACKGROUND : true;
// Show a permission review UI only for starting from a foreground app
if (!callerForeground) {
@@ -10784,12 +10822,13 @@
return;
}
- final long token = Binder.clearCallingIdentity();
- try {
- mAppErrors.appNotResponding(host, null, null, false, "ContentProvider not responding");
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mAppErrors.appNotResponding(host, null, null, false,
+ "ContentProvider not responding");
+ }
+ });
}
public final void installSystemProviders() {
@@ -14780,13 +14819,13 @@
String oomAdj = ProcessList.makeOomAdjString(r.setAdj);
char schedGroup;
switch (r.setSchedGroup) {
- case Process.THREAD_GROUP_BG_NONINTERACTIVE:
+ case ProcessList.SCHED_GROUP_BACKGROUND:
schedGroup = 'B';
break;
- case Process.THREAD_GROUP_DEFAULT:
+ case ProcessList.SCHED_GROUP_DEFAULT:
schedGroup = 'F';
break;
- case Process.THREAD_GROUP_TOP_APP:
+ case ProcessList.SCHED_GROUP_TOP_APP:
schedGroup = 'T';
break;
default:
@@ -17194,10 +17233,11 @@
String ssp;
if (data != null && (ssp=data.getSchemeSpecificPart()) != null) {
boolean removed = Intent.ACTION_PACKAGE_REMOVED.equals(action);
- boolean fullUninstall = removed &&
- !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
+ final boolean replacing =
+ intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
final boolean killProcess =
!intent.getBooleanExtra(Intent.EXTRA_DONT_KILL_APP, false);
+ final boolean fullUninstall = removed && !replacing;
if (killProcess) {
forceStopPackageLocked(ssp, UserHandle.getAppId(
intent.getIntExtra(Intent.EXTRA_UID, -1)),
@@ -17205,7 +17245,10 @@
removed ? "pkg removed" : "pkg changed");
}
if (removed) {
- sendPackageBroadcastLocked(IApplicationThread.PACKAGE_REMOVED,
+ final int cmd = killProcess
+ ? IApplicationThread.PACKAGE_REMOVED
+ : IApplicationThread.PACKAGE_REMOVED_DONT_KILL;
+ sendPackageBroadcastLocked(cmd,
new String[] {ssp}, userId);
if (fullUninstall) {
mAppOpsService.packageRemoved(
@@ -17240,7 +17283,23 @@
break;
}
break;
+ case Intent.ACTION_PACKAGE_REPLACED:
+ {
+ final Uri data = intent.getData();
+ final String ssp;
+ if (data != null && (ssp = data.getSchemeSpecificPart()) != null) {
+ final ApplicationInfo aInfo =
+ getPackageManagerInternalLocked().getApplicationInfo(
+ ssp,
+ userId);
+ mStackSupervisor.updateActivityApplicationInfoLocked(aInfo);
+ sendPackageBroadcastLocked(IApplicationThread.PACKAGE_REPLACED,
+ new String[] {ssp}, userId);
+ }
+ break;
+ }
case Intent.ACTION_PACKAGE_ADDED:
+ {
// Special case for adding a package: by default turn on compatibility mode.
Uri data = intent.getData();
String ssp;
@@ -17258,6 +17317,7 @@
}
}
break;
+ }
case Intent.ACTION_TIMEZONE_CHANGED:
// If this is the time zone changed action, queue up a message that will reset
// the timezone of all currently running processes. This message will get
@@ -18321,7 +18381,7 @@
if (app.thread == null) {
app.adjSeq = mAdjSeq;
- app.curSchedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
+ app.curSchedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
app.curProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
return (app.curAdj=app.curRawAdj=ProcessList.CACHED_APP_MAX_ADJ);
}
@@ -18341,7 +18401,7 @@
app.adjSeq = mAdjSeq;
app.curRawAdj = app.maxAdj;
app.foregroundActivities = false;
- app.curSchedGroup = Process.THREAD_GROUP_DEFAULT;
+ app.curSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
app.curProcState = ActivityManager.PROCESS_STATE_PERSISTENT;
// System processes can do UI, and when they do we want to have
// them trim their memory after the user leaves the UI. To
@@ -18378,14 +18438,14 @@
if (app == TOP_APP) {
// The last app on the list is the foreground app.
adj = ProcessList.FOREGROUND_APP_ADJ;
- schedGroup = Process.THREAD_GROUP_TOP_APP;
+ schedGroup = ProcessList.SCHED_GROUP_TOP_APP;
app.adjType = "top-activity";
foregroundActivities = true;
procState = PROCESS_STATE_CUR_TOP;
} else if (app.instrumentationClass != null) {
// Don't want to kill running instrumentation.
adj = ProcessList.FOREGROUND_APP_ADJ;
- schedGroup = Process.THREAD_GROUP_DEFAULT;
+ schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
app.adjType = "instrumentation";
procState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
} else if ((queue = isReceivingBroadcast(app)) != null) {
@@ -18395,7 +18455,7 @@
// broadcast as reflected by which queue it's active in.
adj = ProcessList.FOREGROUND_APP_ADJ;
schedGroup = (queue == mFgBroadcastQueue)
- ? Process.THREAD_GROUP_DEFAULT : Process.THREAD_GROUP_BG_NONINTERACTIVE;
+ ? ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND;
app.adjType = "broadcast";
procState = ActivityManager.PROCESS_STATE_RECEIVER;
} else if (app.executingServices.size() > 0) {
@@ -18403,13 +18463,13 @@
// counts as being in the foreground.
adj = ProcessList.FOREGROUND_APP_ADJ;
schedGroup = app.execServicesFg ?
- Process.THREAD_GROUP_DEFAULT : Process.THREAD_GROUP_BG_NONINTERACTIVE;
+ ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND;
app.adjType = "exec-service";
procState = ActivityManager.PROCESS_STATE_SERVICE;
//Slog.i(TAG, "EXEC " + (app.execServicesFg ? "FG" : "BG") + ": " + app);
} else {
// As far as we know the process is empty. We may change our mind later.
- schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
+ schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
// At this point we don't actually know the adjustment. Use the cached adj
// value that the caller wants us to.
adj = cachedAdj;
@@ -18438,7 +18498,7 @@
if (procState > PROCESS_STATE_CUR_TOP) {
procState = PROCESS_STATE_CUR_TOP;
}
- schedGroup = Process.THREAD_GROUP_DEFAULT;
+ schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
app.cached = false;
app.empty = false;
foregroundActivities = true;
@@ -18457,7 +18517,7 @@
if (procState > PROCESS_STATE_CUR_TOP) {
procState = PROCESS_STATE_CUR_TOP;
}
- schedGroup = Process.THREAD_GROUP_DEFAULT;
+ schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
app.cached = false;
app.empty = false;
foregroundActivities = true;
@@ -18501,7 +18561,7 @@
procState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
app.cached = false;
app.adjType = "fg-service";
- schedGroup = Process.THREAD_GROUP_DEFAULT;
+ schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
} else if (app.forcingToForeground != null) {
// The user is aware of this app, so make it visible.
adj = ProcessList.PERCEPTIBLE_APP_ADJ;
@@ -18509,7 +18569,7 @@
app.cached = false;
app.adjType = "force-fg";
app.adjSource = app.forcingToForeground;
- schedGroup = Process.THREAD_GROUP_DEFAULT;
+ schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
}
}
@@ -18517,7 +18577,7 @@
if (adj > ProcessList.HEAVY_WEIGHT_APP_ADJ) {
// We don't want to kill the current heavy-weight process.
adj = ProcessList.HEAVY_WEIGHT_APP_ADJ;
- schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
+ schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
app.cached = false;
app.adjType = "heavy";
}
@@ -18531,7 +18591,7 @@
// This process is hosting what we currently consider to be the
// home app, so we don't want to let it go into the background.
adj = ProcessList.HOME_APP_ADJ;
- schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
+ schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
app.cached = false;
app.adjType = "home";
}
@@ -18546,7 +18606,7 @@
// We want to try to keep it around more aggressively, to give
// a good experience around switching between two apps.
adj = ProcessList.PREVIOUS_APP_ADJ;
- schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
+ schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
app.cached = false;
app.adjType = "previous";
}
@@ -18586,7 +18646,7 @@
for (int is = app.services.size()-1;
is >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
- || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE
+ || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
|| procState > ActivityManager.PROCESS_STATE_TOP);
is--) {
ServiceRecord s = app.services.valueAt(is);
@@ -18624,13 +18684,13 @@
}
for (int conni = s.connections.size()-1;
conni >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
- || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE
+ || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
|| procState > ActivityManager.PROCESS_STATE_TOP);
conni--) {
ArrayList<ConnectionRecord> clist = s.connections.valueAt(conni);
for (int i = 0;
i < clist.size() && (adj > ProcessList.FOREGROUND_APP_ADJ
- || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE
+ || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
|| procState > ActivityManager.PROCESS_STATE_TOP);
i++) {
// XXX should compute this based on the max of
@@ -18722,7 +18782,7 @@
if ((cr.flags&Context.BIND_IMPORTANT) != 0) {
schedGroup = client.curSchedGroup;
} else {
- schedGroup = Process.THREAD_GROUP_DEFAULT;
+ schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
}
}
if (clientProcState <= ActivityManager.PROCESS_STATE_TOP) {
@@ -18792,9 +18852,9 @@
adj = ProcessList.FOREGROUND_APP_ADJ;
if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
if ((cr.flags&Context.BIND_IMPORTANT) != 0) {
- schedGroup = Process.THREAD_GROUP_TOP_APP;
+ schedGroup = ProcessList.SCHED_GROUP_TOP_APP;
} else {
- schedGroup = Process.THREAD_GROUP_DEFAULT;
+ schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
}
}
app.cached = false;
@@ -18812,13 +18872,13 @@
for (int provi = app.pubProviders.size()-1;
provi >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
- || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE
+ || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
|| procState > ActivityManager.PROCESS_STATE_TOP);
provi--) {
ContentProviderRecord cpr = app.pubProviders.valueAt(provi);
for (int i = cpr.connections.size()-1;
i >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
- || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE
+ || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
|| procState > ActivityManager.PROCESS_STATE_TOP);
i--) {
ContentProviderConnection conn = cpr.connections.get(i);
@@ -18876,7 +18936,7 @@
procState = clientProcState;
}
if (client.curSchedGroup > schedGroup) {
- schedGroup = Process.THREAD_GROUP_DEFAULT;
+ schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
}
}
// If the provider has external (non-framework) process
@@ -18885,7 +18945,7 @@
if (cpr.hasExternalProcessHandles()) {
if (adj > ProcessList.FOREGROUND_APP_ADJ) {
adj = ProcessList.FOREGROUND_APP_ADJ;
- schedGroup = Process.THREAD_GROUP_DEFAULT;
+ schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
app.cached = false;
app.adjType = "provider";
app.adjTarget = cpr.name;
@@ -18899,7 +18959,7 @@
if (app.lastProviderTime > 0 && (app.lastProviderTime+CONTENT_PROVIDER_RETAIN_TIME) > now) {
if (adj > ProcessList.PREVIOUS_APP_ADJ) {
adj = ProcessList.PREVIOUS_APP_ADJ;
- schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
+ schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
app.cached = false;
app.adjType = "provider";
}
@@ -18978,7 +19038,7 @@
if (adj > app.maxAdj) {
adj = app.maxAdj;
if (app.maxAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) {
- schedGroup = Process.THREAD_GROUP_DEFAULT;
+ schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
}
}
@@ -19394,17 +19454,29 @@
if (app.setSchedGroup != app.curSchedGroup) {
app.setSchedGroup = app.curSchedGroup;
if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ,
- "Setting process group of " + app.processName
+ "Setting sched group of " + app.processName
+ " to " + app.curSchedGroup);
if (app.waitingToKill != null && app.curReceiver == null
- && app.setSchedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE) {
+ && app.setSchedGroup == ProcessList.SCHED_GROUP_BACKGROUND) {
app.kill(app.waitingToKill, true);
success = false;
} else {
+ int processGroup;
+ switch (app.curSchedGroup) {
+ case ProcessList.SCHED_GROUP_BACKGROUND:
+ processGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
+ break;
+ case ProcessList.SCHED_GROUP_TOP_APP:
+ processGroup = Process.THREAD_GROUP_TOP_APP;
+ break;
+ default:
+ processGroup = Process.THREAD_GROUP_DEFAULT;
+ break;
+ }
if (true) {
long oldId = Binder.clearCallingIdentity();
try {
- Process.setProcessGroup(app.pid, app.curSchedGroup);
+ Process.setProcessGroup(app.pid, processGroup);
} catch (Exception e) {
Slog.w(TAG, "Failed setting process group of " + app.pid
+ " to " + app.curSchedGroup);
@@ -19415,7 +19487,7 @@
} else {
if (app.thread != null) {
try {
- app.thread.setSchedulingGroup(app.curSchedGroup);
+ app.thread.setSchedulingGroup(processGroup);
} catch (RemoteException e) {
}
}
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index d5e40cf..e430dad 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -76,6 +76,7 @@
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashSet;
import java.util.Objects;
@@ -252,6 +253,10 @@
pw.print(prefix); pw.print("resDir="); pw.println(appInfo.publicSourceDir);
}
pw.print(prefix); pw.print("dataDir="); pw.println(appInfo.dataDir);
+ if (appInfo.splitSourceDirs != null) {
+ pw.print(prefix); pw.print("splitDir=");
+ pw.println(Arrays.toString(appInfo.splitSourceDirs));
+ }
}
pw.print(prefix); pw.print("stateNotNeeded="); pw.print(stateNotNeeded);
pw.print(" componentSpecified="); pw.print(componentSpecified);
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 769bee4..d1e1d27 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -65,6 +65,8 @@
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.net.Uri;
@@ -184,7 +186,7 @@
* The back history of all previous (and possibly still
* running) activities. It contains #TaskRecord objects.
*/
- private ArrayList<TaskRecord> mTaskHistory = new ArrayList<>();
+ private final ArrayList<TaskRecord> mTaskHistory = new ArrayList<>();
/**
* Used for validating app tokens with window manager.
@@ -839,6 +841,18 @@
}
}
+ void updateActivityApplicationInfoLocked(ApplicationInfo aInfo) {
+ final String packageName = aInfo.packageName;
+ for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+ final List<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
+ for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+ if (packageName.equals(activities.get(activityNdx).packageName)) {
+ activities.get(activityNdx).info.applicationInfo = aInfo;
+ }
+ }
+ }
+ }
+
/**
* @return true if something must be done before going to sleep.
*/
@@ -1101,7 +1115,7 @@
r.stopped = true;
r.state = ActivityState.STOPPED;
- mWindowManager.notifyAppStopped(r.appToken);
+ mWindowManager.notifyAppStopped(r.appToken, true);
if (getVisibleBehindActivity() == r) {
mStackSupervisor.requestVisibleBehindLocked(r, false);
@@ -2233,6 +2247,10 @@
next.app.thread.scheduleNewIntent(next.newIntents, next.appToken);
}
+ // Well the app will no longer be stopped.
+ // Clear app token stopped state in window manager if needed.
+ mWindowManager.notifyAppStopped(next.appToken, false);
+
EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY, next.userId,
System.identityHashCode(next), next.task.taskId, next.shortComponentName);
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index c58cad0..48f31f9 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1700,6 +1700,15 @@
return false;
}
+ void updateActivityApplicationInfoLocked(ApplicationInfo aInfo) {
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
+ for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ stacks.get(stackNdx).updateActivityApplicationInfoLocked(aInfo);
+ }
+ }
+ }
+
TaskRecord finishTopRunningActivityLocked(ProcessRecord app, String reason) {
TaskRecord finishedTask = null;
ActivityStack focusedStack = getFocusedStack();
@@ -2034,6 +2043,25 @@
|| tempOtherTaskInsetBounds != null);
}
+ void resizePinnedStackLocked(Rect pinnedBounds, Rect tempPinnedTaskBounds) {
+ final ActivityStack stack = getStack(PINNED_STACK_ID);
+ if (stack == null) {
+ Slog.w(TAG, "resizePinnedStackLocked: pinned stack not found");
+ return;
+ }
+ Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizePinnedStack");
+ mWindowManager.deferSurfaceLayout();
+ try {
+ ActivityRecord r = stack.topRunningActivityLocked();
+ resizeStackUncheckedLocked(stack, pinnedBounds, tempPinnedTaskBounds,
+ null);
+ ensureConfigurationAndResume(stack, r, false);
+ } finally {
+ mWindowManager.continueSurfaceLayout();
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ }
+ }
+
boolean resizeTaskLocked(TaskRecord task, Rect bounds, int resizeMode, boolean preserveWindow) {
if (!task.isResizeable()) {
Slog.w(TAG, "resizeTask: task " + task + " not resizeable.");
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 055935d..6cd7561 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -61,6 +61,7 @@
import java.util.concurrent.Semaphore;
import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ANR;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.ActivityManagerService.MY_PID;
@@ -806,8 +807,10 @@
if (pid > 0 && pid != app.pid && pid != parentPid && pid != MY_PID) {
if (r.persistent) {
firstPids.add(pid);
+ if (DEBUG_ANR) Slog.i(TAG, "Adding persistent proc: " + r);
} else {
lastPids.put(pid, Boolean.TRUE);
+ if (DEBUG_ANR) Slog.i(TAG, "Adding ANR proc: " + r);
}
}
}
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 37b0af1..45e3a76 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -654,7 +654,7 @@
}
final boolean callerForeground = receiverRecord.callerApp != null
- ? receiverRecord.callerApp.setSchedGroup != Process.THREAD_GROUP_BG_NONINTERACTIVE
+ ? receiverRecord.callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND
: true;
// Show a permission review UI only for explicit broadcast from a foreground app
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index b49370b..f073e5c 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -124,6 +124,13 @@
// Memory pages are 4K.
static final int PAGE_SIZE = 4*1024;
+ // Activity manager's version of Process.THREAD_GROUP_BG_NONINTERACTIVE
+ static final int SCHED_GROUP_BACKGROUND = 0;
+ // Activity manager's version of Process.THREAD_GROUP_DEFAULT
+ static final int SCHED_GROUP_DEFAULT = 1;
+ // Activity manager's version of Process.THREAD_GROUP_TOP_APP
+ static final int SCHED_GROUP_TOP_APP = 2;
+
// The minimum number of cached apps we want to be able to keep around,
// without empty apps being able to push them out of memory.
static final int MIN_CACHED_APPS = 2;
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 5a3639a..f2a9c2c 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -849,7 +849,7 @@
AudioSystem.setForceUse(AudioSystem.FOR_DOCK,
mDockAudioMediaEnabled ?
AudioSystem.FORCE_ANALOG_DOCK : AudioSystem.FORCE_NONE);
- readEncodedSurroundMode(mContentResolver);
+ sendEncodedSurroundMode(mContentResolver);
}
if (mHdmiManager != null) {
synchronized (mHdmiManager) {
@@ -1023,7 +1023,7 @@
AudioSystem.setMasterMono(masterMono);
}
- private void readEncodedSurroundMode(ContentResolver cr)
+ private void sendEncodedSurroundMode(ContentResolver cr)
{
int encodedSurroundMode = Settings.Global.getInt(
cr, Settings.Global.ENCODED_SURROUND_OUTPUT,
@@ -1102,7 +1102,7 @@
updateRingerModeAffectedStreams();
readDockAudioSettings(cr);
- readEncodedSurroundMode(cr);
+ sendEncodedSurroundMode(cr);
}
mMuteAffectedStreams = System.getIntForUser(cr,
@@ -4642,6 +4642,8 @@
private class SettingsObserver extends ContentObserver {
+ private int mEncodedSurroundMode;
+
SettingsObserver() {
super(new Handler());
mContentResolver.registerContentObserver(Settings.System.getUriFor(
@@ -4650,6 +4652,12 @@
Settings.Global.DOCK_AUDIO_MEDIA_ENABLED), false, this);
mContentResolver.registerContentObserver(Settings.System.getUriFor(
Settings.System.MASTER_MONO), false, this);
+
+ mEncodedSurroundMode = Settings.Global.getInt(
+ mContentResolver, Settings.Global.ENCODED_SURROUND_OUTPUT,
+ Settings.Global.ENCODED_SURROUND_OUTPUT_AUTO);
+ mContentResolver.registerContentObserver(Settings.Global.getUriFor(
+ Settings.Global.ENCODED_SURROUND_OUTPUT), false, this);
}
@Override
@@ -4669,7 +4677,33 @@
}
readDockAudioSettings(mContentResolver);
updateMasterMono(mContentResolver);
- readEncodedSurroundMode(mContentResolver);
+ updateEncodedSurroundOutput();
+ }
+ }
+
+ private void updateEncodedSurroundOutput() {
+ int newSurroundMode = Settings.Global.getInt(
+ mContentResolver, Settings.Global.ENCODED_SURROUND_OUTPUT,
+ Settings.Global.ENCODED_SURROUND_OUTPUT_AUTO);
+ // Did it change?
+ if (mEncodedSurroundMode != newSurroundMode) {
+ // Send to AudioPolicyManager
+ sendEncodedSurroundMode(newSurroundMode);
+ synchronized(mConnectedDevices) {
+ // Is HDMI connected?
+ String key = makeDeviceListKey(AudioSystem.DEVICE_OUT_HDMI, "");
+ DeviceListSpec deviceSpec = mConnectedDevices.get(key);
+ if (deviceSpec != null) {
+ // Toggle HDMI to retrigger broadcast with proper formats.
+ setWiredDeviceConnectionState(AudioSystem.DEVICE_OUT_HDMI,
+ AudioSystem.DEVICE_STATE_UNAVAILABLE, "", "",
+ "android"); // disconnect
+ setWiredDeviceConnectionState(AudioSystem.DEVICE_OUT_HDMI,
+ AudioSystem.DEVICE_STATE_AVAILABLE, "", "",
+ "android"); // reconnect
+ }
+ }
+ mEncodedSurroundMode = newSurroundMode;
}
}
}
diff --git a/services/core/java/com/android/server/connectivity/ApfFilter.java b/services/core/java/com/android/server/connectivity/ApfFilter.java
new file mode 100644
index 0000000..25c84e1
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/ApfFilter.java
@@ -0,0 +1,499 @@
+/*
+ * 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.connectivity;
+
+import static android.system.OsConstants.*;
+
+import android.net.NetworkUtils;
+import android.net.apf.ApfGenerator;
+import android.net.apf.ApfGenerator.IllegalInstructionException;
+import android.net.apf.ApfGenerator.Register;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.PacketSocketAddress;
+import android.util.Log;
+import android.util.Pair;
+
+import com.android.internal.util.HexDump;
+import com.android.server.ConnectivityService;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.lang.Thread;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import libcore.io.IoBridge;
+
+/**
+ * For networks that support packet filtering via APF programs, {@code ApfFilter}
+ * listens for IPv6 ICMPv6 router advertisements (RAs) and generates APF programs to
+ * filter out redundant duplicate ones.
+ *
+ * @hide
+ */
+public class ApfFilter {
+ // Thread to listen for RAs.
+ private class ReceiveThread extends Thread {
+ private final byte[] mPacket = new byte[1514];
+ private final FileDescriptor mSocket;
+ private volatile boolean mStopped;
+
+ public ReceiveThread(FileDescriptor socket) {
+ mSocket = socket;
+ }
+
+ public void halt() {
+ mStopped = true;
+ try {
+ // Interrupts the read() call the thread is blocked in.
+ IoBridge.closeAndSignalBlockedThreads(mSocket);
+ } catch (IOException ignored) {}
+ }
+
+ @Override
+ public void run() {
+ log("begin monitoring");
+ while (!mStopped) {
+ try {
+ int length = Os.read(mSocket, mPacket, 0, mPacket.length);
+ processRa(mPacket, length);
+ } catch (IOException|ErrnoException e) {
+ if (!mStopped) {
+ Log.e(TAG, "Read error", e);
+ }
+ }
+ }
+ }
+ }
+
+ private static final String TAG = "ApfFilter";
+
+ private final ConnectivityService mConnectivityService;
+ private final NetworkAgentInfo mNai;
+ private ReceiveThread mReceiveThread;
+ private String mIfaceName;
+ private long mUniqueCounter;
+
+ private ApfFilter(ConnectivityService connectivityService, NetworkAgentInfo nai) {
+ mConnectivityService = connectivityService;
+ mNai = nai;
+ maybeStartFilter();
+ }
+
+ private void log(String s) {
+ Log.d(TAG, "(" + mNai.network.netId + "): " + s);
+ }
+
+ private long getUniqueNumber() {
+ return mUniqueCounter++;
+ }
+
+ /**
+ * Attempt to start listening for RAs and, if RAs are received, generating and installing
+ * filters to ignore useless RAs.
+ */
+ private void maybeStartFilter() {
+ mIfaceName = mNai.linkProperties.getInterfaceName();
+ if (mIfaceName == null) return;
+ FileDescriptor socket;
+ try {
+ socket = Os.socket(AF_PACKET, SOCK_RAW, ETH_P_IPV6);
+ PacketSocketAddress addr = new PacketSocketAddress((short) ETH_P_IPV6,
+ NetworkInterface.getByName(mIfaceName).getIndex());
+ Os.bind(socket, addr);
+ NetworkUtils.attachRaFilter(socket, mNai.networkMisc.apfPacketFormat);
+ } catch(SocketException|ErrnoException e) {
+ Log.e(TAG, "Error filtering raw socket", e);
+ return;
+ }
+ mReceiveThread = new ReceiveThread(socket);
+ mReceiveThread.start();
+ }
+
+ /**
+ * mNai's LinkProperties may have changed, take appropriate action.
+ */
+ public void updateFilter() {
+ // If we're not listening for RAs, try starting.
+ if (mReceiveThread == null) {
+ maybeStartFilter();
+ // If interface name has changed, restart.
+ } else if (!mIfaceName.equals(mNai.linkProperties.getInterfaceName())) {
+ shutdown();
+ maybeStartFilter();
+ }
+ }
+
+ // Returns seconds since Unix Epoch.
+ private static long curTime() {
+ return System.currentTimeMillis() / 1000L;
+ }
+
+ // A class to hold information about an RA.
+ private class Ra {
+ private static final int ETH_HEADER_LEN = 14;
+
+ private static final int IPV6_HEADER_LEN = 40;
+
+ // From RFC4861:
+ private static final int ICMP6_RA_HEADER_LEN = 16;
+ private static final int ICMP6_RA_OPTION_OFFSET =
+ ETH_HEADER_LEN + IPV6_HEADER_LEN + ICMP6_RA_HEADER_LEN;
+ private static final int ICMP6_RA_ROUTER_LIFETIME_OFFSET =
+ ETH_HEADER_LEN + IPV6_HEADER_LEN + 6;
+ private static final int ICMP6_RA_ROUTER_LIFETIME_LEN = 2;
+ // Prefix information option.
+ private static final int ICMP6_PREFIX_OPTION_TYPE = 3;
+ private static final int ICMP6_PREFIX_OPTION_LEN = 32;
+ private static final int ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET = 4;
+ private static final int ICMP6_PREFIX_OPTION_VALID_LIFETIME_LEN = 4;
+ private static final int ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET = 8;
+ private static final int ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_LEN = 4;
+
+ // From RFC6106: Recursive DNS Server option
+ private static final int ICMP6_RDNSS_OPTION_TYPE = 25;
+ // From RFC6106: DNS Search List option
+ private static final int ICMP6_DNSSL_OPTION_TYPE = 31;
+
+ // From RFC4191: Route Information option
+ private static final int ICMP6_ROUTE_INFO_OPTION_TYPE = 24;
+ // Above three options all have the same format:
+ private static final int ICMP6_4_BYTE_LIFETIME_OFFSET = 4;
+ private static final int ICMP6_4_BYTE_LIFETIME_LEN = 4;
+
+ private final ByteBuffer mPacket;
+ // List of binary ranges that include the whole packet except the lifetimes.
+ // Pairs consist of offset and length.
+ private final ArrayList<Pair<Integer, Integer>> mNonLifetimes =
+ new ArrayList<Pair<Integer, Integer>>();
+ // Minimum lifetime in packet
+ long mMinLifetime;
+ // When the packet was last captured, in seconds since Unix Epoch
+ long mLastSeen;
+
+ /**
+ * Add a binary range of the packet that does not include a lifetime to mNonLifetimes.
+ * Assumes mPacket.position() is as far as we've parsed the packet.
+ * @param lastNonLifetimeStart offset within packet of where the last binary range of
+ * data not including a lifetime.
+ * @param lifetimeOffset offset from mPacket.position() to the next lifetime data.
+ * @param lifetimeLength length of the next lifetime data.
+ * @return offset within packet of where the next binary range of data not including
+ * a lifetime. This can be passed into the next invocation of this function
+ * via {@code lastNonLifetimeStart}.
+ */
+ private int addNonLifetime(int lastNonLifetimeStart, int lifetimeOffset,
+ int lifetimeLength) {
+ lifetimeOffset += mPacket.position();
+ mNonLifetimes.add(new Pair<Integer, Integer>(lastNonLifetimeStart,
+ lifetimeOffset - lastNonLifetimeStart));
+ return lifetimeOffset + lifetimeLength;
+ }
+
+ // Note that this parses RA and may throw IllegalArgumentException (from
+ // Buffer.position(int) ) or IndexOutOfBoundsException (from ByteBuffer.get(int) ) if
+ // parsing encounters something non-compliant with specifications.
+ Ra(byte[] packet, int length) {
+ mPacket = ByteBuffer.allocate(length).put(ByteBuffer.wrap(packet, 0, length));
+ mPacket.clear();
+ mLastSeen = curTime();
+
+ // Parse router lifetime
+ int lastNonLifetimeStart = addNonLifetime(0, ICMP6_RA_ROUTER_LIFETIME_OFFSET,
+ ICMP6_RA_ROUTER_LIFETIME_LEN);
+ // Parse ICMP6 options
+ mPacket.position(ICMP6_RA_OPTION_OFFSET);
+ while (mPacket.hasRemaining()) {
+ int optionType = ((int)mPacket.get(mPacket.position())) & 0xff;
+ int optionLength = (((int)mPacket.get(mPacket.position() + 1)) & 0xff) * 8;
+ switch (optionType) {
+ case ICMP6_PREFIX_OPTION_TYPE:
+ // Parse valid lifetime
+ lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart,
+ ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET,
+ ICMP6_PREFIX_OPTION_VALID_LIFETIME_LEN);
+ // Parse preferred lifetime
+ lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart,
+ ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET,
+ ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_LEN);
+ break;
+ // These three options have the same lifetime offset and size, so process
+ // together:
+ case ICMP6_ROUTE_INFO_OPTION_TYPE:
+ case ICMP6_RDNSS_OPTION_TYPE:
+ case ICMP6_DNSSL_OPTION_TYPE:
+ // Parse lifetime
+ lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart,
+ ICMP6_4_BYTE_LIFETIME_OFFSET,
+ ICMP6_4_BYTE_LIFETIME_LEN);
+ break;
+ default:
+ // RFC4861 section 4.2 dictates we ignore unknown options for fowards
+ // compatibility.
+ break;
+ }
+ mPacket.position(mPacket.position() + optionLength);
+ }
+ // Mark non-lifetime bytes since last lifetime.
+ addNonLifetime(lastNonLifetimeStart, 0, 0);
+ mMinLifetime = minLifetime(packet, length);
+ }
+
+ // Ignoring lifetimes (which may change) does {@code packet} match this RA?
+ boolean matches(byte[] packet, int length) {
+ if (length != mPacket.limit()) return false;
+ ByteBuffer a = ByteBuffer.wrap(packet);
+ ByteBuffer b = mPacket;
+ for (Pair<Integer, Integer> nonLifetime : mNonLifetimes) {
+ a.clear();
+ b.clear();
+ a.position(nonLifetime.first);
+ b.position(nonLifetime.first);
+ a.limit(nonLifetime.first + nonLifetime.second);
+ b.limit(nonLifetime.first + nonLifetime.second);
+ if (a.compareTo(b) != 0) return false;
+ }
+ return true;
+ }
+
+ // What is the minimum of all lifetimes within {@code packet} in seconds?
+ // Precondition: matches(packet, length) already returned true.
+ long minLifetime(byte[] packet, int length) {
+ long minLifetime = Long.MAX_VALUE;
+ // Wrap packet in ByteBuffer so we can read big-endian values easily
+ ByteBuffer byteBuffer = ByteBuffer.wrap(packet);
+ for (int i = 0; (i + 1) < mNonLifetimes.size(); i++) {
+ int offset = mNonLifetimes.get(i).first + mNonLifetimes.get(i).second;
+ int lifetimeLength = mNonLifetimes.get(i+1).first - offset;
+ long val;
+ switch (lifetimeLength) {
+ case 2: val = byteBuffer.getShort(offset); break;
+ case 4: val = byteBuffer.getInt(offset); break;
+ default: throw new IllegalStateException("bogus lifetime size " + length);
+ }
+ // Mask to size, converting signed to unsigned
+ val &= (1L << (lifetimeLength * 8)) - 1;
+ minLifetime = Math.min(minLifetime, val);
+ }
+ return minLifetime;
+ }
+
+ // How many seconds does this RA's have to live, taking into account the fact
+ // that we might have seen it a while ago.
+ long currentLifetime() {
+ return mMinLifetime - (curTime() - mLastSeen);
+ }
+
+ boolean isExpired() {
+ return currentLifetime() < 0;
+ }
+
+ // Append a filter for this RA to {@code gen}. Jump to DROP_LABEL if it should be dropped.
+ // Jump to the next filter if packet doesn't match this RA.
+ long generateFilter(ApfGenerator gen) throws IllegalInstructionException {
+ String nextFilterLabel = "Ra" + getUniqueNumber();
+ // Skip if packet is not the right size
+ gen.addLoadFromMemory(Register.R0, gen.PACKET_SIZE_MEMORY_SLOT);
+ gen.addJumpIfR0NotEquals(mPacket.limit(), nextFilterLabel);
+ int filterLifetime = (int)(currentLifetime() / FRACTION_OF_LIFETIME_TO_FILTER);
+ // Skip filter if expired
+ gen.addLoadFromMemory(Register.R0, gen.FILTER_AGE_MEMORY_SLOT);
+ gen.addJumpIfR0GreaterThan(filterLifetime, nextFilterLabel);
+ for (int i = 0; i < mNonLifetimes.size(); i++) {
+ // Generate code to match the packet bytes
+ Pair<Integer, Integer> nonLifetime = mNonLifetimes.get(i);
+ gen.addLoadImmediate(Register.R0, nonLifetime.first);
+ gen.addJumpIfBytesNotEqual(Register.R0,
+ Arrays.copyOfRange(mPacket.array(), nonLifetime.first,
+ nonLifetime.first + nonLifetime.second),
+ nextFilterLabel);
+ // Generate code to test the lifetimes haven't gone down too far
+ if ((i + 1) < mNonLifetimes.size()) {
+ Pair<Integer, Integer> nextNonLifetime = mNonLifetimes.get(i + 1);
+ int offset = nonLifetime.first + nonLifetime.second;
+ int length = nextNonLifetime.first - offset;
+ switch (length) {
+ case 4: gen.addLoad32(Register.R0, offset); break;
+ case 2: gen.addLoad16(Register.R0, offset); break;
+ default: throw new IllegalStateException("bogus lifetime size " + length);
+ }
+ gen.addJumpIfR0LessThan(filterLifetime, nextFilterLabel);
+ }
+ }
+ gen.addJump(gen.DROP_LABEL);
+ gen.defineLabel(nextFilterLabel);
+ return filterLifetime;
+ }
+ }
+
+ // Maximum number of RAs to filter for.
+ private static final int MAX_RAS = 10;
+ private ArrayList<Ra> mRas = new ArrayList<Ra>();
+
+ // There is always some marginal benefit to updating the installed APF program when an RA is
+ // seen because we can extend the program's lifetime slightly, but there is some cost to
+ // updating the program, so don't bother unless the program is going to expire soon. This
+ // constant defines "soon" in seconds.
+ private static final long MAX_PROGRAM_LIFETIME_WORTH_REFRESHING = 30;
+ // We don't want to filter an RA for it's whole lifetime as it'll be expired by the time we ever
+ // see a refresh. Using half the lifetime might be a good idea except for the fact that
+ // packets may be dropped, so let's use 6.
+ private static final int FRACTION_OF_LIFETIME_TO_FILTER = 6;
+
+ // When did we last install a filter program? In seconds since Unix Epoch.
+ private long mLastTimeInstalledProgram;
+ // How long should the last installed filter program live for? In seconds.
+ private long mLastInstalledProgramMinLifetime;
+
+ private void installNewProgram() {
+ if (mRas.size() == 0) return;
+ final byte[] program;
+ long programMinLifetime = Long.MAX_VALUE;
+ try {
+ ApfGenerator gen = new ApfGenerator();
+ // This is guaranteed to return true because of the check in maybeInstall.
+ gen.setApfVersion(mNai.networkMisc.apfVersionSupported);
+ // Step 1: Determine how many RA filters we can fit in the program.
+ int ras = 0;
+ for (Ra ra : mRas) {
+ if (ra.isExpired()) continue;
+ ra.generateFilter(gen);
+ if (gen.programLengthOverEstimate() > mNai.networkMisc.maximumApfProgramSize) {
+ // We went too far. Use prior number of RAs in "ras".
+ break;
+ } else {
+ // Yay! this RA filter fits, increment "ras".
+ ras++;
+ }
+ }
+ // Step 2: Generate RA filters
+ gen = new ApfGenerator();
+ // This is guaranteed to return true because of the check in maybeInstall.
+ gen.setApfVersion(mNai.networkMisc.apfVersionSupported);
+ for (Ra ra : mRas) {
+ if (ras-- == 0) break;
+ if (ra.isExpired()) continue;
+ programMinLifetime = Math.min(programMinLifetime, ra.generateFilter(gen));
+ }
+ // Execution will reach the end of the program if no filters match, which will pass the
+ // packet to the AP.
+ program = gen.generate();
+ } catch (IllegalInstructionException e) {
+ Log.e(TAG, "Program failed to generate: ", e);
+ return;
+ }
+ mLastTimeInstalledProgram = curTime();
+ mLastInstalledProgramMinLifetime = programMinLifetime;
+ hexDump("Installing filter: ", program, program.length);
+ mConnectivityService.pushApfProgramToNetwork(mNai, program);
+ }
+
+ // Install a new filter program if the last installed one will die soon.
+ private void maybeInstallNewProgram() {
+ if (mRas.size() == 0) return;
+ // If the current program doesn't expire for a while, don't bother updating.
+ long expiry = mLastTimeInstalledProgram + mLastInstalledProgramMinLifetime;
+ if (expiry < curTime() + MAX_PROGRAM_LIFETIME_WORTH_REFRESHING) {
+ installNewProgram();
+ }
+ }
+
+ private void hexDump(String msg, byte[] packet, int length) {
+ log(msg + HexDump.toHexString(packet, 0, length));
+ }
+
+ private void processRa(byte[] packet, int length) {
+ hexDump("Read packet = ", packet, length);
+
+ // Have we seen this RA before?
+ for (int i = 0; i < mRas.size(); i++) {
+ Ra ra = mRas.get(i);
+ if (ra.matches(packet, length)) {
+ log("matched RA");
+ // Update lifetimes.
+ ra.mLastSeen = curTime();
+ ra.mMinLifetime = ra.minLifetime(packet, length);
+
+ // Keep mRas in LRU order so as to prioritize generating filters for recently seen
+ // RAs. LRU prioritizes this because RA filters are generated in order from mRas
+ // until the filter program exceeds the maximum filter program size allowed by the
+ // chipset, so RAs appearing earlier in mRas are more likely to make it into the
+ // filter program.
+ // TODO: consider sorting the RAs in order of increasing expiry time as well.
+ // Swap to front of array.
+ mRas.add(0, mRas.remove(i));
+
+ maybeInstallNewProgram();
+ return;
+ }
+ }
+ // Purge expired RAs.
+ for (int i = 0; i < mRas.size();) {
+ if (mRas.get(i).isExpired()) {
+ log("expired RA");
+ mRas.remove(i);
+ } else {
+ i++;
+ }
+ }
+ // TODO: figure out how to proceed when we've received more then MAX_RAS RAs.
+ if (mRas.size() >= MAX_RAS) return;
+ try {
+ log("adding RA");
+ mRas.add(new Ra(packet, length));
+ } catch (Exception e) {
+ Log.e(TAG, "Error parsing RA: " + e);
+ return;
+ }
+ installNewProgram();
+ }
+
+ /**
+ * Install an {@link ApfFilter} on {@code nai} if {@code nai} supports packet
+ * filtering using APF programs.
+ */
+ public static void maybeInstall(ConnectivityService connectivityService, NetworkAgentInfo nai) {
+ if (nai.networkMisc == null) return;
+ if (nai.networkMisc.apfVersionSupported == 0) return;
+ if (nai.networkMisc.maximumApfProgramSize < 200) {
+ Log.e(TAG, "Uselessly small APF size limit: " + nai.networkMisc.maximumApfProgramSize);
+ return;
+ }
+ // For now only support generating programs for Ethernet frames. If this restriction is
+ // lifted:
+ // 1. the program generator will need its offsets adjusted.
+ // 2. the packet filter attached to our packet socket will need its offset adjusted.
+ if (nai.networkMisc.apfPacketFormat != ARPHRD_ETHER) return;
+ if (!new ApfGenerator().setApfVersion(nai.networkMisc.apfVersionSupported)) {
+ Log.e(TAG, "Unsupported APF version: " + nai.networkMisc.apfVersionSupported);
+ return;
+ }
+ nai.apfFilter = new ApfFilter(connectivityService, nai);
+ }
+
+ public void shutdown() {
+ if (mReceiveThread != null) {
+ log("shuting down");
+ mReceiveThread.halt(); // Also closes socket.
+ mReceiveThread = null;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
index 90c9ddf..9e1f6b8 100644
--- a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
+++ b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
@@ -60,7 +60,7 @@
public class KeepaliveTracker {
private static final String TAG = "KeepaliveTracker";
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
public static final String PERMISSION = android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD;
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index c5d38cb..b4c71c1 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -32,6 +32,7 @@
import com.android.internal.util.AsyncChannel;
import com.android.server.ConnectivityService;
import com.android.server.connectivity.NetworkMonitor;
+import com.android.server.connectivity.ApfFilter;
import java.util.ArrayList;
import java.util.Comparator;
@@ -163,6 +164,8 @@
// Used by ConnectivityService to keep track of 464xlat.
public Nat464Xlat clatd;
+ public ApfFilter apfFilter;
+
public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, Network net, NetworkInfo info,
LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler,
NetworkMisc misc, NetworkRequest defaultRequest, ConnectivityService connService) {
@@ -175,6 +178,7 @@
currentScore = score;
networkMonitor = connService.createNetworkMonitor(context, handler, this, defaultRequest);
networkMisc = misc;
+ apfFilter.maybeInstall(connService, this);
}
/**
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index fb8b110..73da427 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -76,7 +76,7 @@
* {@hide}
*/
public class NetworkMonitor extends StateMachine {
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
private static final String TAG = "NetworkMonitor";
private static final String DEFAULT_SERVER = "connectivitycheck.gstatic.com";
private static final int SOCKET_TIMEOUT_MS = 10000;
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index debda14..22cefd1 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -54,7 +54,7 @@
*/
public class PermissionMonitor {
private static final String TAG = "PermissionMonitor";
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
private static final boolean SYSTEM = true;
private static final boolean NETWORK = false;
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 760b218..4eecc81 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -90,7 +90,7 @@
private Context mContext;
private final static String TAG = "Tethering";
- private final static boolean DBG = true;
+ private final static boolean DBG = false;
private final static boolean VDBG = false;
// TODO - remove both of these - should be part of interface inspection/selection stuff
diff --git a/services/core/java/com/android/server/net/IpConfigStore.java b/services/core/java/com/android/server/net/IpConfigStore.java
index 9f1435a..2807ec8 100644
--- a/services/core/java/com/android/server/net/IpConfigStore.java
+++ b/services/core/java/com/android/server/net/IpConfigStore.java
@@ -40,7 +40,7 @@
public class IpConfigStore {
private static final String TAG = "IpConfigStore";
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
protected final DelayedDiskWrite mWriter;
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 09b7a18..3acd2ca 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -42,6 +42,7 @@
import static android.net.NetworkPolicy.WARNING_DISABLED;
import static android.net.NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE;
+import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_POWERSAVE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_STANDBY;
import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW;
import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
@@ -68,6 +69,7 @@
import static android.net.wifi.WifiManager.EXTRA_WIFI_CONFIGURATION;
import static android.net.wifi.WifiManager.EXTRA_WIFI_INFO;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
+
import static com.android.internal.util.ArrayUtils.appendInt;
import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.internal.util.XmlUtils.readBooleanAttribute;
@@ -294,6 +296,7 @@
final SparseIntArray mUidFirewallStandbyRules = new SparseIntArray();
final SparseIntArray mUidFirewallDozableRules = new SparseIntArray();
+ final SparseIntArray mUidFirewallPowerSaveRules = new SparseIntArray();
/** Set of states for the child firewall chains. True if the chain is active. */
final SparseBooleanArray mFirewallChainStates = new SparseBooleanArray();
@@ -522,9 +525,11 @@
new PowerManagerInternal.LowPowerModeListener() {
@Override
public void onLowPowerModeChanged(boolean enabled) {
+ if (LOGD) Slog.d(TAG, "onLowPowerModeChanged(" + enabled + ")");
synchronized (mRulesLock) {
if (mRestrictPower != enabled) {
mRestrictPower = enabled;
+ updateRulesForRestrictPowerLocked();
updateRulesForGlobalChangeLocked(true);
}
}
@@ -1175,13 +1180,6 @@
return;
}
- // If we are in restrict power mode, we want to treat all interfaces
- // as metered, to restrict access to the network by uid. However, we
- // will not have a bandwidth limit. Also only do this if restrict
- // background data use is *not* enabled, since that takes precedence
- // use over those networks can have a cost associated with it).
- final boolean powerSave = mRestrictPower && !mRestrictBackground;
-
// First, generate identities of all connected networks so we can
// quickly compare them against all defined policies below.
final ArrayList<Pair<String, NetworkIdentity>> connIdents = new ArrayList<>(states.length);
@@ -1193,9 +1191,6 @@
final String baseIface = state.linkProperties.getInterfaceName();
if (baseIface != null) {
connIdents.add(Pair.create(baseIface, ident));
- if (powerSave) {
- connIfaces.add(baseIface);
- }
}
// Stacked interfaces are considered to have same identity as
@@ -1205,9 +1200,6 @@
final String stackedIface = stackedLink.getInterfaceName();
if (stackedIface != null) {
connIdents.add(Pair.create(stackedIface, ident));
- if (powerSave) {
- connIfaces.add(stackedIface);
- }
}
}
}
@@ -1254,8 +1246,7 @@
}
if (LOGD) {
- Slog.d(TAG, "applying policy " + policy.toString() + " to ifaces "
- + Arrays.toString(ifaces));
+ Slog.d(TAG, "applying policy " + policy + " to ifaces " + Arrays.toString(ifaces));
}
final boolean hasWarning = policy.warningBytes != LIMIT_DISABLED;
@@ -1286,9 +1277,6 @@
removeInterfaceQuota(iface);
setInterfaceQuota(iface, quotaBytes);
newMeteredIfaces.add(iface);
- if (powerSave) {
- connIfaces.remove(iface);
- }
}
}
@@ -1631,7 +1619,7 @@
try {
final int oldPolicy = mUidPolicy.get(uid, POLICY_NONE);
if (oldPolicy != policy) {
- setUidPolicyUncheckedLocked(uid, policy, true);
+ setUidPolicyUncheckedLocked(uid, oldPolicy, policy, true);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -1651,7 +1639,7 @@
final int oldPolicy = mUidPolicy.get(uid, POLICY_NONE);
policy |= oldPolicy;
if (oldPolicy != policy) {
- setUidPolicyUncheckedLocked(uid, policy, true);
+ setUidPolicyUncheckedLocked(uid, oldPolicy, policy, true);
}
}
}
@@ -1668,11 +1656,22 @@
final int oldPolicy = mUidPolicy.get(uid, POLICY_NONE);
policy = oldPolicy & ~policy;
if (oldPolicy != policy) {
- setUidPolicyUncheckedLocked(uid, policy, true);
+ setUidPolicyUncheckedLocked(uid, oldPolicy, policy, true);
}
}
}
+ private void setUidPolicyUncheckedLocked(int uid, int oldPolicy, int policy, boolean persist) {
+ setUidPolicyUncheckedLocked(uid, policy, persist);
+
+ // Checks if app was added or removed to the blacklist.
+ if ((oldPolicy == POLICY_NONE && policy == POLICY_REJECT_METERED_BACKGROUND)
+ || (oldPolicy == POLICY_REJECT_METERED_BACKGROUND && policy == POLICY_NONE)) {
+ mHandler.obtainMessage(MSG_RESTRICT_BACKGROUND_WHITELIST_CHANGED, uid, 0)
+ .sendToTarget();
+ }
+ }
+
private void setUidPolicyUncheckedLocked(int uid, int policy, boolean persist) {
mUidPolicy.put(uid, policy);
@@ -2000,7 +1999,20 @@
public int getRestrictBackgroundByCaller() {
mContext.enforceCallingOrSelfPermission(ACCESS_NETWORK_STATE, TAG);
final int uid = Binder.getCallingUid();
+
synchronized (mRulesLock) {
+ // Must clear identity because getUidPolicy() is restricted to system.
+ final long token = Binder.clearCallingIdentity();
+ final int policy;
+ try {
+ policy = getUidPolicy(uid);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ if (policy == POLICY_REJECT_METERED_BACKGROUND) {
+ // App is blacklisted.
+ return RESTRICT_BACKGROUND_STATUS_ENABLED;
+ }
if (!mRestrictBackground) {
return RESTRICT_BACKGROUND_STATUS_DISABLED;
}
@@ -2299,9 +2311,14 @@
// state changed, push updated rules
mUidState.put(uid, uidState);
updateRulesForUidStateChangeLocked(uid, oldUidState, uidState);
- if (mDeviceIdleMode && isProcStateAllowedWhileIdle(oldUidState)
- != isProcStateAllowedWhileIdle(uidState)) {
- updateRuleForDeviceIdleLocked(uid);
+ if (isProcStateAllowedWhileIdleOrPowerSaveMode(oldUidState)
+ != isProcStateAllowedWhileIdleOrPowerSaveMode(uidState) ) {
+ if (mDeviceIdleMode) {
+ updateRuleForDeviceIdleLocked(uid);
+ }
+ if (mRestrictPower) {
+ updateRulesForRestrictPowerLocked(uid);
+ }
}
}
}
@@ -2317,6 +2334,9 @@
if (mDeviceIdleMode) {
updateRuleForDeviceIdleLocked(uid);
}
+ if (mRestrictPower) {
+ updateRulesForRestrictPowerLocked(uid);
+ }
}
}
}
@@ -2354,15 +2374,36 @@
}
}
- static boolean isProcStateAllowedWhileIdle(int procState) {
+ static boolean isProcStateAllowedWhileIdleOrPowerSaveMode(int procState) {
return procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
}
+ void updateRulesForRestrictPowerLocked() {
+ updateRulesForWhitelistedPowerSaveLocked(mRestrictPower, FIREWALL_CHAIN_POWERSAVE,
+ mUidFirewallPowerSaveRules);
+ }
+
+ void updateRulesForRestrictPowerLocked(int uid) {
+ updateRulesForWhitelistedPowerSaveLocked(uid, mRestrictPower, FIREWALL_CHAIN_POWERSAVE);
+ }
+
void updateRulesForDeviceIdleLocked() {
- if (mDeviceIdleMode) {
- // sync the whitelists before enable dozable chain. We don't care about the rules if
+ updateRulesForWhitelistedPowerSaveLocked(mDeviceIdleMode, FIREWALL_CHAIN_DOZABLE,
+ mUidFirewallDozableRules);
+ }
+
+ void updateRuleForDeviceIdleLocked(int uid) {
+ updateRulesForWhitelistedPowerSaveLocked(uid, mDeviceIdleMode, FIREWALL_CHAIN_DOZABLE);
+ }
+
+ // NOTE: since both fw_dozable and fw_powersave uses the same map (mPowerSaveTempWhitelistAppIds)
+ // for whitelisting, we can reuse their logic in this method.
+ private void updateRulesForWhitelistedPowerSaveLocked(boolean enabled, int chain,
+ SparseIntArray rules) {
+ if (enabled) {
+ // Sync the whitelists before enabling the chain. We don't care about the rules if
// we are disabling the chain.
- final SparseIntArray uidRules = mUidFirewallDozableRules;
+ final SparseIntArray uidRules = rules;
uidRules.clear();
final List<UserInfo> users = mUserManager.getUsers();
for (int ui = users.size() - 1; ui >= 0; ui--) {
@@ -2381,24 +2422,26 @@
}
}
for (int i = mUidState.size() - 1; i >= 0; i--) {
- if (isProcStateAllowedWhileIdle(mUidState.valueAt(i))) {
+ if (isProcStateAllowedWhileIdleOrPowerSaveMode(mUidState.valueAt(i))) {
uidRules.put(mUidState.keyAt(i), FIREWALL_RULE_ALLOW);
}
}
- setUidFirewallRules(FIREWALL_CHAIN_DOZABLE, uidRules);
+ setUidFirewallRules(chain, uidRules);
}
- enableFirewallChainLocked(FIREWALL_CHAIN_DOZABLE, mDeviceIdleMode);
+ enableFirewallChainLocked(chain, enabled);
}
- void updateRuleForDeviceIdleLocked(int uid) {
- if (mDeviceIdleMode) {
+ // NOTE: since both fw_dozable and fw_powersave uses the same map (mPowerSaveTempWhitelistAppIds)
+ // for whitelisting, we can reuse their logic in this method.
+ private void updateRulesForWhitelistedPowerSaveLocked(int uid, boolean enabled, int chain) {
+ if (enabled) {
int appId = UserHandle.getAppId(uid);
if (mPowerSaveTempWhitelistAppIds.get(appId) || mPowerSaveWhitelistAppIds.get(appId)
- || isProcStateAllowedWhileIdle(mUidState.get(uid))) {
- setUidFirewallRule(FIREWALL_CHAIN_DOZABLE, uid, FIREWALL_RULE_ALLOW);
+ || isProcStateAllowedWhileIdleOrPowerSaveMode(mUidState.get(uid))) {
+ setUidFirewallRule(chain, uid, FIREWALL_RULE_ALLOW);
} else {
- setUidFirewallRule(FIREWALL_CHAIN_DOZABLE, uid, FIREWALL_RULE_DEFAULT);
+ setUidFirewallRule(chain, uid, FIREWALL_RULE_DEFAULT);
}
}
@@ -2454,10 +2497,14 @@
* {@link #mRestrictPower}, or {@link #mDeviceIdleMode} value.
*/
void updateRulesForGlobalChangeLocked(boolean restrictedNetworksChanged) {
+ long start;
+ if (LOGD) start = System.currentTimeMillis();
+
final PackageManager pm = mContext.getPackageManager();
updateRulesForDeviceIdleLocked();
updateRulesForAppIdleLocked();
+ updateRulesForRestrictPowerLocked();
// update rules for all installed applications
final List<UserInfo> users = mUserManager.getUsers();
@@ -2465,8 +2512,12 @@
PackageManager.GET_UNINSTALLED_PACKAGES | PackageManager.GET_DISABLED_COMPONENTS
| PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE);
- for (UserInfo user : users) {
- for (ApplicationInfo app : apps) {
+ final int usersSize = users.size();
+ final int appsSize = apps.size();
+ for (int i = 0; i < usersSize; i++) {
+ final UserInfo user = users.get(i);
+ for (int j = 0; j < appsSize; j++) {
+ final ApplicationInfo app = apps.get(j);
final int uid = UserHandle.getUid(user.id, app.uid);
updateRulesForUidLocked(uid);
}
@@ -2481,16 +2532,23 @@
normalizePoliciesLocked();
updateNetworkRulesLocked();
}
+ if (LOGD) {
+ final long delta = System.currentTimeMillis() - start;
+ Slog.d(TAG, "updateRulesForGlobalChangeLocked(" + restrictedNetworksChanged + ") took "
+ + delta + "ms");
+ }
}
void updateRulesForTempWhitelistChangeLocked() {
final List<UserInfo> users = mUserManager.getUsers();
- for (UserInfo user : users) {
- for (int i = mPowerSaveTempWhitelistAppIds.size() - 1; i >= 0; i--) {
- int appId = mPowerSaveTempWhitelistAppIds.keyAt(i);
+ for (int i = 0; i < users.size(); i++) {
+ final UserInfo user = users.get(i);
+ for (int j = mPowerSaveTempWhitelistAppIds.size() - 1; j >= 0; j--) {
+ int appId = mPowerSaveTempWhitelistAppIds.keyAt(j);
int uid = UserHandle.getUid(user.id, appId);
updateRuleForAppIdleLocked(uid);
updateRuleForDeviceIdleLocked(uid);
+ updateRulesForRestrictPowerLocked(uid);
}
}
}
@@ -2583,6 +2641,12 @@
uidRules = RULE_REJECT_ALL;
}
+ // Check powersave state, which is whitelist
+ if (mFirewallChainStates.get(FIREWALL_CHAIN_POWERSAVE)
+ && mUidFirewallPowerSaveRules.get(uid, FIREWALL_RULE_DEFAULT) != FIREWALL_RULE_ALLOW) {
+ uidRules = RULE_REJECT_ALL;
+ }
+
// Check standby state, which is blacklist
if (mFirewallChainStates.get(FIREWALL_CHAIN_STANDBY)
&& mUidFirewallStandbyRules.get(uid, FIREWALL_RULE_DEFAULT) == FIREWALL_RULE_DENY) {
@@ -2810,6 +2874,8 @@
mUidFirewallDozableRules.put(uid, rule);
} else if (chain == FIREWALL_CHAIN_STANDBY) {
mUidFirewallStandbyRules.put(uid, rule);
+ } else if (chain == FIREWALL_CHAIN_POWERSAVE) {
+ mUidFirewallPowerSaveRules.put(uid, rule);
}
try {
diff --git a/services/core/java/com/android/server/notification/NotificationComparator.java b/services/core/java/com/android/server/notification/NotificationComparator.java
index 5e4703d..7dff2c1 100644
--- a/services/core/java/com/android/server/notification/NotificationComparator.java
+++ b/services/core/java/com/android/server/notification/NotificationComparator.java
@@ -28,10 +28,11 @@
final int leftImportance = left.getImportance();
final int rightImportance = right.getImportance();
if (leftImportance != rightImportance) {
- // by priority, high to low
+ // by importance, high to low
return -1 * Integer.compare(leftImportance, rightImportance);
}
+ // Whether or not the notification can bypass DND.
final int leftPackagePriority = left.getPackagePriority();
final int rightPackagePriority = right.getPackagePriority();
if (leftPackagePriority != rightPackagePriority) {
@@ -39,6 +40,13 @@
return -1 * Integer.compare(leftPackagePriority, rightPackagePriority);
}
+ final int leftPriority = left.sbn.getNotification().priority;
+ final int rightPriority = right.sbn.getNotification().priority;
+ if (leftPriority != rightPriority) {
+ // by priority, high to low
+ return -1 * Integer.compare(leftPriority, rightPriority);
+ }
+
final float leftPeople = left.getContactAffinity();
final float rightPeople = right.getContactAffinity();
if (leftPeople != rightPeople) {
diff --git a/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java b/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
index b57cc75..bcdeb66 100644
--- a/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
+++ b/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
@@ -23,7 +23,7 @@
import android.util.Slog;
/**
- * This {@link com.android.server.notification.NotificationSignalExtractor} noticies noisy
+ * This {@link com.android.server.notification.NotificationSignalExtractor} notices noisy
* notifications and marks them to get a temporary ranking bump.
*/
public class NotificationIntrusivenessExtractor implements NotificationSignalExtractor {
@@ -44,9 +44,15 @@
return null;
}
- final Notification notification = record.getNotification();
- if (record.getImportance() > NotificationListenerService.Ranking.IMPORTANCE_DEFAULT) {
- record.setRecentlyIntrusive(true);
+ if (record.getImportance() >= NotificationListenerService.Ranking.IMPORTANCE_DEFAULT) {
+ final Notification notification = record.getNotification();
+ if ((notification.defaults & Notification.DEFAULT_VIBRATE) != 0 ||
+ notification.vibrate != null ||
+ (notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
+ notification.sound != null ||
+ notification.fullScreenIntent != null) {
+ record.setRecentlyIntrusive(true);
+ }
}
return new RankingReconsideration(record.getKey(), HANG_TIME_MS) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index e8d27db..3855579 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1320,7 +1320,7 @@
@Override
public int getImportance(String pkg, int uid) {
- checkCallerIsSystem();
+ enforceSystemOrSystemUI("Caller not system or systemui");
return mRankingHelper.getImportance(pkg, uid);
}
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 1476e6e..93dcc72 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -203,6 +203,11 @@
mInstaller.execute("linkfile", relativePath, fromBase, toBase);
}
+ public void moveAb(String apkPath, String instructionSet, String outputPath)
+ throws InstallerException {
+ mInstaller.execute("move_ab", apkPath, instructionSet, outputPath);
+ }
+
private static void assertValidInstructionSet(String instructionSet)
throws InstallerException {
for (String abi : Build.SUPPORTED_ABIS) {
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 3d2a355..67aeed1 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -16,36 +16,28 @@
package com.android.server.pm;
-import android.app.AppGlobals;
+import static com.android.server.pm.Installer.DEXOPT_OTA;
+import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
+import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
+
import android.content.Context;
-import android.content.Intent;
import android.content.pm.IOtaDexopt;
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.Package;
-import android.content.pm.ResolveInfo;
import android.os.Environment;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
-import android.os.UserHandle;
import android.os.storage.StorageManager;
-import android.util.ArraySet;
import android.util.Log;
+import android.util.Slog;
-import dalvik.system.DexFile;
+import com.android.internal.os.InstallerConnection.InstallerException;
import java.io.File;
import java.io.FileDescriptor;
-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.Installer.DEXOPT_OTA;
/**
* A service for A/B OTA dexopting.
@@ -70,6 +62,9 @@
// Use the package manager install and install lock here for the OTA dex optimizer.
mPackageDexOptimizer = new OTADexoptPackageDexOptimizer(packageManagerService.mInstaller,
packageManagerService.mInstallLock, context);
+
+ // Now it's time to check whether we need to move any A/B artifacts.
+ moveAbArtifacts(packageManagerService.mInstaller);
}
public static OtaDexoptService main(Context context,
@@ -150,20 +145,50 @@
false /* extractOnly */);
}
- private ArraySet<String> getPackageNamesForIntent(Intent intent, int userId) {
- List<ResolveInfo> ris = null;
- try {
- ris = AppGlobals.getPackageManager().queryIntentReceivers(
- intent, null, 0, userId);
- } catch (RemoteException e) {
+ private void moveAbArtifacts(Installer installer) {
+ if (mDexoptPackages != null) {
+ throw new IllegalStateException("Should not be ota-dexopting when trying to move.");
}
- ArraySet<String> pkgNames = new ArraySet<String>(ris == null ? 0 : ris.size());
- if (ris != null) {
- for (ResolveInfo ri : ris) {
- pkgNames.add(ri.activityInfo.packageName);
+
+ // Look into all packages.
+ Collection<PackageParser.Package> pkgs = mPackageManagerService.getPackages();
+ for (PackageParser.Package pkg : pkgs) {
+ if (pkg == null) {
+ continue;
+ }
+
+ // Does the package have code? If not, there won't be any artifacts.
+ if (!PackageDexOptimizer.canOptimizePackage(pkg)) {
+ continue;
+ }
+ if (pkg.codePath == null) {
+ Slog.w(TAG, "Package " + pkg + " can be optimized but has null codePath");
+ continue;
+ }
+
+ // If the path is in /system or /vendor, ignore. It will have been ota-dexopted into
+ // /data/ota and moved into the dalvik-cache already.
+ if (pkg.codePath.startsWith("/system") || pkg.codePath.startsWith("/vendor")) {
+ continue;
+ }
+
+ final String[] instructionSets = getAppDexInstructionSets(pkg.applicationInfo);
+ final List<String> paths = pkg.getAllCodePathsExcludingResourceOnly();
+ final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
+ for (String dexCodeInstructionSet : dexCodeInstructionSets) {
+ for (String path : paths) {
+ String oatDir = PackageDexOptimizer.getOatDir(new File(pkg.codePath)).
+ getAbsolutePath();
+
+ // TODO: Check first whether there is an artifact, to save the roundtrip time.
+
+ try {
+ installer.moveAb(path, dexCodeInstructionSet, oatDir);
+ } catch (InstallerException e) {
+ }
+ }
}
}
- return pkgNames;
}
private static class OTADexoptPackageDexOptimizer extends
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 86f2f97..b2ee0b2 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -103,6 +103,7 @@
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.IActivityManager;
+import android.app.admin.DevicePolicyManagerInternal;
import android.app.admin.IDevicePolicyManager;
import android.app.backup.IBackupManager;
import android.content.BroadcastReceiver;
@@ -360,6 +361,7 @@
static final int SCAN_MOVE = 1<<13;
static final int SCAN_INITIAL = 1<<14;
static final int SCAN_CHECK_ONLY = 1<<15;
+ static final int SCAN_DONT_KILL_APP = 1<<17;
static final int REMOVE_CHATTY = 1<<16;
@@ -499,6 +501,9 @@
final ArrayMap<String, PackageParser.Package> mPackages =
new ArrayMap<String, PackageParser.Package>();
+ final ArrayMap<String, Set<String>> mKnownCodebase =
+ new ArrayMap<String, Set<String>>();
+
// Tracks available target package names -> overlay package paths.
final ArrayMap<String, ArrayMap<String, PackageParser.Package>> mOverlays =
new ArrayMap<String, ArrayMap<String, PackageParser.Package>>();
@@ -1425,19 +1430,21 @@
final boolean grantPermissions = (args.installFlags
& PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0;
+ final boolean killApp = (args.installFlags
+ & PackageManager.INSTALL_DONT_KILL_APP) == 0;
final String[] grantedPermissions = args.installGrantPermissions;
// Handle the parent package
- handlePackagePostInstall(parentRes, grantPermissions, grantedPermissions,
- args.observer);
+ handlePackagePostInstall(parentRes, grantPermissions, killApp,
+ grantedPermissions, args.observer);
// Handle the child packages
final int childCount = (parentRes.addedChildPackages != null)
? parentRes.addedChildPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
PackageInstalledInfo childRes = parentRes.addedChildPackages.valueAt(i);
- handlePackagePostInstall(childRes, grantPermissions, grantedPermissions,
- args.observer);
+ handlePackagePostInstall(childRes, grantPermissions, killApp,
+ grantedPermissions, args.observer);
}
// Log tracing if needed
@@ -1632,11 +1639,12 @@
}
private void handlePackagePostInstall(PackageInstalledInfo res, boolean grantPermissions,
- String[] grantedPermissions, IPackageInstallObserver2 installObserver) {
+ boolean killApp, String[] grantedPermissions,
+ IPackageInstallObserver2 installObserver) {
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
// Send the removed broadcasts
if (res.removedInfo != null) {
- res.removedInfo.sendPackageRemovedBroadcasts();
+ res.removedInfo.sendPackageRemovedBroadcasts(killApp);
}
// Now that we successfully installed the package, grant runtime
@@ -7899,13 +7907,17 @@
// Request the ActivityManager to kill the process(only for existing packages)
// so that we do not end up in a confused state while the user is still using the older
// version of the application while the new one gets installed.
- if ((scanFlags & SCAN_REPLACING) != 0) {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "killApplication");
+ final boolean isReplacing = (scanFlags & SCAN_REPLACING) != 0;
+ final boolean killApp = (scanFlags & SCAN_DONT_KILL_APP) == 0;
+ if (killApp) {
+ if (isReplacing) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "killApplication");
- killApplication(pkg.applicationInfo.packageName,
- pkg.applicationInfo.uid, "replace pkg");
+ killApplication(pkg.applicationInfo.packageName,
+ pkg.applicationInfo.uid, "replace pkg");
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
}
// Also need to kill any apps that are dependent on the library.
@@ -10594,7 +10606,7 @@
info.removedPackage = packageName;
info.removedUsers = new int[] {userId};
info.uid = UserHandle.getUid(userId, pkgSetting.appId);
- info.sendPackageRemovedBroadcasts();
+ info.sendPackageRemovedBroadcasts(true /*killApp*/);
}
private void sendPackagesSuspendedForUser(String[] pkgList, int userId, boolean suspended) {
@@ -13077,6 +13089,15 @@
}
}
+ public List<String> getPreviousCodePaths(String packageName) {
+ final PackageSetting ps = mSettings.mPackages.get(packageName);
+ final List<String> result = new ArrayList<String>();
+ if (ps != null && ps.oldCodePaths != null) {
+ result.addAll(ps.oldCodePaths);
+ }
+ return result;
+ }
+
private void replaceNonSystemPackageLI(PackageParser.Package deletedPackage,
PackageParser.Package pkg, int parseFlags, int scanFlags, UserHandle user,
int[] allUsers, String installerPackageName, PackageInstalledInfo res) {
@@ -13086,12 +13107,16 @@
String pkgName = deletedPackage.packageName;
boolean deletedPkg = true;
boolean addedPkg = false;
+ boolean updatedSettings = false;
+ final boolean killApp = (scanFlags & SCAN_DONT_KILL_APP) == 0;
+ final int deleteFlags = PackageManager.DELETE_KEEP_DATA
+ | (killApp ? 0 : PackageManager.DELETE_DONT_KILL_APP);
final long origUpdateTime = (pkg.mExtras != null)
? ((PackageSetting)pkg.mExtras).lastUpdateTime : 0;
// First delete the existing package while retaining the data directory
- if (!deletePackageLI(pkgName, null, true, allUsers, PackageManager.DELETE_KEEP_DATA,
+ if (!deletePackageLI(pkgName, null, true, allUsers, deleteFlags,
res.removedInfo, true, pkg)) {
// If the existing package wasn't successfully deleted
res.setError(INSTALL_FAILED_REPLACE_COULDNT_DELETE, "replaceNonSystemPackageLI");
@@ -13117,6 +13142,27 @@
final PackageParser.Package newPackage = scanPackageTracedLI(pkg, parseFlags,
scanFlags | SCAN_UPDATE_TIME, System.currentTimeMillis(), user);
updateSettingsLI(newPackage, installerPackageName, allUsers, res, user);
+
+ // Update the in-memory copy of the previous code paths.
+ PackageSetting ps = mSettings.mPackages.get(pkgName);
+ if (!killApp) {
+ if (ps.oldCodePaths == null) {
+ ps.oldCodePaths = new ArraySet<>();
+ }
+ Collections.addAll(ps.oldCodePaths, deletedPackage.baseCodePath);
+ if (deletedPackage.splitCodePaths != null) {
+ Collections.addAll(ps.oldCodePaths, deletedPackage.splitCodePaths);
+ }
+ } else {
+ ps.oldCodePaths = null;
+ }
+ if (ps.childPackageNames != null) {
+ for (int i = ps.childPackageNames.size() - 1; i >= 0; --i) {
+ final String childPkgName = ps.childPackageNames.get(i);
+ final PackageSetting childPs = mSettings.mPackages.get(childPkgName);
+ childPs.oldCodePaths = ps.oldCodePaths;
+ }
+ }
prepareAppDataAfterInstall(newPackage);
addedPkg = true;
} catch (PackageManagerException e) {
@@ -13129,7 +13175,7 @@
// Revert all internal state mutations and added folders for the failed install
if (addedPkg) {
- deletePackageLI(pkgName, null, true, allUsers, PackageManager.DELETE_KEEP_DATA,
+ deletePackageLI(pkgName, null, true, allUsers, deleteFlags,
res.removedInfo, true, null);
}
@@ -13582,6 +13628,9 @@
// moving a complete application; perform an initial scan on the new install location
scanFlags |= SCAN_INITIAL;
}
+ if ((installFlags & PackageManager.INSTALL_DONT_KILL_APP) != 0) {
+ scanFlags |= SCAN_DONT_KILL_APP;
+ }
// Result object to be returned
res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
@@ -14302,7 +14351,8 @@
}
if (res) {
- info.sendPackageRemovedBroadcasts();
+ final boolean killApp = (flags & PackageManager.INSTALL_DONT_KILL_APP) == 0;
+ info.sendPackageRemovedBroadcasts(killApp);
info.sendSystemPackageUpdatedBroadcasts();
info.sendSystemPackageAppearedBroadcasts();
}
@@ -14334,12 +14384,12 @@
ArrayMap<String, PackageRemovedInfo> removedChildPackages;
ArrayMap<String, PackageInstalledInfo> appearedChildPackages;
- void sendPackageRemovedBroadcasts() {
- sendPackageRemovedBroadcastInternal();
+ void sendPackageRemovedBroadcasts(boolean killApp) {
+ sendPackageRemovedBroadcastInternal(killApp);
final int childCount = removedChildPackages != null ? removedChildPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
PackageRemovedInfo childInfo = removedChildPackages.valueAt(i);
- childInfo.sendPackageRemovedBroadcastInternal();
+ childInfo.sendPackageRemovedBroadcastInternal(killApp);
}
}
@@ -14381,10 +14431,11 @@
null, 0, removedPackage, null, null);
}
- private void sendPackageRemovedBroadcastInternal() {
+ private void sendPackageRemovedBroadcastInternal(boolean killApp) {
Bundle extras = new Bundle(2);
extras.putInt(Intent.EXTRA_UID, removedAppId >= 0 ? removedAppId : uid);
extras.putBoolean(Intent.EXTRA_DATA_REMOVED, dataRemoved);
+ extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, !killApp);
if (isUpdate || isRemovedPackageSystemUpdate) {
extras.putBoolean(Intent.EXTRA_REPLACING, true);
}
@@ -14875,7 +14926,10 @@
} else {
if (DEBUG_REMOVE) Slog.d(TAG, "Removing non-system package: " + ps.name);
// Kill application pre-emptively especially for apps on sd.
- killApplication(packageName, ps.appId, "uninstall pkg");
+ final boolean killApp = (flags & PackageManager.DELETE_DONT_KILL_APP) == 0;
+ if (killApp) {
+ killApplication(packageName, ps.appId, "uninstall pkg");
+ }
ret = deleteInstalledPackageLI(ps, deleteCodeAndResources, flags, allUserHandles,
outInfo, writeSettings, replacingPackage);
}
@@ -15057,8 +15111,15 @@
final IPackageDataObserver observer, final int userId) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CLEAR_APP_USER_DATA, null);
+
enforceCrossUserPermission(Binder.getCallingUid(), userId,
true /* requireFullPermission */, false /* checkShell */, "clear application data");
+
+ final DevicePolicyManagerInternal dpmi = LocalServices
+ .getService(DevicePolicyManagerInternal.class);
+ if (dpmi != null && dpmi.hasDeviceOwnerOrProfileOwner(packageName, userId)) {
+ throw new SecurityException("Cannot clear data for a device owner or a profile owner");
+ }
// Queue up an async operation since the package deletion may take a little while.
mHandler.post(new Runnable() {
public void run() {
@@ -15070,8 +15131,8 @@
clearExternalStorageDataSync(packageName, userId, true);
if (succeeded) {
// invoke DeviceStorageMonitor's update method to clear any notifications
- DeviceStorageMonitorInternal
- dsm = LocalServices.getService(DeviceStorageMonitorInternal.class);
+ DeviceStorageMonitorInternal dsm = LocalServices
+ .getService(DeviceStorageMonitorInternal.class);
if (dsm != null) {
dsm.checkMemory();
}
@@ -19124,6 +19185,11 @@
return permissionsState.isPermissionReviewRequired(userId);
}
}
+
+ @Override
+ public ApplicationInfo getApplicationInfo(String packageName, int userId) {
+ return PackageManagerService.this.getApplicationInfo(packageName, 0 /*flags*/, userId);
+ }
}
@Override
@@ -19151,4 +19217,14 @@
boolean isHistoricalPackageUsageAvailable() {
return mPackageUsage.isHistoricalPackageUsageAvailable();
}
+
+ /**
+ * Return a <b>copy</b> of the collection of packages known to the package manager.
+ * @return A copy of the values of mPackages.
+ */
+ Collection<PackageParser.Package> getPackages() {
+ synchronized (mPackages) {
+ return new ArrayList<>(mPackages.values());
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index e5eec7e..1434718 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -30,6 +30,7 @@
import java.io.File;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
/**
* Settings base class for pending and resolved classes.
@@ -118,7 +119,14 @@
* platform will refuse to launch packages in a frozen state.
*/
boolean frozen = false;
-
+ /**
+ * Non-persisted value. During an "upgrade without restart", we need the set
+ * of all previous code paths so we can surgically add the new APKs to the
+ * active classloader. If at any point an application is upgraded with a
+ * restart, this field will be cleared since the classloader would be created
+ * using the full set of code paths when the package's process is started.
+ */
+ Set<String> oldCodePaths;
PackageSettingBase origPackage;
/** Package name of the app that installed this package */
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 310ad53..bf5a8f6 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -2604,7 +2604,6 @@
if (pkg.volumeUuid != null) {
serializer.attribute(null, "volumeUuid", pkg.volumeUuid);
}
-
if (pkg.parentPackageName != null) {
serializer.attribute(null, "parentPackageName", pkg.parentPackageName);
}
@@ -4171,9 +4170,19 @@
};
static final Object[] PRIVATE_FLAG_DUMP_SPEC = new Object[] {
- ApplicationInfo.PRIVATE_FLAG_PRIVILEGED, "PRIVILEGED",
- ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK, "FORWARD_LOCK",
+ ApplicationInfo.PRIVATE_FLAG_HIDDEN, "HIDDEN",
ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE, "CANT_SAVE_STATE",
+ ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK, "FORWARD_LOCK",
+ ApplicationInfo.PRIVATE_FLAG_PRIVILEGED, "PRIVILEGED",
+ ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS, "HAS_DOMAIN_URLS",
+ ApplicationInfo.PRIVATE_FLAG_FORCE_DEVICE_ENCRYPTED, "FORCE_DEVICE_ENCRYPTED",
+ ApplicationInfo.PRIVATE_FLAG_ENCRYPTION_AWARE, "ENCRYPTION_AWARE",
+ ApplicationInfo.PRIVATE_FLAG_AUTOPLAY, "AUTOPLAY",
+ ApplicationInfo.PRIVATE_FLAG_PARTIALLY_ENCRYPTION_AWARE, "PARTIALLY_ENCRYPTION_AWARE",
+ ApplicationInfo.PRIVATE_FLAG_EPHEMERAL, "EPHEMERAL",
+ ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER, "REQUIRED_FOR_SYSTEM_USER",
+ ApplicationInfo.PRIVATE_FLAG_RESIZEABLE_ACTIVITIES, "RESIZEABLE_ACTIVITIES",
+ ApplicationInfo.PRIVATE_FLAG_BACKUP_IN_FOREGROUND, "BACKUP_IN_FOREGROUND",
};
void dumpVersionLPr(IndentingPrintWriter pw) {
@@ -4316,6 +4325,10 @@
}
pw.print(prefix); pw.print(" versionName="); pw.println(ps.pkg.mVersionName);
pw.print(prefix); pw.print(" splits="); dumpSplitNames(pw, ps.pkg); pw.println();
+ final int apkSigningVersion = PackageParser.getApkSigningVersion(ps.pkg);
+ if (apkSigningVersion != PackageParser.APK_SIGNING_UNKNOWN) {
+ pw.print(prefix); pw.print(" apkSigningVersion="); pw.println(apkSigningVersion);
+ }
pw.print(prefix); pw.print(" applicationInfo=");
pw.println(ps.pkg.applicationInfo.toString());
pw.print(prefix); pw.print(" flags="); printFlags(pw, ps.pkg.applicationInfo.flags,
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 6320413..252dda7 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -535,6 +535,7 @@
boolean mForceStatusBar;
boolean mForceStatusBarFromKeyguard;
private boolean mForceStatusBarTransparent;
+ boolean mForceNavBarOpaque;
boolean mHideLockScreen;
boolean mForcingShowNavBar;
int mForcingShowNavBarLayer;
@@ -1715,6 +1716,9 @@
if (mContext.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
mShortPressWindowBehavior = SHORT_PRESS_WINDOW_PICTURE_IN_PICTURE;
}
+
+ mForceNavBarOpaque = res.getBoolean(
+ com.android.internal.R.bool.config_forceNavBarAlwaysOpaque);
}
@Override
@@ -7080,6 +7084,10 @@
| View.SYSTEM_UI_TRANSPARENT);
}
+ if (mForceNavBarOpaque) {
+ vis &= ~(View.NAVIGATION_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSPARENT);
+ }
+
if (mForceWindowDrawsStatusBarBackground) {
vis |= View.STATUS_BAR_TRANSPARENT;
vis &= ~View.STATUS_BAR_TRANSLUCENT;
diff --git a/services/core/java/com/android/server/policy/ShortcutManager.java b/services/core/java/com/android/server/policy/ShortcutManager.java
index 9284442..57ae523 100644
--- a/services/core/java/com/android/server/policy/ShortcutManager.java
+++ b/services/core/java/com/android/server/policy/ShortcutManager.java
@@ -138,14 +138,16 @@
ComponentName componentName = new ComponentName(packageName, className);
try {
info = packageManager.getActivityInfo(componentName,
- PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE);
+ PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE
+ | PackageManager.MATCH_UNINSTALLED_PACKAGES);
} catch (PackageManager.NameNotFoundException e) {
String[] packages = packageManager.canonicalToCurrentPackageNames(
new String[] { packageName });
componentName = new ComponentName(packages[0], className);
try {
info = packageManager.getActivityInfo(componentName,
- PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE);
+ PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE
+ | PackageManager.MATCH_UNINSTALLED_PACKAGES);
} catch (PackageManager.NameNotFoundException e1) {
Log.w(TAG, "Unable to add bookmark: " + packageName
+ "/" + className, e);
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index f9e258d..2731f429 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -354,6 +354,11 @@
continue;
}
+ if (DEBUG_ADD_REMOVE) Slog.e(TAG_WM, "win=" + win
+ + " destroySurfaces: mAppStopped=" + mAppStopped
+ + " win.mWindowRemovalAllowed=" + win.mWindowRemovalAllowed
+ + " win.mRemoveOnExit=" + win.mRemoveOnExit);
+
win.destroyOrSaveSurface();
if (win.mRemoveOnExit) {
win.mAnimatingExit = false;
@@ -372,14 +377,19 @@
}
}
- // The application has stopped, so destroy any surfaces which were keeping alive
- // in case they were still being used.
- void notifyAppStopped() {
- mAppStopped = true;
- destroySurfaces();
+ /**
+ * If the application has stopped it is okay to destroy any surfaces which were keeping alive
+ * in case they were still being used.
+ */
+ void notifyAppStopped(boolean stopped) {
+ if (DEBUG_ADD_REMOVE) Slog.v(TAG, "notifyAppStopped: stopped=" + stopped + " " + this);
+ mAppStopped = stopped;
- // Remove any starting window that was added for this app if they are still around.
- mTask.mService.scheduleRemoveStartingWindowLocked(this);
+ if (stopped) {
+ destroySurfaces();
+ // Remove any starting window that was added for this app if they are still around.
+ mTask.mService.scheduleRemoveStartingWindowLocked(this);
+ }
}
/**
@@ -472,7 +482,7 @@
winNdx = Math.min(winNdx - 1, allAppWindows.size() - 1)) {
WindowState win = allAppWindows.get(winNdx);
if (win.mAppDied) {
- if (DEBUG_WINDOW_MOVEMENT) {
+ if (DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) {
Slog.w(TAG, "removeAllDeadWindows: " + win);
}
// Set mDestroying, we don't want any animation or delayed removal here.
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java
index f0efebe..79d3d84 100644
--- a/services/core/java/com/android/server/wm/BoundsAnimationController.java
+++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java
@@ -54,7 +54,8 @@
private final AnimateBoundsUser mTarget;
private final Rect mFrom;
private final Rect mTo;
- private final Rect mTmpRect;
+ private final Rect mTmpRect = new Rect();
+ private final Rect mTmpTaskBounds = new Rect();
private final boolean mMoveToFullScreen;
// True if this this animation was cancelled and will be replaced the another animation from
// the same {@link #AnimateBoundsUser} target.
@@ -63,17 +64,40 @@
// {@link #AnimateBoundsUser} target.
private final boolean mReplacement;
+ // Depending on whether we are animating from
+ // a smaller to a larger size
+ private final int mFrozenTaskWidth;
+ private final int mFrozenTaskHeight;
+
BoundsAnimator(AnimateBoundsUser target, Rect from, Rect to,
boolean moveToFullScreen, boolean replacement) {
super();
mTarget = target;
mFrom = from;
mTo = to;
- mTmpRect = new Rect();
mMoveToFullScreen = moveToFullScreen;
mReplacement = replacement;
addUpdateListener(this);
addListener(this);
+
+ // If we are animating from smaller to larger, we want to change the task bounds
+ // to their final size immediately so we can use scaling to make the window
+ // larger. Likewise if we are going from bigger to smaller, we want to wait until
+ // the end so we don't have to upscale from the smaller finished size.
+ if (animatingToLargerSize()) {
+ mFrozenTaskWidth = mTo.width();
+ mFrozenTaskHeight = mTo.height();
+ } else {
+ mFrozenTaskWidth = mFrom.width();
+ mFrozenTaskHeight = mFrom.height();
+ }
+ }
+
+ boolean animatingToLargerSize() {
+ if (mFrom.width() * mFrom.height() > mTo.width() * mTo.height()) {
+ return false;
+ }
+ return true;
}
@Override
@@ -87,7 +111,13 @@
if (DEBUG) Slog.d(TAG, "animateUpdate: mTarget=" + mTarget + " mBounds="
+ mTmpRect + " from=" + mFrom + " mTo=" + mTo + " value=" + value
+ " remains=" + remains);
- if (!mTarget.setSize(mTmpRect)) {
+
+ if (remains != 0) {
+ mTmpTaskBounds.set(mTmpRect.left, mTmpRect.top,
+ mTmpRect.left + mFrozenTaskWidth, mTmpRect.top + mFrozenTaskHeight);
+ }
+
+ if (!mTarget.setPinnedStackSize(mTmpRect, remains != 0 ? mTmpTaskBounds : null)) {
// Whoops, the target doesn't feel like animating anymore. Let's immediately finish
// any further animation.
animation.cancel();
@@ -99,6 +129,10 @@
public void onAnimationStart(Animator animation) {
if (DEBUG) Slog.d(TAG, "onAnimationStart: mTarget=" + mTarget
+ " mReplacement=" + mReplacement);
+ if (animatingToLargerSize()) {
+ mTarget.setPinnedStackSize(mFrom, mTo);
+ }
+
if (!mReplacement) {
mTarget.onAnimationStart();
}
@@ -108,6 +142,7 @@
public void onAnimationEnd(Animator animation) {
if (DEBUG) Slog.d(TAG, "onAnimationEnd: mTarget=" + mTarget
+ " mMoveToFullScreen=" + mMoveToFullScreen + " mWillReplace=" + mWillReplace);
+
finishAnimation();
if (mMoveToFullScreen && !mWillReplace) {
mTarget.moveToFullscreen();
@@ -159,6 +194,12 @@
* from the hierarchy and is not valid anymore.
*/
boolean setSize(Rect bounds);
+ /**
+ * Behaves as setSize, but freezes the bounds of any tasks in the target at taskBounds,
+ * to allow for more flexibility during resizing. Only
+ * works for the pinned stack at the moment.
+ */
+ boolean setPinnedStackSize(Rect bounds, Rect taskBounds);
void onAnimationStart();
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index c7b5599..f097eb2 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -19,6 +19,7 @@
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.PINNED_STACK_ID;
import static android.app.ActivityManager.StackId.HOME_STACK_ID;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
@@ -685,6 +686,10 @@
&& mStack != null && StackId.isTaskResizeableByDockedStack(mStack.mStackId);
}
+ boolean isFloating() {
+ return StackId.tasksAreFloating(mStack.mStackId);
+ }
+
/**
* Whether the task should be treated as if it's docked. Returns true if the task
* is currently in docked workspace, or it's side-by-side to a docked task.
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 07a6514..86327f7 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -111,6 +111,17 @@
private float mMinimizeAmount;
private final int mDockedStackMinimizeThickness;
+ // If this is true, the task will be down or upscaled
+ // to perfectly fit the region it would have been cropped
+ // to.
+ private boolean mForceScaleToCrop = false;
+ // By default, movement animations are applied to all
+ // window movement. If this is true, animations will not
+ // be applied within this stack. This is useful for example
+ // if the windows are moving as the result of a stack animation,
+ // in which case a second window animation would cause jitter.
+ private boolean mFreezeMovementAnimations = false;
+
TaskStack(WindowManagerService service, int stackId) {
mService = service;
mStackId = stackId;
@@ -1128,17 +1139,38 @@
return true;
}
+ public boolean setPinnedStackSize(Rect bounds, Rect tempTaskBounds) {
+ synchronized (mService.mWindowMap) {
+ if (mDisplayContent == null) {
+ return false;
+ }
+ if (mStackId != PINNED_STACK_ID) {
+ Slog.w(TAG_WM, "Attempt to use pinned stack resize animation helper on"
+ + "non pinned stack");
+ return false;
+ }
+ }
+ try {
+ mService.mActivityManager.resizePinnedStack(bounds, tempTaskBounds);
+ } catch (RemoteException e) {
+ // I don't believe you.
+ }
+ return true;
+ }
+
@Override // AnimatesBounds
public void onAnimationStart() {
synchronized (mService.mWindowMap) {
- setDragResizingLocked(true);
+ mFreezeMovementAnimations = true;
+ mForceScaleToCrop = true;
}
}
@Override // AnimatesBounds
public void onAnimationEnd() {
synchronized (mService.mWindowMap) {
- setDragResizingLocked(false);
+ mFreezeMovementAnimations = false;
+ mForceScaleToCrop = false;
mService.requestTraversal();
}
if (mStackId == PINNED_STACK_ID) {
@@ -1163,4 +1195,12 @@
public void getFullScreenBounds(Rect bounds) {
getDisplayContent().getContentRect(bounds);
}
+
+ public boolean getFreezeMovementAnimations() {
+ return mFreezeMovementAnimations;
+ }
+
+ public boolean getForceScaleToCrop() {
+ return mForceScaleToCrop;
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index fe215d5..b64aaa8 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2147,12 +2147,15 @@
void removeWindowLocked(WindowState win) {
win.mWindowRemovalAllowed = true;
+ if (DEBUG_ADD_REMOVE) Slog.v(TAG,
+ "removeWindowLocked: " + win + " callers=" + Debug.getCallers(4));
+
final boolean startingWindow = win.mAttrs.type == TYPE_APPLICATION_STARTING;
if (startingWindow) {
if (DEBUG_STARTING_WINDOW) Slog.d(TAG_WM, "Starting window removed " + win);
}
- if (localLOGV || DEBUG_FOCUS || DEBUG_FOCUS_LIGHT && win==mCurrentFocus) Slog.v(
+ if (localLOGV || DEBUG_FOCUS || DEBUG_FOCUS_LIGHT && win == mCurrentFocus) Slog.v(
TAG_WM, "Remove " + win + " client="
+ Integer.toHexString(System.identityHashCode(win.mClient.asBinder()))
+ ", surfaceController=" + win.mWinAnimator.mSurfaceController + " Callers="
@@ -4194,7 +4197,7 @@
}
@Override
- public void notifyAppStopped(IBinder token) {
+ public void notifyAppStopped(IBinder token, boolean stopped) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"notifyAppStopped()")) {
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
@@ -4207,7 +4210,7 @@
Slog.w(TAG_WM, "Attempted to set visibility of non-existing app token: " + token);
return;
}
- wtoken.notifyAppStopped();
+ wtoken.notifyAppStopped(stopped);
}
}
@@ -4244,6 +4247,8 @@
wtoken.appDied = false;
wtoken.removeAllWindows();
} else if (visible) {
+ if (DEBUG_ADD_REMOVE) Slog.v(
+ TAG_WM, "No longer Stopped: " + wtoken);
wtoken.mAppStopped = false;
wtoken.setWindowsExiting(false);
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index e8f1b5d..4ad021e 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -184,6 +184,10 @@
private Configuration mConfiguration = Configuration.EMPTY;
private Configuration mOverrideConfig = Configuration.EMPTY;
+ // Represents the changes from our override configuration applied
+ // to the global configuration. This is the only form of configuration
+ // which is suitable for delivery to the client.
+ private Configuration mMergedConfiguration = new Configuration();
// Sticky answer to isConfigChanged(), remains true until new Configuration is assigned.
// Used only on {@link #TYPE_KEYGUARD}.
private boolean mConfigHasChanged;
@@ -434,9 +438,9 @@
// If not null, the window that will be used to replace the old one. This is being set when
// the window is added and unset when this window reports its first draw.
WindowState mReplacingWindow = null;
-
// Whether this window is being moved via the resize API
boolean mMovedByResize;
+
/**
* Wake lock for drawing.
* Even though it's slightly more expensive to do so, we will use a separate wake lock
@@ -635,7 +639,7 @@
final Task task = getTask();
final boolean fullscreenTask = task == null || task.isFullscreen();
- final boolean freeformWorkspace = task != null && task.inFreeformWorkspace();
+ final boolean windowsAreFloating = task != null && task.isFloating();
if (fullscreenTask || (isChildWindow()
&& (mAttrs.privateFlags & PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME) != 0)) {
@@ -661,10 +665,10 @@
mContainingFrame.top -= mContainingFrame.bottom - cf.bottom;
}
- if (freeformWorkspace) {
- // In free form mode we have only to set the rectangle if it wasn't set already. No
- // need to intersect it with the (visible) "content frame" since it is allowed to
- // be outside the visible desktop.
+ if (windowsAreFloating) {
+ // In floating modes (e.g. freeform, pinned) we have only to set the rectangle
+ // if it wasn't set already. No need to intersect it with the (visible)
+ // "content frame" since it is allowed to be outside the visible desktop.
if (mContainingFrame.isEmpty()) {
mContainingFrame.set(cf);
}
@@ -720,7 +724,7 @@
// Make sure the content and visible frames are inside of the
// final window frame.
- if (freeformWorkspace && !mFrame.isEmpty()) {
+ if (windowsAreFloating && !mFrame.isEmpty()) {
// Keep the frame out of the blocked system area, limit it in size to the content area
// and make sure that there is always a minimum visible so that the user can drag it
// into a usable area..
@@ -772,9 +776,9 @@
Math.min(mStableFrame.bottom, frame.bottom));
}
- if (!inFreeformWorkspace()) {
- // Freeform windows can be positioned outside of the display frame, but that is not a
- // reason to provide them with overscan insets.
+ if (!windowsAreFloating) {
+ // Windows from floating tasks (e.g. freeform, pinned) may be positioned outside
+ // of the display frame, but that is not a reason to provide them with overscan insets.
mOverscanInsets.set(Math.max(mOverscanFrame.left - frame.left, 0),
Math.max(mOverscanFrame.top - frame.top, 0),
Math.max(frame.right - mOverscanFrame.right, 0),
@@ -1355,6 +1359,11 @@
mConfiguration = newConfig;
mOverrideConfig = newOverrideConfig;
mConfigHasChanged = false;
+
+ mMergedConfiguration.setTo(newConfig);
+ if (newOverrideConfig != null && newOverrideConfig != Configuration.EMPTY) {
+ mMergedConfiguration.updateFrom(newOverrideConfig);
+ }
}
void setHasSurface(boolean hasSurface) {
@@ -1616,9 +1625,10 @@
mTurnOnScreen = true;
}
if (isConfigChanged()) {
+ final Configuration newConfig = updateConfiguration();
if (DEBUG_CONFIGURATION) Slog.i(TAG, "Window " + this + " visible with new config: "
- + mService.mCurConfiguration);
- outConfig.setTo(mService.mCurConfiguration);
+ + newConfig);
+ outConfig.setTo(newConfig);
}
}
@@ -2061,21 +2071,30 @@
}
}
+ /**
+ * Update our current configurations, based on task configuration.
+ *
+ * @return A configuration suitable for sending to the client.
+ */
+ private Configuration updateConfiguration() {
+ final Task task = getTask();
+ final Configuration overrideConfig =
+ (task != null) ? task.mOverrideConfig : Configuration.EMPTY;
+ final boolean configChanged = isConfigChanged();
+ if ((DEBUG_RESIZE || DEBUG_ORIENTATION || DEBUG_CONFIGURATION) && configChanged) {
+ Slog.i(TAG, "Sending new config to window " + this + ": " +
+ " / config=" + mService.mCurConfiguration + " overrideConfig=" + overrideConfig);
+ }
+ setConfiguration(mService.mCurConfiguration, overrideConfig);
+ return mMergedConfiguration;
+ }
+
void reportResized() {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wm.reportResized_" + getWindowTag());
try {
if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG, "Reporting new frame to " + this
+ ": " + mCompatFrame);
- final boolean configChanged = isConfigChanged();
- final Task task = getTask();
- final Configuration overrideConfig =
- (task != null) ? task.mOverrideConfig : Configuration.EMPTY;
- if ((DEBUG_RESIZE || DEBUG_ORIENTATION || DEBUG_CONFIGURATION) && configChanged) {
- Slog.i(TAG, "Sending new config to window " + this + ": "
- + " / config="
- + mService.mCurConfiguration + " overrideConfig=" + overrideConfig);
- }
- setConfiguration(mService.mCurConfiguration, overrideConfig);
+ final Configuration newConfig = isConfigChanged() ? updateConfiguration() : null;
if (DEBUG_ORIENTATION && mWinAnimator.mDrawState == WindowStateAnimator.DRAW_PENDING)
Slog.i(TAG, "Resizing " + this + " WITH DRAW PENDING");
@@ -2086,7 +2105,6 @@
final Rect stableInsets = mLastStableInsets;
final Rect outsets = mLastOutsets;
final boolean reportDraw = mWinAnimator.mDrawState == WindowStateAnimator.DRAW_PENDING;
- final Configuration newConfig = configChanged ? mConfiguration : null;
if (mAttrs.type != WindowManager.LayoutParams.TYPE_APPLICATION_STARTING
&& mClient instanceof IWindow.Stub) {
// To prevent deadlock simulate one-way call if win.mClient is a local object.
@@ -2485,7 +2503,8 @@
final int ph = mContainingFrame.height();
final Task task = getTask();
final boolean nonFullscreenTask = task != null && !task.isFullscreen();
-
+ final boolean fitToDisplay = task != null &&
+ !task.isFloating();
float x, y;
int w,h;
@@ -2542,7 +2561,17 @@
(int) (y + mAttrs.verticalMargin * ph), mFrame);
// Now make sure the window fits in the overall display frame.
- Gravity.applyDisplay(mAttrs.gravity, mDisplayFrame, mFrame);
+ if (fitToDisplay) {
+ Gravity.applyDisplay(mAttrs.gravity, mDisplayFrame, mFrame);
+ }
+
+ // We need to make sure we update the CompatFrame as it is used for
+ // cropping decisions, etc, on systems where we lack a decor layer.
+ mCompatFrame.set(mFrame);
+ if (mEnforceSizeCompat) {
+ // See comparable block in computeFrameLw.
+ mCompatFrame.scale(mInvGlobalScale);
+ }
}
boolean isChildWindow() {
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 02f9aa1..c1ff96e 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -20,6 +20,7 @@
import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
import static android.view.WindowManager.LayoutParams.FLAG_SCALED;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
@@ -467,9 +468,8 @@
return;
}
- if (WindowManagerService.localLOGV) Slog.v(
- TAG, "Exit animation finished in " + this
- + ": remove=" + mWin.mRemoveOnExit);
+ if (WindowManagerService.localLOGV || DEBUG_ADD_REMOVE) Slog.v(TAG,
+ "Exit animation finished in " + this + ": remove=" + mWin.mRemoveOnExit);
mWin.mDestroying = true;
@@ -1115,11 +1115,11 @@
}
}
- void updateSurfaceWindowCrop(final boolean recoveringMemory) {
+ Rect calculateSurfaceWindowCrop() {
final WindowState w = mWin;
final DisplayContent displayContent = w.getDisplayContent();
if (displayContent == null) {
- return;
+ return null;
}
final DisplayInfo displayInfo = displayContent.getDisplayInfo();
if (DEBUG_WINDOW_CROP) Slog.d(TAG, "Updating crop for window: " + w + ", " + "mLastCrop=" +
@@ -1187,7 +1187,7 @@
clipRect.offset(attrs.surfaceInsets.left, attrs.surfaceInsets.top);
adjustCropToStackBounds(w, clipRect, isFreeformResizing);
- if (DEBUG_WINDOW_CROP) Slog.d(TAG, "Clip rect after stack adjustment=" + mClipRect);
+ if (DEBUG_WINDOW_CROP) Slog.d(TAG, "Clip rect after stack adjustment=" + clipRect);
w.transformFromScreenToSurfaceSpace(clipRect);
@@ -1196,6 +1196,10 @@
clipRect.setEmpty();
}
+ return clipRect;
+ }
+
+ void updateSurfaceWindowCrop(Rect clipRect, boolean recoveringMemory) {
if (!clipRect.equals(mLastClipRect)) {
mLastClipRect.set(clipRect);
mSurfaceController.setCropInTransaction(clipRect, recoveringMemory);
@@ -1237,6 +1241,7 @@
w.mFrame.left + mWin.mXOffset - w.getAttrs().surfaceInsets.left;
final int frameY = isFreeformResizing ? (int) mSurfaceController.getY() :
w.mFrame.top + mWin.mYOffset - w.getAttrs().surfaceInsets.top;
+
// We need to do some acrobatics with surface position, because their clip region is
// relative to the inside of the surface, but the stack bounds aren't.
clipRect.left = Math.max(0,
@@ -1251,14 +1256,44 @@
void setSurfaceBoundariesLocked(final boolean recoveringMemory) {
final WindowState w = mWin;
+ final Task task = w.getTask();
mTmpSize.set(w.mShownPosition.x, w.mShownPosition.y, 0, 0);
calculateSurfaceBounds(w, w.getAttrs());
- mSurfaceController.setPositionInTransaction(mTmpSize.left, mTmpSize.top, recoveringMemory);
+ float extraHScale = (float) 1.0;
+ float extraVScale = (float) 1.0;
- mSurfaceController.setMatrixInTransaction(mDsDx * w.mHScale, mDtDx * w.mVScale,
- mDsDy * w.mHScale, mDtDy * w.mVScale, recoveringMemory);
+ final Rect crop = calculateSurfaceWindowCrop();
+ if (task != null && task.mStack.getForceScaleToCrop()) {
+ extraHScale = crop.width() / (float)mTmpSize.width();
+ extraVScale = crop.height() / (float)mTmpSize.height();
+
+ // In the case of ForceScaleToCrop we scale entire tasks together,
+ // and so we need to scale our offsets relative to the task bounds
+ // or parent and child windows would fall out of alignment.
+ int posX = (int) (mTmpSize.left - w.mAttrs.x * (1 - extraHScale));
+ int posY = (int) (mTmpSize.top - w.mAttrs.y * (1 - extraVScale));
+ posX += w.getAttrs().surfaceInsets.left * (1 - extraHScale);
+ posY += w.getAttrs().surfaceInsets.top * (1 - extraVScale);
+ mSurfaceController.setPositionInTransaction(posX, posY, recoveringMemory);
+
+ // Since we are scaled to fit in our previously desired crop, we can now
+ // expose the whole window in buffer space, and not risk extending
+ // past where the system would have cropped us
+ crop.set(0, 0, mTmpSize.width(), mTmpSize.height());
+ updateSurfaceWindowCrop(crop, recoveringMemory);
+ } else {
+ mSurfaceController.setPositionInTransaction(mTmpSize.left, mTmpSize.top,
+ recoveringMemory);
+ updateSurfaceWindowCrop(crop, recoveringMemory);
+ }
+
+
+ mSurfaceController.setMatrixInTransaction(mDsDx * w.mHScale * extraHScale,
+ mDtDx * w.mVScale * extraVScale,
+ mDsDy * w.mHScale * extraHScale,
+ mDtDy * w.mVScale * extraVScale, recoveringMemory);
mSurfaceResized = mSurfaceController.setSizeInTransaction(
mTmpSize.width(), mTmpSize.height(),
recoveringMemory);
@@ -1270,7 +1305,6 @@
w.applyDimLayerIfNeeded();
}
- updateSurfaceWindowCrop(recoveringMemory);
}
void prepareSurfaceLocked(final boolean recoveringMemory) {
@@ -1409,7 +1443,7 @@
SurfaceControl.openTransaction();
mSurfaceController.setPositionInTransaction(mWin.mFrame.left + left,
mWin.mFrame.top + top, false);
- updateSurfaceWindowCrop(false);
+ updateSurfaceWindowCrop(calculateSurfaceWindowCrop(), false);
} catch (RuntimeException e) {
Slog.w(TAG, "Error positioning surface of " + mWin
+ " pos=(" + left + "," + top + ")", e);
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index 2cdf471..fb07512 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -138,7 +138,7 @@
void destroyInTransaction() {
// if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) {
- Slog.i(TAG, "Destroying surface " + this + " called by " + Debug.getCallers(4));
+ Slog.i(TAG, "Destroying surface " + this + " called by " + Debug.getCallers(8));
// }
try {
if (mSurfaceControl != null) {
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 856d30a..e3955fe 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -651,6 +651,7 @@
for (int i = windows.size() - 1; i >= 0; i--) {
WindowState w = windows.get(i);
+ final Task task = w.getTask();
final boolean obscuredChanged = w.mObscured != mObscured;
// Update effect.
@@ -683,7 +684,8 @@
final boolean adjustedForMinimizedDockedStack = w.getTask() != null &&
w.getTask().mStack.isAdjustedForMinimizedDockedStack();
if ((w.mAttrs.privateFlags & PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0
- && !w.isDragResizing() && !adjustedForMinimizedDockedStack) {
+ && !w.isDragResizing() && !adjustedForMinimizedDockedStack
+ && (task == null || !w.getTask().mStack.getFreezeMovementAnimations())) {
winAnimator.setMoveAnimation(left, top);
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 1490109..d8aa5d7 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -31,8 +31,8 @@
import android.Manifest.permission;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accounts.AccountManager;
+import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
@@ -151,6 +151,8 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.nio.charset.StandardCharsets;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
@@ -280,6 +282,20 @@
private static final int PROFILE_KEYGUARD_FEATURES =
PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER | PROFILE_KEYGUARD_FEATURES_PROFILE_ONLY;
+ private static final int CODE_OK = 0;
+ private static final int CODE_HAS_DEVICE_OWNER = 1;
+ private static final int CODE_USER_HAS_PROFILE_OWNER = 2;
+ private static final int CODE_USER_NOT_RUNNING = 3;
+ private static final int CODE_USER_SETUP_COMPLETED = 4;
+ private static final int CODE_NONSYSTEM_USER_EXISTS = 5;
+ private static final int CODE_ACCOUNTS_NOT_EMPTY = 6;
+ private static final int CODE_NOT_SYSTEM_USER = 7;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({ CODE_OK, CODE_HAS_DEVICE_OWNER, CODE_USER_HAS_PROFILE_OWNER, CODE_USER_NOT_RUNNING,
+ CODE_USER_SETUP_COMPLETED, CODE_NOT_SYSTEM_USER })
+ private @interface DeviceOwnerPreConditionCode {}
+
private static final int DEVICE_ADMIN_DEACTIVATE_TIMEOUT = 10000;
final Context mContext;
@@ -308,7 +324,7 @@
* Whether or not device admin feature is supported. If it isn't return defaults for all
* public methods.
*/
- private boolean mHasFeature;
+ boolean mHasFeature;
private final SecurityLogMonitor mSecurityLogMonitor;
@@ -328,7 +344,7 @@
@Override
public void onReceive(Context context, Intent intent) {
- if (RemoteBugreportUtils.ACTION_REMOTE_BUGREPORT_DISPATCH.equals(intent.getAction())
+ if (DevicePolicyManager.ACTION_REMOTE_BUGREPORT_DISPATCH.equals(intent.getAction())
&& mRemoteBugreportServiceIsActive.get()) {
onBugreportFinished(intent);
}
@@ -342,10 +358,9 @@
String action = intent.getAction();
mInjector.getNotificationManager().cancel(LOG_TAG,
RemoteBugreportUtils.NOTIFICATION_ID);
- if (RemoteBugreportUtils.ACTION_REMOTE_BUGREPORT_SHARING_ACCEPTED.equals(action)) {
+ if (DevicePolicyManager.ACTION_BUGREPORT_SHARING_ACCEPTED.equals(action)) {
onBugreportSharingAccepted();
- } else if (RemoteBugreportUtils.ACTION_REMOTE_BUGREPORT_SHARING_DECLINED
- .equals(action)) {
+ } else if (DevicePolicyManager.ACTION_BUGREPORT_SHARING_DECLINED.equals(action)) {
onBugreportSharingDeclined();
}
mContext.unregisterReceiver(mRemoteBugreportConsentReceiver);
@@ -437,15 +452,14 @@
&& userHandle == mOwners.getDeviceOwnerUserId()
&& getDeviceOwnerRemoteBugreportUri() != null) {
IntentFilter filterConsent = new IntentFilter();
- filterConsent.addAction(
- RemoteBugreportUtils.ACTION_REMOTE_BUGREPORT_SHARING_DECLINED);
- filterConsent.addAction(
- RemoteBugreportUtils.ACTION_REMOTE_BUGREPORT_SHARING_ACCEPTED);
+ filterConsent.addAction(DevicePolicyManager.ACTION_BUGREPORT_SHARING_DECLINED);
+ filterConsent.addAction(DevicePolicyManager.ACTION_BUGREPORT_SHARING_ACCEPTED);
mContext.registerReceiver(mRemoteBugreportConsentReceiver, filterConsent);
- mInjector.getNotificationManager().notify(LOG_TAG,
+ mInjector.getNotificationManager().notifyAsUser(LOG_TAG,
RemoteBugreportUtils.NOTIFICATION_ID,
RemoteBugreportUtils.buildNotification(mContext,
- RemoteBugreportUtils.NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED));
+ DevicePolicyManager.NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED),
+ UserHandle.ALL);
}
if (Intent.ACTION_BOOT_COMPLETED.equals(action)
|| ACTION_EXPIRED_PASSWORD_NOTIFICATION.equals(action)) {
@@ -5068,9 +5082,9 @@
mRemoteBugreportServiceIsActive.set(true);
mRemoteBugreportSharingAccepted.set(false);
registerRemoteBugreportReceivers();
- mInjector.getNotificationManager().notify(LOG_TAG, RemoteBugreportUtils.NOTIFICATION_ID,
+ mInjector.getNotificationManager().notifyAsUser(LOG_TAG, RemoteBugreportUtils.NOTIFICATION_ID,
RemoteBugreportUtils.buildNotification(mContext,
- RemoteBugreportUtils.NOTIFICATION_BUGREPORT_STARTED));
+ DevicePolicyManager.NOTIFICATION_BUGREPORT_STARTED), UserHandle.ALL);
mHandler.postDelayed(mRemoteBugreportTimeoutRunnable,
RemoteBugreportUtils.REMOTE_BUGREPORT_TIMEOUT_MILLIS);
return true;
@@ -5104,7 +5118,7 @@
private void registerRemoteBugreportReceivers() {
try {
IntentFilter filterFinished = new IntentFilter(
- RemoteBugreportUtils.ACTION_REMOTE_BUGREPORT_DISPATCH,
+ DevicePolicyManager.ACTION_REMOTE_BUGREPORT_DISPATCH,
RemoteBugreportUtils.BUGREPORT_MIMETYPE);
mContext.registerReceiver(mRemoteBugreportFinishedReceiver, filterFinished);
} catch (IntentFilter.MalformedMimeTypeException e) {
@@ -5112,8 +5126,8 @@
Slog.w(LOG_TAG, "Failed to set type " + RemoteBugreportUtils.BUGREPORT_MIMETYPE, e);
}
IntentFilter filterConsent = new IntentFilter();
- filterConsent.addAction(RemoteBugreportUtils.ACTION_REMOTE_BUGREPORT_SHARING_DECLINED);
- filterConsent.addAction(RemoteBugreportUtils.ACTION_REMOTE_BUGREPORT_SHARING_ACCEPTED);
+ filterConsent.addAction(DevicePolicyManager.ACTION_BUGREPORT_SHARING_DECLINED);
+ filterConsent.addAction(DevicePolicyManager.ACTION_BUGREPORT_SHARING_ACCEPTED);
mContext.registerReceiver(mRemoteBugreportConsentReceiver, filterConsent);
}
@@ -5126,16 +5140,17 @@
bugreportUriString = bugreportUri.toString();
}
String bugreportHash = intent.getStringExtra(
- RemoteBugreportUtils.EXTRA_REMOTE_BUGREPORT_HASH);
+ DevicePolicyManager.EXTRA_REMOTE_BUGREPORT_HASH);
if (mRemoteBugreportSharingAccepted.get()) {
shareBugreportWithDeviceOwnerIfExists(bugreportUriString, bugreportHash);
mInjector.getNotificationManager().cancel(LOG_TAG,
RemoteBugreportUtils.NOTIFICATION_ID);
} else {
setDeviceOwnerRemoteBugreportUriAndHash(bugreportUriString, bugreportHash);
- mInjector.getNotificationManager().notify(LOG_TAG, RemoteBugreportUtils.NOTIFICATION_ID,
+ mInjector.getNotificationManager().notifyAsUser(LOG_TAG, RemoteBugreportUtils.NOTIFICATION_ID,
RemoteBugreportUtils.buildNotification(mContext,
- RemoteBugreportUtils.NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED));
+ DevicePolicyManager.NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED),
+ UserHandle.ALL);
}
mContext.unregisterReceiver(mRemoteBugreportFinishedReceiver);
}
@@ -5166,9 +5181,10 @@
if (bugreportUriString != null) {
shareBugreportWithDeviceOwnerIfExists(bugreportUriString, bugreportHash);
} else if (mRemoteBugreportServiceIsActive.get()) {
- mInjector.getNotificationManager().notify(LOG_TAG, RemoteBugreportUtils.NOTIFICATION_ID,
+ mInjector.getNotificationManager().notifyAsUser(LOG_TAG, RemoteBugreportUtils.NOTIFICATION_ID,
RemoteBugreportUtils.buildNotification(mContext,
- RemoteBugreportUtils.NOTIFICATION_BUGREPORT_ACCEPTED_NOT_FINISHED));
+ DevicePolicyManager.NOTIFICATION_BUGREPORT_ACCEPTED_NOT_FINISHED),
+ UserHandle.ALL);
}
}
@@ -5934,52 +5950,40 @@
/**
* The Device owner can only be set by adb or an app with the MANAGE_PROFILE_AND_DEVICE_OWNERS
* permission.
- * The device owner can only be set before the setup phase of the primary user has completed,
- * except for adb if no accounts or additional users are present on the device.
*/
private void enforceCanSetDeviceOwnerLocked(int userId) {
- if (mOwners.hasDeviceOwner()) {
- throw new IllegalStateException("Trying to set the device owner, but device owner "
- + "is already set.");
- }
- if (mOwners.hasProfileOwner(userId)) {
- throw new IllegalStateException("Trying to set the device owner, but the user already "
- + "has a profile owner.");
- }
- if (!mUserManager.isUserRunning(new UserHandle(userId))) {
- throw new IllegalStateException("User not running: " + userId);
- }
-
int callingUid = mInjector.binderGetCallingUid();
- if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) {
- if (!hasUserSetupCompleted(UserHandle.USER_SYSTEM)) {
- return;
- }
- // STOPSHIP Do proper check in split user mode
- if (!mInjector.userManagerIsSplitSystemUser()) {
- if (mUserManager.getUserCount() > 1) {
- throw new IllegalStateException(
- "Not allowed to set the device owner because there "
- + "are already several users on the device");
- }
- if (AccountManager.get(mContext).getAccounts().length > 0) {
- throw new IllegalStateException(
- "Not allowed to set the device owner because there "
- + "are already some accounts on the device");
- }
- }
- return;
+ boolean isAdb = callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID;
+ if (!isAdb) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS, null);
}
- // STOPSHIP check the caller UID with userId
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS, null);
- // STOPSHIP Do proper check in split user mode
- if (!mInjector.userManagerIsSplitSystemUser()) {
- if (hasUserSetupCompleted(UserHandle.USER_SYSTEM)) {
- throw new IllegalStateException("Cannot set the device owner if the device is "
- + "already set-up");
- }
+ final int code = checkSetDeviceOwnerPreCondition(userId, isAdb);
+ switch (code) {
+ case CODE_OK:
+ return;
+ case CODE_HAS_DEVICE_OWNER:
+ throw new IllegalStateException(
+ "Trying to set the device owner, but device owner is already set.");
+ case CODE_USER_HAS_PROFILE_OWNER:
+ throw new IllegalStateException("Trying to set the device owner, but the user "
+ + "already has a profile owner.");
+ case CODE_USER_NOT_RUNNING:
+ throw new IllegalStateException("User not running: " + userId);
+ case CODE_NOT_SYSTEM_USER:
+ throw new IllegalStateException("User is not system user");
+ case CODE_USER_SETUP_COMPLETED:
+ throw new IllegalStateException(
+ "Cannot set the device owner if the device is already set-up");
+ case CODE_NONSYSTEM_USER_EXISTS:
+ throw new IllegalStateException("Not allowed to set the device owner because there "
+ + "are already several users on the device");
+ case CODE_ACCOUNTS_NOT_EMPTY:
+ throw new IllegalStateException("Not allowed to set the device owner because there "
+ + "are already some accounts on the device");
+ default:
+ throw new IllegalStateException("Unknown @DeviceOwnerPreConditionCode " + code);
}
}
@@ -7810,6 +7814,26 @@
listener.onCrossProfileWidgetProvidersChanged(userId, packages);
}
}
+
+ @Override
+ public boolean hasDeviceOwnerOrProfileOwner(String packageName, int userId) {
+ if (!mHasFeature || packageName == null) {
+ return false;
+ }
+ if (userId < 0) {
+ throw new UnsupportedOperationException("userId should be >= 0");
+ }
+ synchronized (DevicePolicyManagerService.this) {
+ if (packageName.equals(mOwners.getProfileOwnerPackage(userId))) {
+ return true;
+ }
+ if (userId == mOwners.getDeviceOwnerUserId()
+ && packageName.equals(mOwners.getDeviceOwnerPackageName())) {
+ return true;
+ }
+ }
+ return false;
+ }
}
/**
@@ -8036,6 +8060,10 @@
@Override
public boolean isProvisioningAllowed(String action) {
+ if (!mHasFeature) {
+ return false;
+ }
+
final int callingUserId = mInjector.userHandleGetCallingUserId();
if (DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE.equals(action)) {
if (!hasFeatureManagedUsers()) {
@@ -8100,23 +8128,55 @@
throw new IllegalArgumentException("Unknown provisioning action " + action);
}
- private boolean isDeviceOwnerProvisioningAllowed(int callingUserId) {
- synchronized (this) {
- if (mOwners.hasDeviceOwner()) {
- return false;
+ /*
+ * The device owner can only be set before the setup phase of the primary user has completed,
+ * except for adb command if no accounts or additional users are present on the device.
+ */
+ private synchronized @DeviceOwnerPreConditionCode int checkSetDeviceOwnerPreCondition(
+ int deviceOwnerUserId, boolean isAdb) {
+ if (mOwners.hasDeviceOwner()) {
+ return CODE_HAS_DEVICE_OWNER;
+ }
+ if (mOwners.hasProfileOwner(deviceOwnerUserId)) {
+ return CODE_USER_HAS_PROFILE_OWNER;
+ }
+ if (!mUserManager.isUserRunning(new UserHandle(deviceOwnerUserId))) {
+ return CODE_USER_NOT_RUNNING;
+ }
+ if (isAdb) {
+ // if shell command runs after user setup completed check device status. Otherwise, OK.
+ if (hasUserSetupCompleted(UserHandle.USER_SYSTEM)) {
+ if (!mInjector.userManagerIsSplitSystemUser()) {
+ if (mUserManager.getUserCount() > 1) {
+ return CODE_NONSYSTEM_USER_EXISTS;
+ }
+ if (AccountManager.get(mContext).getAccounts().length > 0) {
+ return CODE_ACCOUNTS_NOT_EMPTY;
+ }
+ } else {
+ // STOPSHIP Do proper check in split user mode
+ }
}
+ return CODE_OK;
+ } else {
+ if (!mInjector.userManagerIsSplitSystemUser()) {
+ // In non-split user mode, DO has to be user 0
+ if (deviceOwnerUserId != UserHandle.USER_SYSTEM) {
+ return CODE_NOT_SYSTEM_USER;
+ }
+ // In non-split user mode, only provision DO before setup wizard completes
+ if (hasUserSetupCompleted(UserHandle.USER_SYSTEM)) {
+ return CODE_USER_SETUP_COMPLETED;
+ }
+ } else {
+ // STOPSHIP Do proper check in split user mode
+ }
+ return CODE_OK;
}
- if (getProfileOwner(callingUserId) != null) {
- return false;
- }
- if (mInjector.settingsGlobalGetInt(Settings.Global.DEVICE_PROVISIONED, 0) != 0) {
- return false;
- }
- if (callingUserId != UserHandle.USER_SYSTEM) {
- // Device owner provisioning can only be initiated from system user.
- return false;
- }
- return true;
+ }
+
+ private boolean isDeviceOwnerProvisioningAllowed(int deviceOwnerUserId) {
+ return CODE_OK == checkSetDeviceOwnerPreCondition(deviceOwnerUserId, /* isAdb */ false);
}
private boolean hasFeatureManagedUsers() {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportUtils.java b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportUtils.java
index 117ba15..6d42dc9 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportUtils.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportUtils.java
@@ -19,8 +19,12 @@
import android.annotation.IntDef;
import android.app.Notification;
import android.app.PendingIntent;
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.os.UserHandle;
+import android.provider.Settings;
import android.text.format.DateUtils;
import com.android.internal.R;
@@ -37,79 +41,62 @@
@Retention(RetentionPolicy.SOURCE)
@IntDef({
- NOTIFICATION_BUGREPORT_STARTED,
- NOTIFICATION_BUGREPORT_ACCEPTED_NOT_FINISHED,
- NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED
+ DevicePolicyManager.NOTIFICATION_BUGREPORT_STARTED,
+ DevicePolicyManager.NOTIFICATION_BUGREPORT_ACCEPTED_NOT_FINISHED,
+ DevicePolicyManager.NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED
})
@interface RemoteBugreportNotificationType {}
- static final int NOTIFICATION_BUGREPORT_STARTED = 1;
- static final int NOTIFICATION_BUGREPORT_ACCEPTED_NOT_FINISHED = 2;
- static final int NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED = 3;
static final long REMOTE_BUGREPORT_TIMEOUT_MILLIS = 10 * DateUtils.MINUTE_IN_MILLIS;
static final String CTL_STOP = "ctl.stop";
static final String REMOTE_BUGREPORT_SERVICE = "bugreportremote";
- static final String ACTION_REMOTE_BUGREPORT_DISPATCH =
- "android.intent.action.REMOTE_BUGREPORT_DISPATCH";
- static final String ACTION_REMOTE_BUGREPORT_SHARING_ACCEPTED =
- "com.android.server.action.REMOTE_BUGREPORT_SHARING_ACCEPTED";
- static final String ACTION_REMOTE_BUGREPORT_SHARING_DECLINED =
- "com.android.server.action.REMOTE_BUGREPORT_SHARING_DECLINED";
- static final String EXTRA_REMOTE_BUGREPORT_HASH = "android.intent.extra.REMOTE_BUGREPORT_HASH";
-
static final String BUGREPORT_MIMETYPE = "application/vnd.android.bugreport";
static Notification buildNotification(Context context,
@RemoteBugreportNotificationType int type) {
+ Intent dialogIntent = new Intent(Settings.ACTION_SHOW_REMOTE_BUGREPORT_DIALOG);
+ dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ dialogIntent.putExtra(DevicePolicyManager.EXTRA_BUGREPORT_NOTIFICATION_TYPE, type);
+ PendingIntent pendingDialogIntent = PendingIntent.getActivityAsUser(context, type,
+ dialogIntent, 0, null, UserHandle.CURRENT);
+
Notification.Builder builder = new Notification.Builder(context)
.setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
.setOngoing(true)
.setLocalOnly(true)
+ .setPriority(Notification.PRIORITY_HIGH)
+ .setContentIntent(pendingDialogIntent)
.setColor(context.getColor(
com.android.internal.R.color.system_notification_accent_color));
- if (type == NOTIFICATION_BUGREPORT_ACCEPTED_NOT_FINISHED) {
+ if (type == DevicePolicyManager.NOTIFICATION_BUGREPORT_ACCEPTED_NOT_FINISHED) {
builder.setContentTitle(context.getString(
- R.string.sharing_remote_bugreport_notification_title))
- .setContentText(context.getString(
- R.string.sharing_remote_bugreport_notification_message))
- .setPriority(Notification.PRIORITY_HIGH)
- .setProgress(0, 0, true)
- .setStyle(new Notification.BigTextStyle().bigText(context.getString(
- R.string.sharing_remote_bugreport_notification_message)));
- } else {
+ R.string.sharing_remote_bugreport_notification_title))
+ .setProgress(0, 0, true);
+ } else if (type == DevicePolicyManager.NOTIFICATION_BUGREPORT_STARTED) {
+ builder.setContentTitle(context.getString(
+ R.string.taking_remote_bugreport_notification_title))
+ .setProgress(0, 0, true);
+ } else if (type == DevicePolicyManager.NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED) {
PendingIntent pendingIntentAccept = PendingIntent.getBroadcast(context, NOTIFICATION_ID,
- new Intent(ACTION_REMOTE_BUGREPORT_SHARING_ACCEPTED),
+ new Intent(DevicePolicyManager.ACTION_BUGREPORT_SHARING_ACCEPTED),
PendingIntent.FLAG_CANCEL_CURRENT);
PendingIntent pendingIntentDecline = PendingIntent.getBroadcast(context,
- NOTIFICATION_ID, new Intent(ACTION_REMOTE_BUGREPORT_SHARING_DECLINED),
+ NOTIFICATION_ID, new Intent(
+ DevicePolicyManager.ACTION_BUGREPORT_SHARING_DECLINED),
PendingIntent.FLAG_CANCEL_CURRENT);
builder.addAction(new Notification.Action.Builder(null /* icon */, context.getString(
- R.string.share_remote_bugreport_notification_decline),
- pendingIntentDecline).build())
+ R.string.decline_remote_bugreport_action), pendingIntentDecline).build())
.addAction(new Notification.Action.Builder(null /* icon */, context.getString(
- R.string.share_remote_bugreport_notification_accept),
- pendingIntentAccept).build())
+ R.string.share_remote_bugreport_action), pendingIntentAccept).build())
.setContentTitle(context.getString(
- R.string.share_remote_bugreport_notification_title));
-
- if (type == NOTIFICATION_BUGREPORT_STARTED) {
- builder.setContentText(context.getString(
- R.string.share_remote_bugreport_notification_message))
- .setStyle(new Notification.BigTextStyle().bigText(context.getString(
- R.string.share_remote_bugreport_notification_message)))
- .setProgress(0, 0, true)
- .setPriority(Notification.PRIORITY_MAX)
- .setVibrate(new long[0]);
- } else if (type == NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED) {
- builder.setContentText(context.getString(
- R.string.share_finished_remote_bugreport_notification_message))
- .setStyle(new Notification.BigTextStyle().bigText(context.getString(
- R.string.share_finished_remote_bugreport_notification_message)))
- .setPriority(Notification.PRIORITY_HIGH);
- }
+ R.string.share_remote_bugreport_notification_title))
+ .setContentText(context.getString(
+ R.string.share_remote_bugreport_notification_message_finished))
+ .setStyle(new Notification.BigTextStyle().bigText(context.getString(
+ R.string.share_remote_bugreport_notification_message_finished)));
}
return builder.build();
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 9aa2b94..b8c31e3 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -431,6 +431,24 @@
mPackageManager = mSystemContext.getPackageManager();
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ // Manages A/B OTA dexopting. This is a bootstrap service as we need it to rename
+ // A/B artifacts after boot, before anything else might touch/need them.
+ // Note: this isn't needed during decryption (we don't have /data anyways).
+ if (!mOnlyCore) {
+ boolean disableOtaDexopt = SystemProperties.getBoolean("config.disable_otadexopt",
+ false);
+ if (!disableOtaDexopt) {
+ traceBeginAndSlog("StartOtaDexOptService");
+ try {
+ OtaDexoptService.main(mSystemContext, mPackageManagerService);
+ } catch (Throwable e) {
+ reportWtf("starting OtaDexOptService", e);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ }
+ }
+ }
+
traceBeginAndSlog("StartUserManagerService");
ServiceManager.addService(Context.USER_SERVICE, UserManagerService.getInstance());
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
@@ -1124,19 +1142,6 @@
reportWtf("starting BackgroundDexOptService", e);
}
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
-
- // Manages A/B OTA dexopting.
- boolean disableOtaDexopt = SystemProperties.getBoolean("config.disable_otadexopt",
- false);
- if (!disableOtaDexopt) {
- traceBeginAndSlog("StartOtaDexOptService");
- try {
- OtaDexoptService.main(mSystemContext, mPackageManagerService);
- } catch (Throwable e) {
- reportWtf("starting BackgroundDexOptService", e);
- }
- Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
- }
}
mSystemServiceManager.startService(LauncherAppsService.class);
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index 4f99bff..e2562cd 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -84,10 +84,10 @@
public class DhcpClient extends StateMachine {
private static final String TAG = "DhcpClient";
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
private static final boolean STATE_DBG = false;
private static final boolean MSG_DBG = false;
- private static final boolean PACKET_DBG = true;
+ private static final boolean PACKET_DBG = false;
// Timers and timeouts.
private static final int SECONDS = 1000;
@@ -342,14 +342,14 @@
@Override
public void run() {
- maybeLog("Receive thread started");
+ if (DBG) Log.d(TAG, "Receive thread started");
while (!stopped) {
int length = 0; // Or compiler can't tell it's initialized if a parse error occurs.
try {
length = Os.read(mPacketSock, mPacket, 0, mPacket.length);
DhcpPacket packet = null;
packet = DhcpPacket.decodeFullPacket(mPacket, length, DhcpPacket.ENCAP_L2);
- maybeLog("Received packet: " + packet);
+ if (DBG) Log.d(TAG, "Received packet: " + packet);
sendMessage(CMD_RECEIVED_PACKET, packet);
} catch (IOException|ErrnoException e) {
if (!stopped) {
@@ -362,7 +362,7 @@
}
}
}
- maybeLog("Receive thread stopped");
+ if (DBG) Log.d(TAG, "Receive thread stopped");
}
}
@@ -373,12 +373,12 @@
private boolean transmitPacket(ByteBuffer buf, String description, Inet4Address to) {
try {
if (to.equals(INADDR_BROADCAST)) {
- maybeLog("Broadcasting " + description);
+ if (DBG) Log.d(TAG, "Broadcasting " + description);
Os.sendto(mPacketSock, buf.array(), 0, buf.limit(), 0, mInterfaceBroadcastAddr);
} else {
// It's safe to call getpeername here, because we only send unicast packets if we
// have an IP address, and we connect the UDP socket in DhcpHaveAddressState#enter.
- maybeLog("Unicasting " + description + " to " + Os.getpeername(mUdpSock));
+ if (DBG) Log.d(TAG, "Unicasting " + description + " to " + Os.getpeername(mUdpSock));
Os.write(mUdpSock, buf);
}
} catch(ErrnoException|IOException e) {
@@ -454,10 +454,6 @@
mController.sendMessage(CMD_ON_QUIT);
}
- private void maybeLog(String msg) {
- if (DBG) Log.d(TAG, msg);
- }
-
abstract class LoggingState extends State {
public void enter() {
if (STATE_DBG) Log.d(TAG, "Entering state " + getName());
@@ -592,7 +588,7 @@
transitionTo(mStoppedState);
return HANDLED;
case CMD_ONESHOT_TIMEOUT:
- maybeLog("Timed out");
+ if (DBG) Log.d(TAG, "Timed out");
notifyFailure();
return HANDLED;
default:
@@ -790,7 +786,7 @@
@Override
public void exit() {
- maybeLog("Clearing IP address");
+ if (DBG) Log.d(TAG, "Clearing IP address");
setIpAddress(new LinkAddress("0.0.0.0/0"));
}
}
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
index afae956..c7c5015 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -63,7 +63,7 @@
* @hide
*/
public class IpManager extends StateMachine {
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
private static final boolean VDBG = false;
// For message logging.
diff --git a/services/net/java/android/net/ip/IpReachabilityMonitor.java b/services/net/java/android/net/ip/IpReachabilityMonitor.java
index 88155f7..5b4fd50 100644
--- a/services/net/java/android/net/ip/IpReachabilityMonitor.java
+++ b/services/net/java/android/net/ip/IpReachabilityMonitor.java
@@ -132,7 +132,7 @@
*/
public class IpReachabilityMonitor {
private static final String TAG = "IpReachabilityMonitor";
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
private static final boolean VDBG = false;
public interface Callback {
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 212b37c..8c47087 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -969,6 +969,8 @@
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+ when(mContext.userManagerForMock.isSplitSystemUser()).thenReturn(true);
+
// Make sure the admin packge is installed to each user.
setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
setUpPackageManagerForAdmin(admin3, DpmMockContext.CALLER_SYSTEM_USER_UID);
@@ -1008,6 +1010,7 @@
* finds the right component from a package name upon migration.
*/
public void testDeviceOwnerMigration() throws Exception {
+ when(mContext.userManagerForMock.isSplitSystemUser()).thenReturn(true);
checkDeviceOwnerWithMultipleDeviceAdmins();
// Overwrite the device owner setting and clears the clas name.
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 6ab0b99..837b4a4 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -75,7 +75,7 @@
*/
public class VoiceInteractionManagerService extends SystemService {
static final String TAG = "VoiceInteractionManagerService";
- static final boolean DEBUG = true;
+ static final boolean DEBUG = false;
final Context mContext;
final ContentResolver mResolver;
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 1278c07..cd1c5e9 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -637,7 +637,7 @@
sDefaults.putBoolean(KEY_SHOW_CDMA_CHOICES_BOOL, false);
sDefaults.putBoolean(KEY_SHOW_ONSCREEN_DIAL_BUTTON_BOOL, true);
sDefaults.putBoolean(KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL, true);
- sDefaults.putBoolean(KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL, true);
+ sDefaults.putBoolean(KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL, false);
sDefaults.putBoolean(KEY_SUPPORT_SWAP_AFTER_MERGE_BOOL, true);
sDefaults.putBoolean(KEY_USE_HFA_FOR_PROVISIONING_BOOL, false);
sDefaults.putBoolean(KEY_USE_OTASP_FOR_PROVISIONING_BOOL, false);
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index ad007c6..39a9295 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -36,7 +36,7 @@
public class ServiceState implements Parcelable {
static final String LOG_TAG = "PHONE";
- static final boolean DBG = true;
+ static final boolean DBG = false;
static final boolean VDBG = false; // STOPSHIP if true
/**
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index e90be91..b482811 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -4403,7 +4403,6 @@
Log.e(TAG, "Error calling ITelephony#getDataEnabled", e);
} catch (NullPointerException e) {
}
- Log.d(TAG, "getDataEnabled: retVal=" + retVal);
return retVal;
}
diff --git a/tests/OneMedia/src/com/android/onemedia/playback/LocalRenderer.java b/tests/OneMedia/src/com/android/onemedia/playback/LocalRenderer.java
index c8a8d6c..6463e1f 100644
--- a/tests/OneMedia/src/com/android/onemedia/playback/LocalRenderer.java
+++ b/tests/OneMedia/src/com/android/onemedia/playback/LocalRenderer.java
@@ -47,7 +47,7 @@
OnBufferingUpdateListener, OnCompletionListener, OnErrorListener,
OnAudioFocusChangeListener {
private static final String TAG = "MediaPlayerManager";
- private static final boolean DEBUG = true;
+ private static final boolean DEBUG = false;
private static long sDebugInstanceId = 0;
private static final String[] SUPPORTED_FEATURES = {
diff --git a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/TestSoundTriggerActivity.java b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/TestSoundTriggerActivity.java
index 4770c05..3ca96d2 100644
--- a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/TestSoundTriggerActivity.java
+++ b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/TestSoundTriggerActivity.java
@@ -40,7 +40,7 @@
public class TestSoundTriggerActivity extends Activity {
private static final String TAG = "TestSoundTriggerActivity";
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
private SoundTriggerUtil mSoundTriggerUtil;
private Random mRandom;
diff --git a/tests/SoundTriggerTests/Android.mk b/tests/SoundTriggerTests/Android.mk
index 407a9d7..ac562b9 100644
--- a/tests/SoundTriggerTests/Android.mk
+++ b/tests/SoundTriggerTests/Android.mk
@@ -18,7 +18,14 @@
LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
+ifeq ($(SOUND_TRIGGER_USE_STUB_MODULE), 1)
+ LOCAL_SRC_FILES := $(call all-subdir-java-files)
+ LOCAL_PRIVILEGED_MODULE := true
+ LOCAL_CERTIFICATE := platform
+ TARGET_OUT_DATA_APPS_PRIVILEGED := $(TARGET_OUT_DATA)/priv-app
+else
+ LOCAL_SRC_FILES := src/android/hardware/soundtrigger/SoundTriggerTest.java
+endif
LOCAL_JAVA_LIBRARIES := android.test.runner
diff --git a/tests/SoundTriggerTests/AndroidManifest.xml b/tests/SoundTriggerTests/AndroidManifest.xml
index 5e5a108..e8b9dd3 100644
--- a/tests/SoundTriggerTests/AndroidManifest.xml
+++ b/tests/SoundTriggerTests/AndroidManifest.xml
@@ -14,7 +14,10 @@
limitations under the License.
-->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android.hardware.soundtrigger">
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.hardware.soundtrigger">
+ <uses-permission android:name="android.permission.MANAGE_SOUND_TRIGGER" />
+
<application>
<uses-library android:name="android.test.runner" />
</application>
diff --git a/tests/SoundTriggerTests/src/android/hardware/soundtrigger/stubhal/GenericSoundModelTest.java b/tests/SoundTriggerTests/src/android/hardware/soundtrigger/stubhal/GenericSoundModelTest.java
new file mode 100644
index 0000000..7acb472
--- /dev/null
+++ b/tests/SoundTriggerTests/src/android/hardware/soundtrigger/stubhal/GenericSoundModelTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.soundtrigger;
+
+import java.util.Random;
+import java.util.UUID;
+
+import android.content.Context;
+import android.hardware.soundtrigger.SoundTrigger.GenericSoundModel;
+import android.media.soundtrigger.SoundTriggerManager;
+import android.os.ParcelUuid;
+import android.os.ServiceManager;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.internal.app.ISoundTriggerService;
+
+import java.util.Arrays;
+import java.util.Random;
+import java.util.UUID;
+
+public class GenericSoundModelTest extends AndroidTestCase {
+ private Random mRandom = new Random();
+
+ @SmallTest
+ public void testUpdateGenericSoundModel() throws Exception {
+ Context context = getContext();
+ ISoundTriggerService mSoundTriggerService = ISoundTriggerService.Stub.asInterface(
+ ServiceManager.getService(Context.SOUND_TRIGGER_SERVICE));
+ SoundTriggerManager mSoundTriggerManager = (SoundTriggerManager) context.getSystemService(
+ Context.SOUND_TRIGGER_SERVICE);
+
+ byte[] data = new byte[1024];
+ mRandom.nextBytes(data);
+ UUID modelUuid = UUID.randomUUID();
+ UUID mVendorUuid = UUID.randomUUID();
+ GenericSoundModel model = new GenericSoundModel(modelUuid, mVendorUuid, data);
+
+ mSoundTriggerService.updateSoundModel(model);
+ GenericSoundModel returnedModel =
+ mSoundTriggerService.getSoundModel(new ParcelUuid(modelUuid));
+
+ assertEquals(model, returnedModel);
+
+ // Cleanup sound model
+ mSoundTriggerService.deleteSoundModel(new ParcelUuid(modelUuid));
+ }
+
+
+ @SmallTest
+ public void testDeleteGenericSoundModel() throws Exception {
+ Context context = getContext();
+ ISoundTriggerService mSoundTriggerService = ISoundTriggerService.Stub.asInterface(
+ ServiceManager.getService(Context.SOUND_TRIGGER_SERVICE));
+ SoundTriggerManager mSoundTriggerManager = (SoundTriggerManager) context.getSystemService(
+ Context.SOUND_TRIGGER_SERVICE);
+
+ byte[] data = new byte[1024];
+ mRandom.nextBytes(data);
+ UUID modelUuid = UUID.randomUUID();
+ UUID mVendorUuid = UUID.randomUUID();
+ GenericSoundModel model = new GenericSoundModel(modelUuid, mVendorUuid, data);
+
+ mSoundTriggerService.updateSoundModel(model);
+ mSoundTriggerService.deleteSoundModel(new ParcelUuid(modelUuid));
+
+ GenericSoundModel returnedModel =
+ mSoundTriggerService.getSoundModel(new ParcelUuid(modelUuid));
+ assertEquals(null, returnedModel);
+ }
+}
diff --git a/tests/VoiceEnrollment/src/com/android/test/voiceenrollment/TestEnrollmentActivity.java b/tests/VoiceEnrollment/src/com/android/test/voiceenrollment/TestEnrollmentActivity.java
index 2494db7..54c944f9 100644
--- a/tests/VoiceEnrollment/src/com/android/test/voiceenrollment/TestEnrollmentActivity.java
+++ b/tests/VoiceEnrollment/src/com/android/test/voiceenrollment/TestEnrollmentActivity.java
@@ -31,7 +31,7 @@
public class TestEnrollmentActivity extends Activity {
private static final String TAG = "TestEnrollmentActivity";
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
/** Keyphrase related constants, must match those defined in enrollment_application.xml */
private static final int KEYPHRASE_ID = 101;
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp
index 3b01827..cbd8480 100644
--- a/tools/aapt/AaptAssets.cpp
+++ b/tools/aapt/AaptAssets.cpp
@@ -373,7 +373,7 @@
void AaptLocaleValue::initFromResTable(const ResTable_config& config) {
config.unpackLanguage(language);
config.unpackRegion(region);
- if (config.localeScriptWasProvided) {
+ if (config.localeScript[0] && !config.localeScriptWasComputed) {
memcpy(script, config.localeScript, sizeof(config.localeScript));
}
@@ -388,10 +388,10 @@
if (script[0]) {
memcpy(out->localeScript, script, sizeof(out->localeScript));
- out->localeScriptWasProvided = true;
+ out->localeScriptWasComputed = false;
} else {
out->computeScript();
- out->localeScriptWasProvided = false;
+ out->localeScriptWasComputed = true;
}
if (variant[0]) {
diff --git a/tools/aapt2/Locale.cpp b/tools/aapt2/Locale.cpp
index 6acf3b0..12f56fc 100644
--- a/tools/aapt2/Locale.cpp
+++ b/tools/aapt2/Locale.cpp
@@ -253,7 +253,7 @@
void LocaleValue::initFromResTable(const ResTable_config& config) {
config.unpackLanguage(language);
config.unpackRegion(region);
- if (config.localeScriptWasProvided) {
+ if (config.localeScript[0] && !config.localeScriptWasComputed) {
memcpy(script, config.localeScript, sizeof(config.localeScript));
}
@@ -268,10 +268,10 @@
if (script[0]) {
memcpy(out->localeScript, script, sizeof(out->localeScript));
- out->localeScriptWasProvided = true;
+ out->localeScriptWasComputed = false;
} else {
out->computeScript();
- out->localeScriptWasProvided = false;
+ out->localeScriptWasComputed = true;
}
if (variant[0]) {
diff --git a/tools/layoutlib/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java
index 22d15e1..eef8235 100644
--- a/tools/layoutlib/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java
@@ -16,7 +16,6 @@
package android.graphics.drawable;
-import com.android.internal.R;
import com.android.layoutlib.bridge.impl.DelegateManager;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
@@ -136,7 +135,21 @@
VPathRenderer_Delegate nativePathRenderer =
getDelegate(rendererPtr);
+ Canvas_Delegate.native_save(canvasWrapperPtr, MATRIX_SAVE_FLAG | CLIP_SAVE_FLAG);
+ Canvas_Delegate.native_translate(canvasWrapperPtr, bounds.left, bounds.top);
+
+ if (needsMirroring) {
+ Canvas_Delegate.native_translate(canvasWrapperPtr, bounds.width(), 0);
+ Canvas_Delegate.native_scale(canvasWrapperPtr, -1.0f, 1.0f);
+ }
+
+ // At this point, canvas has been translated to the right position.
+ // And we use this bound for the destination rect for the drawBitmap, so
+ // we offset to (0, 0);
+ bounds.offsetTo(0, 0);
nativePathRenderer.draw(canvasWrapperPtr, colorFilterPtr, bounds.width(), bounds.height());
+
+ Canvas_Delegate.native_restore(canvasWrapperPtr, true);
}
@LayoutlibDelegate
@@ -492,28 +505,6 @@
super(copy);
}
- public void inflate(Resources r, AttributeSet attrs, Theme theme) {
- final TypedArray a = obtainAttributes(r, theme, attrs,
- R.styleable.VectorDrawableClipPath);
- updateStateFromTypedArray(a);
- a.recycle();
- }
-
- private void updateStateFromTypedArray(TypedArray a) {
- // Account for any configuration changes.
- mChangingConfigurations |= a.getChangingConfigurations();
-
- final String pathName = a.getString(R.styleable.VectorDrawableClipPath_name);
- if (pathName != null) {
- mPathName = pathName;
- }
-
- final String pathData = a.getString(R.styleable.VectorDrawableClipPath_pathData);
- if (pathData != null) {
- mNodes = PathParser_Delegate.createNodesFromPathData(pathData);
- }
- }
-
@Override
public boolean isClipPath() {
return true;
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index d2103c8..97195e4 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -349,7 +349,7 @@
}
@Override
- public void notifyAppStopped(IBinder token) throws RemoteException {
+ public void notifyAppStopped(IBinder token, boolean stopped) throws RemoteException {
// TODO Auto-generated method stub
}
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index ddd8f43..7dc8049 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -1357,6 +1357,7 @@
append(" PROVIDER-NAME: ").append(this.providerFriendlyName).
append(" BSSID: ").append(this.BSSID).append(" FQDN: ").append(this.FQDN)
.append(" PRIO: ").append(this.priority)
+ .append(" HIDDEN: ").append(this.hiddenSSID)
.append('\n');
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index e2dd111..a5bfd3c 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -889,24 +889,6 @@
}
/**
- * 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.
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 69e179d..97dd985 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -192,6 +192,11 @@
* for a given period
*/
public int stepCount;
+ /**
+ * Flag to indicate if the scan settings are targeted for PNO scan.
+ * {@hide}
+ */
+ public boolean isPnoScan;
/** Implement the Parcelable interface {@hide} */
public int describeContents() {
@@ -207,6 +212,7 @@
dest.writeInt(maxScansToCache);
dest.writeInt(maxPeriodInMs);
dest.writeInt(stepCount);
+ dest.writeInt(isPnoScan ? 1 : 0);
if (channels != null) {
dest.writeInt(channels.length);
@@ -234,6 +240,7 @@
settings.maxScansToCache = in.readInt();
settings.maxPeriodInMs = in.readInt();
settings.stepCount = in.readInt();
+ settings.isPnoScan = in.readInt() == 1;
int num_channels = in.readInt();
settings.channels = new ChannelSpec[num_channels];
for (int i = 0; i < num_channels; i++) {
@@ -436,6 +443,158 @@
};
}
+ /** {@hide} */
+ public static final String PNO_PARAMS_PNO_SETTINGS_KEY = "PnoSettings";
+ /** {@hide} */
+ public static final String PNO_PARAMS_SCAN_SETTINGS_KEY = "ScanSettings";
+ /**
+ * PNO scan configuration parameters to be sent to {@link #startPnoScan}.
+ * Note: This structure needs to be in sync with |wifi_epno_params| struct in gscan HAL API.
+ * {@hide}
+ */
+ public static class PnoSettings implements Parcelable {
+ /**
+ * Pno network to be added to the PNO scan filtering.
+ * {@hide}
+ */
+ public static class PnoNetwork {
+ /*
+ * Pno flags bitmask to be set in {@link #PnoNetwork.flags}
+ */
+ /** Whether directed scan needs to be performed (for hidden SSIDs) */
+ public static final byte FLAG_DIRECTED_SCAN = (1 << 0);
+ /** Whether PNO event shall be triggered if the network is found on A band */
+ public static final byte FLAG_A_BAND = (1 << 1);
+ /** Whether PNO event shall be triggered if the network is found on G band */
+ public static final byte FLAG_G_BAND = (1 << 2);
+ /**
+ * Whether strict matching is required
+ * If required then the firmware must store the network's SSID and not just a hash
+ */
+ public static final byte FLAG_STRICT_MATCH = (1 << 3);
+ /**
+ * If this SSID should be considered the same network as the currently connected
+ * one for scoring.
+ */
+ public static final byte FLAG_SAME_NETWORK = (1 << 4);
+
+ /*
+ * Code for matching the beacon AUTH IE - additional codes. Bitmask to be set in
+ * {@link #PnoNetwork.authBitField}
+ */
+ /** Open Network */
+ public static final byte AUTH_CODE_OPEN = (1 << 0);
+ /** WPA_PSK or WPA2PSK */
+ public static final byte AUTH_CODE_PSK = (1 << 1);
+ /** any EAPOL */
+ public static final byte AUTH_CODE_EAPOL = (1 << 2);
+
+ /** SSID of the network */
+ public String ssid;
+ /** Network ID in wpa_supplicant */
+ public int networkId;
+ /** Assigned priority for the network */
+ public int priority;
+ /** Bitmask of the FLAG_XXX */
+ public byte flags;
+ /** Bitmask of the ATUH_XXX */
+ public byte authBitField;
+
+ /**
+ * default constructor for PnoNetwork
+ */
+ public PnoNetwork(String ssid) {
+ this.ssid = ssid;
+ flags = 0;
+ authBitField = 0;
+ }
+ }
+
+ /** Connected vs Disconnected PNO flag {@hide} */
+ public boolean isConnected;
+ /** Minimum 5GHz RSSI for a BSSID to be considered */
+ public int min5GHzRssi;
+ /** Minimum 2.4GHz RSSI for a BSSID to be considered */
+ public int min24GHzRssi;
+ /** Maximum score that a network can have before bonuses */
+ public int initialScoreMax;
+ /**
+ * Only report when there is a network's score this much higher
+ * than the current connection.
+ */
+ public int currentConnectionBonus;
+ /** score bonus for all networks with the same network flag */
+ public int sameNetworkBonus;
+ /** score bonus for networks that are not open */
+ public int secureBonus;
+ /** 5GHz RSSI score bonus (applied to all 5GHz networks) */
+ public int band5GHzBonus;
+ /** Pno Network filter list */
+ public PnoNetwork[] networkList;
+
+ /** Implement the Parcelable interface {@hide} */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(isConnected ? 1 : 0);
+ dest.writeInt(min5GHzRssi);
+ dest.writeInt(min24GHzRssi);
+ dest.writeInt(initialScoreMax);
+ dest.writeInt(currentConnectionBonus);
+ dest.writeInt(sameNetworkBonus);
+ dest.writeInt(secureBonus);
+ dest.writeInt(band5GHzBonus);
+ if (networkList != null) {
+ dest.writeInt(networkList.length);
+ for (int i = 0; i < networkList.length; i++) {
+ dest.writeString(networkList[i].ssid);
+ dest.writeInt(networkList[i].networkId);
+ dest.writeInt(networkList[i].priority);
+ dest.writeByte(networkList[i].flags);
+ dest.writeByte(networkList[i].authBitField);
+ }
+ } else {
+ dest.writeInt(0);
+ }
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public static final Creator<PnoSettings> CREATOR =
+ new Creator<PnoSettings>() {
+ public PnoSettings createFromParcel(Parcel in) {
+ PnoSettings settings = new PnoSettings();
+ settings.isConnected = in.readInt() == 1;
+ settings.min5GHzRssi = in.readInt();
+ settings.min24GHzRssi = in.readInt();
+ settings.initialScoreMax = in.readInt();
+ settings.currentConnectionBonus = in.readInt();
+ settings.sameNetworkBonus = in.readInt();
+ settings.secureBonus = in.readInt();
+ settings.band5GHzBonus = in.readInt();
+ int numNetworks = in.readInt();
+ settings.networkList = new PnoNetwork[numNetworks];
+ for (int i = 0; i < numNetworks; i++) {
+ String ssid = in.readString();
+ PnoNetwork network = new PnoNetwork(ssid);
+ network.networkId = in.readInt();
+ network.priority = in.readInt();
+ network.flags = in.readByte();
+ network.authBitField = in.readByte();
+ settings.networkList[i] = network;
+ }
+ return settings;
+ }
+
+ public PnoSettings[] newArray(int size) {
+ return new PnoSettings[size];
+ }
+ };
+
+ }
+
/**
* interface to get scan events on; specify this on {@link #startBackgroundScan} or
* {@link #startScan}
@@ -456,6 +615,18 @@
public void onFullResult(ScanResult fullScanResult);
}
+ /**
+ * interface to get PNO scan events on; specify this on {@link #startDisconnectedPnoScan} and
+ * {@link #startConnectedPnoScan}.
+ * {@hide}
+ */
+ public interface PnoScanListener extends ScanListener {
+ /**
+ * Invoked when one of the PNO networks are found in scan results.
+ */
+ void onPnoNetworkFound(ScanResult[] results);
+ }
+
/** start wifi scan in background
* @param settings specifies various parameters for the scan; for more information look at
* {@link ScanSettings}
@@ -521,6 +692,75 @@
sAsyncChannel.sendMessage(CMD_STOP_SINGLE_SCAN, 0, key);
}
+ private void startPnoScan(ScanSettings scanSettings, PnoSettings pnoSettings, int key) {
+ // Bundle up both the settings and send it across.
+ Bundle pnoParams = new Bundle();
+ if (pnoParams == null) return;
+ // Set the PNO scan flag.
+ scanSettings.isPnoScan = true;
+ pnoParams.putParcelable(PNO_PARAMS_SCAN_SETTINGS_KEY, scanSettings);
+ pnoParams.putParcelable(PNO_PARAMS_PNO_SETTINGS_KEY, pnoSettings);
+ sAsyncChannel.sendMessage(CMD_START_PNO_SCAN, 0, key, pnoParams);
+ }
+ /**
+ * Start wifi connected PNO scan
+ * @param scanSettings specifies various parameters for the scan; for more information look at
+ * {@link ScanSettings}
+ * @param pnoSettings specifies various parameters for PNO; for more information look at
+ * {@link PnoSettings}
+ * @param listener specifies the object to report events to. This object is also treated as a
+ * key for this scan, and must also be specified to cancel the scan. Multiple
+ * scans should also not share this object.
+ * {@hide}
+ */
+ public void startConnectedPnoScan(ScanSettings scanSettings, PnoSettings pnoSettings,
+ PnoScanListener listener) {
+ Preconditions.checkNotNull(listener, "listener cannot be null");
+ Preconditions.checkNotNull(pnoSettings, "pnoSettings cannot be null");
+ int key = addListener(listener);
+ if (key == INVALID_KEY) return;
+ validateChannel();
+ pnoSettings.isConnected = true;
+ startPnoScan(scanSettings, pnoSettings, key);
+ }
+ /**
+ * Start wifi disconnected PNO scan
+ * @param scanSettings specifies various parameters for the scan; for more information look at
+ * {@link ScanSettings}
+ * @param pnoSettings specifies various parameters for PNO; for more information look at
+ * {@link PnoSettings}
+ * @param listener specifies the object to report events to. This object is also treated as a
+ * key for this scan, and must also be specified to cancel the scan. Multiple
+ * scans should also not share this object.
+ * {@hide}
+ */
+ public void startDisconnectedPnoScan(ScanSettings scanSettings, PnoSettings pnoSettings,
+ PnoScanListener listener) {
+ Preconditions.checkNotNull(listener, "listener cannot be null");
+ Preconditions.checkNotNull(pnoSettings, "pnoSettings cannot be null");
+ int key = addListener(listener);
+ if (key == INVALID_KEY) return;
+ validateChannel();
+ pnoSettings.isConnected = false;
+ startPnoScan(scanSettings, pnoSettings, key);
+ }
+ /**
+ * Stop an ongoing wifi PNO scan
+ * @param pnoSettings specifies various parameters for PNO; for more information look at
+ * {@link PnoSettings}
+ * @param listener specifies which scan to cancel; must be same object as passed in {@link
+ * #startPnoScan}
+ * TODO(rpius): Check if we can remove pnoSettings param in stop.
+ * {@hide}
+ */
+ public void stopPnoScan(PnoSettings pnoSettings, ScanListener listener) {
+ Preconditions.checkNotNull(listener, "listener cannot be null");
+ int key = removeListener(listener);
+ if (key == INVALID_KEY) return;
+ validateChannel();
+ sAsyncChannel.sendMessage(CMD_STOP_PNO_SCAN, 0, key, pnoSettings);
+ }
+
/** specifies information about an access point of interest */
public static class BssidInfo {
/** bssid of the access point; in XX:XX:XX:XX:XX:XX format */
@@ -824,6 +1064,12 @@
public static final int CMD_STOP_SINGLE_SCAN = BASE + 22;
/** @hide */
public static final int CMD_SINGLE_SCAN_COMPLETED = BASE + 23;
+ /** @hide */
+ public static final int CMD_START_PNO_SCAN = BASE + 24;
+ /** @hide */
+ public static final int CMD_STOP_PNO_SCAN = BASE + 25;
+ /** @hide */
+ public static final int CMD_PNO_NETWORK_FOUND = BASE + 26;
private Context mContext;
private IWifiScanner mService;
@@ -1110,6 +1356,10 @@
if (DBG) Log.d(TAG, "removing listener for single scan");
removeListener(msg.arg2);
break;
+ case CMD_PNO_NETWORK_FOUND:
+ ((PnoScanListener) listener).onPnoNetworkFound(
+ ((ParcelableScanResults) msg.obj).getResults());
+ return;
default:
if (DBG) Log.d(TAG, "Ignoring message " + msg.what);
return;