Merge "Get correct hasEnrolled for work profiles"
diff --git a/Android.bp b/Android.bp
index 9f93d39..a726f08 100644
--- a/Android.bp
+++ b/Android.bp
@@ -703,6 +703,7 @@
"android.hardware.vibrator-V1.2-java",
"android.hardware.wifi-V1.0-java-constants",
"android.hardware.radio-V1.0-java",
+ "android.hardware.radio-V1.3-java",
"android.hardware.usb.gadget-V1.0-java",
],
diff --git a/api/current.txt b/api/current.txt
index fe6b2a6..373bc18 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -23,6 +23,7 @@
field public static final java.lang.String BIND_ACCESSIBILITY_SERVICE = "android.permission.BIND_ACCESSIBILITY_SERVICE";
field public static final java.lang.String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET";
field public static final java.lang.String BIND_AUTOFILL_SERVICE = "android.permission.BIND_AUTOFILL_SERVICE";
+ field public static final java.lang.String BIND_CALL_REDIRECTION_SERVICE = "android.permission.BIND_CALL_REDIRECTION_SERVICE";
field public static final deprecated java.lang.String BIND_CARRIER_MESSAGING_SERVICE = "android.permission.BIND_CARRIER_MESSAGING_SERVICE";
field public static final java.lang.String BIND_CARRIER_SERVICES = "android.permission.BIND_CARRIER_SERVICES";
field public static final java.lang.String BIND_CHOOSER_TARGET_SERVICE = "android.permission.BIND_CHOOSER_TARGET_SERVICE";
@@ -280,7 +281,7 @@
field public static final int allowBackup = 16843392; // 0x1010280
field public static final int allowClearUserData = 16842757; // 0x1010005
field public static final int allowEmbedded = 16843765; // 0x10103f5
- field public static final int allowForceDark = 16844171; // 0x101058b
+ field public static final int allowForceDark = 16844172; // 0x101058c
field public static final int allowParallelSyncs = 16843570; // 0x1010332
field public static final int allowSingleTap = 16843353; // 0x1010259
field public static final int allowTaskReparenting = 16843268; // 0x1010204
@@ -774,11 +775,11 @@
field public static final int isFeatureSplit = 16844123; // 0x101055b
field public static final int isGame = 16843764; // 0x10103f4
field public static final int isIndicator = 16843079; // 0x1010147
- field public static final int isLightTheme = 16844175; // 0x101058f
+ field public static final int isLightTheme = 16844176; // 0x1010590
field public static final int isModifier = 16843334; // 0x1010246
field public static final int isRepeatable = 16843336; // 0x1010248
field public static final int isScrollContainer = 16843342; // 0x101024e
- field public static final int isSplitRequired = 16844176; // 0x1010590
+ field public static final int isSplitRequired = 16844177; // 0x1010591
field public static final int isStatic = 16844122; // 0x101055a
field public static final int isSticky = 16843335; // 0x1010247
field public static final int isolatedProcess = 16843689; // 0x10103a9
@@ -938,7 +939,7 @@
field public static final int minSdkVersion = 16843276; // 0x101020c
field public static final int minWidth = 16843071; // 0x101013f
field public static final int minimumHorizontalAngle = 16843901; // 0x101047d
- field public static final int minimumUiTimeout = 16844174; // 0x101058e
+ field public static final int minimumUiTimeout = 16844175; // 0x101058f
field public static final int minimumVerticalAngle = 16843902; // 0x101047e
field public static final int mipMap = 16843725; // 0x10103cd
field public static final int mirrorForRtl = 16843726; // 0x10103ce
@@ -978,10 +979,10 @@
field public static final int onClick = 16843375; // 0x101026f
field public static final int oneshot = 16843159; // 0x1010197
field public static final int opacity = 16843550; // 0x101031e
- field public static final int opticalInsetBottom = 16844170; // 0x101058a
- field public static final int opticalInsetLeft = 16844167; // 0x1010587
- field public static final int opticalInsetRight = 16844169; // 0x1010589
- field public static final int opticalInsetTop = 16844168; // 0x1010588
+ field public static final int opticalInsetBottom = 16844171; // 0x101058b
+ field public static final int opticalInsetLeft = 16844168; // 0x1010588
+ field public static final int opticalInsetRight = 16844170; // 0x101058a
+ field public static final int opticalInsetTop = 16844169; // 0x1010589
field public static final int order = 16843242; // 0x10101ea
field public static final int orderInCategory = 16843231; // 0x10101df
field public static final int ordering = 16843490; // 0x10102e2
@@ -997,6 +998,7 @@
field public static final int overlapAnchor = 16843874; // 0x1010462
field public static final int overridesImplicitlyEnabledSubtype = 16843682; // 0x10103a2
field public static final int packageNames = 16843649; // 0x1010381
+ field public static final int packageType = 16844167; // 0x1010587
field public static final int padding = 16842965; // 0x10100d5
field public static final int paddingBottom = 16842969; // 0x10100d9
field public static final int paddingEnd = 16843700; // 0x10103b4
@@ -1306,7 +1308,7 @@
field public static final int summaryColumn = 16843426; // 0x10102a2
field public static final int summaryOff = 16843248; // 0x10101f0
field public static final int summaryOn = 16843247; // 0x10101ef
- field public static final int supportsAmbientMode = 16844172; // 0x101058c
+ field public static final int supportsAmbientMode = 16844173; // 0x101058d
field public static final int supportsAssist = 16844016; // 0x10104f0
field public static final int supportsLaunchVoiceAssistFromKeyguard = 16844017; // 0x10104f1
field public static final int supportsLocalInteraction = 16844047; // 0x101050f
@@ -6735,14 +6737,21 @@
field public static final java.lang.String EXTRA_PROVISIONING_SKIP_ENCRYPTION = "android.app.extra.PROVISIONING_SKIP_ENCRYPTION";
field public static final java.lang.String EXTRA_PROVISIONING_SKIP_USER_CONSENT = "android.app.extra.PROVISIONING_SKIP_USER_CONSENT";
field public static final java.lang.String EXTRA_PROVISIONING_TIME_ZONE = "android.app.extra.PROVISIONING_TIME_ZONE";
+ field public static final java.lang.String EXTRA_PROVISIONING_WIFI_ANONYMOUS_IDENTITY = "android.app.extra.PROVISIONING_WIFI_ANONYMOUS_IDENTITY";
+ field public static final java.lang.String EXTRA_PROVISIONING_WIFI_CA_CERTIFICATE = "android.app.extra.PROVISIONING_WIFI_CA_CERTIFICATE";
+ field public static final java.lang.String EXTRA_PROVISIONING_WIFI_DOMAIN = "android.app.extra.PROVISIONING_WIFI_DOMAIN";
+ field public static final java.lang.String EXTRA_PROVISIONING_WIFI_EAP_METHOD = "android.app.extra.PROVISIONING_WIFI_EAP_METHOD";
field public static final java.lang.String EXTRA_PROVISIONING_WIFI_HIDDEN = "android.app.extra.PROVISIONING_WIFI_HIDDEN";
+ field public static final java.lang.String EXTRA_PROVISIONING_WIFI_IDENTITY = "android.app.extra.PROVISIONING_WIFI_IDENTITY";
field public static final java.lang.String EXTRA_PROVISIONING_WIFI_PAC_URL = "android.app.extra.PROVISIONING_WIFI_PAC_URL";
field public static final java.lang.String EXTRA_PROVISIONING_WIFI_PASSWORD = "android.app.extra.PROVISIONING_WIFI_PASSWORD";
+ field public static final java.lang.String EXTRA_PROVISIONING_WIFI_PHASE2_AUTH = "android.app.extra.PROVISIONING_WIFI_PHASE2_AUTH";
field public static final java.lang.String EXTRA_PROVISIONING_WIFI_PROXY_BYPASS = "android.app.extra.PROVISIONING_WIFI_PROXY_BYPASS";
field public static final java.lang.String EXTRA_PROVISIONING_WIFI_PROXY_HOST = "android.app.extra.PROVISIONING_WIFI_PROXY_HOST";
field public static final java.lang.String EXTRA_PROVISIONING_WIFI_PROXY_PORT = "android.app.extra.PROVISIONING_WIFI_PROXY_PORT";
field public static final java.lang.String EXTRA_PROVISIONING_WIFI_SECURITY_TYPE = "android.app.extra.PROVISIONING_WIFI_SECURITY_TYPE";
field public static final java.lang.String EXTRA_PROVISIONING_WIFI_SSID = "android.app.extra.PROVISIONING_WIFI_SSID";
+ field public static final java.lang.String EXTRA_PROVISIONING_WIFI_USER_CERTIFICATE = "android.app.extra.PROVISIONING_WIFI_USER_CERTIFICATE";
field public static final int FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY = 1; // 0x1
field public static final int FLAG_MANAGED_CAN_ACCESS_PARENT = 2; // 0x2
field public static final int FLAG_PARENT_CAN_ACCESS_MANAGED = 1; // 0x1
@@ -13397,6 +13406,8 @@
method public void drawCircle(float, float, float, android.graphics.Paint);
method public void drawColor(int);
method public void drawColor(int, android.graphics.PorterDuff.Mode);
+ method public void drawDoubleRoundRect(android.graphics.RectF, float, float, android.graphics.RectF, float, float, android.graphics.Paint);
+ method public void drawDoubleRoundRect(android.graphics.RectF, float[], android.graphics.RectF, float[], android.graphics.Paint);
method public void drawLine(float, float, float, float, android.graphics.Paint);
method public void drawLines(float[], int, int, android.graphics.Paint);
method public void drawLines(float[], android.graphics.Paint);
@@ -13748,6 +13759,7 @@
method public static android.graphics.ImageDecoder.Source createSource(android.content.res.AssetManager, java.lang.String);
method public static android.graphics.ImageDecoder.Source createSource(java.nio.ByteBuffer);
method public static android.graphics.ImageDecoder.Source createSource(java.io.File);
+ method public static android.graphics.ImageDecoder.Source createSource(java.util.concurrent.Callable<android.content.res.AssetFileDescriptor>);
method public static android.graphics.Bitmap decodeBitmap(android.graphics.ImageDecoder.Source, android.graphics.ImageDecoder.OnHeaderDecodedListener) throws java.io.IOException;
method public static android.graphics.Bitmap decodeBitmap(android.graphics.ImageDecoder.Source) throws java.io.IOException;
method public static android.graphics.drawable.Drawable decodeDrawable(android.graphics.ImageDecoder.Source, android.graphics.ImageDecoder.OnHeaderDecodedListener) throws java.io.IOException;
@@ -15966,6 +15978,7 @@
method public int canAuthenticate();
field public static final int ERROR_NONE = 0; // 0x0
field public static final int ERROR_NO_BIOMETRICS = 11; // 0xb
+ field public static final int ERROR_NO_HARDWARE = 12; // 0xc
field public static final int ERROR_UNAVAILABLE = 1; // 0x1
}
@@ -43347,6 +43360,36 @@
}
+package android.telephony.emergency {
+
+ public final class EmergencyNumber implements android.os.Parcelable {
+ method public int describeContents();
+ method public java.lang.String getCountryIso();
+ method public int getEmergencyNumberSourceBitmask();
+ method public java.util.List<java.lang.Integer> getEmergencyNumberSources();
+ method public java.util.List<java.lang.Integer> getEmergencyServiceCategories();
+ method public int getEmergencyServiceCategoryBitmask();
+ method public java.lang.String getNumber();
+ method public boolean isFromSources(int);
+ method public boolean isInEmergencyServiceCategories(int);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.emergency.EmergencyNumber> CREATOR;
+ field public static final int EMERGENCY_NUMBER_SOURCE_DEFAULT = 8; // 0x8
+ field public static final int EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG = 4; // 0x4
+ field public static final int EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING = 1; // 0x1
+ field public static final int EMERGENCY_NUMBER_SOURCE_SIM = 2; // 0x2
+ field public static final int EMERGENCY_SERVICE_CATEGORY_AIEC = 64; // 0x40
+ field public static final int EMERGENCY_SERVICE_CATEGORY_AMBULANCE = 2; // 0x2
+ field public static final int EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE = 4; // 0x4
+ field public static final int EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD = 8; // 0x8
+ field public static final int EMERGENCY_SERVICE_CATEGORY_MIEC = 32; // 0x20
+ field public static final int EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE = 16; // 0x10
+ field public static final int EMERGENCY_SERVICE_CATEGORY_POLICE = 1; // 0x1
+ field public static final int EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED = 0; // 0x0
+ }
+
+}
+
package android.telephony.euicc {
public final class DownloadableSubscription implements android.os.Parcelable {
@@ -46645,7 +46688,12 @@
}
public final class DisplayCutout {
- ctor public DisplayCutout(android.graphics.Rect, java.util.List<android.graphics.Rect>);
+ ctor public DisplayCutout(android.graphics.Insets, android.graphics.Rect, android.graphics.Rect, android.graphics.Rect, android.graphics.Rect);
+ ctor public deprecated DisplayCutout(android.graphics.Rect, java.util.List<android.graphics.Rect>);
+ method public android.graphics.Rect getBoundingRectBottom();
+ method public android.graphics.Rect getBoundingRectLeft();
+ method public android.graphics.Rect getBoundingRectRight();
+ method public android.graphics.Rect getBoundingRectTop();
method public java.util.List<android.graphics.Rect> getBoundingRects();
method public int getSafeInsetBottom();
method public int getSafeInsetLeft();
diff --git a/api/system-current.txt b/api/system-current.txt
index 3f9a904..e134554 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5852,7 +5852,7 @@
field public static final java.lang.String EXTRA_CODEC = "Codec";
field public static final java.lang.String EXTRA_DIALSTRING = "dialstring";
field public static final java.lang.String EXTRA_DISPLAY_TEXT = "DisplayText";
- field public static final java.lang.String EXTRA_E_CALL = "e_call";
+ field public static final java.lang.String EXTRA_EMERGENCY_CALL = "e_call";
field public static final java.lang.String EXTRA_IS_CALL_PULL = "CallPull";
field public static final java.lang.String EXTRA_OI = "oi";
field public static final java.lang.String EXTRA_OIR = "oir";
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 8ab67e3..9d83afd 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -181,6 +181,7 @@
NumFingerprints num_fingerprints = 10031;
DiskIo disk_io = 10032;
PowerProfile power_profile = 10033;
+ ProcStats proc_stats_pkg_proc = 10034;
}
// DO NOT USE field numbers above 100,000 in AOSP.
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index cd215b4..dab64ca 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -211,6 +211,9 @@
// ProcStats.
{android::util::PROC_STATS,
{{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::PROC_STATS)}},
+ // ProcStatsPkgProc.
+ {android::util::PROC_STATS_PKG_PROC,
+ {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::PROC_STATS_PKG_PROC)}},
// Disk I/O stats per uid.
{android::util::DISK_IO,
{{2,3,4,5,6,7,8,9,10,11},
diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp
index 4325f0f..8b1f5cb 100644
--- a/cmds/statsd/src/packages/UidMap.cpp
+++ b/cmds/statsd/src/packages/UidMap.cpp
@@ -489,6 +489,9 @@
{"AID_RESERVED_DISK", 1065},
{"AID_STATSD", 1066},
{"AID_INCIDENTD", 1067},
+ {"AID_SECURE_ELEMENT", 1068},
+ {"AID_LMKD", 1069},
+ {"AID_LLKD", 1070},
{"AID_SHELL", 2000},
{"AID_CACHE", 2001},
{"AID_DIAG", 2002}};
diff --git a/cmds/statsd/tests/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp
index d490701..ced65f2 100644
--- a/cmds/statsd/tests/LogEvent_test.cpp
+++ b/cmds/statsd/tests/LogEvent_test.cpp
@@ -327,7 +327,7 @@
EXPECT_EQ(33, item5.mValue.int_value);
const FieldValue& item6 = event1.getValues()[6];
- EXPECT_EQ(0x2020382, item6.mField.getField());
+ EXPECT_EQ(0x2020383, item6.mField.getField());
EXPECT_EQ(Type::LONG, item6.mValue.getType());
EXPECT_EQ(678L, item6.mValue.long_value);
@@ -337,7 +337,7 @@
EXPECT_EQ(44, item7.mValue.int_value);
const FieldValue& item8 = event1.getValues()[8];
- EXPECT_EQ(0x2020482, item8.mField.getField());
+ EXPECT_EQ(0x2020483, item8.mField.getField());
EXPECT_EQ(Type::LONG, item8.mValue.getType());
EXPECT_EQ(890L, item8.mValue.long_value);
@@ -347,7 +347,7 @@
EXPECT_EQ(1, item9.mValue.int_value);
const FieldValue& item10 = event1.getValues()[10];
- EXPECT_EQ(0x2020583, item10.mField.getField());
+ EXPECT_EQ(0x2020584, item10.mField.getField());
EXPECT_EQ(Type::STRING, item10.mValue.getType());
EXPECT_EQ("test2", item10.mValue.str_value);
@@ -357,7 +357,7 @@
EXPECT_EQ(2, item11.mValue.int_value);
const FieldValue& item12 = event1.getValues()[12];
- EXPECT_EQ(0x2020683, item12.mField.getField());
+ EXPECT_EQ(0x2020684, item12.mField.getField());
EXPECT_EQ(Type::STRING, item12.mValue.getType());
EXPECT_EQ("test1", item12.mValue.str_value);
@@ -367,7 +367,7 @@
EXPECT_EQ(111, item13.mValue.int_value);
const FieldValue& item14 = event1.getValues()[14];
- EXPECT_EQ(0x2020784, item14.mField.getField());
+ EXPECT_EQ(0x2020785, item14.mField.getField());
EXPECT_EQ(Type::FLOAT, item14.mValue.getType());
EXPECT_EQ(2.2f, item14.mValue.float_value);
@@ -377,7 +377,7 @@
EXPECT_EQ(222, item15.mValue.int_value);
const FieldValue& item16 = event1.getValues()[16];
- EXPECT_EQ(0x2028884, item16.mField.getField());
+ EXPECT_EQ(0x2028885, item16.mField.getField());
EXPECT_EQ(Type::FLOAT, item16.mValue.getType());
EXPECT_EQ(1.1f, item16.mValue.float_value);
}
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 294a3ec..76b90f5 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -245,4 +245,6 @@
public abstract ComponentName startServiceInPackage(int uid, Intent service,
String resolvedType, boolean fgRequired, String callingPackage, int userId)
throws TransactionTooLargeException;
+
+ public abstract void disconnectActivityFromServices(Object connectionHolder);
}
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 34c2282..1144e26 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1760,7 +1760,7 @@
/**
* Like {@link #execStartActivity(android.content.Context, android.os.IBinder,
* android.os.IBinder, String, android.content.Intent, int, android.os.Bundle)},
- * but for calls from a {#link Fragment}.
+ * but for calls from a {@link Fragment}.
*
* @param who The Context from which the activity is being started.
* @param contextThread The main thread of the Context from which the activity
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 1839263..09ab671 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -664,8 +664,8 @@
/**
* A String extra indicating the security type of the wifi network in
- * {@link #EXTRA_PROVISIONING_WIFI_SSID} and could be one of {@code NONE}, {@code WPA} or
- * {@code WEP}.
+ * {@link #EXTRA_PROVISIONING_WIFI_SSID} and could be one of {@code NONE}, {@code WPA},
+ * {@code WEP} or {@code EAP}.
*
* <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
* provisioning via an NFC bump.
@@ -680,8 +680,89 @@
* <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
* provisioning via an NFC bump.
*/
- public static final String EXTRA_PROVISIONING_WIFI_PASSWORD
- = "android.app.extra.PROVISIONING_WIFI_PASSWORD";
+ public static final String EXTRA_PROVISIONING_WIFI_PASSWORD =
+ "android.app.extra.PROVISIONING_WIFI_PASSWORD";
+
+ /**
+ * The EAP method of the wifi network in {@link #EXTRA_PROVISIONING_WIFI_SSID}
+ * and could be one of {@code PEAP}, {@code TLS}, {@code TTLS}, {@code PWD}, {@code SIM},
+ * {@code AKA} or {@code AKA_PRIME}. This is only used if the
+ * {@link #EXTRA_PROVISIONING_WIFI_SECURITY_TYPE} is {@code EAP}.
+ *
+ * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+ * provisioning via an NFC bump. It can also be used for QR code provisioning.
+ */
+ public static final String EXTRA_PROVISIONING_WIFI_EAP_METHOD =
+ "android.app.extra.PROVISIONING_WIFI_EAP_METHOD";
+
+ /**
+ * The phase 2 authentication of the wifi network in {@link #EXTRA_PROVISIONING_WIFI_SSID}
+ * and could be one of {@code NONE}, {@code PAP}, {@code MSCHAP}, {@code MSCHAPV2}, {@code GTC},
+ * {@code SIM}, {@code AKA} or {@code AKA_PRIME}. This is only used if the
+ * {@link #EXTRA_PROVISIONING_WIFI_SECURITY_TYPE} is {@code EAP}.
+ *
+ * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+ * provisioning via an NFC bump. It can also be used for QR code provisioning.
+ */
+ public static final String EXTRA_PROVISIONING_WIFI_PHASE2_AUTH =
+ "android.app.extra.PROVISIONING_WIFI_PHASE2_AUTH";
+
+ /**
+ * The CA certificate of the wifi network in {@link #EXTRA_PROVISIONING_WIFI_SSID}. This should
+ * be an X.509 certificate Base64 encoded DER format, ie. PEM representation of a certificate
+ * without header, footer and line breaks. <a href=
+ * "https://tools.ietf.org/html/rfc7468"> More information</a> This is only
+ * used if the {@link
+ * #EXTRA_PROVISIONING_WIFI_SECURITY_TYPE} is {@code EAP}.
+ *
+ * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+ * provisioning via an NFC bump. It can also be used for QR code provisioning.
+ */
+ public static final String EXTRA_PROVISIONING_WIFI_CA_CERTIFICATE =
+ "android.app.extra.PROVISIONING_WIFI_CA_CERTIFICATE";
+
+ /**
+ * The user certificate of the wifi network in {@link #EXTRA_PROVISIONING_WIFI_SSID}. This
+ * should be an X.509 certificate and private key Base64 encoded DER format, ie. PEM
+ * representation of a certificate and key without header, footer and line breaks. <a href=
+ * "https://tools.ietf.org/html/rfc7468"> More information</a> This is only
+ * used if the {@link #EXTRA_PROVISIONING_WIFI_SECURITY_TYPE} is {@code EAP}.
+ *
+ * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+ * provisioning via an NFC bump. It can also be used for QR code provisioning.
+ */
+ public static final String EXTRA_PROVISIONING_WIFI_USER_CERTIFICATE =
+ "android.app.extra.PROVISIONING_WIFI_USER_CERTIFICATE";
+
+ /**
+ * The identity of the wifi network in {@link #EXTRA_PROVISIONING_WIFI_SSID}. This is only used
+ * if the {@link #EXTRA_PROVISIONING_WIFI_SECURITY_TYPE} is {@code EAP}.
+ *
+ * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+ * provisioning via an NFC bump. It can also be used for QR code provisioning.
+ */
+ public static final String EXTRA_PROVISIONING_WIFI_IDENTITY =
+ "android.app.extra.PROVISIONING_WIFI_IDENTITY";
+
+ /**
+ * The anonymous identity of the wifi network in {@link #EXTRA_PROVISIONING_WIFI_SSID}. This is
+ * only used if the {@link #EXTRA_PROVISIONING_WIFI_SECURITY_TYPE} is {@code EAP}.
+ *
+ * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+ * provisioning via an NFC bump. It can also be used for QR code provisioning.
+ */
+
+ public static final String EXTRA_PROVISIONING_WIFI_ANONYMOUS_IDENTITY =
+ "android.app.extra.PROVISIONING_WIFI_ANONYMOUS_IDENTITY";
+ /**
+ * The domain of the wifi network in {@link #EXTRA_PROVISIONING_WIFI_SSID}. This is only used if
+ * the {@link #EXTRA_PROVISIONING_WIFI_SECURITY_TYPE} is {@code EAP}.
+ *
+ * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+ * provisioning via an NFC bump. It can also be used for QR code provisioning.
+ */
+ public static final String EXTRA_PROVISIONING_WIFI_DOMAIN =
+ "android.app.extra.PROVISIONING_WIFI_DOMAIN";
/**
* A String extra holding the proxy host for the wifi network in
@@ -1067,8 +1148,22 @@
* <li>{@link #EXTRA_PROVISIONING_WIFI_PROXY_PORT} (convert to String), optional</li>
* <li>{@link #EXTRA_PROVISIONING_WIFI_PROXY_BYPASS}, optional</li>
* <li>{@link #EXTRA_PROVISIONING_WIFI_PAC_URL}, optional</li>
- * <li>{@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}, optional, supported from
- * {@link android.os.Build.VERSION_CODES#M} </li></ul>
+ * <li>{@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}, optional, supported from {@link
+ * android.os.Build.VERSION_CODES#M} </li>
+ * <li>{@link #EXTRA_PROVISIONING_WIFI_EAP_METHOD}, optional, supported from {@link
+ * android.os.Build.VERSION_CODES#Q}</li>
+ * <li>{@link #EXTRA_PROVISIONING_WIFI_PHASE2_AUTH}, optional, supported from {@link
+ * android.os.Build.VERSION_CODES#Q}</li>
+ * <li>{@link #EXTRA_PROVISIONING_WIFI_CA_CERTIFICATE}, optional, supported from {@link
+ * android.os.Build.VERSION_CODES#Q}</li>
+ * <li>{@link #EXTRA_PROVISIONING_WIFI_USER_CERTIFICATE}, optional, supported from {@link
+ * android.os.Build.VERSION_CODES#Q}</li>
+ * <li>{@link #EXTRA_PROVISIONING_WIFI_IDENTITY}, optional, supported from {@link
+ * android.os.Build.VERSION_CODES#Q}</li>
+ * <li>{@link #EXTRA_PROVISIONING_WIFI_ANONYMOUS_IDENTITY}, optional, supported from {@link
+ * android.os.Build.VERSION_CODES#Q}</li>
+ * <li>{@link #EXTRA_PROVISIONING_WIFI_DOMAIN}, optional, supported from {@link
+ * android.os.Build.VERSION_CODES#Q}</li></ul>
*
* <p>
* As of {@link android.os.Build.VERSION_CODES#M}, the properties should contain
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 654bfaf..8e6a385 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -2973,7 +2973,7 @@
* socket will be encrypted.
* <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening
* {@link BluetoothServerSocket}.
- * <p>The system will assign a dynamic PSM value. This PSM value can be read from the {#link
+ * <p>The system will assign a dynamic PSM value. This PSM value can be read from the {@link
* BluetoothServerSocket#getPsm()} and this value will be released when this server socket is
* closed, Bluetooth is turned off, or the application exits unexpectedly.
* <p>The mechanism of disclosing the assigned dynamic PSM value to the initiating peer is
@@ -3031,7 +3031,7 @@
* <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening
* {@link BluetoothServerSocket}.
* <p>The system will assign a dynamic protocol/service multiplexer (PSM) value. This PSM value
- * can be read from the {#link BluetoothServerSocket#getPsm()} and this value will be released
+ * can be read from the {@link BluetoothServerSocket#getPsm()} and this value will be released
* when this server socket is closed, Bluetooth is turned off, or the application exits
* unexpectedly.
* <p>The mechanism of disclosing the assigned dynamic PSM value to the initiating peer is
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 73e98cd..30d5fbc 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -1596,7 +1596,7 @@
* For example, for Bluetooth 2.1 devices, if any of the devices does not
* have an input and output capability or just has the ability to
* display a numeric key, a secure socket connection is not possible.
- * In such a case, use {#link createInsecureRfcommSocket}.
+ * In such a case, use {@link createInsecureRfcommSocket}.
* For more details, refer to the Security Model section 5.2 (vol 3) of
* Bluetooth Core Specification version 2.1 + EDR.
* <p>Use {@link BluetoothSocket#connect} to initiate the outgoing
@@ -1631,7 +1631,7 @@
* For example, for Bluetooth 2.1 devices, if any of the devices does not
* have an input and output capability or just has the ability to
* display a numeric key, a secure socket connection is not possible.
- * In such a case, use {#link createInsecureRfcommSocket}.
+ * In such a case, use {@link createInsecureRfcommSocket}.
* For more details, refer to the Security Model section 5.2 (vol 3) of
* Bluetooth Core Specification version 2.1 + EDR.
* <p>Use {@link BluetoothSocket#connect} to initiate the outgoing
@@ -1688,7 +1688,7 @@
* For example, for Bluetooth 2.1 devices, if any of the devices does not
* have an input and output capability or just has the ability to
* display a numeric key, a secure socket connection is not possible.
- * In such a case, use {#link createInsecureRfcommSocketToServiceRecord}.
+ * In such a case, use {@link #createInsecureRfcommSocketToServiceRecord}.
* For more details, refer to the Security Model section 5.2 (vol 3) of
* Bluetooth Core Specification version 2.1 + EDR.
* <p>Hint: If you are connecting to a Bluetooth serial board then try
@@ -1972,7 +1972,7 @@
* encrypted.
* <p> Use this socket if an authenticated socket link is possible. Authentication refers
* to the authentication of the link key to prevent man-in-the-middle type of attacks. When a
- * secure socket connection is not possible, use {#link createInsecureLeL2capCocSocket(int,
+ * secure socket connection is not possible, use {@link createInsecureLeL2capCocSocket(int,
* int)}.
*
* @param psm dynamic PSM value from remote device
diff --git a/core/java/android/bluetooth/BluetoothServerSocket.java b/core/java/android/bluetooth/BluetoothServerSocket.java
index 5fc344a..758c68d 100644
--- a/core/java/android/bluetooth/BluetoothServerSocket.java
+++ b/core/java/android/bluetooth/BluetoothServerSocket.java
@@ -203,7 +203,7 @@
/**
* Returns the assigned dynamic protocol/service multiplexer (PSM) value for the listening L2CAP
* Connection-oriented Channel (CoC) server socket. This server socket must be returned by the
- * {#link BluetoothAdapter.listenUsingL2capChannel()} or {#link
+ * {@link BluetoothAdapter.listenUsingL2capChannel()} or {@link
* BluetoothAdapter.listenUsingInsecureL2capChannel()}. The returned value is undefined if this
* method is called on non-L2CAP server sockets.
*
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index b8739b9..0faecb0 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -19,6 +19,7 @@
import static android.Manifest.permission.USE_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
+import android.annotation.IntDef;
import android.annotation.RequiresPermission;
import android.content.Context;
import android.os.RemoteException;
@@ -46,6 +47,14 @@
*/
public static final int ERROR_NO_BIOMETRICS = BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS;
+ /**
+ * There is no biometric hardware.
+ */
+ public static final int ERROR_NO_HARDWARE = BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT;
+
+ @IntDef({ERROR_NONE, ERROR_UNAVAILABLE, ERROR_NO_BIOMETRICS, ERROR_NO_HARDWARE})
+ @interface BiometricError {}
+
private final Context mContext;
private final IBiometricService mService;
@@ -68,7 +77,7 @@
* {@link #ERROR_NONE} if a biometric can currently be used (enrolled and available).
*/
@RequiresPermission(USE_BIOMETRIC)
- public int canAuthenticate() {
+ public @BiometricError int canAuthenticate() {
if (mService != null) {
try {
return mService.canAuthenticate(mContext.getOpPackageName());
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index e8fb287..09113e5 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -297,6 +297,15 @@
*/
public static final int VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL = 1 << 8;
+ /**
+ * Virtual display flag: Indicates that the display should support system decorations. Virtual
+ * displays without this flag shouldn't show home, IME or any other system decorations.
+ *
+ * @see #createVirtualDisplay
+ * @hide
+ */
+ public static final int VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS = 1 << 9;
+
/** @hide */
public DisplayManager(Context context) {
mContext = context;
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index f7f627e..097a3e3 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -2095,7 +2095,7 @@
* Called when the application has reported a new location of its text
* cursor. This is only called if explicitly requested by the input method.
* The default implementation does nothing.
- * @deprecated Use {#link onUpdateCursorAnchorInfo(CursorAnchorInfo)} instead.
+ * @deprecated Use {@link #onUpdateCursorAnchorInfo(CursorAnchorInfo)} instead.
*/
@Deprecated
public void onUpdateCursor(Rect newCursor) {
@@ -2162,7 +2162,7 @@
}
/**
- * @return {#link ExtractEditText} if it is considered to be visible and active. Otherwise
+ * @return {@link ExtractEditText} if it is considered to be visible and active. Otherwise
* {@code null} is returned.
*/
private ExtractEditText getExtractEditTextIfVisible() {
diff --git a/core/java/android/inputmethodservice/SoftInputWindow.java b/core/java/android/inputmethodservice/SoftInputWindow.java
index b4b8887..0513fee 100644
--- a/core/java/android/inputmethodservice/SoftInputWindow.java
+++ b/core/java/android/inputmethodservice/SoftInputWindow.java
@@ -162,9 +162,9 @@
/**
* Set which boundary of the screen the DockWindow sticks to.
*
- * @param gravity The boundary of the screen to stick. See {#link
- * android.view.Gravity.LEFT}, {#link android.view.Gravity.TOP},
- * {#link android.view.Gravity.BOTTOM}, {#link
+ * @param gravity The boundary of the screen to stick. See {@link
+ * android.view.Gravity.LEFT}, {@link android.view.Gravity.TOP},
+ * {@link android.view.Gravity.BOTTOM}, {@link
* android.view.Gravity.RIGHT}.
*/
public void setGravity(int gravity) {
diff --git a/core/java/android/net/NetworkMisc.java b/core/java/android/net/NetworkMisc.java
index 69f50a2..a2da6ea 100644
--- a/core/java/android/net/NetworkMisc.java
+++ b/core/java/android/net/NetworkMisc.java
@@ -46,7 +46,7 @@
/**
* Set if the user desires to use this network even if it is unvalidated. This field has meaning
- * only if {#link explicitlySelected} is true. If it is, this field must also be set to the
+ * only if {@link explicitlySelected} is true. If it is, this field must also be set to the
* appropriate value based on previous user choice.
*/
public boolean acceptUnvalidated;
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 4d96fc3..719a401 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -221,6 +221,18 @@
public static final int FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD = 1 << 5;
/**
+ * Display flag: Indicates that the display should show system decorations.
+ * <p>
+ * This flag identifies secondary displays that should show system decorations, such as status
+ * bar, navigation bar, home activity or IME.
+ * </p>
+ *
+ * @see #supportsSystemDecorations
+ * @hide
+ */
+ public static final int FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS = 1 << 6;
+
+ /**
* Display flag: Indicates that the contents of the display should not be scaled
* to fit the physical screen dimensions. Used for development only to emulate
* devices with smaller physicals screens while preserving density.
@@ -874,6 +886,16 @@
}
/**
+ * Returns whether this display should support showing system decorations.
+ *
+ * @see #FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
+ * @hide
+ */
+ public boolean supportsSystemDecorations() {
+ return (mDisplayInfo.flags & FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0;
+ }
+
+ /**
* Returns the display's HDR capabilities.
*
* @see #isHdr()
diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java
index 5f80d31..f428c79 100644
--- a/core/java/android/view/DisplayCutout.java
+++ b/core/java/android/view/DisplayCutout.java
@@ -18,12 +18,19 @@
import static android.util.DisplayMetrics.DENSITY_DEFAULT;
import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE;
-import static android.view.DisplayCutoutProto.BOUNDS;
+import static android.view.DisplayCutoutProto.BOUND_BOTTOM;
+import static android.view.DisplayCutoutProto.BOUND_LEFT;
+import static android.view.DisplayCutoutProto.BOUND_RIGHT;
+import static android.view.DisplayCutoutProto.BOUND_TOP;
import static android.view.DisplayCutoutProto.INSETS;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.res.Resources;
+import android.graphics.Insets;
import android.graphics.Matrix;
import android.graphics.Path;
import android.graphics.Rect;
@@ -42,7 +49,10 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
/**
@@ -68,14 +78,14 @@
"com.android.internal.display_cutout_emulation";
private static final Rect ZERO_RECT = new Rect();
- private static final Region EMPTY_REGION = new Region();
/**
* An instance where {@link #isEmpty()} returns {@code true}.
*
* @hide
*/
- public static final DisplayCutout NO_CUTOUT = new DisplayCutout(ZERO_RECT, EMPTY_REGION,
+ public static final DisplayCutout NO_CUTOUT = new DisplayCutout(
+ ZERO_RECT, ZERO_RECT, ZERO_RECT, ZERO_RECT, ZERO_RECT,
false /* copyArguments */);
@@ -94,7 +104,152 @@
private static Pair<Path, DisplayCutout> sCachedCutout = NULL_PAIR;
private final Rect mSafeInsets;
- private final Region mBounds;
+
+
+ /**
+ * The bound is at the left of the screen.
+ * @hide
+ */
+ public static final int BOUNDS_POSITION_LEFT = 0;
+
+ /**
+ * The bound is at the top of the screen.
+ * @hide
+ */
+ public static final int BOUNDS_POSITION_TOP = 1;
+
+ /**
+ * The bound is at the right of the screen.
+ * @hide
+ */
+ public static final int BOUNDS_POSITION_RIGHT = 2;
+
+ /**
+ * The bound is at the bottom of the screen.
+ * @hide
+ */
+ public static final int BOUNDS_POSITION_BOTTOM = 3;
+
+ /**
+ * The number of possible positions at which bounds can be located.
+ * @hide
+ */
+ public static final int BOUNDS_POSITION_LENGTH = 4;
+
+ /** @hide */
+ @IntDef(prefix = { "BOUNDS_POSITION_" }, value = {
+ BOUNDS_POSITION_LEFT,
+ BOUNDS_POSITION_TOP,
+ BOUNDS_POSITION_RIGHT,
+ BOUNDS_POSITION_BOTTOM
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface BoundsPosition {}
+
+ private static class Bounds {
+ private final Rect[] mRects;
+
+ private Bounds(Rect left, Rect top, Rect right, Rect bottom, boolean copyArguments) {
+ mRects = new Rect[BOUNDS_POSITION_LENGTH];
+ mRects[BOUNDS_POSITION_LEFT] = getCopyOrRef(left, copyArguments);
+ mRects[BOUNDS_POSITION_TOP] = getCopyOrRef(top, copyArguments);
+ mRects[BOUNDS_POSITION_RIGHT] = getCopyOrRef(right, copyArguments);
+ mRects[BOUNDS_POSITION_BOTTOM] = getCopyOrRef(bottom, copyArguments);
+
+ }
+
+ private Bounds(Rect[] rects, boolean copyArguments) {
+ if (rects.length != BOUNDS_POSITION_LENGTH) {
+ throw new IllegalArgumentException(
+ "rects must have exactly 4 elements: rects=" + Arrays.toString(rects));
+ }
+ if (copyArguments) {
+ mRects = new Rect[BOUNDS_POSITION_LENGTH];
+ for (int i = 0; i < BOUNDS_POSITION_LENGTH; ++i) {
+ mRects[i] = new Rect(rects[i]);
+ }
+ } else {
+ for (Rect rect : rects) {
+ if (rect == null) {
+ throw new IllegalArgumentException(
+ "rects must have non-null elements: rects="
+ + Arrays.toString(rects));
+ }
+ }
+ mRects = rects;
+ }
+ }
+
+ private boolean isEmpty() {
+ for (Rect rect : mRects) {
+ if (!rect.isEmpty()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private Rect getRect(@BoundsPosition int pos) {
+ return new Rect(mRects[pos]);
+ }
+
+ private Rect[] getRects() {
+ Rect[] rects = new Rect[BOUNDS_POSITION_LENGTH];
+ for (int i = 0; i < BOUNDS_POSITION_LENGTH; ++i) {
+ rects[i] = new Rect(mRects[i]);
+ }
+ return rects;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 0;
+ for (Rect rect : mRects) {
+ result = result * 48271 + rect.hashCode();
+ }
+ return result;
+ }
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (o instanceof Bounds) {
+ Bounds b = (Bounds) o;
+ return Arrays.deepEquals(mRects, b.mRects);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "Bounds=" + Arrays.toString(mRects);
+ }
+
+ }
+
+ private final Bounds mBounds;
+
+ /**
+ * Creates a DisplayCutout instance.
+ *
+ * @param safeInsets the insets from each edge which avoid the display cutout as returned by
+ * {@link #getSafeInsetTop()} etc.
+ * @param boundLeft the left bounding rect of the display cutout in pixels. If null is passed,
+ * it's treated as an empty rectangle (0,0)-(0,0).
+ * @param boundTop the top bounding rect of the display cutout in pixels. If null is passed,
+ * it's treated as an empty rectangle (0,0)-(0,0).
+ * @param boundRight the right bounding rect of the display cutout in pixels. If null is
+ * passed, it's treated as an empty rectangle (0,0)-(0,0).
+ * @param boundBottom the bottom bounding rect of the display cutout in pixels. If null is
+ * passed, it's treated as an empty rectangle (0,0)-(0,0).
+ */
+ // TODO(b/73953958): @VisibleForTesting(visibility = PRIVATE)
+ public DisplayCutout(
+ Insets safeInsets, @Nullable Rect boundLeft, @Nullable Rect boundTop,
+ @Nullable Rect boundRight, @Nullable Rect boundBottom) {
+ this(safeInsets.toRect(), boundLeft, boundTop, boundRight, boundBottom, true);
+ }
/**
* Creates a DisplayCutout instance.
@@ -103,25 +258,85 @@
* {@link #getSafeInsetTop()} etc.
* @param boundingRects the bounding rects of the display cutouts as returned by
* {@link #getBoundingRects()} ()}.
+ * @deprecated Use {@link DisplayCutout#DisplayCutout(Insets, Rect, Rect, Rect, Rect)} instead.
*/
// TODO(b/73953958): @VisibleForTesting(visibility = PRIVATE)
+ @Deprecated
public DisplayCutout(Rect safeInsets, List<Rect> boundingRects) {
- this(safeInsets != null ? new Rect(safeInsets) : ZERO_RECT,
- boundingRectsToRegion(boundingRects),
+ this(safeInsets, extractBoundsFromList(safeInsets, boundingRects),
true /* copyArguments */);
}
/**
* Creates a DisplayCutout instance.
*
+ * @param safeInsets the insets from each edge which avoid the display cutout as returned by
+ * {@link #getSafeInsetTop()} etc.
* @param copyArguments if true, create a copy of the arguments. If false, the passed arguments
* are not copied and MUST remain unchanged forever.
*/
- private DisplayCutout(Rect safeInsets, Region bounds, boolean copyArguments) {
- mSafeInsets = safeInsets == null ? ZERO_RECT :
- (copyArguments ? new Rect(safeInsets) : safeInsets);
- mBounds = bounds == null ? Region.obtain() :
- (copyArguments ? Region.obtain(bounds) : bounds);
+ private DisplayCutout(Rect safeInsets, Rect boundLeft, Rect boundTop, Rect boundRight,
+ Rect boundBottom, boolean copyArguments) {
+ mSafeInsets = getCopyOrRef(safeInsets, copyArguments);
+ mBounds = new Bounds(boundLeft, boundTop, boundRight, boundBottom, copyArguments);
+ }
+
+ private DisplayCutout(Rect safeInsets, Rect[] bounds, boolean copyArguments) {
+ mSafeInsets = getCopyOrRef(safeInsets, copyArguments);
+ mBounds = new Bounds(bounds, copyArguments);
+ }
+
+ private DisplayCutout(Rect safeInsets, Bounds bounds) {
+ mSafeInsets = safeInsets;
+ mBounds = bounds;
+
+ }
+
+ private static Rect getCopyOrRef(Rect r, boolean copyArguments) {
+ if (r == null) {
+ return ZERO_RECT;
+ } else if (copyArguments) {
+ return new Rect(r);
+ } else {
+ return r;
+ }
+ }
+
+ /**
+ * Find the position of the bounding rect, and create an array of Rect whose index represents
+ * the position (= BoundsPosition).
+ *
+ * @hide
+ */
+ public static Rect[] extractBoundsFromList(Rect safeInsets, List<Rect> boundingRects) {
+ Rect[] sortedBounds = new Rect[BOUNDS_POSITION_LENGTH];
+ for (int i = 0; i < sortedBounds.length; ++i) {
+ sortedBounds[i] = ZERO_RECT;
+ }
+ for (Rect bound : boundingRects) {
+ // There will be at most one non-functional area per short edge of the device, and none
+ // on the long edges, so either safeInsets.right or safeInsets.bottom must be 0.
+ // TODO(b/117199965): Refine the logic to handle edge cases.
+ if (bound.left == 0) {
+ sortedBounds[BOUNDS_POSITION_LEFT] = bound;
+ } else if (bound.top == 0) {
+ sortedBounds[BOUNDS_POSITION_TOP] = bound;
+ } else if (safeInsets.right > 0) {
+ sortedBounds[BOUNDS_POSITION_RIGHT] = bound;
+ } else if (safeInsets.bottom > 0) {
+ sortedBounds[BOUNDS_POSITION_BOTTOM] = bound;
+ }
+ }
+ return sortedBounds;
+ }
+
+ /**
+ * Returns true if there is no cutout, i.e. the bounds are empty.
+ *
+ * @hide
+ */
+ public boolean isBoundsEmpty() {
+ return mBounds.isEmpty();
}
/**
@@ -134,15 +349,6 @@
return mSafeInsets.equals(ZERO_RECT);
}
- /**
- * Returns true if there is no cutout, i.e. the bounds are empty.
- *
- * @hide
- */
- public boolean isBoundsEmpty() {
- return mBounds.isEmpty();
- }
-
/** Returns the inset from the top which avoids the display cutout in pixels. */
public int getSafeInsetTop() {
return mSafeInsets.top;
@@ -174,69 +380,89 @@
}
/**
- * Returns the bounding region of the cutout.
- *
- * <p>
- * <strong>Note:</strong> There may be more than one cutout, in which case the returned
- * {@code Region} will be non-contiguous and its bounding rect will be meaningless without
- * intersecting it first.
- *
- * Example:
- * <pre>
- * // Getting the bounding rectangle of the top display cutout
- * Region bounds = displayCutout.getBounds();
- * bounds.op(0, 0, Integer.MAX_VALUE, displayCutout.getSafeInsetTop(), Region.Op.INTERSECT);
- * Rect topDisplayCutout = bounds.getBoundingRect();
- * </pre>
- *
- * @return the bounding region of the cutout. Coordinates are relative
- * to the top-left corner of the content view and in pixel units.
- * @hide
- */
- public Region getBounds() {
- return Region.obtain(mBounds);
- }
-
- /**
* Returns a list of {@code Rect}s, each of which is the bounding rectangle for a non-functional
* area on the display.
*
* There will be at most one non-functional area per short edge of the device, and none on
* the long edges.
*
- * @return a list of bounding {@code Rect}s, one for each display cutout area.
+ * @return a list of bounding {@code Rect}s, one for each display cutout area. No empty Rect is
+ * returned.
*/
public List<Rect> getBoundingRects() {
List<Rect> result = new ArrayList<>();
- Region bounds = Region.obtain();
- // top
- bounds.set(mBounds);
- bounds.op(0, 0, Integer.MAX_VALUE, getSafeInsetTop(), Region.Op.INTERSECT);
- if (!bounds.isEmpty()) {
- result.add(bounds.getBounds());
+ for (Rect bound : getBoundingRectsAll()) {
+ if (!bound.isEmpty()) {
+ result.add(new Rect(bound));
+ }
}
- // left
- bounds.set(mBounds);
- bounds.op(0, 0, getSafeInsetLeft(), Integer.MAX_VALUE, Region.Op.INTERSECT);
- if (!bounds.isEmpty()) {
- result.add(bounds.getBounds());
- }
- // right & bottom
- bounds.set(mBounds);
- bounds.op(getSafeInsetLeft() + 1, getSafeInsetTop() + 1,
- Integer.MAX_VALUE, Integer.MAX_VALUE, Region.Op.INTERSECT);
- if (!bounds.isEmpty()) {
- result.add(bounds.getBounds());
- }
- bounds.recycle();
return result;
}
+ /**
+ * Returns an array of {@code Rect}s, each of which is the bounding rectangle for a non-
+ * functional area on the display. Ordinal value of BoundPosition is used as an index of
+ * the array.
+ *
+ * There will be at most one non-functional area per short edge of the device, and none on
+ * the long edges.
+ *
+ * @return an array of bounding {@code Rect}s, one for each display cutout area. This might
+ * contain ZERO_RECT, which means there is no cutout area at the position.
+ *
+ * @hide
+ */
+ public Rect[] getBoundingRectsAll() {
+ return mBounds.getRects();
+ }
+
+ /**
+ * Returns a bounding rectangle for a non-functional area on the display which is located on
+ * the left of the screen.
+ *
+ * @return bounding rectangle in pixels. In case of no bounding rectangle, an empty rectangle
+ * is returned.
+ */
+ public @NonNull Rect getBoundingRectLeft() {
+ return mBounds.getRect(BOUNDS_POSITION_LEFT);
+ }
+
+ /**
+ * Returns a bounding rectangle for a non-functional area on the display which is located on
+ * the top of the screen.
+ *
+ * @return bounding rectangle in pixels. In case of no bounding rectangle, an empty rectangle
+ * is returned.
+ */
+ public @NonNull Rect getBoundingRectTop() {
+ return mBounds.getRect(BOUNDS_POSITION_TOP);
+ }
+
+ /**
+ * Returns a bounding rectangle for a non-functional area on the display which is located on
+ * the right of the screen.
+ *
+ * @return bounding rectangle in pixels. In case of no bounding rectangle, an empty rectangle
+ * is returned.
+ */
+ public @NonNull Rect getBoundingRectRight() {
+ return mBounds.getRect(BOUNDS_POSITION_RIGHT);
+ }
+
+ /**
+ * Returns a bounding rectangle for a non-functional area on the display which is located on
+ * the bottom of the screen.
+ *
+ * @return bounding rectangle in pixels. In case of no bounding rectangle, an empty rectangle
+ * is returned.
+ */
+ public @NonNull Rect getBoundingRectBottom() {
+ return mBounds.getRect(BOUNDS_POSITION_BOTTOM);
+ }
+
@Override
public int hashCode() {
- int result = mSafeInsets.hashCode();
- result = result * 31 + mBounds.getBounds().hashCode();
- return result;
+ return mSafeInsets.hashCode() * 48271 + mBounds.hashCode();
}
@Override
@@ -246,8 +472,7 @@
}
if (o instanceof DisplayCutout) {
DisplayCutout c = (DisplayCutout) o;
- return mSafeInsets.equals(c.mSafeInsets)
- && mBounds.equals(c.mBounds);
+ return mSafeInsets.equals(c.mSafeInsets) && mBounds.equals(c.mBounds);
}
return false;
}
@@ -255,7 +480,7 @@
@Override
public String toString() {
return "DisplayCutout{insets=" + mSafeInsets
- + " boundingRect=" + mBounds.getBounds()
+ + " boundingRect={" + mBounds + "}"
+ "}";
}
@@ -265,7 +490,10 @@
public void writeToProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
mSafeInsets.writeToProto(proto, INSETS);
- mBounds.getBounds().writeToProto(proto, BOUNDS);
+ mBounds.getRect(BOUNDS_POSITION_LEFT).writeToProto(proto, BOUND_LEFT);
+ mBounds.getRect(BOUNDS_POSITION_TOP).writeToProto(proto, BOUND_TOP);
+ mBounds.getRect(BOUNDS_POSITION_RIGHT).writeToProto(proto, BOUND_RIGHT);
+ mBounds.getRect(BOUNDS_POSITION_BOTTOM).writeToProto(proto, BOUND_BOTTOM);
proto.end(token);
}
@@ -276,13 +504,12 @@
* @hide
*/
public DisplayCutout inset(int insetLeft, int insetTop, int insetRight, int insetBottom) {
- if (mBounds.isEmpty()
+ if (isBoundsEmpty()
|| insetLeft == 0 && insetTop == 0 && insetRight == 0 && insetBottom == 0) {
return this;
}
Rect safeInsets = new Rect(mSafeInsets);
- Region bounds = Region.obtain(mBounds);
// Note: it's not really well defined what happens when the inset is negative, because we
// don't know if the safe inset needs to expand in general.
@@ -299,7 +526,13 @@
safeInsets.right = atLeastZero(safeInsets.right - insetRight);
}
- bounds.translate(-insetLeft, -insetTop);
+ Rect[] bounds = mBounds.getRects();
+ for (int i = 0; i < bounds.length; ++i) {
+ if (!bounds[i].equals(ZERO_RECT)) {
+ bounds[i].offset(-insetLeft, -insetTop);
+ }
+ }
+
return new DisplayCutout(safeInsets, bounds, false /* copyArguments */);
}
@@ -312,7 +545,7 @@
* @hide
*/
public DisplayCutout replaceSafeInsets(Rect safeInsets) {
- return new DisplayCutout(new Rect(safeInsets), mBounds, false /* copyArguments */);
+ return new DisplayCutout(new Rect(safeInsets), mBounds);
}
private static int atLeastZero(int value) {
@@ -326,10 +559,13 @@
* @hide
*/
@VisibleForTesting
- public static DisplayCutout fromBoundingRect(int left, int top, int right, int bottom) {
- Region r = Region.obtain();
- r.set(left, top, right, bottom);
- return fromBounds(r);
+ public static DisplayCutout fromBoundingRect(
+ int left, int top, int right, int bottom, @BoundsPosition int pos) {
+ Rect[] bounds = new Rect[BOUNDS_POSITION_LENGTH];
+ for (int i = 0; i < BOUNDS_POSITION_LENGTH; ++i) {
+ bounds[i] = (pos == i) ? new Rect(left, top, right, bottom) : new Rect();
+ }
+ return new DisplayCutout(ZERO_RECT, bounds, false /* copyArguments */);
}
/**
@@ -337,8 +573,8 @@
*
* @hide
*/
- public static DisplayCutout fromBounds(Region region) {
- return new DisplayCutout(ZERO_RECT, region, false /* copyArguments */);
+ public static DisplayCutout fromBounds(Rect[] bounds) {
+ return new DisplayCutout(ZERO_RECT, bounds, false /* copyArguments */);
}
/**
@@ -423,10 +659,11 @@
m.postTranslate(offsetX, 0);
p.transform(m);
- final Rect tmpRect = new Rect();
- toRectAndAddToRegion(p, r, tmpRect);
- final int topInset = tmpRect.bottom;
+ Rect boundTop = new Rect();
+ toRectAndAddToRegion(p, r, boundTop);
+ final int topInset = boundTop.bottom;
+ Rect boundBottom = null;
final int bottomInset;
if (bottomSpec != null) {
final Path bottomPath;
@@ -440,15 +677,17 @@
m.postTranslate(0, displayHeight);
bottomPath.transform(m);
p.addPath(bottomPath);
- toRectAndAddToRegion(bottomPath, r, tmpRect);
- bottomInset = displayHeight - tmpRect.top;
+ boundBottom = new Rect();
+ toRectAndAddToRegion(bottomPath, r, boundBottom);
+ bottomInset = displayHeight - boundBottom.top;
} else {
bottomInset = 0;
}
- // Reuse tmpRect as the inset rect we store into the DisplayCutout instance.
- tmpRect.set(0, topInset, 0, bottomInset);
- final DisplayCutout cutout = new DisplayCutout(tmpRect, r, false /* copyArguments */);
+ Rect safeInset = new Rect(0, topInset, 0, bottomInset);
+ final DisplayCutout cutout = new DisplayCutout(
+ safeInset, null /* boundLeft */, boundTop, null /* boundRight */, boundBottom,
+ false /* copyArguments */);
final Pair<Path, DisplayCutout> result = new Pair<>(p, cutout);
synchronized (CACHE_LOCK) {
@@ -468,16 +707,6 @@
inoutRegion.op(inoutRect, Op.UNION);
}
- private static Region boundingRectsToRegion(List<Rect> rects) {
- Region result = Region.obtain();
- if (rects != null) {
- for (Rect r : rects) {
- result.op(r, Region.Op.UNION);
- }
- }
- return result;
- }
-
/**
* Helper class for passing {@link DisplayCutout} through binder.
*
@@ -520,7 +749,7 @@
} else {
out.writeInt(1);
out.writeTypedObject(cutout.mSafeInsets, flags);
- out.writeTypedObject(cutout.mBounds, flags);
+ out.writeTypedArray(cutout.mBounds.getRects(), flags);
}
}
@@ -561,7 +790,8 @@
}
Rect safeInsets = in.readTypedObject(Rect.CREATOR);
- Region bounds = in.readTypedObject(Region.CREATOR);
+ Rect[] bounds = new Rect[BOUNDS_POSITION_LENGTH];
+ in.readTypedArray(bounds, Rect.CREATOR);
return new DisplayCutout(safeInsets, bounds, false /* copyArguments */);
}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 3bab87a..3e559d9 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -87,7 +87,6 @@
void setEventDispatching(boolean enabled);
void addWindowToken(IBinder token, int type, int displayId);
void removeWindowToken(IBinder token, int displayId);
- void setFocusedApp(IBinder token, boolean moveFocusNow);
void prepareAppTransition(int transit, boolean alwaysKeepCurrent);
int getPendingAppTransition();
void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim,
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index c38fcf3..0a3403b 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -896,8 +896,8 @@
* @deprecated No longer used by the input system.
* {@link #getAction} value: multiple duplicate key events have
* occurred in a row, or a complex string is being delivered. If the
- * key code is not {#link {@link #KEYCODE_UNKNOWN} then the
- * {#link {@link #getRepeatCount()} method returns the number of times
+ * key code is not {@link #KEYCODE_UNKNOWN} then the
+ * {@link #getRepeatCount()} method returns the number of times
* the given key code should be executed.
* Otherwise, if the key code is {@link #KEYCODE_UNKNOWN}, then
* this is a sequence of characters as returned by {@link #getCharacters}.
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 0a812c6..fd38917 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -16,6 +16,8 @@
package com.android.internal.app;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+
import android.annotation.Nullable;
import android.annotation.StringRes;
import android.annotation.UiThread;
@@ -66,6 +68,7 @@
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
+
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
@@ -81,8 +84,6 @@
import java.util.Objects;
import java.util.Set;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-
/**
* This activity is displayed when the system attempts to start an Intent for
* which there is more than one matching activity, allowing the user to decide
@@ -288,6 +289,7 @@
mTitle = title;
mDefaultTitleResId = defaultTitleRes;
+ mIconFactory = IconDrawableFactory.newInstance(this, true);
if (configureContentView(mIntents, initialIntents, rList)) {
return;
}
@@ -335,7 +337,6 @@
: MetricsProto.MetricsEvent.ACTION_SHOW_APP_DISAMBIG_NONE_FEATURED,
intent.getAction() + ":" + intent.getType() + ":"
+ (categories != null ? Arrays.toString(categories.toArray()) : ""));
- mIconFactory = IconDrawableFactory.newInstance(this, true);
}
@Override
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 644a974..2635969 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -7,14 +7,9 @@
#include "SkImageEncoder.h"
#include "SkImageInfo.h"
#include "SkColor.h"
-#include "SkColorPriv.h"
#include "SkColorSpace.h"
-#include "SkHalf.h"
#include "SkMatrix44.h"
-#include "SkPM4f.h"
-#include "SkPM4fPriv.h"
#include "GraphicsJNI.h"
-#include "SkUnPreMultiply.h"
#include "SkStream.h"
#include <binder/Parcel.h>
diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
index 343aef2..dca2da3 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -273,6 +273,32 @@
get_canvas(canvasHandle)->drawRect(left, top, right, bottom, *paint);
}
+static void drawDoubleRoundRectXY(JNIEnv* env, jobject, jlong canvasHandle, jfloat outerLeft,
+ jfloat outerTop, jfloat outerRight, jfloat outerBottom, jfloat outerRx,
+ jfloat outerRy, jfloat innerLeft, jfloat innerTop, jfloat innerRight,
+ jfloat innerBottom, jfloat innerRx, jfloat innerRy, jlong paintHandle) {
+ const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ get_canvas(canvasHandle)->drawDoubleRoundRectXY(
+ outerLeft, outerTop, outerRight, outerBottom, outerRx, outerRy,
+ innerLeft, innerTop, innerRight, innerBottom, innerRx, innerRy, *paint);
+}
+
+static void drawDoubleRoundRectRadii(JNIEnv* env, jobject, jlong canvasHandle, jfloat outerLeft,
+ jfloat outerTop, jfloat outerRight, jfloat outerBottom, jfloatArray jouterRadii,
+ jfloat innerLeft, jfloat innerTop, jfloat innerRight,
+ jfloat innerBottom, jfloatArray jinnerRadii, jlong paintHandle) {
+ const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+
+ float outerRadii[8];
+ float innerRadii[8];
+ env->GetFloatArrayRegion(jouterRadii, 0, 8, outerRadii);
+ env->GetFloatArrayRegion(jinnerRadii, 0, 8, innerRadii);
+ get_canvas(canvasHandle)->drawDoubleRoundRectRadii(
+ outerLeft, outerTop, outerRight, outerBottom, outerRadii,
+ innerLeft, innerTop, innerRight, innerBottom, innerRadii, *paint);
+
+}
+
static void drawRegion(JNIEnv* env, jobject, jlong canvasHandle, jlong regionHandle,
jlong paintHandle) {
const SkRegion* region = reinterpret_cast<SkRegion*>(regionHandle);
@@ -651,6 +677,8 @@
{"nDrawRect","(JFFFFJ)V", (void*) CanvasJNI::drawRect},
{"nDrawRegion", "(JJJ)V", (void*) CanvasJNI::drawRegion },
{"nDrawRoundRect","(JFFFFFFJ)V", (void*) CanvasJNI::drawRoundRect},
+ {"nDrawDoubleRoundRect", "(JFFFFFFFFFFFFJ)V", (void*) CanvasJNI::drawDoubleRoundRectXY},
+ {"nDrawDoubleRoundRect", "(JFFFF[FFFFF[FJ)V", (void*) CanvasJNI::drawDoubleRoundRectRadii},
{"nDrawCircle","(JFFFJ)V", (void*) CanvasJNI::drawCircle},
{"nDrawOval","(JFFFFJ)V", (void*) CanvasJNI::drawOval},
{"nDrawArc","(JFFFFFFZJ)V", (void*) CanvasJNI::drawArc},
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index d33ea0c..c1c86f0 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -158,6 +158,7 @@
optional ScreenRotationAnimationProto screen_rotation_animation = 12;
optional DisplayFramesProto display_frames = 13;
optional int32 surface_size = 14;
+ optional string focused_app = 15;
}
/* represents DisplayFrames */
diff --git a/core/proto/android/view/displaycutout.proto b/core/proto/android/view/displaycutout.proto
index f4744da..0a33101 100644
--- a/core/proto/android/view/displaycutout.proto
+++ b/core/proto/android/view/displaycutout.proto
@@ -26,5 +26,9 @@
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
optional .android.graphics.RectProto insets = 1;
- optional .android.graphics.RectProto bounds = 2;
+ reserved 2;
+ optional .android.graphics.RectProto bound_left = 3;
+ optional .android.graphics.RectProto bound_top = 4;
+ optional .android.graphics.RectProto bound_right = 5;
+ optional .android.graphics.RectProto bound_bottom = 6;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index e452f5d..f3f012d 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1932,6 +1932,13 @@
<permission android:name="android.permission.BIND_SCREENING_SERVICE"
android:protectionLevel="signature|privileged" />
+ <!-- Must be required by a {@link android.telecom.CallRedirectionService},
+ to ensure that only the system can bind to it.
+ <p>Protection level: signature|privileged
+ -->
+ <permission android:name="android.permission.BIND_CALL_REDIRECTION_SERVICE"
+ android:protectionLevel="signature|privileged" />
+
<!-- Must be required by a {@link android.telecom.ConnectionService},
to ensure that only the system can bind to it.
@deprecated {@link android.telecom.ConnectionService}s should require
diff --git a/core/res/res/values-night/values.xml b/core/res/res/values-night/values.xml
index a2ad3b9..9de8842 100644
--- a/core/res/res/values-night/values.xml
+++ b/core/res/res/values-night/values.xml
@@ -20,7 +20,7 @@
<!-- Color palette -->
<item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
<item name="colorSecondary">@color/secondary_device_default_settings</item>
- <item name="colorAccent">@color/accent_device_default_dark</item>
+ <item name="colorAccent">@color/white</item>
<item name="colorError">@color/error_color_device_default_dark</item>
<item name="colorControlNormal">?attr/textColorPrimary</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 8ff29ba..99af0de 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1442,8 +1442,11 @@
{@link #AndroidManifestService service},
{@link #AndroidManifestReceiver receiver},
{@link #AndroidManifestActivity activity},
- {@link #AndroidManifestActivityAlias activity-alias}, and
- {@link #AndroidManifestUsesLibrary uses-library}. The application tag
+ {@link #AndroidManifestActivityAlias activity-alias},
+ {@link #AndroidManifestUsesLibrary uses-library},
+ {@link #AndroidManifestUsesStaticLibrary uses-static-library}, and
+ {@link #AndroidManifestUsesPackage uses-package}.
+ The application tag
appears as a child of the root {@link #AndroidManifest manifest} tag in
an application's manifest file. -->
<declare-styleable name="AndroidManifestApplication" parent="AndroidManifest">
@@ -1877,12 +1880,35 @@
library is singed with more than one certificate.
<p>This appears as a child tag of the
- {@link #AndroidManifestUsesStaticLibrary uses-static-library} tag. -->
+ {@link #AndroidManifestUsesStaticLibrary uses-static-library} or
+ {@link #AndroidManifestUsesPackage uses-package} tag. -->
<declare-styleable name="AndroidManifestAdditionalCertificate" parent="AndroidManifestUsesStaticLibrary">
<!-- The SHA-256 digest of the library signing certificate. -->
<attr name="certDigest" />
</declare-styleable>
+ <!-- The <code>uses-package</code> specifies some kind of dependency on another
+ package. It does not have any impact on the app's execution on the device,
+ but provides information about dependencies it has on other packages that need
+ to be satisfied for it to run correctly. That is, this is primarily for
+ installers to know what other apps need to be installed along with this one.
+
+ <p>This appears as a child tag of the
+ {@link #AndroidManifestApplication application} tag. -->
+ <declare-styleable name="AndroidManifestUsesPackage" parent="AndroidManifestApplication">
+ <!-- Required type of association with the package, for example "android.package.ad_service"
+ if it provides an advertising service. -->
+ <attr name="packageType" format="string" />
+ <!-- Required name of the package you use. -->
+ <attr name="name" />
+ <!-- Optional minimum version of the package that satisfies the dependency. -->
+ <attr name="version" />
+ <!-- Optional minimum major version of the package that satisfies the dependency. -->
+ <attr name="versionMajor" format="integer" />
+ <!-- Optional SHA-256 digest of the package signing certificate. -->
+ <attr name="certDigest" format="string" />
+ </declare-styleable>
+
<!-- The <code>supports-screens</code> specifies the screen dimensions an
application supports. By default a modern application supports all
screen sizes and must explicitly disable certain screen sizes here;
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index f55f391..8b73b67 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3550,4 +3550,7 @@
<!-- Pre-scale volume at volume step 3 for Absolute Volume -->
<fraction name="config_prescaleAbsoluteVolume_index3">85%</fraction>
+
+ <!-- Whether or not the "SMS app service" feature is enabled -->
+ <bool name="config_useSmsAppService">true</bool>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index fadefff..2e42e4a 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2903,6 +2903,7 @@
<eat-comment />
<public-group type="attr" first-id="0x01010587">
+ <public name="packageType" />
<public name="opticalInsetLeft" />
<public name="opticalInsetTop" />
<public name="opticalInsetRight" />
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 72ae0d6..a7b6dde 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3475,4 +3475,6 @@
<java-symbol type="fraction" name="config_prescaleAbsoluteVolume_index1" />
<java-symbol type="fraction" name="config_prescaleAbsoluteVolume_index2" />
<java-symbol type="fraction" name="config_prescaleAbsoluteVolume_index3" />
+
+ <java-symbol type="bool" name="config_useSmsAppService" />
</resources>
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index d704957..f60d8d0ad 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -61,6 +61,7 @@
<uses-permission android:name="android.permission.READ_LOGS"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_SMS"/>
+ <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
diff --git a/core/tests/coretests/src/android/os/PowerManagerTest.java b/core/tests/coretests/src/android/os/PowerManagerTest.java
index 0d250b8..da17b56 100644
--- a/core/tests/coretests/src/android/os/PowerManagerTest.java
+++ b/core/tests/coretests/src/android/os/PowerManagerTest.java
@@ -64,7 +64,7 @@
// TODO: Some sort of functional test (maybe not in the unit test here?)
// that confirms that things are really happening e.g. screen power, keyboard power.
-}
+ }
/**
* Confirm that we can't create dysfunctional wakelocks.
@@ -85,6 +85,25 @@
}
/**
+ * Ensure that we can have work sources with work chains when uid is not set directly on work
+ * source, and that this doesn't crash system server.
+ *
+ * @throws Exception
+ */
+ @SmallTest
+ public void testWakeLockWithWorkChains() throws Exception {
+ PowerManager.WakeLock wakeLock = mPm.newWakeLock(
+ PowerManager.SCREEN_DIM_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP,
+ "TEST_LOCK");
+ WorkSource workSource = new WorkSource();
+ WorkSource.WorkChain workChain = workSource.createWorkChain();
+ workChain.addNode(1000, "test");
+ wakeLock.setWorkSource(workSource);
+
+ doTestWakeLock(wakeLock);
+ }
+
+ /**
* Apply a few tests to a wakelock to make sure it's healthy.
*
* @param wl The wakelock to be tested.
diff --git a/core/tests/coretests/src/android/view/DisplayCutoutTest.java b/core/tests/coretests/src/android/view/DisplayCutoutTest.java
index fe45fe7..c8d994c 100644
--- a/core/tests/coretests/src/android/view/DisplayCutoutTest.java
+++ b/core/tests/coretests/src/android/view/DisplayCutoutTest.java
@@ -17,6 +17,7 @@
package android.view;
import static android.view.DisplayCutout.NO_CUTOUT;
+import static android.view.DisplayCutout.extractBoundsFromList;
import static android.view.DisplayCutout.fromSpec;
import static org.hamcrest.Matchers.equalTo;
@@ -28,6 +29,7 @@
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
+import android.graphics.Insets;
import android.graphics.Rect;
import android.os.Parcel;
import android.platform.test.annotations.Presubmit;
@@ -39,38 +41,83 @@
import org.junit.runner.RunWith;
import java.util.Arrays;
+import java.util.Collections;
+
@RunWith(AndroidJUnit4.class)
@SmallTest
@Presubmit
public class DisplayCutoutTest {
+ private static final Rect ZERO_RECT = new Rect();
+
/** This is not a consistent cutout. Useful for verifying insets in one go though. */
final DisplayCutout mCutoutNumbers = new DisplayCutout(
- new Rect(1, 2, 3, 4),
- Arrays.asList(new Rect(5, 6, 7, 8)));
+ Insets.of(5, 6, 7, 8) /* safeInsets */,
+ null /* boundLeft */,
+ new Rect(9, 0, 10, 1) /* boundTop */,
+ null /* boundRight */,
+ null /* boundBottom */);
final DisplayCutout mCutoutTop = createCutoutTop();
@Test
+ public void testExtractBoundsFromList_left() {
+ Rect safeInsets = new Rect(10, 0, 0, 0);
+ Rect bound = new Rect(0, 80, 10, 120);
+ assertThat(extractBoundsFromList(safeInsets, Collections.singletonList(bound)),
+ equalTo(new Rect[]{bound, ZERO_RECT, ZERO_RECT, ZERO_RECT}));
+ }
+
+ @Test
+ public void testExtractBoundsFromList_top() {
+ Rect safeInsets = new Rect(0, 10, 0, 0);
+ Rect bound = new Rect(80, 0, 120, 10);
+ assertThat(extractBoundsFromList(safeInsets, Collections.singletonList(bound)),
+ equalTo(new Rect[]{ZERO_RECT, bound, ZERO_RECT, ZERO_RECT}));
+ }
+
+ @Test
+ public void testExtractBoundsFromList_right() {
+ Rect safeInsets = new Rect(0, 0, 10, 0);
+ Rect bound = new Rect(190, 80, 200, 120);
+ assertThat(extractBoundsFromList(safeInsets, Collections.singletonList(bound)),
+ equalTo(new Rect[]{ZERO_RECT, ZERO_RECT, bound, ZERO_RECT}));
+ }
+
+ @Test
+ public void testExtractBoundsFromList_bottom() {
+ Rect safeInsets = new Rect(0, 0, 0, 10);
+ Rect bound = new Rect(80, 190, 120, 200);
+ assertThat(extractBoundsFromList(safeInsets, Collections.singletonList(bound)),
+ equalTo(new Rect[]{ZERO_RECT, ZERO_RECT, ZERO_RECT, bound}));
+ }
+
+ @Test
+ public void testExtractBoundsFromList_top_and_bottom() {
+ Rect safeInsets = new Rect(0, 1, 0, 10);
+ Rect boundTop = new Rect(80, 0, 120, 10);
+ Rect boundBottom = new Rect(80, 190, 120, 200);
+ assertThat(extractBoundsFromList(safeInsets,
+ Arrays.asList(new Rect[]{boundTop, boundBottom})),
+ equalTo(new Rect[]{ZERO_RECT, boundTop, ZERO_RECT, boundBottom}));
+ }
+
+
+ @Test
public void hasCutout() throws Exception {
assertTrue(NO_CUTOUT.isEmpty());
assertFalse(mCutoutTop.isEmpty());
}
@Test
- public void getSafeInsets() throws Exception {
- assertEquals(1, mCutoutNumbers.getSafeInsetLeft());
- assertEquals(2, mCutoutNumbers.getSafeInsetTop());
- assertEquals(3, mCutoutNumbers.getSafeInsetRight());
- assertEquals(4, mCutoutNumbers.getSafeInsetBottom());
+ public void testGetSafeInsets() throws Exception {
+ assertEquals(5, mCutoutNumbers.getSafeInsetLeft());
+ assertEquals(6, mCutoutNumbers.getSafeInsetTop());
+ assertEquals(7, mCutoutNumbers.getSafeInsetRight());
+ assertEquals(8, mCutoutNumbers.getSafeInsetBottom());
- assertEquals(new Rect(1, 2, 3, 4), mCutoutNumbers.getSafeInsets());
- }
-
- @Test
- public void getBoundingRect() throws Exception {
- assertEquals(new Rect(50, 0, 75, 100), mCutoutTop.getBounds().getBounds());
+ assertEquals(new Rect(5, 6, 7, 8), mCutoutNumbers.getSafeInsets());
}
@Test
@@ -102,30 +149,30 @@
public void inset_insets_withLeftCutout() throws Exception {
DisplayCutout cutout = createCutoutWithInsets(100, 0, 0, 0).inset(1, 2, 3, 4);
- assertEquals(cutout.getSafeInsetLeft(), 99);
- assertEquals(cutout.getSafeInsetTop(), 0);
- assertEquals(cutout.getSafeInsetRight(), 0);
- assertEquals(cutout.getSafeInsetBottom(), 0);
+ assertEquals(99, cutout.getSafeInsetLeft());
+ assertEquals(0, cutout.getSafeInsetTop());
+ assertEquals(0, cutout.getSafeInsetRight());
+ assertEquals(0, cutout.getSafeInsetBottom());
}
@Test
public void inset_insets_withTopCutout() throws Exception {
DisplayCutout cutout = mCutoutTop.inset(1, 2, 3, 4);
- assertEquals(cutout.getSafeInsetLeft(), 0);
- assertEquals(cutout.getSafeInsetTop(), 98);
- assertEquals(cutout.getSafeInsetRight(), 0);
- assertEquals(cutout.getSafeInsetBottom(), 0);
+ assertEquals(0, cutout.getSafeInsetLeft());
+ assertEquals(98, cutout.getSafeInsetTop());
+ assertEquals(0, cutout.getSafeInsetRight());
+ assertEquals(0, cutout.getSafeInsetBottom());
}
@Test
public void inset_insets_withRightCutout() throws Exception {
DisplayCutout cutout = createCutoutWithInsets(0, 0, 100, 0).inset(1, 2, 3, 4);
- assertEquals(cutout.getSafeInsetLeft(), 0);
- assertEquals(cutout.getSafeInsetTop(), 0);
- assertEquals(cutout.getSafeInsetRight(), 97);
- assertEquals(cutout.getSafeInsetBottom(), 0);
+ assertEquals(0, cutout.getSafeInsetLeft());
+ assertEquals(0, cutout.getSafeInsetTop());
+ assertEquals(97, cutout.getSafeInsetRight());
+ assertEquals(0, cutout.getSafeInsetBottom());
}
@Test
@@ -153,16 +200,20 @@
@Test
public void inset_bounds() throws Exception {
DisplayCutout cutout = mCutoutTop.inset(1, 2, 3, 4);
-
- assertEquals(new Rect(49, -2, 74, 98), cutout.getBounds().getBounds());
+ assertThat(cutout.getBoundingRectsAll(), equalTo(
+ new Rect[]{ ZERO_RECT, new Rect(49, -2, 74, 98), ZERO_RECT, ZERO_RECT }));
}
+
+ // TODO: Deprecate fromBoundingRect.
+ /*
@Test
public void fromBoundingPolygon() throws Exception {
assertEquals(
new Rect(50, 0, 75, 100),
DisplayCutout.fromBoundingRect(50, 0, 75, 100).getBounds().getBounds());
}
+ */
@Test
public void parcel_unparcel_regular() {
@@ -231,6 +282,10 @@
DisplayCutout cutout = fromSpec("M -50,0 v 20 h 100 v -20 z"
+ "@bottom M -50,0 v -10,0 h 100 v 20 z", 200, 400, 2f);
assertThat(cutout.getSafeInsets(), equalTo(new Rect(0, 20, 0, 10)));
+ assertThat(cutout.getBoundingRectsAll(), equalTo(new Rect[]{
+ ZERO_RECT, new Rect(50, 0, 150, 20),
+ ZERO_RECT, new Rect(50, 390, 150, 410)
+ }));
}
@Test
@@ -281,8 +336,10 @@
}
private static DisplayCutout createCutoutWithInsets(int left, int top, int right, int bottom) {
+ Insets safeInset = Insets.of(left, top, right, bottom);
+ Rect boundTop = new Rect(50, 0, 75, 100);
return new DisplayCutout(
- new Rect(left, top, right, bottom),
- Arrays.asList(new Rect(50, 0, 75, 100)));
+ safeInset, null /* boundLeft */, boundTop, null /* boundRight */,
+ null /* boundBottom */);
}
}
diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
index 9edbf3e..1b00e09 100644
--- a/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
@@ -26,8 +26,8 @@
import android.content.pm.ServiceInfo;
import android.os.Bundle;
import android.os.Parcel;
-import android.support.test.filters.LargeTest;
import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import com.android.frameworks.coretests.R;
@@ -35,7 +35,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-@LargeTest
+@SmallTest
@RunWith(AndroidJUnit4.class)
public class InputMethodInfoTest {
diff --git a/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java b/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java
index cac4e88..218566e 100644
--- a/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java
@@ -27,6 +27,7 @@
import static org.junit.Assert.assertThat;
import android.content.Context;
+import android.graphics.Insets;
import android.graphics.Rect;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
@@ -44,18 +45,20 @@
import org.junit.runner.RunWith;
import java.lang.reflect.Field;
-import java.util.Arrays;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class ActionBarOverlayLayoutTest {
- private static final Rect TOP_INSET_5 = new Rect(0, 5, 0, 0);
- private static final Rect TOP_INSET_25 = new Rect(0, 25, 0, 0);
- private static final Rect ZERO_INSET = new Rect(0, 0, 0, 0);
+ private static final Insets TOP_INSET_5 = Insets.of(0, 5, 0, 0);
+ private static final Insets TOP_INSET_25 = Insets.of(0, 25, 0, 0);
private static final DisplayCutout CONSUMED_CUTOUT = null;
- private static final DisplayCutout CUTOUT_5 = new DisplayCutout(TOP_INSET_5, Arrays.asList(
- new Rect(100, 0, 200, 5)));
+ private static final DisplayCutout CUTOUT_5 = new DisplayCutout(
+ TOP_INSET_5,
+ null /* boundLeft */,
+ new Rect(100, 0, 200, 5),
+ null /* boundRight */,
+ null /* boundBottom*/);
private static final int EXACTLY_1000 = makeMeasureSpec(1000, EXACTLY);
private Context mContext;
@@ -112,7 +115,7 @@
mLayout.measure(EXACTLY_1000, EXACTLY_1000);
- assertThat(mContentInsetsListener.captured, is(insetsWith(ZERO_INSET, CONSUMED_CUTOUT)));
+ assertThat(mContentInsetsListener.captured, is(insetsWith(Insets.NONE, CONSUMED_CUTOUT)));
}
@Test
@@ -136,7 +139,7 @@
mLayout.measure(EXACTLY_1000, EXACTLY_1000);
- assertThat(mContentInsetsListener.captured, is(insetsWith(ZERO_INSET, NO_CUTOUT)));
+ assertThat(mContentInsetsListener.captured, is(insetsWith(Insets.NONE, NO_CUTOUT)));
}
@Test
@@ -160,11 +163,11 @@
mLayout.measure(EXACTLY_1000, EXACTLY_1000);
- assertThat(mContentInsetsListener.captured, is(insetsWith(ZERO_INSET, NO_CUTOUT)));
+ assertThat(mContentInsetsListener.captured, is(insetsWith(Insets.NONE, NO_CUTOUT)));
}
- private WindowInsets insetsWith(Rect content, DisplayCutout cutout) {
- return new WindowInsets(content, null, null, false, false, cutout);
+ private WindowInsets insetsWith(Insets content, DisplayCutout cutout) {
+ return new WindowInsets(content.toRect(), null, null, false, false, cutout);
}
private ViewGroup createViewGroupWithId(int id) {
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index fa37bed..0885a05 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -376,6 +376,53 @@
drawRoundRect(rect.left, rect.top, rect.right, rect.bottom, rx, ry, paint);
}
+ /**
+ * Make lint happy.
+ * See {@link Canvas#drawDoubleRoundRect(RectF, float, float, RectF, float, float, Paint)}
+ */
+ public void drawDoubleRoundRect(@NonNull RectF outer, float outerRx, float outerRy,
+ @NonNull RectF inner, float innerRx, float innerRy, @NonNull Paint paint) {
+ throwIfHasHwBitmapInSwMode(paint);
+ float outerLeft = outer.left;
+ float outerTop = outer.top;
+ float outerRight = outer.right;
+ float outerBottom = outer.bottom;
+
+ float innerLeft = inner.left;
+ float innerTop = inner.top;
+ float innerRight = inner.right;
+ float innerBottom = inner.bottom;
+ nDrawDoubleRoundRect(mNativeCanvasWrapper, outerLeft, outerTop, outerRight, outerBottom,
+ outerRx, outerRy, innerLeft, innerTop, innerRight, innerBottom, innerRx, innerRy,
+ paint.getNativeInstance());
+ }
+
+ /**
+ * Make lint happy.
+ * See {@link Canvas#drawDoubleRoundRect(RectF, float[], RectF, float[], Paint)}
+ */
+ public void drawDoubleRoundRect(@NonNull RectF outer, float[] outerRadii,
+ @NonNull RectF inner, float[] innerRadii, @NonNull Paint paint) {
+ throwIfHasHwBitmapInSwMode(paint);
+ if (innerRadii == null || outerRadii == null
+ || innerRadii.length != 8 || outerRadii.length != 8) {
+ throw new IllegalArgumentException("Both inner and outer radii arrays must contain "
+ + "exactly 8 values");
+ }
+ float outerLeft = outer.left;
+ float outerTop = outer.top;
+ float outerRight = outer.right;
+ float outerBottom = outer.bottom;
+
+ float innerLeft = inner.left;
+ float innerTop = inner.top;
+ float innerRight = inner.right;
+ float innerBottom = inner.bottom;
+ nDrawDoubleRoundRect(mNativeCanvasWrapper, outerLeft, outerTop, outerRight,
+ outerBottom, outerRadii, innerLeft, innerTop, innerRight, innerBottom, innerRadii,
+ paint.getNativeInstance());
+ }
+
public void drawText(@NonNull char[] text, int index, int count, float x, float y,
@NonNull Paint paint) {
if ((index | count | (index + count) |
@@ -631,6 +678,16 @@
private static native void nDrawRoundRect(long nativeCanvas, float left, float top, float right,
float bottom, float rx, float ry, long nativePaint);
+ private static native void nDrawDoubleRoundRect(long nativeCanvas, float outerLeft,
+ float outerTop, float outerRight, float outerBottom, float outerRx, float outerRy,
+ float innerLeft, float innerTop, float innerRight, float innerBottom, float innerRx,
+ float innerRy, long nativePaint);
+
+ private static native void nDrawDoubleRoundRect(long nativeCanvas, float outerLeft,
+ float outerTop, float outerRight, float outerBottom, float[] outerRadii,
+ float innerLeft, float innerTop, float innerRight, float innerBottom,
+ float[] innerRadii, long nativePaint);
+
private static native void nDrawPath(long nativeCanvas, long nativePath, long nativePaint);
private static native void nDrawRegion(long nativeCanvas, long nativeRegion, long nativePaint);
diff --git a/graphics/java/android/graphics/BaseRecordingCanvas.java b/graphics/java/android/graphics/BaseRecordingCanvas.java
index 6e93691..fb30ca2 100644
--- a/graphics/java/android/graphics/BaseRecordingCanvas.java
+++ b/graphics/java/android/graphics/BaseRecordingCanvas.java
@@ -377,6 +377,24 @@
}
@Override
+ public final void drawDoubleRoundRect(@NonNull RectF outer, float outerRx, float outerRy,
+ @NonNull RectF inner, float innerRx, float innerRy, @NonNull Paint paint) {
+ nDrawDoubleRoundRect(mNativeCanvasWrapper,
+ outer.left, outer.top, outer.right, outer.bottom, outerRx, outerRy,
+ inner.left, inner.top, inner.right, inner.bottom, innerRx, innerRy,
+ paint.getNativeInstance());
+ }
+
+ @Override
+ public final void drawDoubleRoundRect(@NonNull RectF outer, float[] outerRadii,
+ @NonNull RectF inner, float[] innerRadii, @NonNull Paint paint) {
+ nDrawDoubleRoundRect(mNativeCanvasWrapper,
+ outer.left, outer.top, outer.right, outer.bottom, outerRadii,
+ inner.left, inner.top, inner.right, inner.bottom, innerRadii,
+ paint.getNativeInstance());
+ }
+
+ @Override
public final void drawText(@NonNull char[] text, int index, int count, float x, float y,
@NonNull Paint paint) {
if ((index | count | (index + count)
@@ -593,6 +611,18 @@
float bottom, float rx, float ry, long nativePaint);
@FastNative
+ private static native void nDrawDoubleRoundRect(long nativeCanvas,
+ float outerLeft, float outerTop, float outerRight, float outerBottom,
+ float outerRx, float outerRy, float innerLeft, float innerTop, float innerRight,
+ float innerBottom, float innerRx, float innerRy, long nativePaint);
+
+ @FastNative
+ private static native void nDrawDoubleRoundRect(long nativeCanvas, float outerLeft,
+ float outerTop, float outerRight, float outerBottom, float[] outerRadii,
+ float innerLeft, float innerTop, float innerRight, float innerBottom,
+ float[] innerRadii, long nativePaint);
+
+ @FastNative
private static native void nDrawPath(long nativeCanvas, long nativePath, long nativePaint);
@FastNative
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 36c1c21..e35a3be 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -1877,6 +1877,51 @@
}
/**
+ * Draws a double rounded rectangle using the specified paint. The resultant round rect
+ * will be filled in the area defined between the outer and inner rectangular bounds if
+ * the {@link Paint} configured with {@link Paint.Style#FILL}.
+ * Otherwise if {@link Paint.Style#STROKE} is used, then 2 rounded rect strokes will
+ * be drawn at the outer and inner rounded rectangles
+ *
+ * @param outer The outer rectangular bounds of the roundRect to be drawn
+ * @param outerRx The x-radius of the oval used to round the corners on the outer rectangle
+ * @param outerRy The y-radius of the oval used to round the corners on the outer rectangle
+ * @param inner The inner rectangular bounds of the roundRect to be drawn
+ * @param innerRx The x-radius of the oval used to round the corners on the inner rectangle
+ * @param innerRy The y-radius of the oval used to round the corners on the outer rectangle
+ * @param paint The paint used to draw the double roundRect
+ */
+ @Override
+ public void drawDoubleRoundRect(@NonNull RectF outer, float outerRx, float outerRy,
+ @NonNull RectF inner, float innerRx, float innerRy, @NonNull Paint paint) {
+ super.drawDoubleRoundRect(outer, outerRx, outerRy, inner, innerRx, innerRy, paint);
+ }
+
+ /**
+ * Draws a double rounded rectangle using the specified paint. The resultant round rect
+ * will be filled in the area defined between the outer and inner rectangular bounds if
+ * the {@link Paint} configured with {@link Paint.Style#FILL}.
+ * Otherwise if {@link Paint.Style#STROKE} is used, then 2 rounded rect strokes will
+ * be drawn at the outer and inner rounded rectangles
+ *
+ * @param outer The outer rectangular bounds of the roundRect to be drawn
+ * @param outerRadii Array of 8 float representing the x, y corner radii for top left,
+ * top right, bottom right, bottom left corners respectively on the outer
+ * rounded rectangle
+ *
+ * @param inner The inner rectangular bounds of the roundRect to be drawn
+ * @param innerRadii Array of 8 float representing the x, y corner radii for top left,
+ * top right, bottom right, bottom left corners respectively on the
+ * outer rounded rectangle
+ * @param paint The paint used to draw the double roundRect
+ */
+ @Override
+ public void drawDoubleRoundRect(@NonNull RectF outer, float[] outerRadii,
+ @NonNull RectF inner, float[] innerRadii, @NonNull Paint paint) {
+ super.drawDoubleRoundRect(outer, outerRadii, inner, innerRadii, paint);
+ }
+
+ /**
* Draw the text, with origin at (x,y), using the specified paint. The origin is interpreted
* based on the Align setting in the paint.
*
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index 6ce66bd..009e042 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -59,6 +59,7 @@
import java.io.InputStream;
import java.lang.annotation.Retention;
import java.nio.ByteBuffer;
+import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicBoolean;
/**
@@ -283,26 +284,7 @@
return createFromStream(is, true, this);
}
-
- final FileDescriptor fd = assetFd.getFileDescriptor();
- final long offset = assetFd.getStartOffset();
-
- ImageDecoder decoder = null;
- try {
- try {
- Os.lseek(fd, offset, SEEK_SET);
- decoder = nCreate(fd, this);
- } catch (ErrnoException e) {
- decoder = createFromStream(new FileInputStream(fd), true, this);
- }
- } finally {
- if (decoder == null) {
- IoUtils.closeQuietly(assetFd);
- } else {
- decoder.mAssetFd = assetFd;
- }
- }
- return decoder;
+ return createFromAssetFileDescriptor(assetFd, this);
}
}
@@ -354,6 +336,30 @@
return decoder;
}
+ @NonNull
+ private static ImageDecoder createFromAssetFileDescriptor(@NonNull AssetFileDescriptor assetFd,
+ Source source) throws IOException {
+ final FileDescriptor fd = assetFd.getFileDescriptor();
+ final long offset = assetFd.getStartOffset();
+
+ ImageDecoder decoder = null;
+ try {
+ try {
+ Os.lseek(fd, offset, SEEK_SET);
+ decoder = nCreate(fd, source);
+ } catch (ErrnoException e) {
+ decoder = createFromStream(new FileInputStream(fd), true, source);
+ }
+ } finally {
+ if (decoder == null) {
+ IoUtils.closeQuietly(assetFd);
+ } else {
+ decoder.mAssetFd = assetFd;
+ }
+ }
+ return decoder;
+ }
+
/**
* For backwards compatibility, this does *not* close the InputStream.
*
@@ -528,6 +534,29 @@
}
}
+ private static class CallableSource extends Source {
+ CallableSource(@NonNull Callable<AssetFileDescriptor> callable) {
+ mCallable = callable;
+ }
+
+ private final Callable<AssetFileDescriptor> mCallable;
+
+ @Override
+ public ImageDecoder createImageDecoder() throws IOException {
+ AssetFileDescriptor assetFd = null;
+ try {
+ assetFd = mCallable.call();
+ } catch (Exception e) {
+ if (e instanceof IOException) {
+ throw (IOException) e;
+ } else {
+ throw new IOException(e);
+ }
+ }
+ return createFromAssetFileDescriptor(assetFd, this);
+ }
+ }
+
/**
* Information about an encoded image.
*/
@@ -971,6 +1000,27 @@
}
/**
+ * Create a new {@link Source Source} from a {@link Callable} that returns a
+ * new {@link AssetFileDescriptor} for each request. This provides control
+ * over how the {@link AssetFileDescriptor} is created, such as passing
+ * options into {@link ContentResolver#openTypedAssetFileDescriptor}, or
+ * enabling use of a {@link android.os.CancellationSignal}.
+ * <p>
+ * It's important for the given {@link Callable} to return a new, unique
+ * {@link AssetFileDescriptor} for each invocation, to support reuse of the
+ * returned {@link Source Source}.
+ *
+ * @return a new Source object, which can be passed to
+ * {@link #decodeDrawable decodeDrawable} or {@link #decodeBitmap
+ * decodeBitmap}.
+ */
+ @AnyThread
+ @NonNull
+ public static Source createSource(@NonNull Callable<AssetFileDescriptor> callable) {
+ return new CallableSource(callable);
+ }
+
+ /**
* Return the width and height of a given sample size.
*
* <p>This takes an input that functions like
diff --git a/graphics/java/android/graphics/Insets.java b/graphics/java/android/graphics/Insets.java
index c3449dd..de110c8 100644
--- a/graphics/java/android/graphics/Insets.java
+++ b/graphics/java/android/graphics/Insets.java
@@ -73,6 +73,15 @@
}
/**
+ * Returns a Rect intance with the appropriate values.
+ *
+ * @hide
+ */
+ public @NonNull Rect toRect() {
+ return new Rect(left, top, right, bottom);
+ }
+
+ /**
* Two Insets instances are equal iff they belong to the same class and their fields are
* pairwise equal.
*
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 17f1a3b..2e5aef5 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -504,6 +504,11 @@
mCanvas->drawRoundRect(rect, rx, ry, *filterPaint(paint));
}
+void SkiaCanvas::drawDoubleRoundRect(const SkRRect& outer, const SkRRect& inner,
+ const SkPaint& paint) {
+ mCanvas->drawDRRect(outer, inner, *filterPaint(paint));
+}
+
void SkiaCanvas::drawCircle(float x, float y, float radius, const SkPaint& paint) {
if (CC_UNLIKELY(radius <= 0 || paint.nothingToDraw())) return;
mCanvas->drawCircle(x, y, radius, *filterPaint(paint));
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 24b7ec6..3a877cf 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -107,6 +107,10 @@
virtual void drawRegion(const SkRegion& region, const SkPaint& paint) override;
virtual void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,
const SkPaint& paint) override;
+
+ virtual void drawDoubleRoundRect(const SkRRect& outer, const SkRRect& inner,
+ const SkPaint& paint) override;
+
virtual void drawCircle(float x, float y, float radius, const SkPaint& paint) override;
virtual void drawOval(float left, float top, float right, float bottom,
const SkPaint& paint) override;
diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp
index af7f013..e2ea2bc 100644
--- a/libs/hwui/hwui/Canvas.cpp
+++ b/libs/hwui/hwui/Canvas.cpp
@@ -178,6 +178,41 @@
MinikinUtils::forFontRun(layout, &paint, f);
}
+void Canvas::drawDoubleRoundRectXY(float outerLeft, float outerTop, float outerRight,
+ float outerBottom, float outerRx, float outerRy, float innerLeft,
+ float innerTop, float innerRight, float innerBottom, float innerRx,
+ float innerRy, const SkPaint& paint) {
+ if (CC_UNLIKELY(paint.nothingToDraw())) return;
+ SkRect outer = SkRect::MakeLTRB(outerLeft, outerTop, outerRight, outerBottom);
+ SkRect inner = SkRect::MakeLTRB(innerLeft, innerTop, innerRight, innerBottom);
+
+ SkRRect outerRRect;
+ outerRRect.setRectXY(outer, outerRx, outerRy);
+
+ SkRRect innerRRect;
+ innerRRect.setRectXY(inner, innerRx, innerRy);
+ drawDoubleRoundRect(outerRRect, innerRRect, paint);
+}
+
+void Canvas::drawDoubleRoundRectRadii(float outerLeft, float outerTop, float outerRight,
+ float outerBottom, const float* outerRadii, float innerLeft,
+ float innerTop, float innerRight, float innerBottom,
+ const float* innerRadii, const SkPaint& paint) {
+ static_assert(sizeof(SkVector) == sizeof(float) * 2);
+ if (CC_UNLIKELY(paint.nothingToDraw())) return;
+ SkRect outer = SkRect::MakeLTRB(outerLeft, outerTop, outerRight, outerBottom);
+ SkRect inner = SkRect::MakeLTRB(innerLeft, innerTop, innerRight, innerBottom);
+
+ SkRRect outerRRect;
+ const SkVector* outerSkVector = reinterpret_cast<const SkVector*>(outerRadii);
+ outerRRect.setRectRadii(outer, outerSkVector);
+
+ SkRRect innerRRect;
+ const SkVector* innerSkVector = reinterpret_cast<const SkVector*>(innerRadii);
+ innerRRect.setRectRadii(inner, innerSkVector);
+ drawDoubleRoundRect(outerRRect, innerRRect, paint);
+}
+
class DrawTextOnPathFunctor {
public:
DrawTextOnPathFunctor(const minikin::Layout& layout, Canvas* canvas, float hOffset,
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index b9af7de2..e99742b 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -236,6 +236,8 @@
virtual void drawRegion(const SkRegion& region, const SkPaint& paint) = 0;
virtual void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,
const SkPaint& paint) = 0;
+ virtual void drawDoubleRoundRect(const SkRRect& outer, const SkRRect& inner,
+ const SkPaint& paint) = 0;
virtual void drawCircle(float x, float y, float radius, const SkPaint& paint) = 0;
virtual void drawOval(float left, float top, float right, float bottom,
const SkPaint& paint) = 0;
@@ -284,6 +286,16 @@
const SkPath& path, float hOffset, float vOffset, const Paint& paint,
const Typeface* typeface);
+ void drawDoubleRoundRectXY(float outerLeft, float outerTop, float outerRight,
+ float outerBottom, float outerRx, float outerRy, float innerLeft,
+ float innerTop, float innerRight, float innerBottom, float innerRx,
+ float innerRy, const SkPaint& paint);
+
+ void drawDoubleRoundRectRadii(float outerLeft, float outerTop, float outerRight,
+ float outerBottom, const float* outerRadii, float innerLeft,
+ float innerTop, float innerRight, float innerBottom,
+ const float* innerRadii, const SkPaint& paint);
+
static int GetApiLevel() { return sApiLevel; }
protected:
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 85eac4b..c074cce 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -4607,7 +4607,7 @@
/**
* The message sent to apps when the contents of the device list changes if they provide
- * a {#link Handler} object to addOnAudioDeviceConnectionListener().
+ * a {@link Handler} object to addOnAudioDeviceConnectionListener().
*/
private final static int MSG_DEVICES_CALLBACK_REGISTERED = 0;
private final static int MSG_DEVICES_DEVICES_ADDED = 1;
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 340f279..18d36eb 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -227,7 +227,7 @@
* transfers the object to the <em>Prepared</em> state once the method call
* returns, or a call to {@link #prepareAsync()} (asynchronous) which
* first transfers the object to the <em>Preparing</em> state after the
- * call returns (which occurs almost right way) while the internal
+ * call returns (which occurs almost right away) while the internal
* player engine continues working on the rest of preparation work
* until the preparation work completes. When the preparation completes or when {@link #prepare()} call returns,
* the internal player engine then calls a user supplied callback method,
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index 0f83fd5..8665f28 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -47,414 +47,190 @@
/**
* @hide
- * MediaPlayer2 class can be used to control playback
- * of audio/video files and streams. An example on how to use the methods in
- * this class can be found in {@link android.widget.VideoView}.
+ *
+ * MediaPlayer2 class can be used to control playback of audio/video files and streams.
*
* <p>Topics covered here are:
* <ol>
- * <li><a href="#StateDiagram">State Diagram</a>
- * <li><a href="#Valid_and_Invalid_States">Valid and Invalid States</a>
+ * <li><a href="#PlayerStates">Player states</a>
+ * <li><a href="#InvalidStates">Invalid method calls</a>
* <li><a href="#Permissions">Permissions</a>
- * <li><a href="#Callbacks">Register informational and error callbacks</a>
+ * <li><a href="#Callbacks">Callbacks</a>
* </ol>
*
- * <div class="special reference">
- * <h3>Developer Guides</h3>
- * <p>For more information about how to use MediaPlayer2, read the
- * <a href="{@docRoot}guide/topics/media/mediaplayer.html">Media Playback</a> developer guide.</p>
- * </div>
*
- * <a name="StateDiagram"></a>
- * <h3>State Diagram</h3>
+ * <h3 id="PlayerStates">Player states</h3>
*
- * <p>Playback control of audio/video files and streams is managed as a state
- * machine. The following diagram shows the life cycle and the states of a
- * MediaPlayer2 object driven by the supported playback control operations.
- * The ovals represent the states a MediaPlayer2 object may reside
- * in. The arcs represent the playback control operations that drive the object
- * state transition. There are two types of arcs. The arcs with a single arrow
- * head represent synchronous method calls, while those with
- * a double arrow head represent asynchronous method calls.</p>
+ * <p>The playback control of audio/video files is managed as a state machine.</p>
+ * <p><div style="text-align:center;"><img src="../../../images/mediaplayer2_state_diagram.png"
+ * alt="MediaPlayer2 State diagram"
+ * border="0" /></div></p>
+ * <p>The MediaPlayer2 object has five states:</p>
+ * <ol>
+ * <li><p>{@link #PLAYER_STATE_IDLE}: MediaPlayer2 is in the <strong>Idle</strong>
+ * state after you create it using
+ * {@link #create()}, or after calling {@link #reset()}.</p>
*
- * <p><img src="../../../images/mediaplayer_state_diagram.gif"
- * alt="MediaPlayer State diagram"
- * border="0" /></p>
+ * <p>While in this state, you should call
+ * {@link #setDataSource(DataSourceDesc2) setDataSource()}. It is a good
+ * programming practice to register an {@link EventCallback#onCallCompleted onCallCompleted}
+ * <a href="#Callbacks">callback</a> and watch for {@link #CALL_STATUS_BAD_VALUE} and
+ * {@link #CALL_STATUS_ERROR_IO}, which might be caused by <code>setDataSource</code>.
+ * </p>
*
- * <p>From this state diagram, one can see that a MediaPlayer2 object has the
- * following states:</p>
+ * <p>Calling {@link #prepare()} transfers a MediaPlayer2 object to
+ * the <strong>Prepared</strong> state. Note
+ * that {@link #prepare()} is asynchronous. When the preparation completes,
+ * if you register an {@link EventCallback#onInfo onInfo} <a href="#Callbacks">callback</a>,
+ * the player executes the callback
+ * with {@link #MEDIA_INFO_PREPARED} and transitions to the
+ * <strong>Prepared</strong> state.</p>
+ * </li>
+ *
+ * <li>{@link #PLAYER_STATE_PREPARED}: A MediaPlayer object must be in the
+ * <strong>Prepared</strong> state before playback can be started for the first time.
+ * While in this state, you can set player properties
+ * such as audio/sound volume and looping by invoking the corresponding set methods.
+ * Calling {@link #play()} transfers a MediaPlayer2 object to
+ * the <strong>Playing</strong> state.
+ * </li>
+ *
+ * <li>{@link #PLAYER_STATE_PLAYING}:
+ * <p>The player plays the data source while in this state.
+ * If you register an {@link EventCallback#onInfo onInfo} <a href="#Callbacks">callback</a>,
+ * the player regularly executes the callback with
+ * {@link #MEDIA_INFO_BUFFERING_UPDATE}.
+ * This allows applications to keep track of the buffering status
+ * while streaming audio/video.</p>
+ *
+ * <p> When the playback reaches the end of stream, the behavior depends on whether or
+ * not you've enabled looping by calling {@link #loopCurrent(boolean) loopCurrent}:</p>
+ * <ul>
+ * <li>If the looping mode was set to <code>false</code>, the player will transfer
+ * to the <strong>Paused</strong> state. If you registered an {@link EventCallback#onInfo
+ * onInfo} <a href="#Callbacks">callback</a>
+ * the player calls the callback with {@link #MEDIA_INFO_DATA_SOURCE_END} and enters
+ * the <strong>Paused</strong> state.
+ * </li>
+ * <li>If the looping mode was set to <code>true</code>,
+ * the MediaPlayer2 object remains in the <strong>Playing</strong> state and replays its
+ * data source from the beginning.</li>
+ * </ul>
+ * </li>
+ *
+ * <li>{@link #PLAYER_STATE_PAUSED}: Audio/video playback pauses while in this state.
+ * Call {@link #play()} to resume playback from the position where it paused.</li>
+ *
+ * <li>{@link #PLAYER_STATE_ERROR}: <p>In general, playback might fail due to various
+ * reasons such as unsupported audio/video format, poorly interleaved
+ * audio/video, resolution too high, streaming timeout, and others.
+ * In addition, due to programming errors, a playback
+ * control operation might be performed from an <a href="#InvalidStates">invalid state</a>.
+ * In these cases the player transitions to the <strong>Error</strong> state.</p>
+ *
+ * <p>If you register an {@link EventCallback#onError onError}}
+ * <a href="#Callbacks">callback</a>,
+ * the callback will be performed when entering the state. When programming errors happen,
+ * such as calling {@link #prepare() prepare} and
+ * {@link #setDataSource(DataSourceDesc) setDataSource} methods
+ * from an <a href="#InvalidStates">invalid state</a>, the callback is called with
+ * {@link #CALL_STATUS_INVALID_OPERATION}. The MediaPlayer2 object enters the
+ * <strong>Error</strong> state whether or not a callback exists. </p>
+ *
+ * <p>To recover from an error and reuse a MediaPlayer2 object that is in the <strong>
+ * Error</strong> state,
+ * call {@link #reset() reset}. The object will return to the <strong>Idle</strong>
+ * state and all state information will be lost.</p>
+ * </li>
+ * </ol>
+ *
+ * <p>You should follow these best practices when coding an app that uses MediaPlayer2:</p>
+ *
* <ul>
- * <li>When a MediaPlayer2 object is just created using <code>create</code> or
- * after {@link #reset()} is called, it is in the <em>Idle</em> state; and after
- * {@link #close()} is called, it is in the <em>End</em> state. Between these
- * two states is the life cycle of the MediaPlayer2 object.
- * <ul>
- * <li> It is a programming error to invoke methods such
- * as {@link #getCurrentPosition()},
- * {@link #getDuration()}, {@link #getVideoHeight()},
- * {@link #getVideoWidth()}, {@link #setAudioAttributes(AudioAttributes)},
- * {@link #setPlayerVolume(float)}, {@link #pause()}, {@link #play()},
- * {@link #seekTo(long, int)} or
- * {@link #prepare()} in the <em>Idle</em> state.
- * <li>It is also recommended that once
- * a MediaPlayer2 object is no longer being used, call {@link #close()} immediately
- * so that resources used by the internal player engine associated with the
- * MediaPlayer2 object can be released immediately. Resource may include
- * singleton resources such as hardware acceleration components and
- * failure to call {@link #close()} may cause subsequent instances of
- * MediaPlayer2 objects to fallback to software implementations or fail
- * altogether. Once the MediaPlayer2
- * object is in the <em>End</em> state, it can no longer be used and
- * there is no way to bring it back to any other state. </li>
- * <li>Furthermore,
- * the MediaPlayer2 objects created using <code>new</code> is in the
- * <em>Idle</em> state.
- * </li>
- * </ul>
- * </li>
- * <li>In general, some playback control operation may fail due to various
- * reasons, such as unsupported audio/video format, poorly interleaved
- * audio/video, resolution too high, streaming timeout, and the like.
- * Thus, error reporting and recovery is an important concern under
- * these circumstances. Sometimes, due to programming errors, invoking a playback
- * control operation in an invalid state may also occur. Under all these
- * error conditions, the internal player engine invokes a user supplied
- * EventCallback.onError() method if an EventCallback has been
- * registered beforehand via
- * {@link #setEventCallback(Executor, EventCallback)}.
- * <ul>
- * <li>It is important to note that once an error occurs, the
- * MediaPlayer2 object enters the <em>Error</em> state (except as noted
- * above), even if an error listener has not been registered by the application.</li>
- * <li>In order to reuse a MediaPlayer2 object that is in the <em>
- * Error</em> state and recover from the error,
- * {@link #reset()} can be called to restore the object to its <em>Idle</em>
- * state.</li>
- * <li>It is good programming practice to have your application
- * register a OnErrorListener to look out for error notifications from
- * the internal player engine.</li>
- * <li>IllegalStateException is
- * thrown to prevent programming errors such as calling
- * {@link #prepare()}, {@link #setDataSource(DataSourceDesc)}
- * methods in an invalid state. </li>
- * </ul>
- * </li>
- * <li>Calling
- * {@link #setDataSource(DataSourceDesc)} transfers a
- * MediaPlayer2 object in the <em>Idle</em> state to the
- * <em>Initialized</em> state.
- * <ul>
- * <li>An IllegalStateException is thrown if
- * setDataSource() is called in any other state.</li>
- * <li>It is good programming
- * practice to always look out for <code>IllegalArgumentException</code>
- * and <code>IOException</code> that may be thrown from
- * <code>setDataSource</code>.</li>
- * </ul>
- * </li>
- * <li>A MediaPlayer2 object must first enter the <em>Prepared</em> state
- * before playback can be started.
- * <ul>
- * <li>There are an asynchronous way that the <em>Prepared</em> state can be reached:
- * a call to {@link #prepare()} (asynchronous) which
- * first transfers the object to the <em>Preparing</em> state after the
- * call returns (which occurs almost right way) while the internal
- * player engine continues working on the rest of preparation work
- * until the preparation work completes. When the preparation completes,
- * the internal player engine then calls a user supplied callback method,
- * onInfo() of the EventCallback interface with {@link #MEDIA_INFO_PREPARED},
- * if an EventCallback is registered beforehand via
- * {@link #setEventCallback(Executor, EventCallback)}.</li>
- * <li>It is important to note that
- * the <em>Preparing</em> state is a transient state, and the behavior
- * of calling any method with side effect while a MediaPlayer2 object is
- * in the <em>Preparing</em> state is undefined.</li>
- * <li>An IllegalStateException is
- * thrown if {@link #prepare()} is called in
- * any other state.</li>
- * <li>While in the <em>Prepared</em> state, properties
- * such as audio/sound volume, screenOnWhilePlaying, looping can be
- * adjusted by invoking the corresponding set methods.</li>
- * </ul>
- * </li>
- * <li>To start the playback, {@link #play()} must be called. After
- * {@link #play()} returns successfully, the MediaPlayer2 object is in the
- * <em>Started</em> state. {@link #getPlayerState()} can be called to test
- * whether the MediaPlayer2 object is in the <em>Started</em> state.
- * <ul>
- * <li>While in the <em>Started</em> state, the internal player engine calls
- * a user supplied callback method EventCallback.onInfo() with
- * {@link #MEDIA_INFO_BUFFERING_UPDATE} if an EventCallback has been
- * registered beforehand via
- * {@link #setEventCallback(Executor, EventCallback)}.
- * This callback allows applications to keep track of the buffering status
- * while streaming audio/video.</li>
- * <li>Calling {@link #play()} has not effect
- * on a MediaPlayer2 object that is already in the <em>Started</em> state.</li>
- * </ul>
- * </li>
- * <li>Playback can be paused and stopped, and the current playback position
- * can be adjusted. Playback can be paused via {@link #pause()}. When the call to
- * {@link #pause()} returns, the MediaPlayer2 object enters the
- * <em>Paused</em> state. Note that the transition from the <em>Started</em>
- * state to the <em>Paused</em> state and vice versa happens
- * asynchronously in the player engine. It may take some time before
- * the state is updated in calls to {@link #getPlayerState()}, and it can be
- * a number of seconds in the case of streamed content.
- * <ul>
- * <li>Calling {@link #play()} to resume playback for a paused
- * MediaPlayer2 object, and the resumed playback
- * position is the same as where it was paused. When the call to
- * {@link #play()} returns, the paused MediaPlayer2 object goes back to
- * the <em>Started</em> state.</li>
- * <li>Calling {@link #pause()} has no effect on
- * a MediaPlayer2 object that is already in the <em>Paused</em> state.</li>
- * </ul>
- * </li>
- * <li>The playback position can be adjusted with a call to
- * {@link #seekTo(long, int)}.
- * <ul>
- * <li>Although the asynchronuous {@link #seekTo(long, int)}
- * call returns right away, the actual seek operation may take a while to
- * finish, especially for audio/video being streamed. When the actual
- * seek operation completes, the internal player engine calls a user
- * supplied EventCallback.onCallCompleted() with
- * {@link #CALL_COMPLETED_SEEK_TO}
- * if an EventCallback has been registered beforehand via
- * {@link #setEventCallback(Executor, EventCallback)}.</li>
- * <li>Please
- * note that {@link #seekTo(long, int)} can also be called in the other states,
- * such as <em>Prepared</em>, <em>Paused</em> and <em>PlaybackCompleted
- * </em> state. When {@link #seekTo(long, int)} is called in those states,
- * one video frame will be displayed if the stream has video and the requested
- * position is valid.
- * </li>
- * <li>Furthermore, the actual current playback position
- * can be retrieved with a call to {@link #getCurrentPosition()}, which
- * is helpful for applications such as a Music player that need to keep
- * track of the playback progress.</li>
- * </ul>
- * </li>
- * <li>When the playback reaches the end of stream, the playback completes.
- * <ul>
- * <li>If current source is set to loop by {@link #loopCurrent(boolean)},
- * the MediaPlayer2 object shall remain in the <em>Started</em> state.</li>
- * <li>If the looping mode was set to <var>false
- * </var>, the player engine calls a user supplied callback method,
- * EventCallback.onCompletion(), if an EventCallback is
- * registered beforehand via
- * {@link #setEventCallback(Executor, EventCallback)}.
- * The invoke of the callback signals that the object is now in the <em>
- * PlaybackCompleted</em> state.</li>
- * <li>While in the <em>PlaybackCompleted</em>
- * state, calling {@link #play()} can restart the playback from the
- * beginning of the audio/video source.</li>
+ *
+ * <li>Use <a href="#Callbacks">callbacks</a> to respond to state changes and errors.</li>
+ *
+ * <li>When a MediaPlayer2 object is no longer being used, call {@link #close() close} as soon as
+ * possible to release the resources used by the internal player engine associated with the
+ * MediaPlayer2. Failure to call {@link #close() close} may cause subsequent instances of
+ * MediaPlayer2 objects to fallback to software implementations or fail altogether.
+ * You cannot use MediaPlayer2
+ * after you call {@link #close() close}. There is no way to bring it back to any other state.</li>
+ *
+ * <li>The current playback position can be retrieved with a call to
+ * {@link #getCurrentPosition() getCurrentPosition},
+ * which is helpful for applications such as a Music player that need to keep track of the playback
+ * progress.</li>
+ *
+ * <li>The playback position can be adjusted with a call to {@link #seekTo seekTo}. Although the
+ * asynchronous {@link #seekTo seekTo} call returns right away, the actual seek operation may take a
+ * while to finish, especially for audio/video being streamed. If you register an
+ * {@link EventCallback#onCallCompleted onCallCompleted} <a href="#Callbacks">callback</a>,
+ * the callback is
+ * called When the seek operation completes with {@link #CALL_COMPLETED_SEEK_TO}.</li>
+ *
+ * <li>You can call {@link #seekTo seekTo} from the <strong>Paused</strong> state.
+ * In this case, if you are playing a video stream and
+ * the requested position is valid one video frame is displayed.</li>
+ *
* </ul>
*
+ * <h3 id="InvalidStates">Invalid method calls</h3>
*
- * <a name="Valid_and_Invalid_States"></a>
- * <h3>Valid and invalid states</h3>
+ * <p>The only methods you safely call from the <strong>Error</strong> state are
+ * {@link #close() close},
+ * {@link #reset() reset},
+ * {@link #notifyWhenCommandLabelReached notifyWhenCommandLabelReached},
+ * {@link #clearPendingCommands() clearPendingCommands},
+ * {@link #setEventCallback setEventCallback},
+ * {@link #clearEventCallback() clearEventCallback}
+ * and {@link #getState() getState}.
+ * Any other methods might throw an exception, return meaningless data, or invoke a
+ * {@link EventCallback#onCallCompleted onCallCompleted} with an error code.</p>
+ *
+ * <p>Most methods can be called from any non-Error state. They will either perform their work or
+ * silently have no effect. The following table lists the methods that will invoke a
+ * {@link EventCallback#onCallCompleted onCallCompleted} with an error code
+ * or throw an exception when they are called from the associated invalid states.</p>
*
* <table border="0" cellspacing="0" cellpadding="0">
- * <tr><td>Method Name </p></td>
- * <td>Valid Sates </p></td>
- * <td>Invalid States </p></td>
- * <td>Comments </p></td></tr>
- * <tr><td>attachAuxEffect </p></td>
- * <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted} </p></td>
- * <td>{Idle, Error} </p></td>
- * <td>This method must be called after setDataSource.
- * Calling it does not change the object state. </p></td></tr>
- * <tr><td>getAudioSessionId </p></td>
- * <td>any </p></td>
- * <td>{} </p></td>
- * <td>This method can be called in any state and calling it does not change
- * the object state. </p></td></tr>
- * <tr><td>getCurrentPosition </p></td>
- * <td>{Idle, Initialized, Prepared, Started, Paused, Stopped,
- * PlaybackCompleted} </p></td>
- * <td>{Error}</p></td>
- * <td>Successful invoke of this method in a valid state does not change the
- * state. Calling this method in an invalid state transfers the object
- * to the <em>Error</em> state. </p></td></tr>
- * <tr><td>getDuration </p></td>
- * <td>{Prepared, Started, Paused, Stopped, PlaybackCompleted} </p></td>
- * <td>{Idle, Initialized, Error} </p></td>
- * <td>Successful invoke of this method in a valid state does not change the
- * state. Calling this method in an invalid state transfers the object
- * to the <em>Error</em> state. </p></td></tr>
- * <tr><td>getVideoHeight </p></td>
- * <td>{Idle, Initialized, Prepared, Started, Paused, Stopped,
- * PlaybackCompleted}</p></td>
- * <td>{Error}</p></td>
- * <td>Successful invoke of this method in a valid state does not change the
- * state. Calling this method in an invalid state transfers the object
- * to the <em>Error</em> state. </p></td></tr>
- * <tr><td>getVideoWidth </p></td>
- * <td>{Idle, Initialized, Prepared, Started, Paused, Stopped,
- * PlaybackCompleted}</p></td>
- * <td>{Error}</p></td>
- * <td>Successful invoke of this method in a valid state does not change
- * the state. Calling this method in an invalid state transfers the
- * object to the <em>Error</em> state. </p></td></tr>
- * <tr><td>getPlayerState </p></td>
- * <td>{Idle, Initialized, Prepared, Started, Paused, Stopped,
- * PlaybackCompleted}</p></td>
- * <td>{Error}</p></td>
- * <td>Successful invoke of this method in a valid state does not change
- * the state. Calling this method in an invalid state transfers the
- * object to the <em>Error</em> state. </p></td></tr>
- * <tr><td>pause </p></td>
- * <td>{Started, Paused, PlaybackCompleted}</p></td>
- * <td>{Idle, Initialized, Prepared, Stopped, Error}</p></td>
- * <td>Successful invoke of this method in a valid state transfers the
- * object to the <em>Paused</em> state. Calling this method in an
- * invalid state transfers the object to the <em>Error</em> state.</p></td></tr>
- * <tr><td>prepare </p></td>
- * <td>{Initialized, Stopped} </p></td>
- * <td>{Idle, Prepared, Started, Paused, PlaybackCompleted, Error} </p></td>
- * <td>Successful invoke of this method in a valid state transfers the
- * object to the <em>Preparing</em> state. Calling this method in an
- * invalid state throws an IllegalStateException.</p></td></tr>
- * <tr><td>release </p></td>
- * <td>any </p></td>
- * <td>{} </p></td>
- * <td>After {@link #close()}, the object is no longer available. </p></td></tr>
- * <tr><td>reset </p></td>
- * <td>{Idle, Initialized, Prepared, Started, Paused, Stopped,
- * PlaybackCompleted, Error}</p></td>
- * <td>{}</p></td>
- * <td>After {@link #reset()}, the object is like being just created.</p></td></tr>
- * <tr><td>seekTo </p></td>
- * <td>{Prepared, Started, Paused, PlaybackCompleted} </p></td>
- * <td>{Idle, Initialized, Stopped, Error}</p></td>
- * <td>Successful invoke of this method in a valid state does not change
- * the state. Calling this method in an invalid state transfers the
- * object to the <em>Error</em> state. </p></td></tr>
- * <tr><td>setAudioAttributes </p></td>
- * <td>{Idle, Initialized, Stopped, Prepared, Started, Paused,
- * PlaybackCompleted}</p></td>
- * <td>{Error}</p></td>
- * <td>Successful invoke of this method does not change the state. In order for the
- * target audio attributes type to become effective, this method must be called before
- * prepare().</p></td></tr>
- * <tr><td>setAudioSessionId </p></td>
- * <td>{Idle} </p></td>
- * <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted,
- * Error} </p></td>
- * <td>This method must be called in idle state as the audio session ID must be known before
- * calling setDataSource. Calling it does not change the object
- * state. </p></td></tr>
- * <tr><td>setAudioStreamType (deprecated)</p></td>
- * <td>{Idle, Initialized, Stopped, Prepared, Started, Paused,
- * PlaybackCompleted}</p></td>
- * <td>{Error}</p></td>
- * <td>Successful invoke of this method does not change the state. In order for the
- * target audio stream type to become effective, this method must be called before
- * prepare().</p></td></tr>
- * <tr><td>setAuxEffectSendLevel </p></td>
- * <td>any</p></td>
- * <td>{} </p></td>
- * <td>Calling this method does not change the object state. </p></td></tr>
- * <tr><td>setDataSource </p></td>
- * <td>{Idle} </p></td>
- * <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted,
- * Error} </p></td>
- * <td>Successful invoke of this method in a valid state transfers the
- * object to the <em>Initialized</em> state. Calling this method in an
- * invalid state throws an IllegalStateException.</p></td></tr>
- * <tr><td>setDisplay </p></td>
- * <td>any </p></td>
- * <td>{} </p></td>
- * <td>This method can be called in any state and calling it does not change
- * the object state. </p></td></tr>
- * <tr><td>setSurface </p></td>
- * <td>any </p></td>
- * <td>{} </p></td>
- * <td>This method can be called in any state and calling it does not change
- * the object state. </p></td></tr>
- * <tr><td>loopCurrent </p></td>
- * <td>{Idle, Initialized, Stopped, Prepared, Started, Paused,
- * PlaybackCompleted}</p></td>
- * <td>{Error}</p></td>
- * <td>Successful invoke of this method in a valid state does not change
- * the state. Calling this method in an
- * invalid state transfers the object to the <em>Error</em> state.</p></td></tr>
- * <tr><td>isLooping </p></td>
- * <td>any </p></td>
- * <td>{} </p></td>
- * <td>This method can be called in any state and calling it does not change
- * the object state. </p></td></tr>
- * <tr><td>setDrmEventCallback </p></td>
- * <td>any </p></td>
- * <td>{} </p></td>
- * <td>This method can be called in any state and calling it does not change
- * the object state. </p></td></tr>
- * <tr><td>setEventCallback </p></td>
- * <td>any </p></td>
- * <td>{} </p></td>
- * <td>This method can be called in any state and calling it does not change
- * the object state. </p></td></tr>
- * <tr><td>setPlaybackParams</p></td>
- * <td>{Initialized, Prepared, Started, Paused, PlaybackCompleted, Error}</p></td>
- * <td>{Idle, Stopped} </p></td>
- * <td>This method will change state in some cases, depending on when it's called.
- * </p></td></tr>
- * <tr><td>setPlayerVolume </p></td>
- * <td>{Idle, Initialized, Stopped, Prepared, Started, Paused,
- * PlaybackCompleted}</p></td>
- * <td>{Error}</p></td>
- * <td>Successful invoke of this method does not change the state.
- * <tr><td>play </p></td>
- * <td>{Prepared, Started, Paused, PlaybackCompleted}</p></td>
- * <td>{Idle, Initialized, Stopped, Error}</p></td>
- * <td>Successful invoke of this method in a valid state transfers the
- * object to the <em>Started</em> state. Calling this method in an
- * invalid state transfers the object to the <em>Error</em> state.</p></td></tr>
- * <tr><td>stop </p></td>
- * <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td>
- * <td>{Idle, Initialized, Error}</p></td>
- * <td>Successful invoke of this method in a valid state transfers the
- * object to the <em>Stopped</em> state. Calling this method in an
- * invalid state transfers the object to the <em>Error</em> state.</p></td></tr>
- * <tr><td>getTrackInfo </p></td>
- * <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td>
- * <td>{Idle, Initialized, Error}</p></td>
- * <td>Successful invoke of this method does not change the state.</p></td></tr>
- * <tr><td>selectTrack </p></td>
- * <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td>
- * <td>{Idle, Initialized, Error}</p></td>
- * <td>Successful invoke of this method does not change the state.</p></td></tr>
- * <tr><td>deselectTrack </p></td>
- * <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td>
- * <td>{Idle, Initialized, Error}</p></td>
- * <td>Successful invoke of this method does not change the state.</p></td></tr>
+ * <tr><th>Method Name</th>
+ * <th>Invalid States</th></tr>
*
+ * <tr><td>setDataSource</td> <td>{Prepared, Paused, Playing}</td></tr>
+ * <tr><td>prepare</td> <td>{Prepared, Paused, Playing}</td></tr>
+ * <tr><td>play</td> <td>{Idle}</td></tr>
+ * <tr><td>pause</td> <td>{Idle}</td></tr>
+ * <tr><td>seekTo</td> <td>{Idle}</td></tr>
+ * <tr><td>getCurrentPosition</td> <td>{Idle}</td></tr>
+ * <tr><td>getDuration</td> <td>{Idle}</td></tr>
+ * <tr><td>getBufferedPosition</td> <td>{Idle}</td></tr>
+ * <tr><td>getTrackInfo</td> <td>{Idle}</td></tr>
+ * <tr><td>getSelectedTrack</td> <td>{Idle}</td></tr>
+ * <tr><td>selectTrack</td> <td>{Idle}</td></tr>
+ * <tr><td>deselectTrack</td> <td>{Idle}</td></tr>
* </table>
*
- * <a name="Permissions"></a>
- * <h3>Permissions</h3>
- * <p>One may need to declare a corresponding WAKE_LOCK permission {@link
- * android.R.styleable#AndroidManifestUsesPermission <uses-permission>}
- * element.
- *
+ * <h3 id="Permissions">Permissions</h3>
* <p>This class requires the {@link android.Manifest.permission#INTERNET} permission
* when used with network-based content.
*
- * <a name="Callbacks"></a>
- * <h3>Callbacks</h3>
- * <p>Applications may want to register for informational and error
- * events in order to be informed of some internal state update and
- * possible runtime errors during playback or streaming. Registration for
- * these events is done by properly setting the appropriate listeners (via calls
- * to
- * {@link #setEventCallback(Executor, EventCallback)},
- * {@link #setDrmEventCallback(Executor, DrmEventCallback)}).
- * In order to receive the respective callback
- * associated with these listeners, applications are required to create
- * MediaPlayer2 objects on a thread with its own Looper running (main UI
- * thread by default has a Looper running).
+ * <h3 id="Callbacks">Callbacks</h3>
+ * <p>Many errors do not result in a transition to the <strong>Error</strong> state.
+ * It is good programming practice to register callback listeners using
+ * {@link #setEventCallback(Executor, EventCallback) setEventCallback} and
+ * {@link #setDrmEventCallback(Executor, DrmEventCallback) setDrmEventCallback}).
+ * You can receive a callback at any time and from any state.</p>
*
+ * <p>If it's important for your app to respond to state changes (for instance, to update the
+ * controls on a transport UI), you should register an
+ * {@link EventCallback#onCallCompleted onCallCompleted} and
+ * detect state change commands by testing the <code>what</code> parameter for a callback from one
+ * of the state transition methods: {@link #CALL_COMPLETED_PREPARE}, {@link #CALL_COMPLETED_PLAY},
+ * and {@link #CALL_COMPLETED_PAUSE}.
+ * Then check the <code>status</code> parameter. The value {@link #CALL_STATUS_NO_ERROR} indicates a
+ * successful transition. Any other value will be an error. Call {@link #getState()} to
+ * determine the current state. </p>
*/
public abstract class MediaPlayer2 implements SubtitleController.Listener
, AutoCloseable
@@ -467,7 +243,6 @@
* @return A MediaPlayer2 object created
*/
public static final MediaPlayer2 create() {
- // TODO: load MediaUpdate APK
return new MediaPlayer2Impl();
}
diff --git a/media/java/android/media/MediaPlayer2Impl.java b/media/java/android/media/MediaPlayer2Impl.java
index 53735e5..678cb9a 100644
--- a/media/java/android/media/MediaPlayer2Impl.java
+++ b/media/java/android/media/MediaPlayer2Impl.java
@@ -53,7 +53,6 @@
import com.android.framework.protobuf.InvalidProtocolBufferException;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.Preconditions;
import libcore.io.IoBridge;
@@ -350,7 +349,7 @@
addTask(new Task(CALL_COMPLETED_SET_DATA_SOURCE, false) {
@Override
void process() throws IOException {
- Preconditions.checkArgument(dsd != null, "the DataSourceDesc cannot be null");
+ checkArgument(dsd != null, "the DataSourceDesc cannot be null");
int state = getState();
if (state != PLAYER_STATE_ERROR && state != PLAYER_STATE_IDLE) {
throw new IllegalStateException("called in wrong state " + state);
@@ -376,7 +375,7 @@
addTask(new Task(CALL_COMPLETED_SET_NEXT_DATA_SOURCE, false) {
@Override
void process() {
- Preconditions.checkArgument(dsd != null, "the DataSourceDesc cannot be null");
+ checkArgument(dsd != null, "the DataSourceDesc cannot be null");
synchronized (mSrcLock) {
mNextDSDs = new ArrayList<DataSourceDesc>(1);
mNextDSDs.add(dsd);
@@ -690,7 +689,7 @@
private void handleDataSource(boolean isCurrent, @NonNull DataSourceDesc dsd, long srcId)
throws IOException {
- Preconditions.checkNotNull(dsd, "the DataSourceDesc cannot be null");
+ checkArgument(dsd != null, "the DataSourceDesc cannot be null");
switch (dsd.getType()) {
case DataSourceDesc.TYPE_CALLBACK:
@@ -1290,7 +1289,7 @@
addTask(new Task(CALL_COMPLETED_SET_BUFFERING_PARAMS, false) {
@Override
void process() {
- Preconditions.checkArgument(params != null, "the BufferingParams cannot be null");
+ checkArgument(params != null, "the BufferingParams cannot be null");
_setBufferingParams(params);
}
});
@@ -1354,7 +1353,7 @@
addTask(new Task(CALL_COMPLETED_SET_PLAYBACK_PARAMS, false) {
@Override
void process() {
- Preconditions.checkArgument(params != null, "the PlaybackParams cannot be null");
+ checkArgument(params != null, "the PlaybackParams cannot be null");
_setPlaybackParams(params);
}
});
@@ -1387,7 +1386,7 @@
addTask(new Task(CALL_COMPLETED_SET_SYNC_PARAMS, false) {
@Override
void process() {
- Preconditions.checkArgument(params != null, "the SyncParams cannot be null");
+ checkArgument(params != null, "the SyncParams cannot be null");
_setSyncParams(params);
}
});
@@ -3027,6 +3026,12 @@
}
}
+ public static void checkArgument(boolean expression, String errorMessage) {
+ if (!expression) {
+ throw new IllegalArgumentException(errorMessage);
+ }
+ }
+
private void sendEvent(final EventNotifier notifier) {
synchronized (mEventCbLock) {
try {
@@ -3803,7 +3808,7 @@
private native void _prepareDrm(@NonNull byte[] uuid, @NonNull byte[] drmSessionId);
- // Modular DRM helpers
+ // Modular DRM helpers
private void prepareDrm_createDrmStep(@NonNull UUID uuid)
throws UnsupportedSchemeException {
@@ -4681,7 +4686,7 @@
private void sendCompleteNotification(int status) {
// In {@link #notifyWhenCommandLabelReached} case, a separate callback
- // {#link #onCommandLabelReached} is already called in {@code process()}.
+ // {@link #onCommandLabelReached} is already called in {@code process()}.
if (mMediaCallType == CALL_COMPLETED_NOTIFY_WHEN_COMMAND_LABEL_REACHED) {
return;
}
diff --git a/media/java/android/media/ThumbnailUtils.java b/media/java/android/media/ThumbnailUtils.java
index 4cc86fd..fd14060 100644
--- a/media/java/android/media/ThumbnailUtils.java
+++ b/media/java/android/media/ThumbnailUtils.java
@@ -160,7 +160,15 @@
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
try {
retriever.setDataSource(filePath);
- bitmap = retriever.getFrameAtTime(-1);
+ // First retrieve album art in metadata if set.
+ byte[] embeddedPicture = retriever.getEmbeddedPicture();
+ if (embeddedPicture != null && embeddedPicture.length > 0) {
+ bitmap = BitmapFactory.decodeByteArray(embeddedPicture, 0, embeddedPicture.length);
+ }
+ // Fall back to first frame of the video.
+ if (bitmap == null) {
+ bitmap = retriever.getFrameAtTime(-1);
+ }
} catch (IllegalArgumentException ex) {
// Assume this is a corrupt video file
} catch (RuntimeException ex) {
diff --git a/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureSource.java b/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureSource.java
index 6595baa..7919723 100644
--- a/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureSource.java
+++ b/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureSource.java
@@ -38,7 +38,7 @@
* <p>To use, connect up the sourceListener callback, and then when executing
* the graph, use the SurfaceTexture object passed to the callback to feed
* frames into the filter graph. For example, pass the SurfaceTexture into
- * {#link
+ * {@link
* android.hardware.Camera.setPreviewTexture(android.graphics.SurfaceTexture)}.
* This filter is intended for applications that need for flexibility than the
* CameraSource and MediaSource provide. Note that the application needs to
diff --git a/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java b/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java
index 0bf8bc1..4408ef5 100644
--- a/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java
+++ b/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java
@@ -95,7 +95,6 @@
private long mFullBackupSize;
private FileInputStream mCurFullRestoreStream;
- private FileOutputStream mFullRestoreSocketStream;
private byte[] mFullRestoreBuffer;
private final LocalTransportParameters mParameters;
@@ -195,6 +194,15 @@
@Override
public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data, int flags) {
+ try {
+ return performBackupInternal(packageInfo, data, flags);
+ } finally {
+ IoUtils.closeQuietly(data);
+ }
+ }
+
+ private int performBackupInternal(
+ PackageInfo packageInfo, ParcelFileDescriptor data, int flags) {
boolean isIncremental = (flags & FLAG_INCREMENTAL) != 0;
boolean isNonIncremental = (flags & FLAG_NON_INCREMENTAL) != 0;
@@ -750,7 +758,6 @@
private void resetFullRestoreState() {
IoUtils.closeQuietly(mCurFullRestoreStream);
mCurFullRestoreStream = null;
- mFullRestoreSocketStream = null;
mFullRestoreBuffer = null;
}
@@ -795,10 +802,11 @@
Log.e(TAG, "Unable to read archive for " + name);
return TRANSPORT_PACKAGE_REJECTED;
}
- mFullRestoreSocketStream = new FileOutputStream(socket.getFileDescriptor());
mFullRestoreBuffer = new byte[2*1024];
}
+ FileOutputStream stream = new FileOutputStream(socket.getFileDescriptor());
+
int nRead;
try {
nRead = mCurFullRestoreStream.read(mFullRestoreBuffer);
@@ -815,14 +823,12 @@
if (DEBUG) {
Log.i(TAG, " delivering restore chunk: " + nRead);
}
- mFullRestoreSocketStream.write(mFullRestoreBuffer, 0, nRead);
+ stream.write(mFullRestoreBuffer, 0, nRead);
}
} catch (IOException e) {
return TRANSPORT_ERROR; // Hard error accessing the file; shouldn't happen
} finally {
- // Most transports will need to explicitly close 'socket' here, but this transport
- // is in the same process as the caller so it can leave it up to the backup manager
- // to manage both socket fds.
+ IoUtils.closeQuietly(socket);
}
return nRead;
diff --git a/packages/SystemUI/docs/plugins.md b/packages/SystemUI/docs/plugins.md
index ed91f3d..6892005 100644
--- a/packages/SystemUI/docs/plugins.md
+++ b/packages/SystemUI/docs/plugins.md
@@ -73,7 +73,7 @@
1. They must be signed with the platform cert
2. They must include SystemUIPluginLib in LOCAL_JAVA_LIBRARIES (NOT LOCAL_STATIC_JAVA_LIBRARIES)
-Basically just copy the [example file](/packages/SystemUI/plugin/ExamplePlugin/Android.mk).
+Basically just copy the [example blueprint file](/packages/SystemUI/plugin/ExamplePlugin/Android.bp).
To declare a plugin, you add a service to your manifest. Add an intent filter to match the action for the plugin, and set the name to point at the class that implements the plugin interface.
diff --git a/packages/SystemUI/res/drawable/ic_lock.xml b/packages/SystemUI/res/drawable/ic_lock.xml
new file mode 100644
index 0000000..3fb8c8d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_lock.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ Copyright (C) 2018 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="32dp"
+ android:height="32dp"
+ android:viewportWidth="32.0"
+ android:viewportHeight="32.0">
+ <path
+ android:pathData="M20.25,6.89C20.25,4.62 18.36,2.75 16,2.75C13.64,2.75 11.75,4.62 11.75,6.89V11.25H20.25V6.89ZM21.75,11.25V6.89C21.75,3.76 19.16,1.25 16,1.25C12.84,1.25 10.25,3.76 10.25,6.89V11.25H10C8.48,11.25 7.25,12.48 7.25,14V26C7.25,27.52 8.48,28.75 10,28.75H22C23.52,28.75 24.75,27.52 24.75,26V14C24.75,12.48 23.52,11.25 22,11.25H21.75ZM10,12.75C9.31,12.75 8.75,13.31 8.75,14V26C8.75,26.69 9.31,27.25 10,27.25H22C22.69,27.25 23.25,26.69 23.25,26V14C23.25,13.31 22.69,12.75 22,12.75H10ZM18,20C18,21.1 17.1,22 16,22C14.9,22 14,21.1 14,20C14,18.9 14.9,18 16,18C17.1,18 18,18.9 18,20Z"
+ android:fillType="evenOdd"
+ android:fillColor="?attr/wallpaperTextColor"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_lock_24dp.xml b/packages/SystemUI/res/drawable/ic_lock_24dp.xml
deleted file mode 100644
index bf0dc95..0000000
--- a/packages/SystemUI/res/drawable/ic_lock_24dp.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<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:fillColor="?attr/wallpaperTextColor"
- android:pathData="M18.0,8.0l-1.0,0.0L17.0,6.0c0.0,-2.8 -2.2,-5.0 -5.0,-5.0C9.2,1.0 7.0,3.2 7.0,6.0l0.0,2.0L6.0,8.0c-1.1,0.0 -2.0,0.9 -2.0,2.0l0.0,10.0c0.0,1.1 0.9,2.0 2.0,2.0l12.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L20.0,10.0C20.0,8.9 19.1,8.0 18.0,8.0zM12.0,17.0c-1.1,0.0 -2.0,-0.9 -2.0,-2.0s0.9,-2.0 2.0,-2.0c1.1,0.0 2.0,0.9 2.0,2.0S13.1,17.0 12.0,17.0zM15.1,8.0L8.9,8.0L8.9,6.0c0.0,-1.7 1.4,-3.1 3.1,-3.1c1.7,0.0 3.1,1.4 3.1,3.1L15.1,8.0z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_lock_open.xml b/packages/SystemUI/res/drawable/ic_lock_open.xml
new file mode 100644
index 0000000..12a811c
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_lock_open.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ Copyright (C) 2018 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="32dp"
+ android:height="32dp"
+ android:viewportWidth="32.0"
+ android:viewportHeight="32.0">
+ <path
+ android:pathData="M21.75,6.89C21.75,4.62 23.64,2.75 26,2.75C28.36,2.75 30.25,4.62 30.25,6.89V12H31.75V6.89C31.75,3.76 29.16,1.25 26,1.25C22.84,1.25 20.25,3.76 20.25,6.89V11.25H10C8.48,11.25 7.25,12.48 7.25,14V26C7.25,27.52 8.48,28.75 10,28.75H22C23.52,28.75 24.75,27.52 24.75,26V14C24.75,12.48 23.52,11.25 22,11.25H21.75V6.89ZM10,12.75C9.31,12.75 8.75,13.31 8.75,14V26C8.75,26.69 9.31,27.25 10,27.25H22C22.69,27.25 23.25,26.69 23.25,26V14C23.25,13.31 22.69,12.75 22,12.75H10ZM18,20C18,21.1 17.1,22 16,22C14.9,22 14,21.1 14,20C14,18.9 14.9,18 16,18C17.1,18 18,18.9 18,20Z"
+ android:fillType="evenOdd"
+ android:fillColor="?attr/wallpaperTextColor"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index d0d379c..96a1bab 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -88,7 +88,7 @@
android:layout_width="@dimen/keyguard_affordance_width"
android:layout_height="@dimen/keyguard_affordance_height"
android:layout_gravity="bottom|center_horizontal"
- android:src="@drawable/ic_lock_24dp"
+ android:src="@drawable/ic_lock"
android:contentDescription="@string/accessibility_unlock_button"
android:scaleType="center" />
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 7c355c9..ab7dec9 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -645,10 +645,6 @@
<dimen name="keyguard_affordance_height">56dp</dimen>
<dimen name="keyguard_affordance_width">56dp</dimen>
- <!-- The width/height of the phone/camera/unlock icon drawable on keyguard. -->
- <dimen name="keyguard_affordance_icon_height">24dp</dimen>
- <dimen name="keyguard_affordance_icon_width">24dp</dimen>
-
<dimen name="keyguard_indication_margin_bottom">65dp</dimen>
<!-- The text size for battery level -->
diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
index 1af2156..d351c4f3 100644
--- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
@@ -17,6 +17,10 @@
package com.android.systemui;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_UP;
+import static android.view.MotionEvent.ACTION_CANCEL;
+
import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_SWIPE_UP;
import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_SHOW_OVERVIEW_BUTTON;
import static com.android.systemui.shared.system.NavigationBarCompat.InteractionType;
@@ -86,6 +90,7 @@
private boolean mIsEnabled;
private int mCurrentBoundedUserId = -1;
private float mBackButtonAlpha;
+ private MotionEvent mStatusBarGestureDownEvent;
private ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() {
@@ -108,6 +113,9 @@
}
public void onStatusBarMotionEvent(MotionEvent event) {
+ if (!verifyCaller("onStatusBarMotionEvent")) {
+ return;
+ }
long token = Binder.clearCallingIdentity();
try {
// TODO move this logic to message queue
@@ -115,6 +123,16 @@
StatusBar bar = SysUiServiceProvider.getComponent(mContext, StatusBar.class);
if (bar != null) {
bar.dispatchNotificationsPanelTouchEvent(event);
+
+ int action = event.getActionMasked();
+ if (action == ACTION_DOWN) {
+ mStatusBarGestureDownEvent = MotionEvent.obtain(event);
+ }
+ if (action == ACTION_UP || action == ACTION_CANCEL) {
+ mStatusBarGestureDownEvent.recycle();
+ mStatusBarGestureDownEvent = null;
+ }
+ event.recycle();
}
});
} finally {
@@ -298,7 +316,7 @@
// This is the death handler for the binder from the launcher service
private final IBinder.DeathRecipient mOverviewServiceDeathRcpt
- = this::startConnectionToCurrentUser;
+ = this::cleanupAfterDeath;
public OverviewProxyService(Context context) {
mContext = context;
@@ -328,6 +346,22 @@
return mBackButtonAlpha;
}
+ public void cleanupAfterDeath() {
+ if (mStatusBarGestureDownEvent != null) {
+ mHandler.post(()-> {
+ StatusBar bar = SysUiServiceProvider.getComponent(mContext, StatusBar.class);
+ if (bar != null) {
+ System.out.println("MERONG dispatchNotificationPanelTouchEvent");
+ mStatusBarGestureDownEvent.setAction(MotionEvent.ACTION_CANCEL);
+ bar.dispatchNotificationsPanelTouchEvent(mStatusBarGestureDownEvent);
+ mStatusBarGestureDownEvent.recycle();
+ mStatusBarGestureDownEvent = null;
+ }
+ });
+ }
+ startConnectionToCurrentUser();
+ }
+
public void startConnectionToCurrentUser() {
if (mHandler.getLooper() != Looper.myLooper()) {
mHandler.post(mConnectionRunnable);
diff --git a/packages/SystemUI/src/com/android/systemui/RegionInterceptingFrameLayout.java b/packages/SystemUI/src/com/android/systemui/RegionInterceptingFrameLayout.java
index 646f69e..6dc2d67 100644
--- a/packages/SystemUI/src/com/android/systemui/RegionInterceptingFrameLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/RegionInterceptingFrameLayout.java
@@ -76,7 +76,7 @@
continue;
}
- internalInsetsInfo.touchableRegion.op(riv.getInterceptRegion(), Op.UNION);
+ internalInsetsInfo.touchableRegion.op(unionRegion, Op.UNION);
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 48181bc..4d24d82 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -76,6 +76,9 @@
import com.android.systemui.tuner.TunerService.Tunable;
import com.android.systemui.util.leak.RotationUtils;
+import java.util.ArrayList;
+import java.util.List;
+
import androidx.annotation.VisibleForTesting;
/**
@@ -108,6 +111,23 @@
private boolean mPendingRotationChange;
private Handler mHandler;
+ /**
+ * Converts a set of {@link Rect}s into a {@link Region}
+ *
+ * @hide
+ */
+ public static Region rectsToRegion(List<Rect> rects) {
+ Region result = Region.obtain();
+ if (rects != null) {
+ for (Rect r : rects) {
+ if (r != null && !r.isEmpty()) {
+ result.op(r, Region.Op.UNION);
+ }
+ }
+ }
+ return result;
+ }
+
@Override
public void start() {
mHandler = startHandlerThread();
@@ -539,7 +559,7 @@
private final DisplayInfo mInfo = new DisplayInfo();
private final Paint mPaint = new Paint();
- private final Region mBounds = new Region();
+ private final List<Rect> mBounds = new ArrayList();
private final Rect mBoundingRect = new Rect();
private final Path mBoundingPath = new Path();
private final int[] mLocation = new int[2];
@@ -629,12 +649,12 @@
mStart = isStart();
requestLayout();
getDisplay().getDisplayInfo(mInfo);
- mBounds.setEmpty();
+ mBounds.clear();
mBoundingRect.setEmpty();
mBoundingPath.reset();
int newVisible;
if (shouldDrawCutout(getContext()) && hasCutout()) {
- mBounds.set(mInfo.displayCutout.getBounds());
+ mBounds.addAll(mInfo.displayCutout.getBoundingRects());
localBounds(mBoundingRect);
updateBoundingPath();
invalidate();
@@ -713,32 +733,17 @@
public static void boundsFromDirection(DisplayCutout displayCutout, int gravity,
Rect out) {
- Region bounds = boundsFromDirection(displayCutout, gravity);
- out.set(bounds.getBounds());
- bounds.recycle();
- }
-
- public static Region boundsFromDirection(DisplayCutout displayCutout, int gravity) {
- Region bounds = displayCutout.getBounds();
switch (gravity) {
case Gravity.TOP:
- bounds.op(0, 0, Integer.MAX_VALUE, displayCutout.getSafeInsetTop(),
- Region.Op.INTERSECT);
- break;
+ out.set(displayCutout.getBoundingRectTop());
case Gravity.LEFT:
- bounds.op(0, 0, displayCutout.getSafeInsetLeft(), Integer.MAX_VALUE,
- Region.Op.INTERSECT);
- break;
+ out.set(displayCutout.getBoundingRectLeft());
case Gravity.BOTTOM:
- bounds.op(0, displayCutout.getSafeInsetTop() + 1, Integer.MAX_VALUE,
- Integer.MAX_VALUE, Region.Op.INTERSECT);
- break;
+ out.set(displayCutout.getBoundingRectBottom());
case Gravity.RIGHT:
- bounds.op(displayCutout.getSafeInsetLeft() + 1, 0, Integer.MAX_VALUE,
- Integer.MAX_VALUE, Region.Op.INTERSECT);
- break;
+ out.set(displayCutout.getBoundingRectRight());
}
- return bounds;
+ out.setEmpty();
}
private void localBounds(Rect out) {
@@ -771,7 +776,8 @@
}
View rootView = getRootView();
- Region cutoutBounds = mInfo.displayCutout.getBounds();
+ Region cutoutBounds = rectsToRegion(
+ mInfo.displayCutout.getBoundingRects());
// Transform to window's coordinate space
rootView.getLocationOnScreen(mLocation);
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 608e303..512cd82 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -1543,7 +1543,6 @@
* ScrimController.GRADIENT_SCRIM_ALPHA * 255);
mGradientDrawable.setAlpha(alpha);
})
- .withEndAction(() -> getWindow().getDecorView().requestAccessibilityFocus())
.start();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 4b5ab2a..c398a4a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -6,7 +6,9 @@
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.content.Context;
+import android.content.res.Configuration;
import android.content.res.Resources;
+import android.os.Bundle;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
@@ -29,6 +31,7 @@
public class PagedTileLayout extends ViewPager implements QSTileLayout {
private static final boolean DEBUG = false;
+ private static final String CURRENT_PAGE = "current_page";
private static final String TAG = "PagedTileLayout";
private static final int REVEAL_SCROLL_DURATION_MILLIS = 750;
@@ -54,6 +57,9 @@
private AnimatorSet mBounceAnimatorSet;
private float mLastExpansion;
private boolean mDistributeTiles = false;
+ private int mPageToRestore = -1;
+ private int mLayoutOrientation;
+ private int mLayoutDirection;
public PagedTileLayout(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -61,13 +67,37 @@
setAdapter(mAdapter);
setOnPageChangeListener(mOnPageChangeListener);
setCurrentItem(0, false);
+ mLayoutOrientation = getResources().getConfiguration().orientation;
+ mLayoutDirection = getLayoutDirection();
+ }
+
+ public void saveInstanceState(Bundle outState) {
+ outState.putInt(CURRENT_PAGE, getCurrentItem());
+ }
+
+ public void restoreInstanceState(Bundle savedInstanceState) {
+ // There's only 1 page at this point. We want to restore the correct page once the
+ // pages have been inflated
+ mPageToRestore = savedInstanceState.getInt(CURRENT_PAGE, -1);
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ if (mLayoutOrientation != newConfig.orientation) {
+ mLayoutOrientation = newConfig.orientation;
+ setCurrentItem(0, false);
+ }
}
@Override
public void onRtlPropertiesChanged(int layoutDirection) {
super.onRtlPropertiesChanged(layoutDirection);
- setAdapter(mAdapter);
- setCurrentItem(0, false);
+ if (mLayoutDirection != layoutDirection) {
+ mLayoutDirection = layoutDirection;
+ setAdapter(mAdapter);
+ setCurrentItem(0, false);
+ }
}
@Override
@@ -116,6 +146,7 @@
super.onFinishInflate();
mPages.add((TilePage) LayoutInflater.from(getContext())
.inflate(R.layout.qs_paged_page, this, false));
+ mAdapter.notifyDataSetChanged();
}
public void setPageIndicator(PageIndicator indicator) {
@@ -218,7 +249,10 @@
mPageIndicator.setNumPages(mPages.size());
setAdapter(mAdapter);
mAdapter.notifyDataSetChanged();
- setCurrentItem(0, false);
+ if (mPageToRestore != -1) {
+ setCurrentItem(mPageToRestore, false);
+ mPageToRestore = -1;
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 79e5086..42dfcee 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -103,6 +103,9 @@
setListening(savedInstanceState.getBoolean(EXTRA_LISTENING));
setEditLocation(view);
mQSCustomizer.restoreInstanceState(savedInstanceState);
+ if (mQsExpanded) {
+ mQSPanel.getTileLayout().restoreInstanceState(savedInstanceState);
+ }
}
SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).addCallbacks(this);
}
@@ -127,6 +130,9 @@
outState.putBoolean(EXTRA_EXPANDED, mQsExpanded);
outState.putBoolean(EXTRA_LISTENING, mListening);
mQSCustomizer.saveInstanceState(outState);
+ if (mQsExpanded) {
+ mQSPanel.getTileLayout().saveInstanceState(outState);
+ }
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index e98ef4c..cf63e47 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -25,6 +25,7 @@
import android.content.res.Configuration;
import android.content.res.Resources;
import android.metrics.LogMaker;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.service.quicksettings.Tile;
@@ -666,6 +667,11 @@
}
public interface QSTileLayout {
+
+ default void saveInstanceState(Bundle outState) {}
+
+ default void restoreInstanceState(Bundle savedInstanceState) {}
+
void addTile(TileRecord tile);
void removeTile(TileRecord tile);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
index e12b574..9d773ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
@@ -76,7 +76,7 @@
private float mCircleStartRadius;
private float mMaxCircleSize;
private Animator mPreviewClipper;
- private float mRestingAlpha = KeyguardAffordanceHelper.SWIPE_RESTING_ALPHA_AMOUNT;
+ private float mRestingAlpha = 1f;
private boolean mSupportHardware;
private boolean mFinishing;
private boolean mLaunchingAffordance;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index 8969aca..0577841 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -426,6 +426,10 @@
return mDarkAmount == 1;
}
+ public boolean isDarkAtAll() {
+ return mDarkAmount != 0;
+ }
+
public void setDarkTopPadding(int darkTopPadding) {
mDarkTopPadding = darkTopPadding;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 9978ec3..9ddab7c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -25,7 +25,6 @@
import android.animation.PropertyValuesHolder;
import android.animation.TimeAnimator;
import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.annotation.Nullable;
import android.app.WallpaperManager;
import android.content.Context;
@@ -40,7 +39,6 @@
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.os.Bundle;
-import android.os.Handler;
import android.os.ServiceManager;
import android.provider.Settings;
import android.service.notification.StatusBarNotification;
@@ -82,7 +80,6 @@
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.SwipeHelper;
-import com.android.systemui.SwipeHelper.Callback;
import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
@@ -3734,12 +3731,14 @@
return y < getHeight() - getEmptyBottomMargin();
}
+ @VisibleForTesting
@ShadeViewRefactor(RefactorComponent.INPUT)
- private void setIsBeingDragged(boolean isDragged) {
+ void setIsBeingDragged(boolean isDragged) {
mIsBeingDragged = isDragged;
if (isDragged) {
requestDisallowInterceptTouchEvent(true);
cancelLongPress();
+ resetExposedMenuView(true /* animate */, true /* force */);
}
}
@@ -3869,6 +3868,7 @@
public void onPanelTrackingStarted() {
mPanelTracking = true;
mAmbientState.setPanelTracking(true);
+ resetExposedMenuView(true /* animate */, true /* force */);
}
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
@@ -4271,8 +4271,10 @@
mLinearDarkAmount = linearDarkAmount;
mInterpolatedDarkAmount = interpolatedDarkAmount;
boolean wasFullyDark = mAmbientState.isFullyDark();
+ boolean wasDarkAtAll = mAmbientState.isDarkAtAll();
mAmbientState.setDarkAmount(interpolatedDarkAmount);
boolean nowFullyDark = mAmbientState.isFullyDark();
+ boolean nowDarkAtAll = mAmbientState.isDarkAtAll();
if (nowFullyDark != wasFullyDark) {
updateContentHeight();
DozeParameters dozeParameters = DozeParameters.getInstance(mContext);
@@ -4283,6 +4285,9 @@
mIconAreaController.setFullyDark(nowFullyDark);
}
}
+ if (!wasDarkAtAll && nowDarkAtAll) {
+ resetExposedMenuView(true /* animate */, true /* animate */);
+ }
updateAlgorithmHeightAndPadding();
updateBackgroundDimming();
updatePanelTranslation();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index cfc3271..976327a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -22,6 +22,8 @@
import android.content.res.Configuration;
import android.content.res.Resources;
import androidx.collection.ArraySet;
+
+import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.Region.Op;
import android.util.Log;
@@ -324,11 +326,10 @@
// Expand touchable region such that we also catch touches that just start below the notch
// area.
- Region bounds = ScreenDecorations.DisplayCutoutView.boundsFromDirection(
- cutout, Gravity.TOP);
- bounds.translate(0, mDisplayCutoutTouchableRegionSize);
+ Rect bounds = new Rect();
+ ScreenDecorations.DisplayCutoutView.boundsFromDirection(cutout, Gravity.TOP, bounds);
+ bounds.offset(0, mDisplayCutoutTouchableRegionSize);
region.op(bounds, Op.UNION);
- bounds.recycle();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
index e2f3319..49d421b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
@@ -36,7 +36,6 @@
*/
public class KeyguardAffordanceHelper {
- public static final float SWIPE_RESTING_ALPHA_AMOUNT = 0.5f;
public static final long HINT_PHASE1_DURATION = 200;
private static final long HINT_PHASE2_DURATION = 350;
private static final float BACKGROUND_RADIUS_SCALE_FACTOR = 0.25f;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index f667726..538a260 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -29,7 +29,6 @@
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
import android.app.admin.DevicePolicyManager;
-import android.hardware.biometrics.BiometricSourceType;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -41,6 +40,7 @@
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.graphics.drawable.Drawable;
+import android.hardware.biometrics.BiometricSourceType;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.IBinder;
@@ -54,7 +54,6 @@
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
-import android.util.MathUtils;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
@@ -68,16 +67,14 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.systemui.EventLogTags;
import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.assist.AssistManager;
+import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.IntentButtonProvider;
import com.android.systemui.plugins.IntentButtonProvider.IntentButton;
import com.android.systemui.plugins.IntentButtonProvider.IntentButton.IconState;
-import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.KeyguardAffordanceView;
import com.android.systemui.statusbar.KeyguardIndicationController;
@@ -705,8 +702,6 @@
startFinishDozeAnimationElement(mLeftAffordanceView, delay);
delay += DOZE_ANIMATION_STAGGER_DELAY;
}
- startFinishDozeAnimationElement(mLockIcon, delay);
- delay += DOZE_ANIMATION_STAGGER_DELAY;
if (mRightAffordanceView.getVisibility() == View.VISIBLE) {
startFinishDozeAnimationElement(mRightAffordanceView, delay);
}
@@ -823,10 +818,8 @@
updateLeftAffordanceIcon();
if (dozing) {
- mLockIcon.setVisibility(INVISIBLE);
mOverlayContainer.setVisibility(INVISIBLE);
} else {
- mLockIcon.setVisibility(VISIBLE);
mOverlayContainer.setVisibility(VISIBLE);
if (animate) {
startFinishDozeAnimation();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
index 8928530..2edc294 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -51,7 +51,6 @@
private boolean mScreenOn;
private boolean mLastScreenOn;
private Drawable mUserAvatarIcon;
- private TrustDrawable mTrustDrawable;
private final UnlockMethodCache mUnlockMethodCache;
private AccessibilityController mAccessibilityController;
private boolean mHasFingerPrintIcon;
@@ -62,28 +61,10 @@
public LockIcon(Context context, AttributeSet attrs) {
super(context, attrs);
- mTrustDrawable = new TrustDrawable(context);
- setBackground(mTrustDrawable);
mUnlockMethodCache = UnlockMethodCache.getInstance(context);
}
@Override
- protected void onVisibilityChanged(View changedView, int visibility) {
- super.onVisibilityChanged(changedView, visibility);
- if (isShown()) {
- mTrustDrawable.start();
- } else {
- mTrustDrawable.stop();
- }
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- mTrustDrawable.stop();
- }
-
- @Override
public void onUserInfoChanged(String name, Drawable picture, String userAccount) {
mUserAvatarIcon = picture;
update();
@@ -110,9 +91,6 @@
final int density = newConfig.densityDpi;
if (density != mDensity) {
mDensity = density;
- mTrustDrawable.stop();
- mTrustDrawable = new TrustDrawable(getContext());
- setBackground(mTrustDrawable);
update();
}
}
@@ -122,18 +100,9 @@
}
public void update(boolean force) {
- boolean visible = isShown()
- && KeyguardUpdateMonitor.getInstance(mContext).isDeviceInteractive();
- if (visible) {
- mTrustDrawable.start();
- } else {
- mTrustDrawable.stop();
- }
int state = getState();
boolean anyFingerprintIcon = state == STATE_FINGERPRINT || state == STATE_FINGERPRINT_ERROR;
mHasFaceUnlockIcon = state == STATE_FACE_UNLOCK;
- boolean useAdditionalPadding = anyFingerprintIcon;
- boolean trustHidden = anyFingerprintIcon;
if (state != mLastState || mDeviceInteractive != mLastDeviceInteractive
|| mScreenOn != mLastScreenOn || force) {
int iconAnimRes =
@@ -142,16 +111,10 @@
boolean isAnim = iconAnimRes != -1;
if (iconAnimRes == R.drawable.lockscreen_fingerprint_draw_off_animation) {
anyFingerprintIcon = true;
- useAdditionalPadding = true;
- trustHidden = true;
} else if (iconAnimRes == R.drawable.trusted_state_to_error_animation) {
anyFingerprintIcon = true;
- useAdditionalPadding = false;
- trustHidden = true;
} else if (iconAnimRes == R.drawable.error_to_trustedstate_animation) {
anyFingerprintIcon = true;
- useAdditionalPadding = false;
- trustHidden = false;
}
Drawable icon;
@@ -166,20 +129,6 @@
final AnimatedVectorDrawable animation = icon instanceof AnimatedVectorDrawable
? (AnimatedVectorDrawable) icon
: null;
- int iconHeight = getResources().getDimensionPixelSize(
- R.dimen.keyguard_affordance_icon_height);
- int iconWidth = getResources().getDimensionPixelSize(
- R.dimen.keyguard_affordance_icon_width);
- if (!anyFingerprintIcon && (icon.getIntrinsicHeight() != iconHeight
- || icon.getIntrinsicWidth() != iconWidth)) {
- icon = new IntrinsicSizeDrawable(icon, iconWidth, iconHeight);
- }
- setPaddingRelative(0, 0, 0, useAdditionalPadding
- ? getResources().getDimensionPixelSize(
- R.dimen.fingerprint_icon_additional_padding)
- : 0);
- setRestingAlpha(
- anyFingerprintIcon ? 1f : KeyguardAffordanceHelper.SWIPE_RESTING_ALPHA_AMOUNT);
setImageDrawable(icon, false);
if (mHasFaceUnlockIcon) {
announceForAccessibility(getContext().getString(
@@ -204,9 +153,6 @@
mLastScreenOn = mScreenOn;
}
- // Hide trust circle when fingerprint is running.
- boolean trustManaged = mUnlockMethodCache.isTrustManaged() && !trustHidden;
- mTrustDrawable.setTrustManaged(trustManaged);
updateClickability();
}
@@ -250,27 +196,21 @@
private Drawable getIconForState(int state, boolean screenOn, boolean deviceInteractive) {
int iconRes;
switch (state) {
+ case STATE_FINGERPRINT:
case STATE_LOCKED:
- iconRes = R.drawable.ic_lock_24dp;
+ iconRes = R.drawable.ic_lock;
break;
case STATE_LOCK_OPEN:
if (mUnlockMethodCache.isTrustManaged() && mUnlockMethodCache.isTrusted()
&& mUserAvatarIcon != null) {
return mUserAvatarIcon;
} else {
- iconRes = R.drawable.ic_lock_open_24dp;
+ iconRes = R.drawable.ic_lock_open;
}
break;
case STATE_FACE_UNLOCK:
iconRes = R.drawable.ic_face_unlock;
break;
- case STATE_FINGERPRINT:
- // If screen is off and device asleep, use the draw on animation so the first frame
- // gets drawn.
- iconRes = screenOn && deviceInteractive
- ? R.drawable.ic_fingerprint
- : R.drawable.lockscreen_fingerprint_draw_on_animation;
- break;
case STATE_FINGERPRINT_ERROR:
iconRes = R.drawable.ic_fingerprint_error;
break;
@@ -319,29 +259,4 @@
return STATE_LOCKED;
}
}
-
- /**
- * A wrapper around another Drawable that overrides the intrinsic size.
- */
- private static class IntrinsicSizeDrawable extends InsetDrawable {
-
- private final int mIntrinsicWidth;
- private final int mIntrinsicHeight;
-
- public IntrinsicSizeDrawable(Drawable drawable, int intrinsicWidth, int intrinsicHeight) {
- super(drawable, 0);
- mIntrinsicWidth = intrinsicWidth;
- mIntrinsicHeight = intrinsicHeight;
- }
-
- @Override
- public int getIntrinsicWidth() {
- return mIntrinsicWidth;
- }
-
- @Override
- public int getIntrinsicHeight() {
- return mIntrinsicHeight;
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 6ea4c92..6d53cd3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -124,8 +124,6 @@
private static final int CAP_HEIGHT = 1456;
private static final int FONT_HEIGHT = 2163;
- private static final float LOCK_ICON_ACTIVE_SCALE = 1.2f;
-
static final String COUNTER_PANEL_OPEN = "panel_open";
static final String COUNTER_PANEL_OPEN_QS = "panel_open_qs";
private static final String COUNTER_PANEL_OPEN_PEEK = "panel_open_peek";
@@ -1784,13 +1782,9 @@
KeyguardAffordanceView lockIcon = mKeyguardBottomArea.getLockIcon();
if (active && !mUnlockIconActive && mTracking) {
lockIcon.setImageAlpha(1.0f, true, 150, Interpolators.FAST_OUT_LINEAR_IN, null);
- lockIcon.setImageScale(LOCK_ICON_ACTIVE_SCALE, true, 150,
- Interpolators.FAST_OUT_LINEAR_IN);
} else if (!active && mUnlockIconActive && mTracking) {
lockIcon.setImageAlpha(lockIcon.getRestingAlpha(), true /* animate */,
150, Interpolators.FAST_OUT_LINEAR_IN, null);
- lockIcon.setImageScale(1.0f, true, 150,
- Interpolators.FAST_OUT_LINEAR_IN);
}
mUnlockIconActive = active;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index c3b87af..90ed97f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -2852,6 +2852,7 @@
}
}
else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
+ mStatusBarWindowController.setNotTouchable(false);
finishBarAnimations();
resetUserExpandedStates();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/TrustDrawable.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TrustDrawable.java
deleted file mode 100644
index aa60ec5..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/TrustDrawable.java
+++ /dev/null
@@ -1,290 +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
- */
-
-package com.android.systemui.statusbar.phone;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.ColorFilter;
-import android.graphics.Paint;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.view.animation.Interpolator;
-
-import com.android.settingslib.Utils;
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-
-public class TrustDrawable extends Drawable {
-
- private static final long ENTERING_FROM_UNSET_START_DELAY = 200;
- private static final long VISIBLE_DURATION = 1000;
- private static final long EXIT_DURATION = 500;
- private static final long ENTER_DURATION = 500;
-
- private static final int ALPHA_VISIBLE_MIN = 0x26;
- private static final int ALPHA_VISIBLE_MAX = 0x4c;
-
- private static final int STATE_UNSET = -1;
- private static final int STATE_GONE = 0;
- private static final int STATE_ENTERING = 1;
- private static final int STATE_VISIBLE = 2;
- private static final int STATE_EXITING = 3;
-
- private int mAlpha;
- private boolean mAnimating;
-
- private int mCurAlpha;
- private float mCurInnerRadius;
- private Animator mCurAnimator;
- private int mState = STATE_UNSET;
- private Paint mPaint;
- private boolean mTrustManaged;
-
- private final float mInnerRadiusVisibleMin;
- private final float mInnerRadiusVisibleMax;
- private final float mInnerRadiusExit;
- private final float mInnerRadiusEnter;
- private final float mThickness;
-
- private final Animator mVisibleAnimator;
-
- public TrustDrawable(Context context) {
- Resources r = context.getResources();
- mInnerRadiusVisibleMin = r.getDimension(R.dimen.trust_circle_inner_radius_visible_min);
- mInnerRadiusVisibleMax = r.getDimension(R.dimen.trust_circle_inner_radius_visible_max);
- mInnerRadiusExit = r.getDimension(R.dimen.trust_circle_inner_radius_exit);
- mInnerRadiusEnter = r.getDimension(R.dimen.trust_circle_inner_radius_enter);
- mThickness = r.getDimension(R.dimen.trust_circle_thickness);
-
- mCurInnerRadius = mInnerRadiusEnter;
-
- mVisibleAnimator = makeVisibleAnimator();
-
- mPaint = new Paint();
- mPaint.setStyle(Paint.Style.STROKE);
- mPaint.setColor(Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColor));
- mPaint.setAntiAlias(true);
- mPaint.setStrokeWidth(mThickness);
- }
-
- @Override
- public void draw(Canvas canvas) {
- int newAlpha = (mCurAlpha * mAlpha) / 256;
- if (newAlpha == 0) {
- return;
- }
- final Rect r = getBounds();
- mPaint.setAlpha(newAlpha);
- canvas.drawCircle(r.exactCenterX(), r.exactCenterY(), mCurInnerRadius, mPaint);
- }
-
- @Override
- public void setAlpha(int alpha) {
- mAlpha = alpha;
- }
-
- @Override
- public int getAlpha() {
- return mAlpha;
- }
-
- @Override
- public void setColorFilter(ColorFilter colorFilter) {
- throw new UnsupportedOperationException("not implemented");
- }
-
- @Override
- public int getOpacity() {
- return PixelFormat.TRANSLUCENT;
- }
-
- public void start() {
- if (!mAnimating) {
- mAnimating = true;
- updateState(true);
- invalidateSelf();
- }
- }
-
- public void stop() {
- if (mAnimating) {
- mAnimating = false;
- if (mCurAnimator != null) {
- mCurAnimator.cancel();
- mCurAnimator = null;
- }
- mState = STATE_UNSET;
- mCurAlpha = 0;
- mCurInnerRadius = mInnerRadiusEnter;
- invalidateSelf();
- }
- }
-
- public void setTrustManaged(boolean trustManaged) {
- if (trustManaged == mTrustManaged && mState != STATE_UNSET) return;
- mTrustManaged = trustManaged;
- updateState(true);
- }
-
- private void updateState(boolean allowTransientState) {
- if (!mAnimating) {
- return;
- }
-
- int nextState = mState;
- if (mState == STATE_UNSET) {
- nextState = mTrustManaged ? STATE_ENTERING : STATE_GONE;
- } else if (mState == STATE_GONE) {
- if (mTrustManaged) nextState = STATE_ENTERING;
- } else if (mState == STATE_ENTERING) {
- if (!mTrustManaged) nextState = STATE_EXITING;
- } else if (mState == STATE_VISIBLE) {
- if (!mTrustManaged) nextState = STATE_EXITING;
- } else if (mState == STATE_EXITING) {
- if (mTrustManaged) nextState = STATE_ENTERING;
- }
- if (!allowTransientState) {
- if (nextState == STATE_ENTERING) nextState = STATE_VISIBLE;
- if (nextState == STATE_EXITING) nextState = STATE_GONE;
- }
-
- if (nextState != mState) {
- if (mCurAnimator != null) {
- mCurAnimator.cancel();
- mCurAnimator = null;
- }
-
- if (nextState == STATE_GONE) {
- mCurAlpha = 0;
- mCurInnerRadius = mInnerRadiusEnter;
- } else if (nextState == STATE_ENTERING) {
- mCurAnimator = makeEnterAnimator(mCurInnerRadius, mCurAlpha);
- if (mState == STATE_UNSET) {
- mCurAnimator.setStartDelay(ENTERING_FROM_UNSET_START_DELAY);
- }
- } else if (nextState == STATE_VISIBLE) {
- mCurAlpha = ALPHA_VISIBLE_MAX;
- mCurInnerRadius = mInnerRadiusVisibleMax;
- mCurAnimator = mVisibleAnimator;
- } else if (nextState == STATE_EXITING) {
- mCurAnimator = makeExitAnimator(mCurInnerRadius, mCurAlpha);
- }
-
- mState = nextState;
- if (mCurAnimator != null) {
- mCurAnimator.start();
- }
- invalidateSelf();
- }
- }
-
- private Animator makeVisibleAnimator() {
- return makeAnimators(mInnerRadiusVisibleMax, mInnerRadiusVisibleMin,
- ALPHA_VISIBLE_MAX, ALPHA_VISIBLE_MIN, VISIBLE_DURATION,
- Interpolators.ACCELERATE_DECELERATE,
- true /* repeating */, false /* stateUpdateListener */);
- }
-
- private Animator makeEnterAnimator(float radius, int alpha) {
- return makeAnimators(radius, mInnerRadiusVisibleMax,
- alpha, ALPHA_VISIBLE_MAX, ENTER_DURATION, Interpolators.LINEAR_OUT_SLOW_IN,
- false /* repeating */, true /* stateUpdateListener */);
- }
-
- private Animator makeExitAnimator(float radius, int alpha) {
- return makeAnimators(radius, mInnerRadiusExit,
- alpha, 0, EXIT_DURATION, Interpolators.FAST_OUT_SLOW_IN,
- false /* repeating */, true /* stateUpdateListener */);
- }
-
- private Animator makeAnimators(float startRadius, float endRadius,
- int startAlpha, int endAlpha, long duration, Interpolator interpolator,
- boolean repeating, boolean stateUpdateListener) {
- ValueAnimator alphaAnimator = configureAnimator(
- ValueAnimator.ofInt(startAlpha, endAlpha),
- duration, mAlphaUpdateListener, interpolator, repeating);
- ValueAnimator sizeAnimator = configureAnimator(
- ValueAnimator.ofFloat(startRadius, endRadius),
- duration, mRadiusUpdateListener, interpolator, repeating);
-
- AnimatorSet set = new AnimatorSet();
- set.playTogether(alphaAnimator, sizeAnimator);
- if (stateUpdateListener) {
- set.addListener(new StateUpdateAnimatorListener());
- }
- return set;
- }
-
- private ValueAnimator configureAnimator(ValueAnimator animator, long duration,
- ValueAnimator.AnimatorUpdateListener updateListener, Interpolator interpolator,
- boolean repeating) {
- animator.setDuration(duration);
- animator.addUpdateListener(updateListener);
- animator.setInterpolator(interpolator);
- if (repeating) {
- animator.setRepeatCount(ValueAnimator.INFINITE);
- animator.setRepeatMode(ValueAnimator.REVERSE);
- }
- return animator;
- }
-
- private final ValueAnimator.AnimatorUpdateListener mAlphaUpdateListener =
- new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- mCurAlpha = (int) animation.getAnimatedValue();
- invalidateSelf();
- }
- };
-
- private final ValueAnimator.AnimatorUpdateListener mRadiusUpdateListener =
- new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- mCurInnerRadius = (float) animation.getAnimatedValue();
- invalidateSelf();
- }
- };
-
- private class StateUpdateAnimatorListener extends AnimatorListenerAdapter {
- boolean mCancelled;
-
- @Override
- public void onAnimationStart(Animator animation) {
- mCancelled = false;
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- mCancelled = true;
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- if (!mCancelled) {
- updateState(false);
- }
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index 12b6f9d..298a93e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.policy;
+import static android.view.Display.INVALID_DISPLAY;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK;
@@ -33,6 +34,7 @@
import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.TypedValue;
+import android.view.Display;
import android.view.HapticFeedbackConstants;
import android.view.InputDevice;
import android.view.KeyCharacterMap;
@@ -307,6 +309,14 @@
0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
flags | KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
InputDevice.SOURCE_KEYBOARD);
+ //Make KeyEvent work on multi-display environment
+ if (getDisplay() != null) {
+ final int displayId = getDisplay().getDisplayId();
+
+ if (displayId != INVALID_DISPLAY) {
+ ev.setDisplayId(displayId);
+ }
+ }
InputManager.getInstance().injectInputEvent(ev,
InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index cc96917..b84f85b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -16,6 +16,7 @@
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
+import static com.android.systemui.ScreenDecorations.rectsToRegion;
import static com.android.systemui.tuner.TunablePadding.FLAG_END;
import static com.android.systemui.tuner.TunablePadding.FLAG_START;
@@ -35,6 +36,7 @@
import android.app.Fragment;
import android.content.res.Configuration;
+import android.graphics.Rect;
import android.os.Handler;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -58,6 +60,8 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Collections;
+
@RunWithLooper
@RunWith(AndroidTestingRunner.class)
@SmallTest
@@ -240,4 +244,11 @@
mScreenDecorations.onConfigurationChanged(null);
assertEquals(mScreenDecorations.mRoundedDefault, 5);
}
+
+ @Test
+ public void testBoundingRectsToRegion() throws Exception {
+ Rect rect = new Rect(1, 2, 3, 4);
+ assertThat(rectsToRegion(Collections.singletonList(rect)).getBounds(), is(rect));
+ }
+
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationTest.java
index bb67d6e..45342d4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationTest.java
@@ -47,10 +47,10 @@
return;
}
- Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.DOZE_ALWAYS_ON,
- null);
+ Settings.Secure.putStringForUser(mContext.getContentResolver(),
+ Settings.Secure.DOZE_ALWAYS_ON, null, UserHandle.USER_CURRENT);
boolean defaultValue = mContext.getResources()
.getBoolean(com.android.internal.R.bool.config_dozeAlwaysOnEnabled);
- assertEquals(mDozeConfig.alwaysOnEnabled(UserHandle.USER_CURRENT), defaultValue);
+ assertEquals(defaultValue, mDozeConfig.alwaysOnEnabled(UserHandle.USER_CURRENT));
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index b545e61..f8b2436 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -15,6 +15,8 @@
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -36,6 +38,7 @@
import android.support.test.annotation.UiThreadTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import android.view.View;
import com.android.systemui.ExpandHelper;
import com.android.systemui.R;
@@ -314,6 +317,36 @@
verify(mStackScroller).setEmptyShadeView(any());
}
+ @Test
+ @UiThreadTest
+ public void testSetIsBeingDraggedResetsExposedMenu() {
+ NotificationSwipeHelper swipeActionHelper =
+ (NotificationSwipeHelper) mStackScroller.getSwipeActionHelper();
+ swipeActionHelper.setExposedMenuView(new View(mContext));
+ mStackScroller.setIsBeingDragged(true);
+ assertNull(swipeActionHelper.getExposedMenuView());
+ }
+
+ @Test
+ @UiThreadTest
+ public void testPanelTrackingStartResetsExposedMenu() {
+ NotificationSwipeHelper swipeActionHelper =
+ (NotificationSwipeHelper) mStackScroller.getSwipeActionHelper();
+ swipeActionHelper.setExposedMenuView(new View(mContext));
+ mStackScroller.onPanelTrackingStarted();
+ assertNull(swipeActionHelper.getExposedMenuView());
+ }
+
+ @Test
+ @UiThreadTest
+ public void testDarkModeResetsExposedMenu() {
+ NotificationSwipeHelper swipeActionHelper =
+ (NotificationSwipeHelper) mStackScroller.getSwipeActionHelper();
+ swipeActionHelper.setExposedMenuView(new View(mContext));
+ mStackScroller.setDarkAmount(0.1f, 0.1f);
+ assertNull(swipeActionHelper.getExposedMenuView());
+ }
+
private void setBarStateForTest(int state) {
ArgumentCaptor<StatusBarStateController.StateListener> captor =
ArgumentCaptor.forClass(StatusBarStateController.StateListener.class);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 9bee8db..44ef8b6d 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -379,25 +379,20 @@
@Override
public void onPackageUpdateFinished(String packageName, int uid) {
- // Unbind all services from this package, and then update the user state to
- // re-bind new versions of them.
+ // The package should already be removed from mBoundServices, and added into
+ // mBindingServices in binderDied() during updating. Remove services from this
+ // package from mBindingServices, and then update the user state to re-bind new
+ // versions of them.
synchronized (mLock) {
final int userId = getChangingUserId();
if (userId != mCurrentUserId) {
return;
}
UserState userState = getUserStateLocked(userId);
- boolean unboundAService = false;
- for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) {
- AccessibilityServiceConnection boundService =
- userState.mBoundServices.get(i);
- String servicePkg = boundService.mComponentName.getPackageName();
- if (servicePkg.equals(packageName)) {
- boundService.unbindLocked();
- unboundAService = true;
- }
- }
- if (unboundAService) {
+ boolean reboundAService = userState.mBindingServices.removeIf(
+ component -> component != null
+ && component.getPackageName().equals(packageName));
+ if (reboundAService) {
onUserStateChangedLocked(userState);
}
}
@@ -419,6 +414,7 @@
String compPkg = comp.getPackageName();
if (compPkg.equals(packageName)) {
it.remove();
+ userState.mBindingServices.remove(comp);
// Update the enabled services setting.
persistComponentNamesToSettingLocked(
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
@@ -457,6 +453,7 @@
return true;
}
it.remove();
+ userState.mBindingServices.remove(comp);
persistComponentNamesToSettingLocked(
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
userState.mEnabledServices, userId);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index e0eb269..9d84f57 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -253,11 +253,11 @@
return;
}
mWasConnectedAndDied = true;
- mSystemSupport.getKeyEventDispatcher().flush(this);
UserState userState = mUserStateWeakReference.get();
if (userState != null) {
userState.serviceDisconnectedLocked(this);
}
+ resetLocked();
mSystemSupport.getMagnificationController().resetIfNeeded(mId);
mSystemSupport.onClientChange(false);
}
diff --git a/services/art-profile b/services/art-profile
index 3c60eee..328f8f7 100644
--- a/services/art-profile
+++ b/services/art-profile
@@ -2254,8 +2254,8 @@
HPLcom/android/server/wm/DisplayContent;->lambda$new$8(Lcom/android/server/wm/DisplayContent;Lcom/android/server/wm/WindowState;)V
HPLcom/android/server/wm/DisplayContent;->prepareSurfaces()V
HPLcom/android/server/wm/DisplayContent;->resetAnimationBackgroundAnimator()V
-HPLcom/android/server/wm/DisplayContent;->setTouchExcludeRegion(Lcom/android/server/wm/Task;)V
HPLcom/android/server/wm/DisplayContent;->skipTraverseChild(Lcom/android/server/wm/WindowContainer;)Z
+HPLcom/android/server/wm/DisplayContent;->updateTouchExcludeRegion()V
HPLcom/android/server/wm/DockedStackDividerController;->isResizing()Z
HPLcom/android/server/wm/DragDropController;->dragDropActiveLocked()Z
HPLcom/android/server/wm/InputMonitor$UpdateInputForAllWindowsConsumer;->accept(Lcom/android/server/wm/WindowState;)V
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index cf323fb..c1b620c 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -400,7 +400,9 @@
@Nullable
private AutofillValue findValueLocked(@NonNull AutofillId autofillId) {
final AutofillValue value = findValueFromThisSessionOnlyLocked(autofillId);
- if (value != null) return value;
+ if (value != null) {
+ return getSanitizedValue(createSanitizers(getSaveInfoLocked()), autofillId, value);
+ }
// TODO(b/113281366): rather than explicitly look for previous session, it might be better
// to merge the sessions when created (see note on mergePreviousSessionLocked())
@@ -415,7 +417,8 @@
final AutofillValue previousValue = previousSession
.findValueFromThisSessionOnlyLocked(autofillId);
if (previousValue != null) {
- return previousValue;
+ return getSanitizedValue(createSanitizers(previousSession.getSaveInfoLocked()),
+ autofillId, previousValue);
}
}
}
diff --git a/services/backup/java/com/android/server/backup/encryption/chunk/ChunkOrderingType.java b/services/backup/java/com/android/server/backup/encryption/chunk/ChunkOrderingType.java
new file mode 100644
index 0000000..df36c94
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/encryption/chunk/ChunkOrderingType.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018 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.backup.encryption.chunk;
+
+import static com.android.server.backup.encryption.chunk.ChunksMetadataProto.CHUNK_ORDERING_TYPE_UNSPECIFIED;
+import static com.android.server.backup.encryption.chunk.ChunksMetadataProto.EXPLICIT_STARTS;
+import static com.android.server.backup.encryption.chunk.ChunksMetadataProto.INLINE_LENGTHS;
+
+import android.annotation.IntDef;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** IntDef corresponding to the ChunkOrderingType enum in the ChunksMetadataProto protobuf. */
+@IntDef({CHUNK_ORDERING_TYPE_UNSPECIFIED, EXPLICIT_STARTS, INLINE_LENGTHS})
+@Retention(RetentionPolicy.SOURCE)
+public @interface ChunkOrderingType {}
diff --git a/services/backup/java/com/android/server/backup/encryption/chunking/BackupWriter.java b/services/backup/java/com/android/server/backup/encryption/chunking/BackupWriter.java
new file mode 100644
index 0000000..68d9d14
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/encryption/chunking/BackupWriter.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018 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.backup.encryption.chunking;
+
+import java.io.IOException;
+
+/** Writes backup data either as a diff script or as raw data, determined by the implementation. */
+public interface BackupWriter {
+ /** Writes the given bytes to the output. */
+ void writeBytes(byte[] bytes) throws IOException;
+
+ /**
+ * Writes an existing chunk from the previous backup to the output.
+ *
+ * <p>Note: not all implementations support this method.
+ */
+ void writeChunk(long start, int length) throws IOException;
+
+ /** Returns the number of bytes written, included bytes copied from the old file. */
+ long getBytesWritten();
+
+ /**
+ * Indicates that no more bytes or chunks will be written.
+ *
+ * <p>After calling this, you may not call {@link #writeBytes(byte[])} or {@link
+ * #writeChunk(long, int)}
+ */
+ void flush() throws IOException;
+}
diff --git a/services/backup/java/com/android/server/backup/encryption/chunking/EncryptedChunk.java b/services/backup/java/com/android/server/backup/encryption/chunking/EncryptedChunk.java
new file mode 100644
index 0000000..1f936eb
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/encryption/chunking/EncryptedChunk.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2018 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.backup.encryption.chunking;
+
+import com.android.internal.util.Preconditions;
+import com.android.server.backup.encryption.chunk.ChunkHash;
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * A chunk of a file encrypted using AES/GCM.
+ *
+ * <p>TODO(b/116575321): After all code is ported, remove the factory method and rename
+ * encryptedBytes(), key() and nonce().
+ */
+public class EncryptedChunk {
+ public static final int KEY_LENGTH_BYTES = ChunkHash.HASH_LENGTH_BYTES;
+ public static final int NONCE_LENGTH_BYTES = 12;
+
+ /**
+ * Constructs a new instance with the given key, nonce, and encrypted bytes.
+ *
+ * @param key SHA-256 Hmac of the chunk plaintext.
+ * @param nonce Nonce with which the bytes of the chunk were encrypted.
+ * @param encryptedBytes Encrypted bytes of the chunk.
+ */
+ public static EncryptedChunk create(ChunkHash key, byte[] nonce, byte[] encryptedBytes) {
+ Preconditions.checkArgument(
+ nonce.length == NONCE_LENGTH_BYTES, "Nonce does not have the correct length.");
+ return new EncryptedChunk(key, nonce, encryptedBytes);
+ }
+
+ private ChunkHash mKey;
+ private byte[] mNonce;
+ private byte[] mEncryptedBytes;
+
+ private EncryptedChunk(ChunkHash key, byte[] nonce, byte[] encryptedBytes) {
+ mKey = key;
+ mNonce = nonce;
+ mEncryptedBytes = encryptedBytes;
+ }
+
+ /** The SHA-256 Hmac of the plaintext bytes of the chunk. */
+ public ChunkHash key() {
+ return mKey;
+ }
+
+ /** The nonce with which the chunk was encrypted. */
+ public byte[] nonce() {
+ return mNonce;
+ }
+
+ /** The encrypted bytes of the chunk. */
+ public byte[] encryptedBytes() {
+ return mEncryptedBytes;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof EncryptedChunk)) {
+ return false;
+ }
+
+ EncryptedChunk encryptedChunkOrdering = (EncryptedChunk) o;
+ return Arrays.equals(mEncryptedBytes, encryptedChunkOrdering.mEncryptedBytes)
+ && Arrays.equals(mNonce, encryptedChunkOrdering.mNonce)
+ && mKey.equals(encryptedChunkOrdering.mKey);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mKey, Arrays.hashCode(mNonce), Arrays.hashCode(mEncryptedBytes));
+ }
+}
diff --git a/services/backup/java/com/android/server/backup/encryption/chunking/EncryptedChunkEncoder.java b/services/backup/java/com/android/server/backup/encryption/chunking/EncryptedChunkEncoder.java
new file mode 100644
index 0000000..eaf701c
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/encryption/chunking/EncryptedChunkEncoder.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2018 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.backup.encryption.chunking;
+
+import com.android.server.backup.encryption.chunk.ChunkOrderingType;
+import java.io.IOException;
+
+/** Encodes an {@link EncryptedChunk} as bytes to write to the encrypted backup file. */
+public interface EncryptedChunkEncoder {
+ /**
+ * Encodes the given chunk and asks the writer to write it.
+ *
+ * <p>The chunk will be encoded in the format [nonce]+[encrypted data].
+ *
+ * <p>TODO(b/116575321): Choose a more descriptive method name after the code move is done.
+ */
+ void writeChunkToWriter(BackupWriter writer, EncryptedChunk chunk) throws IOException;
+
+ /**
+ * Returns the length in bytes that this chunk would be if encoded with {@link
+ * #writeChunkToWriter}.
+ */
+ int getEncodedLengthOfChunk(EncryptedChunk chunk);
+
+ /**
+ * Returns the {@link ChunkOrderingType} that must be included in the backup file, when using
+ * this decoder, so that the file may be correctly decoded.
+ */
+ @ChunkOrderingType
+ int getChunkOrderingType();
+}
diff --git a/services/backup/java/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoder.java b/services/backup/java/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoder.java
new file mode 100644
index 0000000..5c902ca
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoder.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2018 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.backup.encryption.chunking;
+
+import com.android.server.backup.encryption.chunk.ChunkOrderingType;
+import com.android.server.backup.encryption.chunk.ChunksMetadataProto;
+import java.io.IOException;
+
+/**
+ * Encodes an {@link EncryptedChunk} as bytes, prepending the length of the chunk.
+ *
+ * <p>This allows us to decode the backup file during restore without any extra information about
+ * the boundaries of the chunks. The backup file should contain a chunk ordering in mode {@link
+ * ChunksMetadataProto#INLINE_LENGTHS}.
+ *
+ * <p>We use this implementation during key value backup.
+ */
+public class InlineLengthsEncryptedChunkEncoder implements EncryptedChunkEncoder {
+ public static final int BYTES_LENGTH = Integer.SIZE / Byte.SIZE;
+
+ private final LengthlessEncryptedChunkEncoder mLengthlessEncryptedChunkEncoder =
+ new LengthlessEncryptedChunkEncoder();
+
+ @Override
+ public void writeChunkToWriter(BackupWriter writer, EncryptedChunk chunk) throws IOException {
+ int length = mLengthlessEncryptedChunkEncoder.getEncodedLengthOfChunk(chunk);
+ writer.writeBytes(toByteArray(length));
+ mLengthlessEncryptedChunkEncoder.writeChunkToWriter(writer, chunk);
+ }
+
+ @Override
+ public int getEncodedLengthOfChunk(EncryptedChunk chunk) {
+ return BYTES_LENGTH + mLengthlessEncryptedChunkEncoder.getEncodedLengthOfChunk(chunk);
+ }
+
+ @Override
+ @ChunkOrderingType
+ public int getChunkOrderingType() {
+ return ChunksMetadataProto.INLINE_LENGTHS;
+ }
+
+ /**
+ * Returns a big-endian representation of {@code value} in a 4-element byte array; equivalent to
+ * {@code ByteBuffer.allocate(4).putInt(value).array()}. For example, the input value {@code
+ * 0x12131415} would yield the byte array {@code {0x12, 0x13, 0x14, 0x15}}.
+ *
+ * <p>Equivalent to guava's Ints.toByteArray.
+ */
+ static byte[] toByteArray(int value) {
+ return new byte[] {
+ (byte) (value >> 24), (byte) (value >> 16), (byte) (value >> 8), (byte) value
+ };
+ }
+}
diff --git a/services/backup/java/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoder.java b/services/backup/java/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoder.java
new file mode 100644
index 0000000..4b84981
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoder.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018 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.backup.encryption.chunking;
+
+import com.android.server.backup.encryption.chunk.ChunkOrderingType;
+import com.android.server.backup.encryption.chunk.ChunksMetadataProto;
+import java.io.IOException;
+
+/**
+ * Encodes an {@link EncryptedChunk} as bytes without including any information about the length of
+ * the chunk.
+ *
+ * <p>In order for us to decode the backup file during restore it must include a chunk ordering in
+ * mode {@link ChunksMetadataProto#EXPLICIT_STARTS}, which contains the boundaries of the chunks in
+ * the encrypted file. This information allows us to decode the backup file and divide it into
+ * chunks without including the length of each chunk inline.
+ *
+ * <p>We use this implementation during full backup.
+ */
+public class LengthlessEncryptedChunkEncoder implements EncryptedChunkEncoder {
+ @Override
+ public void writeChunkToWriter(BackupWriter writer, EncryptedChunk chunk) throws IOException {
+ writer.writeBytes(chunk.nonce());
+ writer.writeBytes(chunk.encryptedBytes());
+ }
+
+ @Override
+ public int getEncodedLengthOfChunk(EncryptedChunk chunk) {
+ return chunk.nonce().length + chunk.encryptedBytes().length;
+ }
+
+ @Override
+ @ChunkOrderingType
+ public int getChunkOrderingType() {
+ return ChunksMetadataProto.EXPLICIT_STARTS;
+ }
+}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 953c99f..5e8ffb7 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1837,7 +1837,7 @@
}
void systemReady() {
- loadGlobalProxy();
+ mProxyTracker.loadGlobalProxy();
registerNetdEventCallback();
synchronized (this) {
@@ -3455,31 +3455,6 @@
mProxyTracker.setGlobalProxy(proxyProperties);
}
- private void loadGlobalProxy() {
- ContentResolver res = mContext.getContentResolver();
- String host = Settings.Global.getString(res, Settings.Global.GLOBAL_HTTP_PROXY_HOST);
- int port = Settings.Global.getInt(res, Settings.Global.GLOBAL_HTTP_PROXY_PORT, 0);
- String exclList = Settings.Global.getString(res,
- Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST);
- String pacFileUrl = Settings.Global.getString(res, Settings.Global.GLOBAL_HTTP_PROXY_PAC);
- if (!TextUtils.isEmpty(host) || !TextUtils.isEmpty(pacFileUrl)) {
- ProxyInfo proxyProperties;
- if (!TextUtils.isEmpty(pacFileUrl)) {
- proxyProperties = new ProxyInfo(pacFileUrl);
- } else {
- proxyProperties = new ProxyInfo(host, port, exclList);
- }
- if (!proxyProperties.isValid()) {
- if (DBG) log("Invalid proxy properties, ignoring: " + proxyProperties.toString());
- return;
- }
-
- synchronized (mProxyTracker.mProxyLock) {
- mProxyTracker.mGlobalProxy = proxyProperties;
- }
- }
- }
-
@Override
@Nullable
public ProxyInfo getGlobalProxy() {
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index 6cba764..376bc0d 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -107,6 +107,124 @@
* Keeps track of device idleness and drives low power mode based on that.
*
* Test: atest com.android.server.DeviceIdleControllerTest
+ *
+ * Current idling state machine (as of Android 9 Pie). This can be visualized using Graphviz:
+
+ digraph {
+ subgraph deep {
+ label="deep";
+
+ STATE_ACTIVE [label="STATE_ACTIVE\nScreen on OR Charging OR Alarm going off soon"]
+ STATE_INACTIVE [label="STATE_INACTIVE\nScreen off AND Not charging"]
+ STATE_IDLE_PENDING [
+ label="STATE_IDLE_PENDING\nSignificant motion monitoring turned on"
+ ]
+ STATE_SENSING [label="STATE_SENSING\nMonitoring for ANY motion"]
+ STATE_LOCATING [
+ label="STATE_LOCATING\nRequesting location, motion monitoring still on"
+ ]
+ STATE_IDLE [
+ label="STATE_IDLE\nLocation and motion detection turned off\n"
+ + "Significant motion monitoring still on"
+ ]
+ STATE_IDLE_MAINTENANCE [label="STATE_IDLE_MAINTENANCE\n"]
+
+ STATE_ACTIVE -> STATE_INACTIVE [label="becomeInactiveIfAppropriateLocked()"]
+
+ STATE_INACTIVE -> STATE_ACTIVE [
+ label="handleMotionDetectedLocked(), becomeActiveLocked()"
+ ]
+ STATE_INACTIVE -> STATE_IDLE_PENDING [label="stepIdleStateLocked()"]
+
+ STATE_IDLE_PENDING -> STATE_ACTIVE [
+ label="handleMotionDetectedLocked(), becomeActiveLocked()"
+ ]
+ STATE_IDLE_PENDING -> STATE_SENSING [label="stepIdleStateLocked()"]
+
+ STATE_SENSING -> STATE_ACTIVE [
+ label="handleMotionDetectedLocked(), becomeActiveLocked()"
+ ]
+ STATE_SENSING -> STATE_LOCATING [label="stepIdleStateLocked()"]
+ STATE_SENSING -> STATE_IDLE [
+ label="stepIdleStateLocked()\n"
+ + "No Location Manager OR (no Network provider AND no GPS provider)"
+ ]
+
+ STATE_LOCATING -> STATE_ACTIVE [
+ label="handleMotionDetectedLocked(), becomeActiveLocked()"
+ ]
+ STATE_LOCATING -> STATE_IDLE [label="stepIdleStateLocked()"]
+
+ STATE_IDLE -> STATE_ACTIVE [label="handleMotionDetectedLocked(), becomeActiveLocked()"]
+ STATE_IDLE -> STATE_IDLE_MAINTENANCE [label="stepIdleStateLocked()"]
+
+ STATE_IDLE_MAINTENANCE -> STATE_ACTIVE [
+ label="handleMotionDetectedLocked(), becomeActiveLocked()"
+ ]
+ STATE_IDLE_MAINTENANCE -> STATE_IDLE [
+ label="stepIdleStateLocked(), exitMaintenanceEarlyIfNeededLocked()"
+ ]
+ }
+
+ subgraph light {
+ label="light"
+
+ LIGHT_STATE_ACTIVE [
+ label="LIGHT_STATE_ACTIVE\nScreen on OR Charging OR Alarm going off soon"
+ ]
+ LIGHT_STATE_INACTIVE [label="LIGHT_STATE_INACTIVE\nScreen off AND Not charging"]
+ LIGHT_STATE_PRE_IDLE [
+ label="LIGHT_STATE_PRE_IDLE\n"
+ + "Delay going into LIGHT_STATE_IDLE due to some running jobs or alarms"
+ ]
+ LIGHT_STATE_IDLE [label="LIGHT_STATE_IDLE\n"]
+ LIGHT_STATE_WAITING_FOR_NETWORK [
+ label="LIGHT_STATE_WAITING_FOR_NETWORK\n"
+ + "Coming out of LIGHT_STATE_IDLE, waiting for network"
+ ]
+ LIGHT_STATE_IDLE_MAINTENANCE [label="LIGHT_STATE_IDLE_MAINTENANCE\n"]
+ LIGHT_STATE_OVERRIDE [
+ label="LIGHT_STATE_OVERRIDE\nDevice in deep doze, light no longer changing states"
+ ]
+
+ LIGHT_STATE_ACTIVE -> LIGHT_STATE_INACTIVE [
+ label="becomeInactiveIfAppropriateLocked()"
+ ]
+ LIGHT_STATE_ACTIVE -> LIGHT_STATE_OVERRIDE [label="deep goes to STATE_IDLE"]
+
+ LIGHT_STATE_INACTIVE -> LIGHT_STATE_ACTIVE [label="becomeActiveLocked()"]
+ LIGHT_STATE_INACTIVE -> LIGHT_STATE_PRE_IDLE [label="active jobs"]
+ LIGHT_STATE_INACTIVE -> LIGHT_STATE_IDLE [label="no active jobs"]
+ LIGHT_STATE_INACTIVE -> LIGHT_STATE_OVERRIDE [label="deep goes to STATE_IDLE"]
+
+ LIGHT_STATE_PRE_IDLE -> LIGHT_STATE_ACTIVE [label="becomeActiveLocked()"]
+ LIGHT_STATE_PRE_IDLE -> LIGHT_STATE_IDLE [
+ label="stepLightIdleStateLocked(), exitMaintenanceEarlyIfNeededLocked()"
+ ]
+ LIGHT_STATE_PRE_IDLE -> LIGHT_STATE_OVERRIDE [label="deep goes to STATE_IDLE"]
+
+ LIGHT_STATE_IDLE -> LIGHT_STATE_ACTIVE [label="becomeActiveLocked()"]
+ LIGHT_STATE_IDLE -> LIGHT_STATE_WAITING_FOR_NETWORK [label="no network"]
+ LIGHT_STATE_IDLE -> LIGHT_STATE_IDLE_MAINTENANCE
+ LIGHT_STATE_IDLE -> LIGHT_STATE_OVERRIDE [label="deep goes to STATE_IDLE"]
+
+ LIGHT_STATE_WAITING_FOR_NETWORK -> LIGHT_STATE_ACTIVE [label="becomeActiveLocked()"]
+ LIGHT_STATE_WAITING_FOR_NETWORK -> LIGHT_STATE_IDLE_MAINTENANCE
+ LIGHT_STATE_WAITING_FOR_NETWORK -> LIGHT_STATE_OVERRIDE [
+ label="deep goes to STATE_IDLE"
+ ]
+
+ LIGHT_STATE_IDLE_MAINTENANCE -> LIGHT_STATE_ACTIVE [label="becomeActiveLocked()"]
+ LIGHT_STATE_IDLE_MAINTENANCE -> LIGHT_STATE_IDLE [
+ label="stepLightIdleStateLocked(), exitMaintenanceEarlyIfNeededLocked()"
+ ]
+ LIGHT_STATE_IDLE_MAINTENANCE -> LIGHT_STATE_OVERRIDE [label="deep goes to STATE_IDLE"]
+
+ LIGHT_STATE_OVERRIDE -> LIGHT_STATE_ACTIVE [
+ label="handleMotionDetectedLocked(), becomeActiveLocked()"
+ ]
+ }
+ }
*/
public class DeviceIdleController extends SystemService
implements AnyMotionDetector.DeviceIdleCallback {
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index b7b5bd9..8077e34 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -356,6 +356,12 @@
public boolean interceptPowerKeyDown(KeyEvent event, boolean interactive,
MutableBoolean outLaunched) {
+ if (event.isLongPress()) {
+ // Long presses are sent as a second key down. If the long press threshold is set lower
+ // than the double tap of sequence interval thresholds, this could cause false double
+ // taps or consecutive taps, so we want to ignore the long press event.
+ return false;
+ }
boolean launched = false;
boolean intercept = false;
long powerTapInterval;
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index a69d416..8c25917 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -19,6 +19,8 @@
import static android.Manifest.permission.DUMP;
import static android.net.IpSecManager.INVALID_RESOURCE_ID;
import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
+import static android.system.OsConstants.AF_UNSPEC;
import static android.system.OsConstants.EINVAL;
import static android.system.OsConstants.IPPROTO_UDP;
import static android.system.OsConstants.SOCK_DGRAM;
@@ -63,6 +65,8 @@
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
@@ -1426,6 +1430,17 @@
+ "or Encryption algorithms");
}
+ private int getFamily(String inetAddress) {
+ int family = AF_UNSPEC;
+ InetAddress checkAddress = NetworkUtils.numericToInetAddress(inetAddress);
+ if (checkAddress instanceof Inet4Address) {
+ family = AF_INET;
+ } else if (checkAddress instanceof Inet6Address) {
+ family = AF_INET6;
+ }
+ return family;
+ }
+
/**
* Checks an IpSecConfig parcel to ensure that the contents are sane and throws an
* IllegalArgumentException if they are not.
@@ -1479,6 +1494,26 @@
// Require a valid source address for all transforms.
checkInetAddress(config.getSourceAddress());
+ // Check to ensure source and destination have the same address family.
+ String sourceAddress = config.getSourceAddress();
+ String destinationAddress = config.getDestinationAddress();
+ int sourceFamily = getFamily(sourceAddress);
+ int destinationFamily = getFamily(destinationAddress);
+ if (sourceFamily != destinationFamily) {
+ throw new IllegalArgumentException(
+ "Source address ("
+ + sourceAddress
+ + ") and destination address ("
+ + destinationAddress
+ + ") have different address families.");
+ }
+
+ // Throw an error if UDP Encapsulation is not used in IPv4.
+ if (config.getEncapType() != IpSecTransform.ENCAP_NONE && sourceFamily != AF_INET) {
+ throw new IllegalArgumentException(
+ "UDP Encapsulation is not supported for this address family");
+ }
+
switch (config.getMode()) {
case IpSecTransform.MODE_TRANSPORT:
break;
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 461d39d..8e64b50 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1466,9 +1466,9 @@
+ ") when binding service " + service);
}
- ActivityRecord activity = null;
+ ActivityServiceConnectionsHolder<ConnectionRecord> activity = null;
if (token != null) {
- activity = ActivityRecord.isInStackLocked(token);
+ activity = mAm.mAtmInternal.getServiceConnectionsHolder(token);
if (activity == null) {
Slog.w(TAG, "Binding with unknown activity: " + token);
return 0;
@@ -1644,10 +1644,7 @@
clist.add(c);
b.connections.add(c);
if (activity != null) {
- if (activity.connections == null) {
- activity.connections = new HashSet<ConnectionRecord>();
- }
- activity.connections.add(c);
+ activity.addConnection(c);
}
b.client.connections.add(c);
c.startAssociationIfNeeded();
@@ -2861,8 +2858,8 @@
smap.ensureNotStartingBackgroundLocked(r);
}
- void removeConnectionLocked(
- ConnectionRecord c, ProcessRecord skipApp, ActivityRecord skipAct) {
+ void removeConnectionLocked(ConnectionRecord c, ProcessRecord skipApp,
+ ActivityServiceConnectionsHolder skipAct) {
IBinder binder = c.conn.asBinder();
AppBindRecord b = c.binding;
ServiceRecord s = b.service;
@@ -2876,9 +2873,7 @@
b.connections.remove(c);
c.stopAssociation();
if (c.activity != null && c.activity != skipAct) {
- if (c.activity.connections != null) {
- c.activity.connections.remove(c);
- }
+ c.activity.removeConnection(c);
}
if (b.client != skipApp) {
b.client.connections.remove(c);
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index fab967c..fcb717e 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -51,6 +51,7 @@
import android.app.ActivityOptions;
import android.app.WindowConfiguration;
import android.graphics.Point;
+import android.os.UserHandle;
import android.util.IntArray;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
@@ -112,6 +113,13 @@
*/
private boolean mRemoved;
+ /**
+ * A focusable stack that is purposely to be positioned at the top. Although the stack may not
+ * have the topmost index, it is used as a preferred candidate to prevent being unable to resume
+ * target stack properly when there are other focusable always-on-top stacks.
+ */
+ private ActivityStack mPreferredTopFocusableStack;
+
// Cached reference to some special stacks we tend to get a lot so we don't need to loop
// through the list to find them.
private ActivityStack mHomeStack = null;
@@ -164,6 +172,9 @@
if (DEBUG_STACK) Slog.v(TAG_STACK, "removeChild: detaching " + stack
+ " from displayId=" + mDisplayId);
mStacks.remove(stack);
+ if (mPreferredTopFocusableStack == stack) {
+ mPreferredTopFocusableStack = null;
+ }
removeStackReferenceIfNeeded(stack);
releaseSelfIfNeeded();
mSupervisor.mService.updateSleepIfNeededLocked();
@@ -185,9 +196,21 @@
private void positionChildAt(ActivityStack stack, int position, boolean includingParents) {
// TODO: Keep in sync with WindowContainer.positionChildAt(), once we change that to adjust
// the position internally, also update the logic here
- mStacks.remove(stack);
+ final boolean wasContained = mStacks.remove(stack);
final int insertPosition = getTopInsertPosition(stack, position);
mStacks.add(insertPosition, stack);
+
+ // The insert position may be adjusted to non-top when there is always-on-top stack. Since
+ // the original position is preferred to be top, the stack should have higher priority when
+ // we are looking for top focusable stack. The condition {@code wasContained} restricts the
+ // preferred stack is set only when moving an existing stack to top instead of adding a new
+ // stack that may be too early (e.g. in the middle of launching or reparenting).
+ if (wasContained && position >= mStacks.size() - 1 && stack.isFocusableAndVisible()) {
+ mPreferredTopFocusableStack = stack;
+ } else if (mPreferredTopFocusableStack == stack) {
+ mPreferredTopFocusableStack = null;
+ }
+
// Since positionChildAt() is called during the creation process of pinned stacks,
// ActivityStack#getWindowContainerController() can be null. In this special case,
// since DisplayContest#positionStackAt() is called in TaskStack#onConfigurationChanged(),
@@ -356,10 +379,18 @@
this, stackId, mSupervisor, windowingMode, activityType, onTop);
}
+ /**
+ * Get the preferred focusable stack in priority. If the preferred stack does not exist, find a
+ * focusable and visible stack from the top of stacks in this display.
+ */
ActivityStack getFocusedStack() {
+ if (mPreferredTopFocusableStack != null) {
+ return mPreferredTopFocusableStack;
+ }
+
for (int i = mStacks.size() - 1; i >= 0; --i) {
final ActivityStack stack = mStacks.get(i);
- if (stack.isFocusable() && stack.shouldBeVisible(null /* starting */)) {
+ if (stack.isFocusableAndVisible()) {
return stack;
}
}
@@ -381,7 +412,7 @@
if (ignoreCurrent && stack == currentFocus) {
continue;
}
- if (!stack.isFocusable() || !stack.shouldBeVisible(null)) {
+ if (!stack.isFocusableAndVisible()) {
continue;
}
@@ -911,6 +942,13 @@
return mDisplayAccessUIDs;
}
+ /**
+ * @see Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
+ */
+ boolean supportsSystemDecorations() {
+ return mDisplay.supportsSystemDecorations();
+ }
+
private boolean shouldDestroyContentOnRemove() {
return mDisplay.getRemoveMode() == REMOVE_MODE_DESTROY_CONTENT;
}
@@ -920,6 +958,10 @@
&& (mSupervisor.mService.mRunningVoice == null);
}
+ void setFocusedApp(ActivityRecord r, boolean moveFocusNow) {
+ mWindowContainerController.setFocusedApp(r.appToken, moveFocusNow);
+ }
+
/**
* @return the stack currently above the {@param stack}. Can be null if the {@param stack} is
* already top-most.
@@ -981,6 +1023,57 @@
positionChildAt(stack, Math.max(0, insertIndex));
}
+ void moveHomeStackToFront(String reason) {
+ if (mHomeStack != null) {
+ mHomeStack.moveToFront(reason);
+ }
+ }
+
+ /** Returns true if the focus activity was adjusted to the home stack top activity. */
+ boolean moveHomeActivityToTop(String reason) {
+ final ActivityRecord top = getHomeActivity();
+ if (top == null) {
+ return false;
+ }
+ mSupervisor.moveFocusableActivityToTop(top, reason);
+ return true;
+ }
+
+ @Nullable
+ ActivityStack getHomeStack() {
+ return mHomeStack;
+ }
+
+ @Nullable
+ ActivityRecord getHomeActivity() {
+ return getHomeActivityForUser(mSupervisor.mCurrentUser);
+ }
+
+ @Nullable
+ ActivityRecord getHomeActivityForUser(int userId) {
+ if (mHomeStack == null) {
+ return null;
+ }
+
+ final ArrayList<TaskRecord> tasks = mHomeStack.getAllTasks();
+ for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
+ final TaskRecord task = tasks.get(taskNdx);
+ if (!task.isActivityTypeHome()) {
+ continue;
+ }
+
+ final ArrayList<ActivityRecord> activities = task.mActivities;
+ for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = activities.get(activityNdx);
+ if (r.isActivityTypeHome()
+ && ((userId == UserHandle.USER_ALL) || (r.userId == userId))) {
+ return r;
+ }
+ }
+ }
+ return null;
+ }
+
boolean isSleeping() {
return mSleeping;
}
@@ -1042,6 +1135,9 @@
if (mSplitScreenPrimaryStack != null) {
pw.println(myPrefix + "mSplitScreenPrimaryStack=" + mSplitScreenPrimaryStack);
}
+ if (mPreferredTopFocusableStack != null) {
+ pw.println(myPrefix + "mPreferredTopFocusableStack=" + mPreferredTopFocusableStack);
+ }
}
public void dumpStacks(PrintWriter pw) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index aa14da0..9c96968 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -90,6 +90,7 @@
import static android.provider.Settings.Global.NETWORK_ACCESS_TIMEOUT_MS;
import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
+import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ANR;
@@ -3725,6 +3726,14 @@
}
boolean startHomeActivityLocked(int userId, String reason) {
+ return startHomeActivityLocked(userId, reason, DEFAULT_DISPLAY);
+ }
+
+ /**
+ * This starts home activity on displays that can have system decorations and only if the
+ * home activity can have multiple instances.
+ */
+ boolean startHomeActivityLocked(int userId, String reason, int displayId) {
if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL
&& mTopAction == null) {
// We are running in factory test mode, but unable to find
@@ -3748,7 +3757,8 @@
// For ANR debugging to verify if the user activity is the one that actually
// launched.
final String myReason = reason + ":" + userId + ":" + resolvedUserId;
- mActivityTaskManager.getActivityStartController().startHomeActivity(intent, aInfo, myReason);
+ mActivityTaskManager.getActivityStartController().startHomeActivity(intent, aInfo,
+ myReason, displayId);
}
} else {
Slog.wtf(TAG, "No home screen found for " + intent, new Throwable());
@@ -8817,7 +8827,7 @@
? new ActivityOptions(options)
: ActivityOptions.makeBasic();
activityOptions.setLaunchTaskId(
- mStackSupervisor.getHomeActivity().getTask().taskId);
+ mStackSupervisor.getDefaultDisplayHomeActivity().getTask().taskId);
mContext.startActivityAsUser(intent, activityOptions.toBundle(),
UserHandle.CURRENT);
} finally {
@@ -10596,9 +10606,13 @@
currApp.importanceReasonImportance =
ActivityManager.RunningAppProcessInfo.procStateToImportance(
app.adjSourceProcState);
- } else if (app.adjSource instanceof ActivityRecord) {
- ActivityRecord r = (ActivityRecord)app.adjSource;
- if (r.app != null) currApp.importanceReasonPid = r.app.getPid();
+ } else if (app.adjSource instanceof ActivityServiceConnectionsHolder) {
+ ActivityServiceConnectionsHolder r =
+ (ActivityServiceConnectionsHolder) app.adjSource;
+ final int pid = r.getActivityPid();
+ if (pid != -1) {
+ currApp.importanceReasonPid = pid;
+ }
}
if (app.adjTarget instanceof ComponentName) {
currApp.importanceReasonComponent = (ComponentName)app.adjTarget;
@@ -17773,10 +17787,10 @@
if ((cr.flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
app.treatLikeActivity = true;
}
- final ActivityRecord a = cr.activity;
+ final ActivityServiceConnectionsHolder a = cr.activity;
if ((cr.flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) {
- if (a != null && adj > ProcessList.FOREGROUND_APP_ADJ && (a.visible
- || a.isState(ActivityState.RESUMED, ActivityState.PAUSING))) {
+ if (a != null && adj > ProcessList.FOREGROUND_APP_ADJ
+ && a.isActivityVisible()) {
adj = ProcessList.FOREGROUND_APP_ADJ;
if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
if ((cr.flags&Context.BIND_IMPORTANT) != 0) {
@@ -20891,6 +20905,16 @@
return res;
}
}
+
+ @Override
+ public void disconnectActivityFromServices(Object connectionHolder) {
+ synchronized(ActivityManagerService.this) {
+ final ActivityServiceConnectionsHolder c =
+ (ActivityServiceConnectionsHolder) connectionHolder;
+ c.forEachConnection(cr -> mServices.removeConnectionLocked(
+ (ConnectionRecord) cr, null, c));
+ }
+ }
}
/**
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 5853ad9..fe10baf 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -283,7 +283,7 @@
ActivityOptions pendingOptions; // most recently given options
ActivityOptions returningOptions; // options that are coming back via convertToTranslucent
AppTimeTracker appTimeTracker; // set if we are tracking the time in this app/task/activity
- HashSet<ConnectionRecord> connections; // All ConnectionRecord we hold
+ ActivityServiceConnectionsHolder mServiceConnectionsHolder; // Service connections.
UriPermissionOwner uriPermissions; // current special URI access perms.
WindowProcessController app; // if non-null, hosting application
private ActivityState mState; // current state we are in
@@ -549,8 +549,8 @@
pw.print(" configChangeFlags=");
pw.println(Integer.toHexString(configChangeFlags));
}
- if (connections != null) {
- pw.print(prefix); pw.print("connections="); pw.println(connections);
+ if (mServiceConnectionsHolder != null) {
+ pw.print(prefix); pw.print("connections="); pw.println(mServiceConnectionsHolder);
}
if (info != null) {
pw.println(prefix + "resizeMode=" + ActivityInfo.resizeModeToString(info.resizeMode));
diff --git a/services/core/java/com/android/server/am/ActivityServiceConnectionsHolder.java b/services/core/java/com/android/server/am/ActivityServiceConnectionsHolder.java
new file mode 100644
index 0000000..b1ced29
--- /dev/null
+++ b/services/core/java/com/android/server/am/ActivityServiceConnectionsHolder.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.am;
+
+import static com.android.server.am.ActivityStack.ActivityState.PAUSING;
+import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
+
+import java.io.PrintWriter;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.function.Consumer;
+
+/**
+ * Class for tracking the connections to services on the AM side that activities on the
+ * WM side (in the future) bind with for things like oom score adjustment. Would normally be one
+ * instance of this per activity for tracking all services connected to that activity. AM will
+ * sometimes query this to bump the OOM score for the processes with services connected to visible
+ * activities.
+ */
+public class ActivityServiceConnectionsHolder<T> {
+
+ private final ActivityTaskManagerService mService;
+
+ /** The activity the owns this service connection object. */
+ private final ActivityRecord mActivity;
+
+ /**
+ * The service connection object bounded with the owning activity. They represent
+ * ConnectionRecord on the AM side, however we don't need to know their object representation
+ * on the WM side since we don't perform operations on the object. Mainly here for communication
+ * and booking with the AM side.
+ */
+ private HashSet<T> mConnections;
+
+ ActivityServiceConnectionsHolder(ActivityTaskManagerService service, ActivityRecord activity) {
+ mService = service;
+ mActivity = activity;
+ }
+
+ /** Adds a connection record that the activity has bound to a specific service. */
+ public void addConnection(T c) {
+ synchronized (mService.mGlobalLock) {
+ if (mConnections == null) {
+ mConnections = new HashSet<>();
+ }
+ mConnections.add(c);
+ }
+ }
+
+ /** Removed a connection record between the activity and a specific service. */
+ public void removeConnection(T c) {
+ synchronized (mService.mGlobalLock) {
+ if (mConnections == null) {
+ return;
+ }
+ mConnections.remove(c);
+ }
+ }
+
+ public boolean isActivityVisible() {
+ synchronized (mService.mGlobalLock) {
+ return mActivity.visible || mActivity.isState(RESUMED, PAUSING);
+ }
+ }
+
+ public int getActivityPid() {
+ synchronized (mService.mGlobalLock) {
+ return mActivity.hasProcess() ? mActivity.app.getPid() : -1;
+ }
+ }
+
+ public void forEachConnection(Consumer<T> consumer) {
+ synchronized (mService.mGlobalLock) {
+ if (mConnections == null || mConnections.isEmpty()) {
+ return;
+ }
+ final Iterator<T> it = mConnections.iterator();
+ while (it.hasNext()) {
+ T c = it.next();
+ consumer.accept(c);
+ }
+ }
+ }
+
+ /** Removes the connection between the activity and all services that were connected to it. */
+ void disconnectActivityFromServices() {
+ if (mConnections == null || mConnections.isEmpty()) {
+ return;
+ }
+ mService.mH.post(() -> mService.mAmInternal.disconnectActivityFromServices(this));
+ }
+
+ public void dump(PrintWriter pw, String prefix) {
+ synchronized (mService.mGlobalLock) {
+ pw.println(prefix + "activity=" + mActivity);
+ }
+ }
+
+}
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 29b04cc..ea807ad 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1102,8 +1102,7 @@
if (!isActivityTypeHome() && returnsToHomeStack()) {
// Make sure the home stack is behind this stack since that is where we should return to
// when this stack is no longer visible.
- // TODO(b/111541062): Move home stack on the current display
- mStackSupervisor.moveHomeStackToFront(reason + " returnToHome");
+ display.moveHomeStackToFront(reason + " returnToHome");
}
display.positionChildAtTop(this, true /* includingParents */);
@@ -1147,6 +1146,10 @@
return mStackSupervisor.isFocusable(this, r != null && r.isFocusable());
}
+ boolean isFocusableAndVisible() {
+ return isFocusable() && shouldBeVisible(null /* starting */);
+ }
+
final boolean isAttached() {
return getParent() != null;
}
@@ -2854,9 +2857,7 @@
if (DEBUG_STATES) Slog.d(TAG_STATES,
"resumeTopActivityInNextFocusableStack: " + reason + ", go home");
if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
- // Only resume home if on home display
- return isOnHomeDisplay() &&
- mStackSupervisor.resumeHomeStackTask(prev, reason);
+ return mStackSupervisor.resumeHomeActivity(prev, reason, mDisplayId);
}
/** Returns the position the input task should be placed in this stack. */
@@ -3450,8 +3451,8 @@
final String myReason = reason + " adjustFocus";
if (next == r) {
- mStackSupervisor.moveFocusableActivityStackToFrontLocked(
- mStackSupervisor.topRunningActivityLocked(), myReason);
+ mStackSupervisor.moveFocusableActivityToTop(mStackSupervisor.topRunningActivityLocked(),
+ myReason);
return;
}
@@ -3482,7 +3483,7 @@
}
// Whatever...go home.
- mStackSupervisor.moveHomeStackTaskToTop(myReason);
+ getDisplay().moveHomeActivityToTop(myReason);
}
/**
@@ -3511,7 +3512,7 @@
if (stack.isActivityTypeHome() && (top == null || !top.visible)) {
// If we will be focusing on the home stack next and its current top activity isn't
// visible, then use the move the home stack task to top to make the activity visible.
- mStackSupervisor.moveHomeStackTaskToTop(reason);
+ stack.getDisplay().moveHomeActivityToTop(reason);
return stack;
}
@@ -4233,15 +4234,11 @@
* Perform clean-up of service connections in an activity record.
*/
private void cleanUpActivityServicesLocked(ActivityRecord r) {
- // Throw away any services that have been bound by this activity.
- if (r.connections != null) {
- Iterator<ConnectionRecord> it = r.connections.iterator();
- while (it.hasNext()) {
- ConnectionRecord c = it.next();
- mService.mAm.mServices.removeConnectionLocked(c, null, r);
- }
- r.connections = null;
+ if (r.mServiceConnectionsHolder == null) {
+ return;
}
+ // Throw away any services that have been bound by this activity.
+ r.mServiceConnectionsHolder.disconnectActivityFromServices();
}
final void scheduleDestroyActivities(WindowProcessController owner, String reason) {
@@ -4619,22 +4616,6 @@
mStackSupervisor.invalidateTaskLayers();
}
- void moveHomeStackTaskToTop() {
- if (!isActivityTypeHome()) {
- throw new IllegalStateException("Calling moveHomeStackTaskToTop() on non-home stack: "
- + this);
- }
- final int top = mTaskHistory.size() - 1;
- if (top >= 0) {
- final TaskRecord task = mTaskHistory.get(top);
- if (DEBUG_TASKS || DEBUG_STACK) Slog.d(TAG_STACK,
- "moveHomeStackTaskToTop: moving " + task);
- mTaskHistory.remove(top);
- mTaskHistory.add(top, task);
- updateTaskMovement(task, true);
- }
- }
-
final void moveTaskToFrontLocked(TaskRecord tr, boolean noAnimation, ActivityOptions options,
AppTimeTracker timeTracker, String reason) {
if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "moveTaskToFront: " + tr);
@@ -4682,7 +4663,7 @@
// Set focus to the top running activity of this stack.
final ActivityRecord r = topRunningActivityLocked();
- mStackSupervisor.moveFocusableActivityStackToFrontLocked(r, reason);
+ mStackSupervisor.moveFocusableActivityToTop(r, reason);
if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to front transition: task=" + tr);
if (noAnimation) {
@@ -5223,11 +5204,11 @@
if (DEBUG_STACK) Slog.i(TAG_STACK, "removeTask: removing stack=" + this);
// We only need to adjust focused stack if this stack is in focus and we are not in the
// process of moving the task to the top of the stack that will be focused.
- if (isOnHomeDisplay() && mode != REMOVE_TASK_MODE_MOVING_TO_TOP
+ if (mode != REMOVE_TASK_MODE_MOVING_TO_TOP
&& mStackSupervisor.isTopDisplayFocusedStack(this)) {
String myReason = reason + " leftTaskHistoryEmpty";
if (!inMultiWindowMode() || adjustFocusToNextFocusableStack(myReason) == null) {
- mStackSupervisor.moveHomeStackToFront(myReason);
+ getDisplay().moveHomeStackToFront(myReason);
}
}
if (isAttached()) {
@@ -5434,7 +5415,7 @@
// Do not sleep activities in this stack if we're marked as focused and the keyguard
// is in the process of going away.
- if (mStackSupervisor.getTopDisplayFocusedStack() == this
+ if (isFocusedStackOnDisplay()
&& mStackSupervisor.getKeyguardController().isKeyguardGoingAway()) {
return false;
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 8ff55f6..a968ae4 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -40,6 +40,8 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.app.WindowConfiguration.activityTypeToString;
import static android.app.WindowConfiguration.windowingModeToString;
+import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
+import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.graphics.Rect.copyOrNull;
@@ -335,10 +337,6 @@
/** The current user */
int mCurrentUser;
- /** The stack containing the launcher app. Assumed to always be attached to
- * Display.DEFAULT_DISPLAY. */
- ActivityStack mHomeStack;
-
/** If this is the same as mFocusedStack then the activity on the top of the focused stack has
* been resumed. If stacks are changing position this will hold the old stack until the new
* stack becomes resumed after which it will be set to mFocusedStack. */
@@ -693,7 +691,8 @@
calculateDefaultMinimalSizeOfResizeableTasks();
final ActivityDisplay defaultDisplay = getDefaultDisplay();
- mHomeStack = mLastFocusedStack = defaultDisplay.getOrCreateStack(
+
+ mLastFocusedStack = defaultDisplay.getOrCreateStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
positionChildAt(defaultDisplay, ActivityDisplay.POSITION_TOP);
}
@@ -734,10 +733,6 @@
}
ActivityRecord getTopResumedActivity() {
- if (mWindowManager == null) {
- return null;
- }
-
final ActivityStack focusedStack = getTopDisplayFocusedStack();
if (focusedStack == null) {
return null;
@@ -782,7 +777,7 @@
if (focusCandidate == null) {
Slog.w(TAG,
"setFocusStackUnchecked: No focusable stack found, focus home as default");
- focusCandidate = mHomeStack;
+ focusCandidate = getDefaultDisplay().getHomeStack();
}
}
@@ -803,10 +798,6 @@
}
}
- void moveHomeStackToFront(String reason) {
- mHomeStack.moveToFront(reason);
- }
-
void moveRecentsStackToFront(String reason) {
final ActivityStack recentsStack = getDefaultDisplay().getStack(
WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_RECENTS);
@@ -815,34 +806,47 @@
}
}
- /** Returns true if the focus activity was adjusted to the home stack top activity. */
- boolean moveHomeStackTaskToTop(String reason) {
- mHomeStack.moveHomeStackTaskToTop();
-
- final ActivityRecord top = getHomeActivity();
- if (top == null) {
- return false;
- }
- moveFocusableActivityStackToFrontLocked(top, reason);
- return true;
- }
-
- boolean resumeHomeStackTask(ActivityRecord prev, String reason) {
+ boolean resumeHomeActivity(ActivityRecord prev, String reason, int displayId) {
if (!mService.isBooting() && !mService.isBooted()) {
// Not ready yet!
return false;
}
- mHomeStack.moveHomeStackTaskToTop();
- ActivityRecord r = getHomeActivity();
- final String myReason = reason + " resumeHomeStackTask";
+ if (displayId == INVALID_DISPLAY) {
+ displayId = DEFAULT_DISPLAY;
+ }
+
+ final ActivityRecord r = getActivityDisplay(displayId).getHomeActivity();
+ final String myReason = reason + " resumeHomeActivity";
// Only resume home activity if isn't finishing.
if (r != null && !r.finishing) {
- moveFocusableActivityStackToFrontLocked(r, myReason);
- return resumeFocusedStacksTopActivitiesLocked(mHomeStack, prev, null);
+ moveFocusableActivityToTop(r, myReason);
+ return resumeFocusedStacksTopActivitiesLocked(r.getStack(), prev, null);
}
- return mService.mAm.startHomeActivityLocked(mCurrentUser, myReason);
+ return mService.mAm.startHomeActivityLocked(mCurrentUser, myReason, displayId);
+ }
+
+ boolean canStartHomeOnDisplay(ActivityInfo homeActivity, int displayId) {
+ if (displayId == DEFAULT_DISPLAY) {
+ // No restrictions to default display.
+ return true;
+ }
+
+ final ActivityDisplay display = getActivityDisplay(displayId);
+ if (display == null || display.isRemoved() || !display.supportsSystemDecorations()) {
+ // Can't launch home on display that doesn't support system decorations.
+ return false;
+ }
+
+ final boolean supportMultipleInstance = homeActivity.launchMode != LAUNCH_SINGLE_TASK
+ && homeActivity.launchMode != LAUNCH_SINGLE_INSTANCE;
+ if (!supportMultipleInstance) {
+ // Can't launch home on other displays if it requested to be single instance.
+ return false;
+ }
+
+ return true;
}
TaskRecord anyTaskForIdLocked(int id) {
@@ -2206,7 +2210,8 @@
*/
void updateUserStackLocked(int userId, ActivityStack stack) {
if (userId != mCurrentUser) {
- mUserStackInFront.put(userId, stack != null ? stack.getStackId() : mHomeStack.mStackId);
+ mUserStackInFront.put(userId, stack != null ? stack.getStackId()
+ : getDefaultDisplay().getHomeStack().mStackId);
}
}
@@ -2275,7 +2280,8 @@
return false;
}
- if (targetStack != null && targetStack.isTopStackOnDisplay()) {
+ if (targetStack != null && (targetStack.isTopStackOnDisplay()
+ || getTopDisplayFocusedStack() == targetStack)) {
return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
}
@@ -2348,7 +2354,7 @@
*/
void findTaskToMoveToFront(TaskRecord task, int flags, ActivityOptions options, String reason,
boolean forceNonResizeable) {
- final ActivityStack currentStack = task.getStack();
+ ActivityStack currentStack = task.getStack();
if (currentStack == null) {
Slog.e(TAG, "findTaskToMoveToFront: can't move task="
+ task + " to front. Stack is null");
@@ -2359,13 +2365,15 @@
mUserLeaving = true;
}
+ // TODO(b/111363427): The moving-to-top task may not be on the top display, so it could be
+ // different from where the prev activity stays on.
final ActivityRecord prev = topRunningActivityLocked();
if ((flags & ActivityManager.MOVE_TASK_WITH_HOME) != 0
|| (prev != null && prev.isActivityTypeRecents())) {
// Caller wants the home activity moved with it or the previous task is recents in which
// case we always return home from the task we are moving to the front.
- moveHomeStackToFront("findTaskToMoveToFront");
+ currentStack.getDisplay().moveHomeStackToFront("findTaskToMoveToFront");
}
if (task.isResizeable() && canUseActivityOptionsLaunchBounds(options)) {
@@ -2377,7 +2385,7 @@
if (stack != currentStack) {
task.reparent(stack, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, !ANIMATE, DEFER_RESUME,
"findTaskToMoveToFront");
- stack = currentStack;
+ currentStack = stack;
// moveTaskToStackUncheckedLocked() should already placed the task on top,
// still need moveTaskToFrontLocked() below for any transition settings.
}
@@ -2644,6 +2652,12 @@
if (preferredFocusableStack != null) {
return preferredFocusableStack;
}
+ if (preferredDisplay.supportsSystemDecorations()) {
+ // Stop looking for focusable stack on other displays because the preferred display
+ // supports system decorations. Home activity would be launched on the same display if
+ // no focusable stack found.
+ return null;
+ }
// Now look through all displays
for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
@@ -2687,25 +2701,12 @@
return null;
}
- ActivityRecord getHomeActivity() {
- return getHomeActivityForUser(mCurrentUser);
+ ActivityRecord getDefaultDisplayHomeActivity() {
+ return getDefaultDisplayHomeActivityForUser(mCurrentUser);
}
- ActivityRecord getHomeActivityForUser(int userId) {
- final ArrayList<TaskRecord> tasks = mHomeStack.getAllTasks();
- for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
- final TaskRecord task = tasks.get(taskNdx);
- if (task.isActivityTypeHome()) {
- final ArrayList<ActivityRecord> activities = task.mActivities;
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = activities.get(activityNdx);
- if (r.isActivityTypeHome()
- && ((userId == UserHandle.USER_ALL) || (r.userId == userId))) {
- return r;
- }
- }
- }
- }
+ ActivityRecord getDefaultDisplayHomeActivityForUser(int userId) {
+ getActivityDisplay(DEFAULT_DISPLAY).getHomeActivityForUser(userId);
return null;
}
@@ -3419,7 +3420,8 @@
}
/** Move activity with its stack to front and make the stack focused. */
- boolean moveFocusableActivityStackToFrontLocked(ActivityRecord r, String reason) {
+ // TODO(b/111363427): Move this method to ActivityRecord.
+ boolean moveFocusableActivityToTop(ActivityRecord r, String reason) {
if (r == null || !r.isFocusable()) {
if (DEBUG_FOCUS) Slog.d(TAG_FOCUS,
"moveActivityStackToFront: unfocusable r=" + r);
@@ -3588,7 +3590,7 @@
stack.goToSleepIfPossible(false /* shuttingDown */);
} else {
stack.awakeFromSleepingLocked();
- if (isTopDisplayFocusedStack(stack) && !getKeyguardController()
+ if (stack.isFocusedStackOnDisplay() && !getKeyguardController()
.isKeyguardOrAodShowing(display.mDisplayId)) {
// If the keyguard is unlocked - resume immediately.
// It is possible that the display will not be awake at the time we
@@ -3828,7 +3830,8 @@
removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
mUserStackInFront.put(mCurrentUser, focusStackId);
- final int restoreStackId = mUserStackInFront.get(userId, mHomeStack.mStackId);
+ final int restoreStackId =
+ mUserStackInFront.get(userId, getDefaultDisplay().getHomeStack().mStackId);
mCurrentUser = userId;
mStartingUsers.add(uss);
@@ -3846,14 +3849,14 @@
ActivityStack stack = getStack(restoreStackId);
if (stack == null) {
- stack = mHomeStack;
+ stack = getDefaultDisplay().getHomeStack();
}
final boolean homeInFront = stack.isActivityTypeHome();
if (stack.isOnHomeDisplay()) {
stack.moveToFront("switchUserOnHomeDisplay");
} else {
// Stack was moved to another display while user was swapped out.
- resumeHomeStackTask(null, "switchUserOnOtherDisplay");
+ resumeHomeActivity(null, "switchUserOnOtherDisplay", DEFAULT_DISPLAY);
}
return homeInFront;
}
@@ -3976,8 +3979,12 @@
}
public void dump(PrintWriter pw, String prefix) {
- pw.print(prefix); pw.print("mFocusedStack=" + getTopDisplayFocusedStack());
- pw.print(" mLastFocusedStack="); pw.println(mLastFocusedStack);
+ pw.println();
+ pw.println("ActivityStackSupervisor state:");
+ pw.print(prefix);
+ pw.println("topDisplayFocusedStack=" + getTopDisplayFocusedStack());
+ pw.print(prefix);
+ pw.println("mLastFocusedStack=" + mLastFocusedStack);
pw.print(prefix);
pw.println("mCurTaskIdForUser=" + mCurTaskIdForUser);
pw.print(prefix); pw.println("mUserStackInFront=" + mUserStackInFront);
@@ -4273,6 +4280,7 @@
private void handleDisplayAdded(int displayId) {
synchronized (mService.mGlobalLock) {
getActivityDisplayOrCreateLocked(displayId);
+ mService.mAm.startHomeActivityLocked(mCurrentUser, "displayAdded", displayId);
}
}
@@ -4838,7 +4846,8 @@
// We always want to return to the home activity instead of the recents activity
// from whatever is started from the recents activity, so move the home stack
// forward.
- moveHomeStackToFront("startActivityFromRecents");
+ // TODO (b/115289124): Multi-display supports for recents.
+ getDefaultDisplay().moveHomeStackToFront("startActivityFromRecents");
}
// If the user must confirm credentials (e.g. when first launching a work app and the
@@ -4881,12 +4890,13 @@
final ActivityStack topSecondaryStack =
display.getTopStackInWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
if (topSecondaryStack.isActivityTypeHome()) {
- // If the home activity if the top split-screen secondary stack, then the
+ // If the home activity is the top split-screen secondary stack, then the
// primary split-screen stack is in the minimized mode which means it can't
// receive input keys, so we should move the focused app to the home app so that
// window manager can correctly calculate the focus window that can receive
// input keys.
- moveHomeStackToFront("startActivityFromRecents: homeVisibleInSplitScreen");
+ display.moveHomeStackToFront(
+ "startActivityFromRecents: homeVisibleInSplitScreen");
// Immediately update the minimized docked stack mode, the upcoming animation
// for the docked activity (WMS.overridePendingAppTransitionMultiThumbFuture)
diff --git a/services/core/java/com/android/server/am/ActivityStartController.java b/services/core/java/com/android/server/am/ActivityStartController.java
index 6e3a79c..5e73bc3 100644
--- a/services/core/java/com/android/server/am/ActivityStartController.java
+++ b/services/core/java/com/android/server/am/ActivityStartController.java
@@ -17,12 +17,14 @@
package com.android.server.am;
import static android.app.ActivityManager.START_SUCCESS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
+import android.app.ActivityOptions;
import android.app.IApplicationThread;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -75,7 +77,7 @@
/** Temporary array to capture start activity results */
private ActivityRecord[] tmpOutRecord = new ActivityRecord[1];
- /**The result of the last home activity we attempted to start. */
+ /** The result of the last home activity we attempted to start. */
private int mLastHomeActivityStartResult;
/** A list of activities that are waiting to launch. */
@@ -161,13 +163,20 @@
mLastStarter.postStartActivityProcessing(r, result, targetStack);
}
- void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason) {
- mSupervisor.moveHomeStackTaskToTop(reason);
+ void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason, int displayId) {
+ if (!mSupervisor.canStartHomeOnDisplay(aInfo, displayId)) {
+ return;
+ }
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ options.setLaunchActivityType(ACTIVITY_TYPE_HOME);
+ options.setLaunchDisplayId(displayId);
mLastHomeActivityStartResult = obtainStarter(intent, "startHomeActivity: " + reason)
.setOutActivity(tmpOutRecord)
.setCallingUid(0)
.setActivityInfo(aInfo)
+ .setActivityOptions(options.toBundle())
.execute();
mLastHomeActivityStartRecord = tmpOutRecord[0];
if (mSupervisor.inResumeTopActivity) {
diff --git a/services/core/java/com/android/server/am/ActivityStartInterceptor.java b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
index 1fb8f87..4789ff3 100644
--- a/services/core/java/com/android/server/am/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
@@ -277,7 +277,7 @@
mActivityOptions = ActivityOptions.makeBasic();
}
- ActivityRecord homeActivityRecord = mSupervisor.getHomeActivity();
+ ActivityRecord homeActivityRecord = mSupervisor.getDefaultDisplayHomeActivity();
if (homeActivityRecord != null && homeActivityRecord.getTask() != null) {
// Showing credential confirmation activity in home task to avoid stopping multi-windowed
// mode after showing the full-screen credential confirmation activity.
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 8236bd0..de3b9cf 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -975,7 +975,8 @@
clearedTask);
break;
case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
- final ActivityStack homeStack = mSupervisor.mHomeStack;
+ final ActivityStack homeStack =
+ startedActivityStack.getDisplay().getHomeStack();
if (homeStack != null && homeStack.shouldBeVisible(null /* starting */)) {
mService.mWindowManager.showRecentApps();
}
@@ -1280,6 +1281,15 @@
setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
voiceInteractor);
+ // Do not start home activity if it cannot be launched on preferred display. We are not
+ // doing this in ActivityStackSupervisor#canPlaceEntityOnDisplay because it might
+ // fallback to launch on other displays.
+ if (r.isActivityTypeHome()
+ && !mSupervisor.canStartHomeOnDisplay(r.info, mPreferredDisplayId)) {
+ Slog.w(TAG, "Cannot launch home on display " + mPreferredDisplayId);
+ return START_CANCELED;
+ }
+
computeLaunchingTaskFlags();
computeSourceStack();
@@ -1430,7 +1440,11 @@
&& top.userId == mStartActivity.userId
&& top.attachedToProcess()
&& ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
- || isLaunchModeOneOf(LAUNCH_SINGLE_TOP, LAUNCH_SINGLE_TASK));
+ || isLaunchModeOneOf(LAUNCH_SINGLE_TOP, LAUNCH_SINGLE_TASK))
+ // This allows home activity to automatically launch on secondary display when
+ // display added, if home was the top activity on default display, instead of
+ // sending new intent to the home activity on default display.
+ && (!top.isActivityTypeHome() || top.getDisplayId() == mPreferredDisplayId);
if (dontStart) {
// For paranoia, make sure we have correctly resumed the top activity.
topStack.mLastPausedActivity = null;
@@ -1858,6 +1872,13 @@
intentActivity = mSupervisor.findTaskLocked(mStartActivity, mPreferredDisplayId);
}
}
+
+ if (mStartActivity.isActivityTypeHome() && intentActivity != null
+ && intentActivity.getDisplayId() != mPreferredDisplayId) {
+ // Do not reuse home activity on other displays.
+ intentActivity = null;
+ }
+
return intentActivity;
}
diff --git a/services/core/java/com/android/server/am/ActivityTaskManagerService.java b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
index 36261b5..c1eab82 100644
--- a/services/core/java/com/android/server/am/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
@@ -1692,8 +1692,7 @@
return;
}
final ActivityRecord r = stack.topRunningActivityLocked();
- if (mStackSupervisor.moveFocusableActivityStackToFrontLocked(
- r, "setFocusedStack")) {
+ if (mStackSupervisor.moveFocusableActivityToTop(r, "setFocusedStack")) {
mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
}
}
@@ -1714,7 +1713,7 @@
return;
}
final ActivityRecord r = task.topRunningActivityLocked();
- if (mStackSupervisor.moveFocusableActivityStackToFrontLocked(r, "setFocusedTask")) {
+ if (mStackSupervisor.moveFocusableActivityToTop(r, "setFocusedTask")) {
mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
}
}
@@ -4846,8 +4845,7 @@
updateResumedAppTrace(r);
mLastResumedActivity = r;
- // TODO(b/111361570): Support multiple focused apps in WM
- mWindowManager.setFocusedApp(r.appToken, true);
+ r.getDisplay().setFocusedApp(r, true);
applyUpdateLockStateLocked(r);
applyUpdateVrModeLocked(r);
@@ -5263,7 +5261,8 @@
@Override
public ComponentName getHomeActivityForUser(int userId) {
synchronized (mGlobalLock) {
- ActivityRecord homeActivity = mStackSupervisor.getHomeActivityForUser(userId);
+ ActivityRecord homeActivity =
+ mStackSupervisor.getDefaultDisplayHomeActivityForUser(userId);
return homeActivity == null ? null : homeActivity.realActivity;
}
}
@@ -5437,8 +5436,7 @@
throw new IllegalArgumentException(
"setFocusedActivity: No activity record matching token=" + token);
}
- if (mStackSupervisor.moveFocusableActivityStackToFrontLocked(
- r, "setFocusedActivity")) {
+ if (mStackSupervisor.moveFocusableActivityToTop(r, "setFocusedActivity")) {
mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
}
}
@@ -5781,5 +5779,21 @@
resultWho, requestCode, intents, resolvedTypes, flags, bOptions);
}
}
+
+ @Override
+ public ActivityServiceConnectionsHolder getServiceConnectionsHolder(IBinder token) {
+ synchronized (mGlobalLock) {
+ final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ if (r == null) {
+ return null;
+ }
+ if (r.mServiceConnectionsHolder == null) {
+ r.mServiceConnectionsHolder = new ActivityServiceConnectionsHolder(
+ ActivityTaskManagerService.this, r);
+ }
+
+ return r.mServiceConnectionsHolder;
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/am/ConnectionRecord.java b/services/core/java/com/android/server/am/ConnectionRecord.java
index fa8e6c4..1242ed6 100644
--- a/services/core/java/com/android/server/am/ConnectionRecord.java
+++ b/services/core/java/com/android/server/am/ConnectionRecord.java
@@ -35,7 +35,7 @@
*/
final class ConnectionRecord {
final AppBindRecord binding; // The application/service binding.
- final ActivityRecord activity; // If non-null, the owning activity.
+ final ActivityServiceConnectionsHolder<ConnectionRecord> activity; // If non-null, the owning activity.
final IServiceConnection conn; // The client connection.
final int flags; // Binding options.
final int clientLabel; // String resource labeling this client.
@@ -85,13 +85,14 @@
void dump(PrintWriter pw, String prefix) {
pw.println(prefix + "binding=" + binding);
if (activity != null) {
- pw.println(prefix + "activity=" + activity);
+ activity.dump(pw, prefix);
}
pw.println(prefix + "conn=" + conn.asBinder()
+ " flags=0x" + Integer.toHexString(flags));
}
- ConnectionRecord(AppBindRecord _binding, ActivityRecord _activity,
+ ConnectionRecord(AppBindRecord _binding,
+ ActivityServiceConnectionsHolder<ConnectionRecord> _activity,
IServiceConnection _conn, int _flags,
int _clientLabel, PendingIntent _clientIntent,
int _clientUid, String _clientProcessName) {
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index bd0eca8..4d0b1da 100644
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ b/services/core/java/com/android/server/am/RecentTasks.java
@@ -31,6 +31,8 @@
import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.os.Process.SYSTEM_UID;
+import static android.view.Display.DEFAULT_DISPLAY;
+
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS_TRIM_TASKS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
@@ -1242,7 +1244,6 @@
*/
protected boolean isTrimmable(TaskRecord task) {
final ActivityStack stack = task.getStack();
- final ActivityStack homeStack = mSupervisor.mHomeStack;
// No stack for task, just trim it
if (stack == null) {
@@ -1250,13 +1251,14 @@
}
// Ignore tasks from different displays
- if (stack.getDisplay() != homeStack.getDisplay()) {
+ // TODO (b/115289124): No Recents on non-default displays.
+ if (stack.mDisplayId != DEFAULT_DISPLAY) {
return false;
}
// Trim tasks that are in stacks that are behind the home stack
final ActivityDisplay display = stack.getDisplay();
- return display.getIndexOf(stack) < display.getIndexOf(homeStack);
+ return display.getIndexOf(stack) < display.getIndexOf(display.getHomeStack());
}
/**
diff --git a/services/core/java/com/android/server/appbinding/AppBindingConstants.java b/services/core/java/com/android/server/appbinding/AppBindingConstants.java
index b0088a8..7184769 100644
--- a/services/core/java/com/android/server/appbinding/AppBindingConstants.java
+++ b/services/core/java/com/android/server/appbinding/AppBindingConstants.java
@@ -40,6 +40,9 @@
private static final String SERVICE_STABLE_CONNECTION_THRESHOLD_SEC_KEY =
"service_stable_connection_threshold_sec";
+ private static final String SMS_SERVICE_ENABLED_KEY =
+ "sms_service_enabled";
+
private static final String SMS_APP_BIND_FLAGS_KEY =
"sms_app_bind_flags";
@@ -67,6 +70,11 @@
public final long SERVICE_STABLE_CONNECTION_THRESHOLD_SEC;
/**
+ * Whether to actually bind to the default SMS app service. (Feature flag)
+ */
+ public final boolean SMS_SERVICE_ENABLED;
+
+ /**
* Extra binding flags for SMS service.
*/
public final int SMS_APP_BIND_FLAGS;
@@ -92,6 +100,8 @@
long serviceReconnectMaxBackoffSec = parser.getLong(
SERVICE_RECONNECT_MAX_BACKOFF_SEC_KEY, TimeUnit.HOURS.toSeconds(1));
+ boolean smsServiceEnabled = parser.getBoolean(SMS_SERVICE_ENABLED_KEY, true);
+
int smsAppBindFlags = parser.getInt(
SMS_APP_BIND_FLAGS_KEY,
Context.BIND_NOT_VISIBLE | Context.BIND_FOREGROUND_SERVICE);
@@ -114,6 +124,7 @@
SERVICE_RECONNECT_BACKOFF_INCREASE = serviceReconnectBackoffIncrease;
SERVICE_RECONNECT_MAX_BACKOFF_SEC = serviceReconnectMaxBackoffSec;
SERVICE_STABLE_CONNECTION_THRESHOLD_SEC = serviceStableConnectionThresholdSec;
+ SMS_SERVICE_ENABLED = smsServiceEnabled;
SMS_APP_BIND_FLAGS = smsAppBindFlags;
}
@@ -129,7 +140,8 @@
*/
public void dump(String prefix, PrintWriter pw) {
pw.print(prefix);
- pw.println("Constants:");
+ pw.print("Constants: ");
+ pw.println(sourceSettings);
pw.print(prefix);
pw.print(" SERVICE_RECONNECT_BACKOFF_SEC: ");
@@ -148,6 +160,10 @@
pw.println(SERVICE_STABLE_CONNECTION_THRESHOLD_SEC);
pw.print(prefix);
+ pw.print(" SMS_SERVICE_ENABLED: ");
+ pw.println(SMS_SERVICE_ENABLED);
+
+ pw.print(prefix);
pw.print(" SMS_APP_BIND_FLAGS: 0x");
pw.println(Integer.toHexString(SMS_APP_BIND_FLAGS));
}
diff --git a/services/core/java/com/android/server/appbinding/AppBindingService.java b/services/core/java/com/android/server/appbinding/AppBindingService.java
index 8c38809..3131255 100644
--- a/services/core/java/com/android/server/appbinding/AppBindingService.java
+++ b/services/core/java/com/android/server/appbinding/AppBindingService.java
@@ -422,7 +422,7 @@
unbindServicesLocked(userId, target, reasonForLog);
}
- final ServiceInfo service = app.findService(userId, mIPackageManager);
+ final ServiceInfo service = app.findService(userId, mIPackageManager, mConstants);
if (service == null) {
continue;
}
diff --git a/services/core/java/com/android/server/appbinding/finders/AppServiceFinder.java b/services/core/java/com/android/server/appbinding/finders/AppServiceFinder.java
index 3d37317..a075c50 100644
--- a/services/core/java/com/android/server/appbinding/finders/AppServiceFinder.java
+++ b/services/core/java/com/android/server/appbinding/finders/AppServiceFinder.java
@@ -69,6 +69,11 @@
mHandler = callbackHandler;
}
+ /** Whether this service should really be enabled. */
+ protected boolean isEnabled(AppBindingConstants constants) {
+ return true;
+ }
+
/** Human readable description of the type of apps; e.g. [Default SMS app] */
@NonNull
public abstract String getAppDescription();
@@ -90,12 +95,20 @@
* Find the target service from the target app on a given user.
*/
@Nullable
- public final ServiceInfo findService(int userId, IPackageManager ipm) {
+ public final ServiceInfo findService(int userId, IPackageManager ipm,
+ AppBindingConstants constants) {
synchronized (mLock) {
mTargetPackages.put(userId, null);
mTargetServices.put(userId, null);
mLastMessages.put(userId, null);
+ if (!isEnabled(constants)) {
+ final String message = "feature disabled";
+ mLastMessages.put(userId, message);
+ Slog.i(TAG, getAppDescription() + " " + message);
+ return null;
+ }
+
final String targetPackage = getTargetPackage(userId);
if (DEBUG) {
Slog.d(TAG, getAppDescription() + " package=" + targetPackage);
diff --git a/services/core/java/com/android/server/appbinding/finders/SmsAppServiceFinder.java b/services/core/java/com/android/server/appbinding/finders/SmsAppServiceFinder.java
index 3340900..fcc28f8 100644
--- a/services/core/java/com/android/server/appbinding/finders/SmsAppServiceFinder.java
+++ b/services/core/java/com/android/server/appbinding/finders/SmsAppServiceFinder.java
@@ -32,7 +32,9 @@
import android.os.UserHandle;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
+import android.util.Slog;
+import com.android.internal.R;
import com.android.internal.telephony.SmsApplication;
import com.android.server.appbinding.AppBindingConstants;
@@ -49,6 +51,12 @@
}
@Override
+ protected boolean isEnabled(AppBindingConstants constants) {
+ return constants.SMS_SERVICE_ENABLED
+ && mContext.getResources().getBoolean(R.bool.config_useSmsAppService);
+ }
+
+ @Override
public String getAppDescription() {
return "[Default SMS app]";
}
@@ -77,7 +85,13 @@
public String getTargetPackage(int userId) {
final ComponentName cn = SmsApplication.getDefaultSmsApplicationAsUser(
mContext, /* updateIfNeeded= */ true, userId);
- return cn == null ? null : cn.getPackageName();
+ String ret = cn == null ? null : cn.getPackageName();
+
+ if (DEBUG) {
+ Slog.d(TAG, "getTargetPackage()=" + ret);
+ }
+
+ return ret;
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 5e860a9..3d2f567 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -63,7 +63,7 @@
*/
public class BiometricService extends SystemService {
- private static final String TAG = "BiometricPromptService";
+ private static final String TAG = "BiometricService";
/**
* No biometric methods or nothing has been enrolled.
diff --git a/services/core/java/com/android/server/connectivity/ProxyTracker.java b/services/core/java/com/android/server/connectivity/ProxyTracker.java
index dc65e1e..b7bbd42 100644
--- a/services/core/java/com/android/server/connectivity/ProxyTracker.java
+++ b/services/core/java/com/android/server/connectivity/ProxyTracker.java
@@ -142,6 +142,35 @@
}
/**
+ * Read the global proxy settings and cache them in memory.
+ */
+ public void loadGlobalProxy() {
+ ContentResolver res = mContext.getContentResolver();
+ String host = Settings.Global.getString(res, Settings.Global.GLOBAL_HTTP_PROXY_HOST);
+ int port = Settings.Global.getInt(res, Settings.Global.GLOBAL_HTTP_PROXY_PORT, 0);
+ String exclList = Settings.Global.getString(res,
+ Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST);
+ String pacFileUrl = Settings.Global.getString(res, Settings.Global.GLOBAL_HTTP_PROXY_PAC);
+ if (!TextUtils.isEmpty(host) || !TextUtils.isEmpty(pacFileUrl)) {
+ ProxyInfo proxyProperties;
+ if (!TextUtils.isEmpty(pacFileUrl)) {
+ proxyProperties = new ProxyInfo(pacFileUrl);
+ } else {
+ proxyProperties = new ProxyInfo(host, port, exclList);
+ }
+ if (!proxyProperties.isValid()) {
+ if (DBG) Slog.d(TAG, "Invalid proxy properties, ignoring: " + proxyProperties);
+ return;
+ }
+
+ synchronized (mProxyLock) {
+ mGlobalProxy = proxyProperties;
+ }
+ }
+ // TODO : shouldn't this function call mPacManager.setCurrentProxyScriptUrl ?
+ }
+
+ /**
* Sends the system broadcast informing apps about a new proxy configuration.
*
* Confusingly this method also sets the PAC file URL. TODO : separate this, it has nothing
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index 512e851..c51dc52 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -110,6 +110,13 @@
public static final int FLAG_MASK_DISPLAY_CUTOUT = 1 << 11;
/**
+ * Flag: This flag identifies secondary displays that should show system decorations, such as
+ * status bar, navigation bar, home activity or IME.
+ * @hide
+ */
+ public static final int FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS = 1 << 12;
+
+ /**
* Touch attachment: Display does not receive touch.
*/
public static final int TOUCH_NONE = 0;
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 5b7c520..6f726e6 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -18,7 +18,6 @@
import android.graphics.Rect;
import android.hardware.display.DisplayManagerInternal;
-import android.os.SystemProperties;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.Surface;
@@ -256,6 +255,9 @@
if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD) != 0) {
mBaseDisplayInfo.flags |= Display.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
}
+ if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0) {
+ mBaseDisplayInfo.flags |= Display.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
+ }
Rect maskingInsets = getMaskingInsets(deviceInfo);
int maskedWidth = deviceInfo.width - maskingInsets.left - maskingInsets.right;
int maskedHeight = deviceInfo.height - maskingInsets.top - maskingInsets.bottom;
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index 244c764..5aa585f 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -17,14 +17,13 @@
package com.android.server.display;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
-import static android.hardware.display.DisplayManager
- .VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
-import static android.hardware.display.DisplayManager
- .VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH;
import android.content.Context;
@@ -367,7 +366,10 @@
mInfo.flags |= DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
}
if ((mFlags & VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL) != 0) {
- mInfo.flags |= DisplayDeviceInfo.FLAG_DESTROY_CONTENT_ON_REMOVAL;
+ mInfo.flags |= DisplayDeviceInfo.FLAG_DESTROY_CONTENT_ON_REMOVAL;
+ }
+ if ((mFlags & VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0) {
+ mInfo.flags |= DisplayDeviceInfo.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
}
mInfo.type = Display.TYPE_VIRTUAL;
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 5bd095d..4913e8b 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -209,7 +209,8 @@
private static native void nativeSetInputDispatchMode(long ptr, boolean enabled, boolean frozen);
private static native void nativeSetSystemUiVisibility(long ptr, int visibility);
private static native void nativeSetFocusedApplication(long ptr,
- InputApplicationHandle application);
+ int displayId, InputApplicationHandle application);
+ private static native void nativeSetFocusedDisplay(long ptr, int displayId);
private static native boolean nativeTransferTouchFocus(long ptr,
InputChannel fromChannel, InputChannel toChannel);
private static native void nativeSetPointerSpeed(long ptr, int speed);
@@ -1431,21 +1432,27 @@
}
}
- public void setInputWindows(InputWindowHandle[] windowHandles,
- InputWindowHandle focusedWindowHandle, int displayId) {
- final IWindow newFocusedWindow =
- focusedWindowHandle != null ? focusedWindowHandle.clientWindow : null;
- if (mFocusedWindow != newFocusedWindow) {
- mFocusedWindow = newFocusedWindow;
- if (mFocusedWindowHasCapture) {
- setPointerCapture(false);
- }
- }
+ public void setInputWindows(InputWindowHandle[] windowHandles, int displayId) {
nativeSetInputWindows(mPtr, windowHandles, displayId);
}
- public void setFocusedApplication(InputApplicationHandle application) {
- nativeSetFocusedApplication(mPtr, application);
+ public void setFocusedApplication(int displayId, InputApplicationHandle application) {
+ nativeSetFocusedApplication(mPtr, displayId, application);
+ }
+
+ public void setFocusedWindow(InputWindowHandle focusedWindowHandle) {
+ final IWindow newFocusedWindow =
+ focusedWindowHandle != null ? focusedWindowHandle.clientWindow : null;
+ if (mFocusedWindow != newFocusedWindow) {
+ if (mFocusedWindowHasCapture) {
+ setPointerCapture(false);
+ }
+ mFocusedWindow = newFocusedWindow;
+ }
+ }
+
+ public void setFocusedDisplay(int displayId) {
+ nativeSetFocusedDisplay(mPtr, displayId);
}
@Override
@@ -1460,16 +1467,18 @@
return;
}
setPointerCapture(enabled);
- try {
- mFocusedWindow.dispatchPointerCaptureChanged(enabled);
- } catch (RemoteException ex) {
- /* ignore */
- }
}
private void setPointerCapture(boolean enabled) {
- mFocusedWindowHasCapture = enabled;
- nativeSetPointerCapture(mPtr, enabled);
+ if (mFocusedWindowHasCapture != enabled) {
+ mFocusedWindowHasCapture = enabled;
+ try {
+ mFocusedWindow.dispatchPointerCaptureChanged(enabled);
+ } catch (RemoteException ex) {
+ /* ignore */
+ }
+ nativeSetPointerCapture(mPtr, enabled);
+ }
}
public void setInputDispatchMode(boolean enabled, boolean frozen) {
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 60e9eaa..3f03169 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -159,8 +159,10 @@
static final boolean LOGD = Log.isLoggable(TAG, Log.DEBUG);
static final boolean LOGV = Log.isLoggable(TAG, Log.VERBOSE);
+ // Perform polling and persist all (FLAG_PERSIST_ALL).
private static final int MSG_PERFORM_POLL = 1;
- private static final int MSG_REGISTER_GLOBAL_ALERT = 2;
+ // Perform polling, persist network, and register the global alert again.
+ private static final int MSG_PERFORM_POLL_REGISTER_ALERT = 2;
/** Flags to control detail level of poll event. */
private static final int FLAG_PERSIST_NETWORK = 0x1;
@@ -168,6 +170,14 @@
private static final int FLAG_PERSIST_ALL = FLAG_PERSIST_NETWORK | FLAG_PERSIST_UID;
private static final int FLAG_PERSIST_FORCE = 0x100;
+ /**
+ * When global alert quota is high, wait for this delay before processing each polling,
+ * and do not schedule further polls once there is already one queued.
+ * This avoids firing the global alert too often on devices with high transfer speeds and
+ * high quota.
+ */
+ private static final int PERFORM_POLL_DELAY_MS = 1000;
+
private static final String TAG_NETSTATS_ERROR = "netstats_error";
private final Context mContext;
@@ -920,7 +930,7 @@
}
// Create baseline stats
- mHandler.sendMessage(mHandler.obtainMessage(MSG_PERFORM_POLL, FLAG_PERSIST_ALL));
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_PERFORM_POLL));
return normalizedRequest;
}
@@ -1055,13 +1065,12 @@
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
if (LIMIT_GLOBAL_ALERT.equals(limitName)) {
- // kick off background poll to collect network stats; UID stats
- // are handled during normal polling interval.
- final int flags = FLAG_PERSIST_NETWORK;
- mHandler.obtainMessage(MSG_PERFORM_POLL, flags, 0).sendToTarget();
-
- // re-arm global alert for next update
- mHandler.obtainMessage(MSG_REGISTER_GLOBAL_ALERT).sendToTarget();
+ // kick off background poll to collect network stats unless there is already
+ // such a call pending; UID stats are handled during normal polling interval.
+ if (!mHandler.hasMessages(MSG_PERFORM_POLL_REGISTER_ALERT)) {
+ mHandler.sendEmptyMessageDelayed(MSG_PERFORM_POLL_REGISTER_ALERT,
+ PERFORM_POLL_DELAY_MS);
+ }
}
}
};
@@ -1673,11 +1682,11 @@
public boolean handleMessage(Message msg) {
switch (msg.what) {
case MSG_PERFORM_POLL: {
- final int flags = msg.arg1;
- mService.performPoll(flags);
+ mService.performPoll(FLAG_PERSIST_ALL);
return true;
}
- case MSG_REGISTER_GLOBAL_ALERT: {
+ case MSG_PERFORM_POLL_REGISTER_ALERT: {
+ mService.performPoll(FLAG_PERSIST_NETWORK);
mService.registerGlobalAlert();
return true;
}
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
index 910ea73..1f05dc9 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -16,6 +16,8 @@
package com.android.server.pm.dex;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.AppOpsManager;
import android.content.Context;
@@ -57,8 +59,6 @@
import dalvik.system.VMRuntime;
import libcore.io.IoUtils;
-import libcore.util.NonNull;
-import libcore.util.Nullable;
import java.io.File;
import java.io.FileNotFoundException;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index f8d7fb6..21bf488 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -4775,6 +4775,7 @@
pf.bottom = df.bottom = of.bottom = displayFrames.mUnrestricted.bottom;
// ...with content insets above the nav bar
cf.bottom = vf.bottom = displayFrames.mStable.bottom;
+ // TODO (b/111364446): Support showing IME on non-default displays
if (mStatusBar != null && mFocusedWindow == mStatusBar && canReceiveInput(mStatusBar)) {
// The status bar forces the navigation bar while it's visible. Make sure the IME
// avoids the navigation bar in that case.
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index b3f2a27..43a9c78 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -1079,18 +1079,39 @@
return false;
}
+ private static WorkChain getFirstNonEmptyWorkChain(WorkSource workSource) {
+ if (workSource.getWorkChains() == null) {
+ return null;
+ }
+
+ for (WorkChain workChain: workSource.getWorkChains()) {
+ if (workChain.getSize() > 0) {
+ return workChain;
+ }
+ }
+
+ return null;
+ }
+
private void applyWakeLockFlagsOnAcquireLocked(WakeLock wakeLock, int uid) {
if ((wakeLock.mFlags & PowerManager.ACQUIRE_CAUSES_WAKEUP) != 0
&& isScreenLock(wakeLock)) {
String opPackageName;
int opUid;
- if (wakeLock.mWorkSource != null && wakeLock.mWorkSource.getName(0) != null) {
- opPackageName = wakeLock.mWorkSource.getName(0);
- opUid = wakeLock.mWorkSource.get(0);
+ if (wakeLock.mWorkSource != null && !wakeLock.mWorkSource.isEmpty()) {
+ WorkSource workSource = wakeLock.mWorkSource;
+ WorkChain workChain = getFirstNonEmptyWorkChain(workSource);
+ if (workChain != null) {
+ opPackageName = workChain.getAttributionTag();
+ opUid = workChain.getAttributionUid();
+ } else {
+ opPackageName = workSource.getName(0) != null
+ ? workSource.getName(0) : wakeLock.mPackageName;
+ opUid = workSource.get(0);
+ }
} else {
opPackageName = wakeLock.mPackageName;
- opUid = wakeLock.mWorkSource != null ? wakeLock.mWorkSource.get(0)
- : wakeLock.mOwnerUid;
+ opUid = wakeLock.mOwnerUid;
}
wakeUpNoUpdateLocked(SystemClock.uptimeMillis(), wakeLock.mTag, opUid,
opPackageName, opUid);
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 97992cf..1abaaf2 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -1259,18 +1259,19 @@
Binder.restoreCallingIdentity(token);
}
- long mLastProcStatsHighWaterMark = readProcStatsHighWaterMark();
-
- private long readProcStatsHighWaterMark() {
+ // read high watermark for section
+ private long readProcStatsHighWaterMark(int section) {
try {
- File[] files = mBaseDir.listFiles();
+ File[] files = mBaseDir.listFiles((d, name) -> {
+ return name.toLowerCase().startsWith(String.valueOf(section) + '_');
+ });
if (files == null || files.length == 0) {
return 0;
}
if (files.length > 1) {
Log.e(TAG, "Only 1 file expected for high water mark. Found " + files.length);
}
- return Long.valueOf(files[0].getName());
+ return Long.valueOf(files[0].getName().split("_")[1]);
} catch (SecurityException e) {
Log.e(TAG, "Failed to get procstats high watermark file.", e);
} catch (NumberFormatException e) {
@@ -1282,13 +1283,13 @@
private IProcessStats mProcessStats =
IProcessStats.Stub.asInterface(ServiceManager.getService(ProcessStats.SERVICE_NAME));
- private void pullProcessStats(
- int tagId, long elapsedNanos, long wallClockNanos,
+ private void pullProcessStats(int section, int tagId, long elapsedNanos, long wallClockNanos,
List<StatsLogEventWrapper> pulledData) {
try {
+ long lastHighWaterMark = readProcStatsHighWaterMark(section);
List<ParcelFileDescriptor> statsFiles = new ArrayList<>();
long highWaterMark = mProcessStats.getCommittedStats(
- mLastProcStatsHighWaterMark, ProcessStats.REPORT_ALL, true, statsFiles);
+ lastHighWaterMark, section, true, statsFiles);
if (statsFiles.size() != 1) {
return;
}
@@ -1298,10 +1299,10 @@
StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeStorage(Arrays.copyOf(stats, len[0]));
pulledData.add(e);
- new File(mBaseDir.getAbsolutePath() + "/" + mLastProcStatsHighWaterMark).delete();
- mLastProcStatsHighWaterMark = highWaterMark;
+ new File(mBaseDir.getAbsolutePath() + "/" + section + "_" + lastHighWaterMark).delete();
new File(
- mBaseDir.getAbsolutePath() + "/" + mLastProcStatsHighWaterMark).createNewFile();
+ mBaseDir.getAbsolutePath() + "/" + section + "_"
+ + highWaterMark).createNewFile();
} catch (IOException e) {
Log.e(TAG, "Getting procstats failed: ", e);
} catch (RemoteException e) {
@@ -1490,7 +1491,12 @@
break;
}
case StatsLog.PROC_STATS: {
- pullProcessStats(tagId, elapsedNanos, wallClockNanos, ret);
+ pullProcessStats(ProcessStats.REPORT_ALL, tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.PROC_STATS_PKG_PROC: {
+ pullProcessStats(ProcessStats.REPORT_PKG_PROC_STATS, tagId, elapsedNanos,
+ wallClockNanos, ret);
break;
}
case StatsLog.DISK_IO: {
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index e449111..74922f6 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -1043,7 +1043,8 @@
// Do not send the windows if there is no current focus as
// the window manager is still looking for where to put it.
// We will do the work when we get a focus change callback.
- if (mService.mCurrentFocus == null) {
+ // TODO(b/112273690): Support multiple displays
+ if (mService.getDefaultDisplayContentLocked().mCurrentFocus == null) {
return;
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index bcf9212..db0bd4f 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -33,6 +33,7 @@
import android.util.SparseIntArray;
import com.android.internal.app.IVoiceInteractor;
+import com.android.server.am.ActivityServiceConnectionsHolder;
import com.android.server.am.PendingIntentRecord;
import com.android.server.am.SafeActivityOptions;
import com.android.server.am.TaskRecord;
@@ -332,4 +333,7 @@
int callingUid, int userId, IBinder token, String resultWho,
int requestCode, Intent[] intents, String[] resolvedTypes, int flags,
Bundle bOptions);
+
+ /** @return the service connection holder for a given activity token. */
+ public abstract ActivityServiceConnectionsHolder getServiceConnectionsHolder(IBinder token);
}
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index d73606f..a9d0978 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -77,6 +77,7 @@
import static com.android.server.wm.AppTransitionProto.LAST_USED_APP_TRANSITION;
import android.annotation.DrawableRes;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.content.ComponentName;
@@ -93,6 +94,7 @@
import android.graphics.drawable.Drawable;
import android.os.Binder;
import android.os.Debug;
+import android.os.Handler;
import android.os.IBinder;
import android.os.IRemoteCallback;
import android.os.RemoteException;
@@ -120,8 +122,8 @@
import com.android.internal.R;
import com.android.internal.util.DumpUtils.Dump;
+import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.AttributeCache;
-import com.android.server.wm.WindowManagerService.H;
import com.android.server.wm.animation.ClipRectLRAnimation;
import com.android.server.wm.animation.ClipRectTBAnimation;
import com.android.server.wm.animation.CurvedTranslateAnimation;
@@ -252,9 +254,13 @@
private RemoteAnimationController mRemoteAnimationController;
+ final Handler mHandler;
+ final Runnable mHandleAppTransitionTimeoutRunnable = () -> handleAppTransitionTimeout();
+
AppTransition(Context context, WindowManagerService service) {
mContext = context;
mService = service;
+ mHandler = new Handler(service.mH.getLooper());
mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
com.android.internal.R.interpolator.linear_out_slow_in);
mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
@@ -1349,7 +1355,8 @@
@Override
public void onAnimationEnd(Animation animation) {
- mService.mH.obtainMessage(H.DO_ANIMATION_CALLBACK, callback).sendToTarget();
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ AppTransition::doAnimationCallback, callback));
}
@Override
@@ -1756,7 +1763,7 @@
void postAnimationCallback() {
if (mNextAppTransitionCallback != null) {
- mService.mH.sendMessage(mService.mH.obtainMessage(H.DO_ANIMATION_CALLBACK,
+ mHandler.sendMessage(PooledLambda.obtainMessage(AppTransition::doAnimationCallback,
mNextAppTransitionCallback));
mNextAppTransitionCallback = null;
}
@@ -1869,7 +1876,7 @@
clear();
mNextAppTransitionType = NEXT_TRANSIT_TYPE_REMOTE;
mRemoteAnimationController = new RemoteAnimationController(mService,
- remoteAnimationAdapter, mService.mH);
+ remoteAnimationAdapter, mHandler);
}
}
@@ -2162,8 +2169,8 @@
}
boolean prepared = prepare();
if (isTransitionSet()) {
- mService.mH.removeMessages(H.APP_TRANSITION_TIMEOUT);
- mService.mH.sendEmptyMessageDelayed(H.APP_TRANSITION_TIMEOUT, APP_TRANSITION_TIMEOUT_MS);
+ removeAppTransitionTimeoutCallbacks();
+ mHandler.postDelayed(mHandleAppTransitionTimeoutRunnable, APP_TRANSITION_TIMEOUT_MS);
}
return prepared;
}
@@ -2208,4 +2215,32 @@
return mGridLayoutRecentsEnabled
|| orientation == Configuration.ORIENTATION_PORTRAIT;
}
+
+ private void handleAppTransitionTimeout() {
+ synchronized (mService.mWindowMap) {
+ if (isTransitionSet() || !mService.mOpeningApps.isEmpty()
+ || !mService.mClosingApps.isEmpty()) {
+ if (DEBUG_APP_TRANSITIONS) {
+ Slog.v(TAG_WM, "*** APP TRANSITION TIMEOUT."
+ + " isTransitionSet()="
+ + mService.mAppTransition.isTransitionSet()
+ + " mOpeningApps.size()=" + mService.mOpeningApps.size()
+ + " mClosingApps.size()=" + mService.mClosingApps.size());
+ }
+ setTimeout();
+ mService.mWindowPlacerLocked.performSurfacePlacement();
+ }
+ }
+ }
+
+ private static void doAnimationCallback(@NonNull IRemoteCallback callback) {
+ try {
+ ((IRemoteCallback) callback).sendResult(null);
+ } catch (RemoteException e) {
+ }
+ }
+
+ void removeAppTransitionTimeoutCallbacks() {
+ mHandler.removeCallbacks(mHandleAppTransitionTimeoutRunnable);
+ }
}
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
index 36280dd..330c54c 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java
@@ -407,8 +407,7 @@
if (mService.mAppTransition.getAppTransition()
== WindowManager.TRANSIT_TASK_OPEN_BEHIND) {
// We're launchingBehind, add the launching activity to mOpeningApps.
- final WindowState win =
- mService.getDefaultDisplayContentLocked().findFocusedWindow();
+ final WindowState win = mContainer.getDisplayContent().findFocusedWindow();
if (win != null) {
final AppWindowToken focusedToken = win.mAppToken;
if (focusedToken != null) {
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index fc76102..e57fea3 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -679,11 +679,12 @@
removed = true;
stopFreezingScreen(true, true);
- if (mService.mFocusedApp == this) {
- if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "Removing focused app token:" + this);
- mService.mFocusedApp = null;
+ final DisplayContent dc = getDisplayContent();
+ if (dc.mFocusedApp == this) {
+ if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "Removing focused app token:" + this
+ + " displayId=" + dc.getDisplayId());
+ dc.setFocusedApp(null);
mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/);
- getDisplayContent().getInputMonitor().setFocusedAppLw(null);
}
if (!delayed) {
@@ -1064,6 +1065,18 @@
}
}
+ @Override
+ void onDisplayChanged(DisplayContent dc) {
+ DisplayContent prevDc = mDisplayContent;
+ super.onDisplayChanged(dc);
+ if (prevDc != null && prevDc.mFocusedApp == this) {
+ prevDc.setFocusedApp(null);
+ if (dc.getTopStack().getTopChild().getTopChild() == this) {
+ dc.setFocusedApp(this);
+ }
+ }
+ }
+
/**
* Freezes the task bounds. The size of this task reported the app will be fixed to the bounds
* freezed by {@link Task#prepareFreezingBounds} until {@link #unfreezeBounds} gets called, even
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index a762fe9..6f728fc 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -70,6 +70,7 @@
import static com.android.server.wm.DisplayContentProto.DISPLAY_INFO;
import static com.android.server.wm.DisplayContentProto.DOCKED_STACK_DIVIDER_CONTROLLER;
import static com.android.server.wm.DisplayContentProto.DPI;
+import static com.android.server.wm.DisplayContentProto.FOCUSED_APP;
import static com.android.server.wm.DisplayContentProto.ID;
import static com.android.server.wm.DisplayContentProto.IME_WINDOWS;
import static com.android.server.wm.DisplayContentProto.PINNED_STACK_CONTROLLER;
@@ -98,12 +99,17 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.CUSTOM_SCREEN_ROTATION;
+import static com.android.server.wm.WindowManagerService.H.REPORT_FOCUS_CHANGE;
+import static com.android.server.wm.WindowManagerService.H.REPORT_LOSING_FOCUS;
import static com.android.server.wm.WindowManagerService.H.SEND_NEW_CONFIGURATION;
import static com.android.server.wm.WindowManagerService.H.UPDATE_DOCKED_STACK_DIVIDER;
import static com.android.server.wm.WindowManagerService.H.WINDOW_HIDE_TIMEOUT;
import static com.android.server.wm.WindowManagerService.LAYOUT_REPEAT_THRESHOLD;
import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION;
import static com.android.server.wm.WindowManagerService.SEAMLESS_ROTATION_TIMEOUT_DURATION;
+import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_PLACING_SURFACES;
+import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_REMOVING_FOCUS;
+import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_ASSIGN_LAYERS;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_ACTIVE;
import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_TIMEOUT;
@@ -114,7 +120,6 @@
import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING;
import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW;
import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_MAY_CHANGE;
-import static com.android.server.wm.utils.CoordinateTransforms.transformPhysicalToLogicalCoordinates;
import android.annotation.CallSuper;
import android.annotation.NonNull;
@@ -152,6 +157,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ToBooleanFunction;
import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.wm.utils.DisplayRotationUtil;
import com.android.server.wm.utils.RotationCache;
import com.android.server.wm.utils.WmDisplayCutout;
@@ -334,6 +340,7 @@
private final Matrix mTmpMatrix = new Matrix();
private final Region mTmpRegion = new Region();
+
/** Used for handing back size of display */
private final Rect mTmpBounds = new Rect();
@@ -371,6 +378,36 @@
private final SurfaceSession mSession = new SurfaceSession();
/**
+ * Window that is currently interacting with the user. This window is responsible for receiving
+ * key events and pointer events from the user.
+ */
+ WindowState mCurrentFocus = null;
+
+ /**
+ * The last focused window that we've notified the client that the focus is changed.
+ */
+ WindowState mLastFocus = null;
+
+ /**
+ * Windows that have lost input focus and are waiting for the new focus window to be displayed
+ * before they are told about this.
+ */
+ ArrayList<WindowState> mLosingFocus = new ArrayList<>();
+
+ /**
+ * The foreground app of this display. Windows below this app cannot be the focused window. If
+ * the user taps on the area outside of the task of the focused app, we will notify AM about the
+ * new task the user wants to interact with.
+ */
+ AppWindowToken mFocusedApp = null;
+
+ /** Windows added since {@link #mCurrentFocus} was set to null. Used for ANR blaming. */
+ final ArrayList<WindowState> mWinAddedSinceNullFocus = new ArrayList<>();
+
+ /** Windows removed since {@link #mCurrentFocus} was set to null. Used for ANR blaming. */
+ final ArrayList<WindowState> mWinRemovedSinceNullFocus = new ArrayList<>();
+
+ /**
* We organize all top-level Surfaces in to the following layers.
* mOverlayLayer contains a few Surfaces which are always on top of others
* and omitted from Screen-Magnification, for example the strict mode flash or
@@ -412,6 +449,8 @@
/** Caches the value whether told display manager that we have content. */
private boolean mLastHasContent;
+ private DisplayRotationUtil mRotationUtil = new DisplayRotationUtil();
+
/**
* The input method window for this display.
*/
@@ -466,7 +505,7 @@
};
private final ToBooleanFunction<WindowState> mFindFocusedWindow = w -> {
- final AppWindowToken focusedApp = mService.mFocusedApp;
+ final AppWindowToken focusedApp = mFocusedApp;
if (DEBUG_FOCUS) Slog.v(TAG_WM, "Looking for focus: " + w
+ ", flags=" + w.mAttrs.flags + ", canReceive=" + w.canReceiveKeys());
@@ -625,8 +664,6 @@
final boolean obscuredChanged = w.mObscured !=
mTmpApplySurfaceChangesTransactionState.obscured;
final RootWindowContainer root = mService.mRoot;
- // Only used if default window
- final boolean someoneLosingFocus = !mService.mLosingFocus.isEmpty();
// Update effect.
w.mObscured = mTmpApplySurfaceChangesTransactionState.obscured;
@@ -717,9 +754,8 @@
}
}
- if (isDefaultDisplay && someoneLosingFocus && w == mService.mCurrentFocus
- && w.isDisplayedLw()) {
- mTmpApplySurfaceChangesTransactionState.focusDisplayed = true;
+ if (!mLosingFocus.isEmpty() && w.isFocused() && w.isDisplayedLw()) {
+ mService.mH.obtainMessage(REPORT_LOSING_FOCUS, this).sendToTarget();
}
w.updateResizingWindowIfNeeded();
@@ -1343,21 +1379,12 @@
cutout, mInitialDisplayWidth, mInitialDisplayHeight);
}
final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
- final List<Rect> bounds = WmDisplayCutout.computeSafeInsets(
+ final Rect[] newBounds = mRotationUtil.getRotatedBounds(
+ WmDisplayCutout.computeSafeInsets(
cutout, mInitialDisplayWidth, mInitialDisplayHeight)
- .getDisplayCutout().getBoundingRects();
- transformPhysicalToLogicalCoordinates(rotation, mInitialDisplayWidth, mInitialDisplayHeight,
- mTmpMatrix);
- final Region region = Region.obtain();
- for (int i = 0; i < bounds.size(); i++) {
- final Rect rect = bounds.get(i);
- final RectF rectF = new RectF(bounds.get(i));
- mTmpMatrix.mapRect(rectF);
- rectF.round(rect);
- region.op(rect, Op.UNION);
- }
-
- return WmDisplayCutout.computeSafeInsets(DisplayCutout.fromBounds(region),
+ .getDisplayCutout().getBoundingRectsAll(),
+ rotation, mInitialDisplayWidth, mInitialDisplayHeight);
+ return WmDisplayCutout.computeSafeInsets(DisplayCutout.fromBounds(newBounds),
rotated ? mInitialDisplayHeight : mInitialDisplayWidth,
rotated ? mInitialDisplayWidth : mInitialDisplayHeight);
}
@@ -2070,22 +2097,22 @@
return null;
}
- void setTouchExcludeRegion(Task focusedTask) {
- // The provided task is the task on this display with focus, so if WindowManagerService's
- // focused app is not on this display, focusedTask will be null.
+ void updateTouchExcludeRegion() {
+ final Task focusedTask = (mFocusedApp != null ? mFocusedApp.getTask() : null);
if (focusedTask == null) {
mTouchExcludeRegion.setEmpty();
} else {
mTouchExcludeRegion.set(mBaseDisplayRect);
final int delta = dipToPixel(RESIZE_HANDLE_WIDTH_IN_DP, mDisplayMetrics);
mTmpRect2.setEmpty();
- for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0;
+ --stackNdx) {
final TaskStack stack = mTaskStackContainers.getChildAt(stackNdx);
stack.setTouchExcludeRegion(focusedTask, delta, mTouchExcludeRegion,
mDisplayFrames.mContent, mTmpRect2);
}
// If we removed the focused task above, add it back and only leave its
- // outside touch area in the exclusion. TapDectector is not interested in
+ // outside touch area in the exclusion. TapDetector is not interested in
// any touch inside the focused task itself.
if (!mTmpRect2.isEmpty()) {
mTouchExcludeRegion.op(mTmpRect2, Region.Op.UNION);
@@ -2096,12 +2123,7 @@
// events to be intercepted and used to change focus. This would likely cause a
// disappearance of the input method.
mInputMethodWindow.getTouchableRegion(mTmpRegion);
- if (mInputMethodWindow.getDisplayId() == mDisplayId) {
- mTouchExcludeRegion.op(mTmpRegion, Op.UNION);
- } else {
- // IME is on a different display, so we need to update its tap detector.
- setTouchExcludeRegion(null /* focusedTask */);
- }
+ mTouchExcludeRegion.op(mTmpRegion, Op.UNION);
}
for (int i = mTapExcludedWindows.size() - 1; i >= 0; i--) {
final WindowState win = mTapExcludedWindows.get(i);
@@ -2372,6 +2394,9 @@
}
mDisplayFrames.writeToProto(proto, DISPLAY_FRAMES);
proto.write(SURFACE_SIZE, mSurfaceSize);
+ if (mFocusedApp != null) {
+ mFocusedApp.writeNameToProto(proto, FOCUSED_APP);
+ }
proto.end(token);
}
@@ -2412,6 +2437,27 @@
pw.print(prefix);
pw.print("mDeferredRotationPauseCount="); pw.println(mDeferredRotationPauseCount);
+ pw.print(" mCurrentFocus="); pw.println(mCurrentFocus);
+ if (mLastFocus != mCurrentFocus) {
+ pw.print(" mLastFocus="); pw.println(mLastFocus);
+ }
+ if (mLosingFocus.size() > 0) {
+ pw.println();
+ pw.println(" Windows losing focus:");
+ for (int i = mLosingFocus.size() - 1; i >= 0; i--) {
+ final WindowState w = mLosingFocus.get(i);
+ pw.print(" Losing #"); pw.print(i); pw.print(' ');
+ pw.print(w);
+ if (dumpAll) {
+ pw.println(":");
+ w.dump(pw, " ", true);
+ } else {
+ pw.println();
+ }
+ }
+ }
+ pw.print(" mFocusedApp="); pw.println(mFocusedApp);
+
pw.println();
pw.println(prefix + "Application tokens in top down Z order:");
for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
@@ -2543,6 +2589,132 @@
return mTmpWindow;
}
+
+ /**
+ * Update the focused window and make some adjustments if the focus has changed.
+ *
+ * @param mode Indicates the situation we are in. Possible modes are:
+ * {@link WindowManagerService#UPDATE_FOCUS_NORMAL},
+ * {@link WindowManagerService#UPDATE_FOCUS_PLACING_SURFACES},
+ * {@link WindowManagerService#UPDATE_FOCUS_WILL_PLACE_SURFACES},
+ * {@link WindowManagerService#UPDATE_FOCUS_REMOVING_FOCUS}
+ * @param updateInputWindows Whether to sync the window information to the input module.
+ * @return {@code true} if the focused window has changed.
+ */
+ boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows, boolean focusFound) {
+ final WindowState newFocus = findFocusedWindow();
+ if (mCurrentFocus == newFocus) {
+ return false;
+ }
+ mService.mH.obtainMessage(REPORT_FOCUS_CHANGE, this).sendToTarget();
+ boolean imWindowChanged = false;
+ // TODO (b/111080190): Multi-Session IME
+ if (!focusFound) {
+ final WindowState imWindow = mInputMethodWindow;
+ if (imWindow != null) {
+ final WindowState prevTarget = mService.mInputMethodTarget;
+
+ final WindowState newTarget = computeImeTarget(true /* updateImeTarget*/);
+ imWindowChanged = prevTarget != newTarget;
+
+ if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS
+ && mode != UPDATE_FOCUS_WILL_PLACE_SURFACES) {
+ assignWindowLayers(false /* setLayoutNeeded */);
+ }
+ }
+ }
+
+ if (imWindowChanged) {
+ mService.mWindowsChanged = true;
+ setLayoutNeeded();
+ }
+
+ if (DEBUG_FOCUS_LIGHT || mService.localLOGV) Slog.v(TAG_WM, "Changing focus from "
+ + mCurrentFocus + " to " + newFocus + " displayId=" + getDisplayId()
+ + " Callers=" + Debug.getCallers(4));
+ final WindowState oldFocus = mCurrentFocus;
+ mCurrentFocus = newFocus;
+ mLosingFocus.remove(newFocus);
+
+ if (newFocus != null) {
+ mWinAddedSinceNullFocus.clear();
+ mWinRemovedSinceNullFocus.clear();
+
+ if (newFocus.canReceiveKeys()) {
+ // Displaying a window implicitly causes dispatching to be unpaused.
+ // This is to protect against bugs if someone pauses dispatching but
+ // forgets to resume.
+ newFocus.mToken.paused = false;
+ }
+ }
+
+ // System UI is only shown on the default display.
+ int focusChanged = isDefaultDisplay
+ ? mService.mPolicy.focusChangedLw(oldFocus, newFocus) : 0;
+
+ if (imWindowChanged && oldFocus != mInputMethodWindow) {
+ // Focus of the input method window changed. Perform layout if needed.
+ if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
+ performLayout(true /*initial*/, updateInputWindows);
+ focusChanged &= ~FINISH_LAYOUT_REDO_LAYOUT;
+ } else if (mode == UPDATE_FOCUS_WILL_PLACE_SURFACES) {
+ // Client will do the layout, but we need to assign layers
+ // for handleNewWindowLocked() below.
+ assignWindowLayers(false /* setLayoutNeeded */);
+ }
+ }
+
+ if ((focusChanged & FINISH_LAYOUT_REDO_LAYOUT) != 0) {
+ // The change in focus caused us to need to do a layout. Okay.
+ setLayoutNeeded();
+ if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
+ performLayout(true /*initial*/, updateInputWindows);
+ } else if (mode == UPDATE_FOCUS_REMOVING_FOCUS) {
+ mService.mRoot.performSurfacePlacement(false);
+ }
+ }
+
+ if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS) {
+ // If we defer assigning layers, then the caller is responsible for doing this part.
+ getInputMonitor().setInputFocusLw(newFocus, updateInputWindows);
+ }
+
+ adjustForImeIfNeeded();
+
+ // We may need to schedule some toast windows to be removed. The toasts for an app that
+ // does not have input focus are removed within a timeout to prevent apps to redress
+ // other apps' UI.
+ scheduleToastWindowsTimeoutIfNeededLocked(oldFocus, newFocus);
+
+ if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
+ pendingLayoutChanges |= FINISH_LAYOUT_REDO_ANIM;
+ }
+ return true;
+ }
+
+ /**
+ * Set the new focused app to this display.
+ *
+ * @param newFocus the new focused AppWindowToken.
+ * @return true if the focused app is changed.
+ */
+ boolean setFocusedApp(AppWindowToken newFocus) {
+ if (newFocus != null) {
+ final DisplayContent appDisplay = newFocus.getDisplayContent();
+ if (appDisplay != this) {
+ throw new IllegalStateException(newFocus + " is not on " + getName()
+ + " but " + ((appDisplay != null) ? appDisplay.getName() : "none"));
+ }
+ }
+ if (mFocusedApp == newFocus) {
+ return false;
+ }
+ mFocusedApp = newFocus;
+ getInputMonitor().setFocusedAppLw(newFocus);
+ updateTouchExcludeRegion();
+ return true;
+ }
+
/** Updates the layer assignment of windows on this display. */
void assignWindowLayers(boolean setLayoutNeeded) {
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "assignWindowLayers");
@@ -2906,8 +3078,8 @@
if (DEBUG_INPUT_METHOD) {
Slog.i(TAG_WM, "Desired input method target: " + imFocus);
- Slog.i(TAG_WM, "Current focus: " + mService.mCurrentFocus);
- Slog.i(TAG_WM, "Last focus: " + mService.mLastFocus);
+ Slog.i(TAG_WM, "Current focus: " + mCurrentFocus + " displayId=" + mDisplayId);
+ Slog.i(TAG_WM, "Last focus: " + mLastFocus + " displayId=" + mDisplayId);
}
if (DEBUG_INPUT_METHOD) {
@@ -2975,7 +3147,7 @@
}
// TODO: Super crazy long method that should be broken down...
- boolean applySurfaceChangesTransaction(boolean recoveringMemory) {
+ void applySurfaceChangesTransaction(boolean recoveringMemory) {
final int dw = mDisplayInfo.logicalWidth;
final int dh = mDisplayInfo.logicalHeight;
@@ -3059,8 +3231,6 @@
// can now be shown.
atoken.updateAllDrawn();
}
-
- return mTmpApplySurfaceChangesTransactionState.focusDisplayed;
}
private void updateBounds() {
@@ -3314,7 +3484,6 @@
boolean displayHasContent;
boolean obscured;
boolean syswin;
- boolean focusDisplayed;
float preferredRefreshRate;
int preferredModeId;
@@ -3322,7 +3491,6 @@
displayHasContent = false;
obscured = false;
syswin = false;
- focusDisplayed = false;
preferredRefreshRate = 0;
preferredModeId = 0;
}
diff --git a/services/core/java/com/android/server/wm/DisplayWindowController.java b/services/core/java/com/android/server/wm/DisplayWindowController.java
index 76b6dbe..ab87759 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowController.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowController.java
@@ -17,11 +17,14 @@
package com.android.server.wm;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
import android.content.res.Configuration;
import android.os.Binder;
+import android.os.IBinder;
import android.util.Slog;
import android.view.Display;
@@ -124,6 +127,42 @@
}
}
+ /**
+ * Sets a focused app on this display.
+ *
+ * @param token Specifies which app should be focused.
+ * @param moveFocusNow Specifies if we should update the focused window immediately.
+ */
+ public void setFocusedApp(IBinder token, boolean moveFocusNow) {
+ synchronized (mWindowMap) {
+ if (mContainer == null) {
+ if (DEBUG_FOCUS_LIGHT) Slog.i(TAG_WM, "setFocusedApp: could not find displayId="
+ + mDisplayId);
+ return;
+ }
+ final AppWindowToken newFocus;
+ if (token == null) {
+ if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "Clearing focused app, displayId="
+ + mDisplayId);
+ newFocus = null;
+ } else {
+ newFocus = mRoot.getAppWindowToken(token);
+ if (newFocus == null) {
+ Slog.w(TAG_WM, "Attempted to set focus to non-existing app token: " + token
+ + ", displayId=" + mDisplayId);
+ }
+ if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "Set focused app to: " + newFocus
+ + " moveFocusNow=" + moveFocusNow + " displayId=" + mDisplayId);
+ }
+
+ final boolean changed = mContainer.setFocusedApp(newFocus);
+ if (moveFocusNow && changed) {
+ mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL,
+ true /*updateInputWindows*/);
+ }
+ }
+ }
+
@Override
public String toString() {
return "{DisplayWindowController displayId=" + mDisplayId + "}";
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index ef3a770..15f6938 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -64,7 +64,6 @@
// Array of window handles to provide to the input dispatcher.
private InputWindowHandle[] mInputWindowHandles;
private int mInputWindowHandleCount;
- private InputWindowHandle mFocusedInputWindowHandle;
private boolean mDisableWallpaperTouchEvents;
private final Rect mTmpRect = new Rect();
@@ -229,16 +228,12 @@
+ child + ", " + inputWindowHandle);
}
addInputWindowHandle(inputWindowHandle);
- if (hasFocus) {
- mFocusedInputWindowHandle = inputWindowHandle;
- }
}
private void clearInputWindowHandlesLw() {
while (mInputWindowHandleCount != 0) {
mInputWindowHandles[--mInputWindowHandleCount] = null;
}
- mFocusedInputWindowHandle = null;
}
void setUpdateInputWindowsNeededLw() {
@@ -325,13 +320,13 @@
public void setFocusedAppLw(AppWindowToken newApp) {
// Focused app has changed.
if (newApp == null) {
- mService.mInputManager.setFocusedApplication(null);
+ mService.mInputManager.setFocusedApplication(mDisplayId, null);
} else {
final InputApplicationHandle handle = newApp.mInputApplicationHandle;
handle.name = newApp.toString();
handle.dispatchingTimeoutNanos = newApp.mInputDispatchingTimeoutNanos;
- mService.mInputManager.setFocusedApplication(handle);
+ mService.mInputManager.setFocusedApplication(mDisplayId, handle);
}
}
@@ -370,8 +365,7 @@
void onRemoved() {
// If DisplayContent removed, we need find a way to remove window handles of this display
// from InputDispatcher, so pass an empty InputWindowHandles to remove them.
- mService.mInputManager.setInputWindows(mInputWindowHandles, mFocusedInputWindowHandle,
- mDisplayId);
+ mService.mInputManager.setInputWindows(mInputWindowHandles, mDisplayId);
}
private final class UpdateInputForAllWindowsConsumer implements Consumer<WindowState> {
@@ -414,8 +408,7 @@
}
// Send windows to native code.
- mService.mInputManager.setInputWindows(mInputWindowHandles, mFocusedInputWindowHandle,
- mDisplayId);
+ mService.mInputManager.setInputWindows(mInputWindowHandles, mDisplayId);
clearInputWindowHandlesLw();
@@ -435,7 +428,6 @@
final int flags = w.mAttrs.flags;
final int privateFlags = w.mAttrs.privateFlags;
final int type = w.mAttrs.type;
- // TODO(b/111361570): multi-display focus, one focus window per display.
final boolean hasFocus = w.isFocused();
final boolean isVisible = w.isVisibleLw();
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index a9571be..3fef87d 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -17,19 +17,20 @@
package com.android.server.wm;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE;
import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
-import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.wm.RootWindowContainerProto.DISPLAYS;
import static com.android.server.wm.RootWindowContainerProto.WINDOWS;
import static com.android.server.wm.RootWindowContainerProto.WINDOW_CONTAINER;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_KEEP_SCREEN_ON;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
@@ -41,9 +42,9 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_KEEP_SCREEN_ON;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.H.REPORT_LOSING_FOCUS;
import static com.android.server.wm.WindowManagerService.H.SEND_NEW_CONFIGURATION;
import static com.android.server.wm.WindowManagerService.H.WINDOW_FREEZE_TIMEOUT;
+import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_PLACING_SURFACES;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_NONE;
@@ -124,6 +125,10 @@
private String mCloseSystemDialogsReason;
+ // The ID of the display which is responsible for receiving display-unspecified key and pointer
+ // events.
+ private int mTopFocusedDisplayId = INVALID_DISPLAY;
+
// Only a seperate transaction until we seperate the apply surface changes
// transaction from the global transaction.
private final SurfaceControl.Transaction mDisplayTransaction = new SurfaceControl.Transaction();
@@ -153,23 +158,40 @@
mWallpaperController = new WallpaperController(mService);
}
- WindowState computeFocusedWindow() {
- // While the keyguard is showing, we must focus anything besides the main display.
- // Otherwise we risk input not going to the keyguard when the user expects it to.
- final boolean forceDefaultDisplay = mService.isKeyguardShowingAndNotOccluded();
-
+ boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
+ boolean changed = false;
+ int topFocusedDisplayId = INVALID_DISPLAY;
for (int i = mChildren.size() - 1; i >= 0; i--) {
final DisplayContent dc = mChildren.get(i);
- final WindowState win = dc.findFocusedWindow();
- if (win != null) {
- if (forceDefaultDisplay && !dc.isDefaultDisplay) {
- EventLog.writeEvent(0x534e4554, "71786287", win.mOwnerUid, "");
- continue;
- }
- return win;
+ changed |= dc.updateFocusedWindowLocked(mode, updateInputWindows,
+ topFocusedDisplayId != INVALID_DISPLAY /* focusFound */);
+ if (topFocusedDisplayId == INVALID_DISPLAY && dc.mCurrentFocus != null) {
+ topFocusedDisplayId = dc.getDisplayId();
}
}
- return null;
+ if (topFocusedDisplayId == INVALID_DISPLAY) {
+ topFocusedDisplayId = DEFAULT_DISPLAY;
+ }
+ if (mTopFocusedDisplayId != topFocusedDisplayId) {
+ mTopFocusedDisplayId = topFocusedDisplayId;
+ mService.mInputManager.setFocusedDisplay(topFocusedDisplayId);
+ if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "New topFocusedDisplayId="
+ + topFocusedDisplayId);
+ }
+ final WindowState topFocusedWindow = getTopFocusedDisplayContent().mCurrentFocus;
+ mService.mInputManager.setFocusedWindow(
+ topFocusedWindow != null ? topFocusedWindow.mInputWindowHandle : null);
+ return changed;
+ }
+
+ DisplayContent getTopFocusedDisplayContent() {
+ return getDisplayContent(mTopFocusedDisplayId == INVALID_DISPLAY
+ ? DEFAULT_DISPLAY : mTopFocusedDisplayId);
+ }
+
+ @Override
+ void onChildPositionChanged() {
+ mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /* updateInputWindows */);
}
DisplayContent getDisplayContent(int displayId) {
@@ -636,7 +658,6 @@
if (mService.updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
false /*updateInputWindows*/)) {
updateInputWindowsNeeded = true;
- defaultDisplay.pendingLayoutChanges |= FINISH_LAYOUT_REDO_ANIM;
}
}
@@ -646,7 +667,7 @@
defaultDisplay.pendingLayoutChanges);
}
- final ArraySet<DisplayContent> touchExcludeRegionUpdateDisplays = handleResizingWindows();
+ handleResizingWindows();
if (DEBUG_ORIENTATION && mService.mDisplayFrozen) Slog.v(TAG,
"With display frozen, orientationChangeComplete=" + mOrientationChangeComplete);
@@ -765,17 +786,7 @@
dc.getInputMonitor().updateInputWindowsLw(false /*force*/);
});
}
- mService.setFocusTaskRegionLocked(null);
- if (touchExcludeRegionUpdateDisplays != null) {
- final DisplayContent focusedDc = mService.mFocusedApp != null
- ? mService.mFocusedApp.getDisplayContent() : null;
- for (DisplayContent dc : touchExcludeRegionUpdateDisplays) {
- // The focused DisplayContent was recalcuated in setFocusTaskRegionLocked
- if (focusedDc != dc) {
- dc.setTouchExcludeRegion(null /* focusedTask */);
- }
- }
- }
+ forAllDisplays(DisplayContent::updateTouchExcludeRegion);
// Check to see if we are now in a state where the screen should
// be enabled, because the window obscured flags have changed.
@@ -808,16 +819,10 @@
mService.getDefaultDisplayRotation());
}
- boolean focusDisplayed = false;
-
final int count = mChildren.size();
for (int j = 0; j < count; ++j) {
final DisplayContent dc = mChildren.get(j);
- focusDisplayed |= dc.applySurfaceChangesTransaction(recoveringMemory);
- }
-
- if (focusDisplayed) {
- mService.mH.sendEmptyMessage(REPORT_LOSING_FOCUS);
+ dc.applySurfaceChangesTransaction(recoveringMemory);
}
// Give the display manager a chance to adjust properties like display rotation if it needs
@@ -828,12 +833,8 @@
/**
* Handles resizing windows during surface placement.
- *
- * @return A set of any DisplayContent whose touch exclude region needs to be recalculated due
- * to a tap-exclude window resizing, or null if no such DisplayContents were found.
*/
- private ArraySet<DisplayContent> handleResizingWindows() {
- ArraySet<DisplayContent> touchExcludeRegionUpdateSet = null;
+ private void handleResizingWindows() {
for (int i = mService.mResizingWindows.size() - 1; i >= 0; i--) {
WindowState win = mService.mResizingWindows.get(i);
if (win.mAppFreezing) {
@@ -842,15 +843,7 @@
}
win.reportResized();
mService.mResizingWindows.remove(i);
- if (WindowManagerService.excludeWindowTypeFromTapOutTask(win.mAttrs.type)) {
- final DisplayContent dc = win.getDisplayContent();
- if (touchExcludeRegionUpdateSet == null) {
- touchExcludeRegionUpdateSet = new ArraySet<>();
- }
- touchExcludeRegionUpdateSet.add(dc);
- }
}
- return touchExcludeRegionUpdateSet;
}
/**
@@ -1004,6 +997,10 @@
}
}
+ void dumpTopFocusedDisplayId(PrintWriter pw) {
+ pw.print(" mTopFocusedDisplayId="); pw.println(mTopFocusedDisplayId);
+ }
+
void dumpLayoutNeededDisplayIds(PrintWriter pw) {
if (!isLayoutNeeded()) {
return;
diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java
index 33416f6..b7e37b2 100644
--- a/services/core/java/com/android/server/wm/TaskPositioningController.java
+++ b/services/core/java/com/android/server/wm/TaskPositioningController.java
@@ -131,9 +131,9 @@
// of the app, it may not have focus since there might be other windows
// on top (eg. a dialog window).
WindowState transferFocusFromWin = win;
- if (mService.mCurrentFocus != null && mService.mCurrentFocus != win
- && mService.mCurrentFocus.mAppToken == win.mAppToken) {
- transferFocusFromWin = mService.mCurrentFocus;
+ if (displayContent.mCurrentFocus != null && displayContent.mCurrentFocus != win
+ && displayContent.mCurrentFocus.mAppToken == win.mAppToken) {
+ transferFocusFromWin = displayContent.mCurrentFocus;
}
if (!mInputManager.transferTouchFocus(
transferFocusFromWin.mInputChannel, mTaskPositioner.mServerChannel)) {
diff --git a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
index f1e1592..52f8510 100644
--- a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
+++ b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
@@ -19,12 +19,12 @@
import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.input.InputManager;
+import android.os.Handler;
import android.view.MotionEvent;
import android.view.WindowManagerPolicyConstants.PointerEventListener;
import com.android.server.wm.WindowManagerService.H;
-import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.PointerIcon.TYPE_NOT_SPECIFIED;
import static android.view.PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW;
import static android.view.PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW;
@@ -36,6 +36,8 @@
final private Region mTouchExcludeRegion = new Region();
private final WindowManagerService mService;
private final DisplayContent mDisplayContent;
+ private final Handler mHandler;
+ private final Runnable mMoveDisplayToTop;
private final Rect mTmpRect = new Rect();
private int mPointerIconType = TYPE_NOT_SPECIFIED;
@@ -43,6 +45,13 @@
DisplayContent displayContent) {
mService = service;
mDisplayContent = displayContent;
+ mHandler = new Handler(mService.mH.getLooper());
+ mMoveDisplayToTop = () -> {
+ synchronized (mService.mWindowMap) {
+ mDisplayContent.getParent().positionChildAt(WindowContainer.POSITION_TOP,
+ mDisplayContent, true /* includingParents */);
+ }
+ };
}
@Override
@@ -61,6 +70,7 @@
mService.mTaskPositioningController.handleTapOutsideTask(
mDisplayContent, x, y);
}
+ mHandler.post(mMoveDisplayToTop);
}
}
break;
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 4883f97..46999a2 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -273,6 +273,7 @@
parent.mTreeWeight += child.mTreeWeight;
parent = parent.getParent();
}
+ onChildPositionChanged();
}
/**
@@ -298,6 +299,7 @@
parent.mTreeWeight -= child.mTreeWeight;
parent = parent.getParent();
}
+ onChildPositionChanged();
}
/**
@@ -455,9 +457,15 @@
mChildren.remove(child);
mChildren.add(position, child);
}
+ onChildPositionChanged();
}
/**
+ * Notify that a child's position has changed. Possible changes are adding or removing a child.
+ */
+ void onChildPositionChanged() { }
+
+ /**
* Update override configuration and recalculate full config.
* @see #mOverrideConfiguration
* @see #mFullConfiguration
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 942e47b..10ba63e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -499,12 +499,6 @@
final ArrayList<WindowState> mDestroyPreservedSurface = new ArrayList<>();
/**
- * Windows that have lost input focus and are waiting for the new
- * focus window to be displayed before they are told about this.
- */
- ArrayList<WindowState> mLosingFocus = new ArrayList<>();
-
- /**
* This is set when we have run out of memory, and will either be an empty
* list or contain windows that need to be force removed.
*/
@@ -639,14 +633,6 @@
*/
final Handler mAnimationHandler = new Handler(AnimationThread.getHandler().getLooper());
- WindowState mCurrentFocus = null;
- WindowState mLastFocus = null;
-
- /** Windows added since {@link #mCurrentFocus} was set to null. Used for ANR blaming. */
- private final ArrayList<WindowState> mWinAddedSinceNullFocus = new ArrayList<>();
- /** Windows removed since {@link #mCurrentFocus} was set to null. Used for ANR blaming. */
- private final ArrayList<WindowState> mWinRemovedSinceNullFocus = new ArrayList<>();
-
/** This just indicates the window the input method is on top of, not
* necessarily the window its input is going to. */
WindowState mInputMethodTarget = null;
@@ -721,9 +707,6 @@
}
}
- // TODO: Move to RootWindowContainer
- AppWindowToken mFocusedApp = null;
-
PowerManager mPowerManager;
PowerManagerInternal mPowerManagerInternal;
@@ -1371,8 +1354,8 @@
// the screen after the activity goes away.
if (addToastWindowRequiresToken
|| (attrs.flags & LayoutParams.FLAG_NOT_FOCUSABLE) == 0
- || mCurrentFocus == null
- || mCurrentFocus.mOwnerUid != callingUid) {
+ || displayContent.mCurrentFocus == null
+ || displayContent.mCurrentFocus.mOwnerUid != callingUid) {
mH.sendMessageDelayed(
mH.obtainMessage(H.WINDOW_HIDE_TIMEOUT, win),
win.mAttrs.hideTimeoutMilliseconds);
@@ -1382,8 +1365,8 @@
// From now on, no exceptions or errors allowed!
res = WindowManagerGlobal.ADD_OKAY;
- if (mCurrentFocus == null) {
- mWinAddedSinceNullFocus.add(win);
+ if (displayContent.mCurrentFocus == null) {
+ displayContent.mWinAddedSinceNullFocus.add(win);
}
if (excludeWindowTypeFromTapOutTask(type)) {
@@ -1504,7 +1487,7 @@
win.getParent().assignChildLayers();
if (focusChanged) {
- displayContent.getInputMonitor().setInputFocusLw(mCurrentFocus,
+ displayContent.getInputMonitor().setInputFocusLw(displayContent.mCurrentFocus,
false /*updateInputWindows*/);
}
displayContent.getInputMonitor().updateInputWindowsLw(false /*force*/);
@@ -1679,8 +1662,9 @@
win.resetAppOpsState();
- if (mCurrentFocus == null) {
- mWinRemovedSinceNullFocus.add(win);
+ final DisplayContent dc = win.getDisplayContent();
+ if (dc.mCurrentFocus == null) {
+ dc.mWinRemovedSinceNullFocus.add(win);
}
mPendingRemove.remove(win);
mResizingWindows.remove(win);
@@ -1716,7 +1700,6 @@
atoken.postWindowRemoveStartingWindowCleanup(win);
}
- final DisplayContent dc = win.getDisplayContent();
if (win.mAttrs.type == TYPE_WALLPAPER) {
dc.mWallpaperController.clearLastWallpaperTimeoutTime();
dc.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
@@ -1978,9 +1961,9 @@
boolean imMayMove = (flagChanges & (FLAG_ALT_FOCUSABLE_IM | FLAG_NOT_FOCUSABLE)) != 0
|| becameVisible;
final boolean isDefaultDisplay = win.isDefaultDisplay();
- boolean focusMayChange = isDefaultDisplay && (win.mViewVisibility != viewVisibility
+ boolean focusMayChange = win.mViewVisibility != viewVisibility
|| ((flagChanges & FLAG_NOT_FOCUSABLE) != 0)
- || (!win.mRelayoutCalled));
+ || (!win.mRelayoutCalled);
boolean wallpaperMayMove = win.mViewVisibility != viewVisibility
&& (win.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0;
@@ -2025,8 +2008,7 @@
}
result |= RELAYOUT_RES_SURFACE_CHANGED;
if (!win.mWillReplaceWindow) {
- focusMayChange = tryStartExitingAnimation(win, winAnimator, isDefaultDisplay,
- focusMayChange);
+ focusMayChange = tryStartExitingAnimation(win, winAnimator, focusMayChange);
}
}
@@ -2051,7 +2033,7 @@
return 0;
}
if ((result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
- focusMayChange = isDefaultDisplay;
+ focusMayChange = true;
}
final DisplayContent displayContent = win.getDisplayContent();
if (win.mAttrs.type == TYPE_INPUT_METHOD
@@ -2199,7 +2181,7 @@
}
private boolean tryStartExitingAnimation(WindowState win, WindowStateAnimator winAnimator,
- boolean isDefaultDisplay, boolean focusMayChange) {
+ boolean focusMayChange) {
// Try starting an animation; if there isn't one, we
// can destroy the surface right away.
int transit = WindowManagerPolicy.TRANSIT_EXIT;
@@ -2207,7 +2189,7 @@
transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
}
if (win.isWinVisibleLw() && winAnimator.applyAnimationLocked(transit, false)) {
- focusMayChange = isDefaultDisplay;
+ focusMayChange = true;
win.mAnimatingExit = true;
} else if (win.isAnimating()) {
// Currently in a hide animation... turn this into
@@ -2504,57 +2486,6 @@
}
}
- void setFocusTaskRegionLocked(AppWindowToken previousFocus) {
- final Task focusedTask = mFocusedApp != null ? mFocusedApp.getTask() : null;
- final Task previousTask = previousFocus != null ? previousFocus.getTask() : null;
- final DisplayContent focusedDisplayContent =
- focusedTask != null ? focusedTask.getDisplayContent() : null;
- final DisplayContent previousDisplayContent =
- previousTask != null ? previousTask.getDisplayContent() : null;
- if (previousDisplayContent != null && previousDisplayContent != focusedDisplayContent) {
- previousDisplayContent.setTouchExcludeRegion(null);
- }
- if (focusedDisplayContent != null) {
- focusedDisplayContent.setTouchExcludeRegion(focusedTask);
- }
- }
-
- @Override
- public void setFocusedApp(IBinder token, boolean moveFocusNow) {
- if (!checkCallingPermission(MANAGE_APP_TOKENS, "setFocusedApp()")) {
- throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
- }
-
- synchronized(mWindowMap) {
- final AppWindowToken newFocus;
- if (token == null) {
- if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "Clearing focused app, was " + mFocusedApp);
- newFocus = null;
- } else {
- newFocus = mRoot.getAppWindowToken(token);
- if (newFocus == null) {
- Slog.w(TAG_WM, "Attempted to set focus to non-existing app token: " + token);
- }
- if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "Set focused app to: " + newFocus
- + " old focus=" + mFocusedApp + " moveFocusNow=" + moveFocusNow);
- }
-
- final boolean changed = mFocusedApp != newFocus;
- if (changed) {
- AppWindowToken prev = mFocusedApp;
- mFocusedApp = newFocus;
- mFocusedApp.getDisplayContent().getInputMonitor().setFocusedAppLw(newFocus);
- setFocusTaskRegionLocked(prev);
- }
-
- if (moveFocusNow && changed) {
- final long origId = Binder.clearCallingIdentity();
- updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/);
- Binder.restoreCallingIdentity(origId);
- }
- }
- }
-
@Override
public void prepareAppTransition(@TransitionType int transit, boolean alwaysKeepCurrent) {
prepareAppTransition(transit, alwaysKeepCurrent, 0 /* flags */, false /* forceOverride */);
@@ -4395,7 +4326,8 @@
}
private WindowState getFocusedWindowLocked() {
- return mCurrentFocus;
+ // Return the focused window in the focused display.
+ return mRoot.getTopFocusedDisplayContent().mCurrentFocus;
}
TaskStack getImeFocusStackLocked() {
@@ -4403,8 +4335,11 @@
// Also don't use mInputMethodTarget's stack, because some window with FLAG_NOT_FOCUSABLE
// and FLAG_ALT_FOCUSABLE_IM flags both set might be set to IME target so they're moved
// to make room for IME, but the window is not the focused window that's taking input.
- return (mFocusedApp != null && mFocusedApp.getTask() != null) ?
- mFocusedApp.getTask().mStack : null;
+ // TODO (b/111080190): Consider the case of multiple IMEs on multi-display.
+ final DisplayContent topFocusedDisplay = mRoot.getTopFocusedDisplayContent();
+ final AppWindowToken focusedApp = topFocusedDisplay.mFocusedApp;
+ return (focusedApp != null && focusedApp.getTask() != null)
+ ? focusedApp.getTask().mStack : null;
}
public boolean detectSafeMode() {
@@ -4542,7 +4477,6 @@
public static final int REPORT_LOSING_FOCUS = 3;
public static final int WINDOW_FREEZE_TIMEOUT = 11;
- public static final int APP_TRANSITION_TIMEOUT = 13;
public static final int PERSIST_ANIMATION_SCALE = 14;
public static final int FORCE_GC = 15;
public static final int ENABLE_SCREEN = 16;
@@ -4554,7 +4488,6 @@
public static final int BOOT_TIMEOUT = 23;
public static final int WAITING_FOR_DRAWN_TIMEOUT = 24;
public static final int SHOW_STRICT_MODE_VIOLATION = 25;
- public static final int DO_ANIMATION_CALLBACK = 26;
public static final int CLIENT_FREEZE_TIMEOUT = 30;
public static final int NOTIFY_ACTIVITY_DRAWN = 32;
@@ -4601,6 +4534,7 @@
}
switch (msg.what) {
case REPORT_FOCUS_CHANGE: {
+ final DisplayContent displayContent = (DisplayContent) msg.obj;
WindowState lastFocus;
WindowState newFocus;
@@ -4608,24 +4542,22 @@
synchronized(mWindowMap) {
// TODO(multidisplay): Accessibility supported only of default desiplay.
- if (mAccessibilityController != null && getDefaultDisplayContentLocked()
- .getDisplayId() == DEFAULT_DISPLAY) {
+ if (mAccessibilityController != null && displayContent.isDefaultDisplay) {
accessibilityController = mAccessibilityController;
}
- lastFocus = mLastFocus;
- newFocus = mCurrentFocus;
+ lastFocus = displayContent.mLastFocus;
+ newFocus = displayContent.mCurrentFocus;
if (lastFocus == newFocus) {
// Focus is not changing, so nothing to do.
return;
}
- mLastFocus = newFocus;
+ displayContent.mLastFocus = newFocus;
if (DEBUG_FOCUS_LIGHT) Slog.i(TAG_WM, "Focus moving from " + lastFocus +
- " to " + newFocus);
- if (newFocus != null && lastFocus != null
- && !newFocus.isDisplayedLw()) {
- //Slog.i(TAG_WM, "Delaying loss of focus...");
- mLosingFocus.add(lastFocus);
+ " to " + newFocus + " displayId=" + displayContent.getDisplayId());
+ if (newFocus != null && lastFocus != null && !newFocus.isDisplayedLw()) {
+ if (DEBUG_FOCUS_LIGHT) Slog.i(TAG_WM, "Delaying loss of focus...");
+ displayContent.mLosingFocus.add(lastFocus);
lastFocus = null;
}
}
@@ -4636,8 +4568,6 @@
accessibilityController.onWindowFocusChangedNotLocked();
}
- //System.out.println("Changing focus from " + lastFocus
- // + " to " + newFocus);
if (newFocus != null) {
if (DEBUG_FOCUS_LIGHT) Slog.i(TAG_WM, "Gaining focus: " + newFocus);
newFocus.reportFocusChangedSerialized(true, mInTouchMode);
@@ -4651,15 +4581,16 @@
} break;
case REPORT_LOSING_FOCUS: {
+ final DisplayContent displayContent = (DisplayContent) msg.obj;
ArrayList<WindowState> losers;
synchronized(mWindowMap) {
- losers = mLosingFocus;
- mLosingFocus = new ArrayList<WindowState>();
+ losers = displayContent.mLosingFocus;
+ displayContent.mLosingFocus = new ArrayList<>();
}
final int N = losers.size();
- for (int i=0; i<N; i++) {
+ for (int i = 0; i < N; i++) {
if (DEBUG_FOCUS_LIGHT) Slog.i(TAG_WM, "Losing delayed focus: " +
losers.get(i));
losers.get(i).reportFocusChangedSerialized(false, mInTouchMode);
@@ -4674,21 +4605,6 @@
break;
}
- case APP_TRANSITION_TIMEOUT: {
- synchronized (mWindowMap) {
- if (mAppTransition.isTransitionSet() || !mOpeningApps.isEmpty()
- || !mClosingApps.isEmpty()) {
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, "*** APP TRANSITION TIMEOUT."
- + " isTransitionSet()=" + mAppTransition.isTransitionSet()
- + " mOpeningApps.size()=" + mOpeningApps.size()
- + " mClosingApps.size()=" + mClosingApps.size());
- mAppTransition.setTimeout();
- mWindowPlacerLocked.performSurfacePlacement();
- }
- }
- break;
- }
-
case PERSIST_ANIMATION_SCALE: {
Settings.Global.putFloat(mContext.getContentResolver(),
Settings.Global.WINDOW_ANIMATION_SCALE, mWindowAnimationScaleSetting);
@@ -4841,14 +4757,6 @@
break;
}
- case DO_ANIMATION_CALLBACK: {
- try {
- ((IRemoteCallback)msg.obj).sendResult(null);
- } catch (RemoteException e) {
- }
- break;
- }
-
case NOTIFY_ACTIVITY_DRAWN:
try {
mActivityTaskManager.notifyActivityDrawn((IBinder) msg.obj);
@@ -5553,89 +5461,11 @@
}
}
- // TODO: Move to DisplayContent
boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
- WindowState newFocus = mRoot.computeFocusedWindow();
- if (mCurrentFocus != newFocus) {
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wmUpdateFocus");
- // This check makes sure that we don't already have the focus
- // change message pending.
- mH.removeMessages(H.REPORT_FOCUS_CHANGE);
- mH.sendEmptyMessage(H.REPORT_FOCUS_CHANGE);
- final DisplayContent displayContent = (newFocus != null) ? newFocus.getDisplayContent()
- : getDefaultDisplayContentLocked();
- boolean imWindowChanged = false;
- if (displayContent.mInputMethodWindow != null) {
- final WindowState prevTarget = mInputMethodTarget;
-
- final WindowState newTarget =
- displayContent.computeImeTarget(true /* updateImeTarget*/);
- imWindowChanged = prevTarget != newTarget;
-
- if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS
- && mode != UPDATE_FOCUS_WILL_PLACE_SURFACES) {
- displayContent.assignWindowLayers(false /* setLayoutNeeded */);
- }
- }
-
- if (imWindowChanged) {
- mWindowsChanged = true;
- displayContent.setLayoutNeeded();
- newFocus = mRoot.computeFocusedWindow();
- }
-
- if (DEBUG_FOCUS_LIGHT || localLOGV) Slog.v(TAG_WM, "Changing focus from " +
- mCurrentFocus + " to " + newFocus + " Callers=" + Debug.getCallers(4));
- final WindowState oldFocus = mCurrentFocus;
- mCurrentFocus = newFocus;
- mLosingFocus.remove(newFocus);
-
- if (mCurrentFocus != null) {
- mWinAddedSinceNullFocus.clear();
- mWinRemovedSinceNullFocus.clear();
- }
-
- int focusChanged = mPolicy.focusChangedLw(oldFocus, newFocus);
-
- if (imWindowChanged && oldFocus != displayContent.mInputMethodWindow) {
- // Focus of the input method window changed. Perform layout if needed.
- if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
- displayContent.performLayout(true /*initial*/, updateInputWindows);
- focusChanged &= ~FINISH_LAYOUT_REDO_LAYOUT;
- } else if (mode == UPDATE_FOCUS_WILL_PLACE_SURFACES) {
- // Client will do the layout, but we need to assign layers
- // for handleNewWindowLocked() below.
- displayContent.assignWindowLayers(false /* setLayoutNeeded */);
- }
- }
-
- if ((focusChanged & FINISH_LAYOUT_REDO_LAYOUT) != 0) {
- // The change in focus caused us to need to do a layout. Okay.
- displayContent.setLayoutNeeded();
- if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
- displayContent.performLayout(true /*initial*/, updateInputWindows);
- } else if (mode == UPDATE_FOCUS_REMOVING_FOCUS) {
- mRoot.performSurfacePlacement(false);
- }
- }
-
- if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS) {
- // If we defer assigning layers, then the caller is responsible for
- // doing this part.
- displayContent.getInputMonitor().setInputFocusLw(mCurrentFocus, updateInputWindows);
- }
-
- displayContent.adjustForImeIfNeeded();
-
- // We may need to schedule some toast windows to be removed. The toasts for an app that
- // does not have input focus are removed within a timeout to prevent apps to redress
- // other apps' UI.
- displayContent.scheduleToastWindowsTimeoutIfNeededLocked(oldFocus, newFocus);
-
- Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
- return true;
- }
- return false;
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wmUpdateFocus");
+ boolean changed = mRoot.updateFocusedWindowLocked(mode, updateInputWindows);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ return changed;
}
void startFreezingDisplayLocked(int exitAnim, int enterAnim) {
@@ -6197,11 +6027,12 @@
void writeToProtoLocked(ProtoOutputStream proto, boolean trim) {
mPolicy.writeToProto(proto, POLICY);
mRoot.writeToProto(proto, ROOT_WINDOW_CONTAINER, trim);
- if (mCurrentFocus != null) {
- mCurrentFocus.writeIdentifierToProto(proto, FOCUSED_WINDOW);
+ final DisplayContent topFocusedDisplayContent = mRoot.getTopFocusedDisplayContent();
+ if (topFocusedDisplayContent.mCurrentFocus != null) {
+ topFocusedDisplayContent.mCurrentFocus.writeIdentifierToProto(proto, FOCUSED_WINDOW);
}
- if (mFocusedApp != null) {
- mFocusedApp.writeNameToProto(proto, FOCUSED_APP);
+ if (topFocusedDisplayContent.mFocusedApp != null) {
+ topFocusedDisplayContent.mFocusedApp.writeNameToProto(proto, FOCUSED_APP);
}
final WindowState imeWindow = mRoot.getCurrentInputMethodWindow();
if (imeWindow != null) {
@@ -6299,23 +6130,6 @@
}
}
}
- if (mLosingFocus.size() > 0) {
- pw.println();
- pw.println(" Windows losing focus:");
- for (int i=mLosingFocus.size()-1; i>=0; i--) {
- WindowState w = mLosingFocus.get(i);
- if (windows == null || windows.contains(w)) {
- pw.print(" Losing #"); pw.print(i); pw.print(' ');
- pw.print(w);
- if (dumpAll) {
- pw.println(":");
- w.dump(pw, " ", true);
- } else {
- pw.println();
- }
- }
- }
- }
if (mResizingWindows.size() > 0) {
pw.println();
pw.println(" Windows waiting to resize:");
@@ -6344,11 +6158,7 @@
pw.println();
pw.print(" mGlobalConfiguration="); pw.println(mRoot.getConfiguration());
pw.print(" mHasPermanentDpad="); pw.println(mHasPermanentDpad);
- pw.print(" mCurrentFocus="); pw.println(mCurrentFocus);
- if (mLastFocus != mCurrentFocus) {
- pw.print(" mLastFocus="); pw.println(mLastFocus);
- }
- pw.print(" mFocusedApp="); pw.println(mFocusedApp);
+ mRoot.dumpTopFocusedDisplayId(pw);
if (mInputMethodTarget != null) {
pw.print(" mInputMethodTarget="); pw.println(mInputMethodTarget);
}
@@ -6479,11 +6289,17 @@
if (reason != null) {
pw.println(" Reason: " + reason);
}
- if (!mWinAddedSinceNullFocus.isEmpty()) {
- pw.println(" Windows added since null focus: " + mWinAddedSinceNullFocus);
- }
- if (!mWinRemovedSinceNullFocus.isEmpty()) {
- pw.println(" Windows removed since null focus: " + mWinRemovedSinceNullFocus);
+ for (int i = mRoot.getChildCount() - 1; i >= 0; i--) {
+ final DisplayContent dc = mRoot.getChildAt(i);
+ final int displayId = dc.getDisplayId();
+ if (!dc.mWinAddedSinceNullFocus.isEmpty()) {
+ pw.println(" Windows added in display #" + displayId + " since null focus: "
+ + dc.mWinAddedSinceNullFocus);
+ }
+ if (!dc.mWinRemovedSinceNullFocus.isEmpty()) {
+ pw.println(" Windows removed in display #" + displayId + " since null focus: "
+ + dc.mWinRemovedSinceNullFocus);
+ }
}
pw.println();
dumpWindowsNoHeaderLocked(pw, true, null);
@@ -6818,9 +6634,9 @@
@Override
public void setDockedStackDividerTouchRegion(Rect touchRegion) {
synchronized (mWindowMap) {
- getDefaultDisplayContentLocked().getDockedDividerController()
- .setTouchRegion(touchRegion);
- setFocusTaskRegionLocked(null);
+ final DisplayContent dc = getDefaultDisplayContentLocked();
+ dc.getDockedDividerController().setTouchRegion(touchRegion);
+ dc.updateTouchExcludeRegion();
}
}
@@ -7373,7 +7189,14 @@
@Override
public boolean isUidFocused(int uid) {
synchronized (mWindowMap) {
- return mCurrentFocus != null ? uid == mCurrentFocus.getOwningUid() : false;
+ for (int i = mRoot.getChildCount() - 1; i >= 0; i--) {
+ final DisplayContent displayContent = mRoot.getChildAt(i);
+ if (displayContent.mCurrentFocus != null
+ && uid == displayContent.mCurrentFocus.getOwningUid()) {
+ return true;
+ }
+ }
+ return false;
}
}
@@ -7396,8 +7219,9 @@
// press home. Sometimes the IME won't go down.)
// Would be nice to fix this more correctly, but it's
// way at the end of a release, and this should be good enough.
- if (mCurrentFocus != null && mCurrentFocus.mSession.mUid == uid
- && mCurrentFocus.mSession.mPid == pid) {
+ final WindowState currentFocus = mRoot.getTopFocusedDisplayContent().mCurrentFocus;
+ if (currentFocus != null && currentFocus.mSession.mUid == uid
+ && currentFocus.mSession.mPid == pid) {
return true;
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index a4bac31..8276952 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1833,7 +1833,7 @@
if (startingWindow && DEBUG_STARTING_WINDOW) Slog.d(TAG_WM,
"Starting window removed " + this);
- if (localLOGV || DEBUG_FOCUS || DEBUG_FOCUS_LIGHT && this == mService.mCurrentFocus)
+ if (localLOGV || DEBUG_FOCUS || DEBUG_FOCUS_LIGHT && isFocused())
Slog.v(TAG_WM, "Remove " + this + " client="
+ Integer.toHexString(System.identityHashCode(mClient.asBinder()))
+ ", surfaceController=" + mWinAnimator.mSurfaceController + " Callers="
@@ -1945,7 +1945,7 @@
if (wasVisible && mService.updateOrientationFromAppTokensLocked(displayId)) {
mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, displayId).sendToTarget();
}
- mService.updateFocusedWindowLocked(mService.mCurrentFocus == this
+ mService.updateFocusedWindowLocked(isFocused()
? UPDATE_FOCUS_REMOVING_FOCUS
: UPDATE_FOCUS_NORMAL,
true /*updateInputWindows*/);
@@ -2180,7 +2180,7 @@
mPolicyVisibility = mPolicyVisibilityAfterAnim;
if (!mPolicyVisibility) {
mWinAnimator.hide("checkPolicyVisibilityChange");
- if (mService.mCurrentFocus == this) {
+ if (isFocused()) {
if (DEBUG_FOCUS_LIGHT) Slog.i(TAG,
"setAnimationLocked: setting mFocusMayChange true");
mService.mFocusMayChange = true;
@@ -2482,6 +2482,7 @@
}
}
mPolicyVisibilityAfterAnim = false;
+ final boolean isFocused = isFocused();
if (!doAnimation) {
if (DEBUG_VISIBILITY) Slog.v(TAG, "Policy visibility false: " + this);
mPolicyVisibility = false;
@@ -2489,7 +2490,7 @@
// for it to be displayed before enabling the display, that
// we allow the display to be enabled now.
mService.enableScreenIfNeededLocked();
- if (mService.mCurrentFocus == this) {
+ if (isFocused) {
if (DEBUG_FOCUS_LIGHT) Slog.i(TAG,
"WindowState.hideLw: setting mFocusMayChange true");
mService.mFocusMayChange = true;
@@ -2498,7 +2499,7 @@
if (requestAnim) {
mService.scheduleAnimationLocked();
}
- if (mService.mCurrentFocus == this) {
+ if (isFocused) {
mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /* updateImWindows */);
}
return true;
@@ -2994,10 +2995,8 @@
}
}
- public boolean isFocused() {
- synchronized(mService.mWindowMap) {
- return mService.mCurrentFocus == this;
- }
+ boolean isFocused() {
+ return getDisplayContent().mCurrentFocus == this;
}
@Override
@@ -4435,7 +4434,12 @@
@Override
public boolean isFocused() {
final WindowState outer = mOuter.get();
- return outer != null && outer.isFocused();
+ if (outer != null) {
+ synchronized (outer.mService.mWindowMap) {
+ return outer.isFocused();
+ }
+ }
+ return false;
}
}
@@ -4663,10 +4667,7 @@
mTapExcludeRegionHolder.updateRegion(regionId, left, top, width, height);
// Trigger touch exclude region update on current display.
- final boolean isAppFocusedOnDisplay = mService.mFocusedApp != null
- && mService.mFocusedApp.getDisplayContent() == currentDisplay;
- currentDisplay.setTouchExcludeRegion(isAppFocusedOnDisplay ? mService.mFocusedApp.getTask()
- : null);
+ currentDisplay.updateTouchExcludeRegion();
}
/** Union the region with current tap exclude region that this window provides. */
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 080a3a2..e13a70a 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -69,7 +69,6 @@
import android.view.animation.Animation;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.wm.WindowManagerService.H;
import java.io.PrintWriter;
import java.util.function.Predicate;
@@ -252,7 +251,7 @@
mService.mSkipAppTransitionAnimation = false;
mService.mNoAnimationNotifyOnTransitionFinished.clear();
- mService.mH.removeMessages(H.APP_TRANSITION_TIMEOUT);
+ mService.mAppTransition.removeAppTransitionTimeoutCallbacks();
final DisplayContent displayContent = mService.getDefaultDisplayContentLocked();
@@ -763,6 +762,7 @@
private void processApplicationsAnimatingInPlace(int transit) {
if (transit == TRANSIT_TASK_IN_PLACE) {
+ // TODO (b/111362605): non-default-display transition.
// Find the focused window
final WindowState win = mService.getDefaultDisplayContentLocked().findFocusedWindow();
if (win != null) {
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index fefd305..0cf79b6 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -255,6 +255,7 @@
super.removeImmediately();
}
+ @Override
void onDisplayChanged(DisplayContent dc) {
dc.reParentWindowToken(this);
mDisplayContent = dc;
diff --git a/services/core/java/com/android/server/wm/utils/DisplayRotationUtil.java b/services/core/java/com/android/server/wm/utils/DisplayRotationUtil.java
new file mode 100644
index 0000000..9f307bb
--- /dev/null
+++ b/services/core/java/com/android/server/wm/utils/DisplayRotationUtil.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2018 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.wm.utils;
+
+import static android.view.DisplayCutout.BOUNDS_POSITION_LENGTH;
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_180;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+
+import static com.android.server.wm.utils.CoordinateTransforms.transformPhysicalToLogicalCoordinates;
+
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.RectF;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Utility to compute bounds after rotating the screen.
+ */
+public class DisplayRotationUtil {
+ private final Matrix mTmpMatrix = new Matrix();
+
+ private static int getRotationToBoundsOffset(int rotation) {
+ switch (rotation) {
+ case ROTATION_0:
+ return 0;
+ case ROTATION_90:
+ return -1;
+ case ROTATION_180:
+ return 2;
+ case ROTATION_270:
+ return 1;
+ default:
+ // should not happen
+ return 0;
+ }
+ }
+
+ @VisibleForTesting
+ static int getBoundIndexFromRotation(int i, int rotation) {
+ return Math.floorMod(i + getRotationToBoundsOffset(rotation),
+ BOUNDS_POSITION_LENGTH);
+ }
+
+ /**
+ * Compute bounds after rotating teh screen.
+ *
+ * @param bounds Bounds before the rotation. The array must contain exactly 4 non-null elements.
+ * @param rotation rotation constant defined in android.view.Surface.
+ * @param initialDisplayWidth width of the display before the rotation.
+ * @param initialDisplayHeight height of the display before the rotation.
+ * @return Bounds after the rotation.
+ *
+ * @hide
+ */
+ public Rect[] getRotatedBounds(
+ Rect[] bounds, int rotation, int initialDisplayWidth, int initialDisplayHeight) {
+ if (bounds.length != BOUNDS_POSITION_LENGTH) {
+ throw new IllegalArgumentException(
+ "bounds must have exactly 4 elements: bounds=" + bounds);
+ }
+ if (rotation == ROTATION_0) {
+ return bounds;
+ }
+ transformPhysicalToLogicalCoordinates(rotation, initialDisplayWidth, initialDisplayHeight,
+ mTmpMatrix);
+ Rect[] newBounds = new Rect[BOUNDS_POSITION_LENGTH];
+ for (int i = 0; i < bounds.length; i++) {
+
+ final Rect rect = bounds[i];
+ if (!rect.isEmpty()) {
+ final RectF rectF = new RectF(rect);
+ mTmpMatrix.mapRect(rectF);
+ rectF.round(rect);
+ }
+ newBounds[getBoundIndexFromRotation(i, rotation)] = rect;
+ }
+ return newBounds;
+ }
+}
diff --git a/services/core/jni/com_android_server_SystemServer.cpp b/services/core/jni/com_android_server_SystemServer.cpp
index 3901ceb..dc0d53b 100644
--- a/services/core/jni/com_android_server_SystemServer.cpp
+++ b/services/core/jni/com_android_server_SystemServer.cpp
@@ -17,6 +17,7 @@
#include <jni.h>
#include <nativehelper/JNIHelp.h>
+#include <binder/IServiceManager.h>
#include <hidl/HidlTransportSupport.h>
#include <schedulerservice/SchedulingPolicyService.h>
@@ -34,7 +35,8 @@
char propBuf[PROPERTY_VALUE_MAX];
property_get("system_init.startsensorservice", propBuf, "1");
if (strcmp(propBuf, "1") == 0) {
- SensorService::instantiate();
+ SensorService::publish(false /* allowIsolated */,
+ IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL);
}
}
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index e2888cc..c66d03c 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -221,7 +221,8 @@
status_t unregisterInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel);
void setInputWindows(JNIEnv* env, jobjectArray windowHandleObjArray, int32_t displayId);
- void setFocusedApplication(JNIEnv* env, jobject applicationHandleObj);
+ void setFocusedApplication(JNIEnv* env, int32_t displayId, jobject applicationHandleObj);
+ void setFocusedDisplay(JNIEnv* env, int32_t displayId);
void setInputDispatchMode(bool enabled, bool frozen);
void setSystemUiVisibility(int32_t visibility);
void setPointerSpeed(int32_t speed);
@@ -428,7 +429,7 @@
// Internal viewport has changed if there wasn't one earlier, and there is one now, or,
// if they are different.
const bool internalViewportChanged = (newInternalViewport != nullptr) &&
- (oldInternalViewport == nullptr || (*newInternalViewport != *newInternalViewport));
+ (oldInternalViewport == nullptr || (*oldInternalViewport != *newInternalViewport));
if (internalViewportChanged) {
sp<PointerController> controller = mLocked.pointerController.promote();
updatePointerControllerFromViewport(controller, newInternalViewport);
@@ -771,10 +772,15 @@
}
}
-void NativeInputManager::setFocusedApplication(JNIEnv* env, jobject applicationHandleObj) {
+void NativeInputManager::setFocusedApplication(JNIEnv* env, int32_t displayId,
+ jobject applicationHandleObj) {
sp<InputApplicationHandle> applicationHandle =
android_server_InputApplicationHandle_getHandle(env, applicationHandleObj);
- mInputManager->getDispatcher()->setFocusedApplication(applicationHandle);
+ mInputManager->getDispatcher()->setFocusedApplication(displayId, applicationHandle);
+}
+
+void NativeInputManager::setFocusedDisplay(JNIEnv* env, int32_t displayId) {
+ mInputManager->getDispatcher()->setFocusedDisplay(displayId);
}
void NativeInputManager::setInputDispatchMode(bool enabled, bool frozen) {
@@ -1413,10 +1419,17 @@
}
static void nativeSetFocusedApplication(JNIEnv* env, jclass /* clazz */,
- jlong ptr, jobject applicationHandleObj) {
+ jlong ptr, jint displayId, jobject applicationHandleObj) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- im->setFocusedApplication(env, applicationHandleObj);
+ im->setFocusedApplication(env, displayId, applicationHandleObj);
+}
+
+static void nativeSetFocusedDisplay(JNIEnv* env, jclass /* clazz */,
+ jlong ptr, jint displayId) {
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
+ im->setFocusedDisplay(env, displayId);
}
static void nativeSetPointerCapture(JNIEnv* env, jclass /* clazz */, jlong ptr,
@@ -1638,8 +1651,10 @@
(void*) nativeToggleCapsLock },
{ "nativeSetInputWindows", "(J[Lcom/android/server/input/InputWindowHandle;I)V",
(void*) nativeSetInputWindows },
- { "nativeSetFocusedApplication", "(JLcom/android/server/input/InputApplicationHandle;)V",
+ { "nativeSetFocusedApplication", "(JILcom/android/server/input/InputApplicationHandle;)V",
(void*) nativeSetFocusedApplication },
+ { "nativeSetFocusedDisplay", "(JI)V",
+ (void*) nativeSetFocusedDisplay },
{ "nativeSetPointerCapture", "(JZ)V",
(void*) nativeSetPointerCapture },
{ "nativeSetInputDispatchMode", "(JZZ)V",
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 84de6b4..66cf48c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -15,20 +15,10 @@
*/
package com.android.server.devicepolicy;
-import android.annotation.UserIdInt;
import android.app.admin.IDevicePolicyManager;
-import android.content.ComponentName;
-import android.os.PersistableBundle;
-import android.security.keymaster.KeymasterCertificateChain;
-import android.security.keystore.ParcelableKeyGenParameterSpec;
-import android.telephony.data.ApnSetting;
import com.android.server.SystemService;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
/**
* Defines the required interface for IDevicePolicyManager implemenation.
*
@@ -64,94 +54,10 @@
*/
abstract void handleStopUser(int userId);
- public void setSystemSetting(ComponentName who, String setting, String value){}
-
- public void transferOwnership(ComponentName admin, ComponentName target, PersistableBundle bundle) {}
-
- public PersistableBundle getTransferOwnershipBundle() {
- return null;
- }
-
- public boolean generateKeyPair(ComponentName who, String callerPackage, String algorithm,
- ParcelableKeyGenParameterSpec keySpec, int idAttestationFlags,
- KeymasterCertificateChain attestationChain) {
- return false;
- }
-
- public boolean isUsingUnifiedPassword(ComponentName who) {
- return true;
- }
-
- public boolean setKeyPairCertificate(ComponentName who, String callerPackage, String alias,
- byte[] cert, byte[] chain, boolean isUserSelectable) {
- return false;
- }
-
- @Override
- public void setStartUserSessionMessage(
- ComponentName admin, CharSequence startUserSessionMessage) {}
-
- @Override
- public void setEndUserSessionMessage(ComponentName admin, CharSequence endUserSessionMessage) {}
-
- @Override
- public String getStartUserSessionMessage(ComponentName admin) {
- return null;
- }
-
- @Override
- public String getEndUserSessionMessage(ComponentName admin) {
- return null;
- }
-
- @Override
- public List<String> setMeteredDataDisabledPackages(ComponentName admin, List<String> packageNames) {
- return packageNames;
- }
-
- @Override
- public List<String> getMeteredDataDisabledPackages(ComponentName admin) {
- return new ArrayList<>();
- }
-
- @Override
- public int addOverrideApn(ComponentName admin, ApnSetting apnSetting) {
- return -1;
- }
-
- @Override
- public boolean updateOverrideApn(ComponentName admin, int apnId, ApnSetting apnSetting) {
- return false;
- }
-
- @Override
- public boolean removeOverrideApn(ComponentName admin, int apnId) {
- return false;
- }
-
- @Override
- public List<ApnSetting> getOverrideApns(ComponentName admin) {
- return Collections.emptyList();
- }
-
- @Override
- public void setOverrideApnsEnabled(ComponentName admin, boolean enabled) {}
-
- @Override
- public boolean isOverrideApnEnabled(ComponentName admin) {
- return false;
- }
-
public void clearSystemUpdatePolicyFreezePeriodRecord() {
}
@Override
- public boolean isMeteredDataDisabledPackageForUser(ComponentName admin,
- String packageName, int userId) {
- return false;
- }
-
- @Override
public long forceNetworkLogs() {
return 0;
}
@@ -160,8 +66,4 @@
public long forceSecurityLogs() {
return 0;
}
-
- @Override
- public void setDefaultSmsApplication(ComponentName admin, String packageName) {
- }
}
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/EncryptedChunkTest.java b/services/robotests/src/com/android/server/backup/encryption/chunking/EncryptedChunkTest.java
new file mode 100644
index 0000000..c5f9b10
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/encryption/chunking/EncryptedChunkTest.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2018 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.backup.encryption.chunking;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.testng.Assert.assertThrows;
+
+import android.platform.test.annotations.Presubmit;
+import com.android.server.backup.encryption.chunk.ChunkHash;
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderPackages;
+import com.google.common.primitives.Bytes;
+import java.util.Arrays;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+@RunWith(FrameworkRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, sdk = 26)
+@SystemLoaderPackages({"com.android.server.backup"})
+@Presubmit
+public class EncryptedChunkTest {
+ private static final byte[] CHUNK_HASH_1_BYTES =
+ Arrays.copyOf(new byte[] {1}, ChunkHash.HASH_LENGTH_BYTES);
+ private static final byte[] NONCE_1 =
+ Arrays.copyOf(new byte[] {2}, EncryptedChunk.NONCE_LENGTH_BYTES);
+ private static final byte[] ENCRYPTED_BYTES_1 =
+ Arrays.copyOf(new byte[] {3}, EncryptedChunk.KEY_LENGTH_BYTES);
+
+ private static final byte[] CHUNK_HASH_2_BYTES =
+ Arrays.copyOf(new byte[] {4}, ChunkHash.HASH_LENGTH_BYTES);
+ private static final byte[] NONCE_2 =
+ Arrays.copyOf(new byte[] {5}, EncryptedChunk.NONCE_LENGTH_BYTES);
+ private static final byte[] ENCRYPTED_BYTES_2 =
+ Arrays.copyOf(new byte[] {6}, EncryptedChunk.KEY_LENGTH_BYTES);
+
+ @Test
+ public void testCreate_withIncorrectLength_throwsException() {
+ ChunkHash chunkHash = new ChunkHash(CHUNK_HASH_1_BYTES);
+ byte[] shortNonce = Arrays.copyOf(new byte[] {2}, EncryptedChunk.NONCE_LENGTH_BYTES - 1);
+
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> EncryptedChunk.create(chunkHash, shortNonce, ENCRYPTED_BYTES_1));
+ }
+
+ @Test
+ public void testEncryptedBytes_forNewlyCreatedObject_returnsCorrectValue() {
+ ChunkHash chunkHash = new ChunkHash(CHUNK_HASH_1_BYTES);
+ EncryptedChunk encryptedChunk =
+ EncryptedChunk.create(chunkHash, NONCE_1, ENCRYPTED_BYTES_1);
+
+ byte[] returnedBytes = encryptedChunk.encryptedBytes();
+
+ assertThat(returnedBytes)
+ .asList()
+ .containsExactlyElementsIn(Bytes.asList(ENCRYPTED_BYTES_1))
+ .inOrder();
+ }
+
+ @Test
+ public void testKey_forNewlyCreatedObject_returnsCorrectValue() {
+ ChunkHash chunkHash = new ChunkHash(CHUNK_HASH_1_BYTES);
+ EncryptedChunk encryptedChunk =
+ EncryptedChunk.create(chunkHash, NONCE_1, ENCRYPTED_BYTES_1);
+
+ ChunkHash returnedKey = encryptedChunk.key();
+
+ assertThat(returnedKey).isEqualTo(chunkHash);
+ }
+
+ @Test
+ public void testNonce_forNewlycreatedObject_returnCorrectValue() {
+ ChunkHash chunkHash = new ChunkHash(CHUNK_HASH_1_BYTES);
+ EncryptedChunk encryptedChunk =
+ EncryptedChunk.create(chunkHash, NONCE_1, ENCRYPTED_BYTES_1);
+
+ byte[] returnedNonce = encryptedChunk.nonce();
+
+ assertThat(returnedNonce).asList().containsExactlyElementsIn(Bytes.asList(NONCE_1));
+ }
+
+ @Test
+ public void testEquals() {
+ ChunkHash chunkHash1 = new ChunkHash(CHUNK_HASH_1_BYTES);
+ ChunkHash equalChunkHash1 = new ChunkHash(CHUNK_HASH_1_BYTES);
+ ChunkHash chunkHash2 = new ChunkHash(CHUNK_HASH_2_BYTES);
+ EncryptedChunk encryptedChunk1 =
+ EncryptedChunk.create(chunkHash1, NONCE_1, ENCRYPTED_BYTES_1);
+ EncryptedChunk equalEncryptedChunk1 =
+ EncryptedChunk.create(equalChunkHash1, NONCE_1, ENCRYPTED_BYTES_1);
+ EncryptedChunk encryptedChunk2 =
+ EncryptedChunk.create(chunkHash2, NONCE_2, ENCRYPTED_BYTES_2);
+
+ assertThat(encryptedChunk1).isEqualTo(equalEncryptedChunk1);
+ assertThat(encryptedChunk1).isNotEqualTo(encryptedChunk2);
+ }
+
+ @Test
+ public void testHashCode() {
+ ChunkHash chunkHash1 = new ChunkHash(CHUNK_HASH_1_BYTES);
+ ChunkHash equalChunkHash1 = new ChunkHash(CHUNK_HASH_1_BYTES);
+ ChunkHash chunkHash2 = new ChunkHash(CHUNK_HASH_2_BYTES);
+ EncryptedChunk encryptedChunk1 =
+ EncryptedChunk.create(chunkHash1, NONCE_1, ENCRYPTED_BYTES_1);
+ EncryptedChunk equalEncryptedChunk1 =
+ EncryptedChunk.create(equalChunkHash1, NONCE_1, ENCRYPTED_BYTES_1);
+ EncryptedChunk encryptedChunk2 =
+ EncryptedChunk.create(chunkHash2, NONCE_2, ENCRYPTED_BYTES_2);
+
+ int hash1 = encryptedChunk1.hashCode();
+ int equalHash1 = equalEncryptedChunk1.hashCode();
+ int hash2 = encryptedChunk2.hashCode();
+
+ assertThat(hash1).isEqualTo(equalHash1);
+ assertThat(hash1).isNotEqualTo(hash2);
+ }
+}
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java b/services/robotests/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java
new file mode 100644
index 0000000..b162557
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2018 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.backup.encryption.chunking;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+
+import android.platform.test.annotations.Presubmit;
+import com.android.server.backup.encryption.chunk.ChunkHash;
+import com.android.server.backup.encryption.chunk.ChunksMetadataProto;
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderPackages;
+import java.util.Arrays;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.robolectric.annotation.Config;
+
+@RunWith(FrameworkRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, sdk = 26)
+@SystemLoaderPackages({"com.android.server.backup"})
+@Presubmit
+public class InlineLengthsEncryptedChunkEncoderTest {
+
+ private static final byte[] TEST_NONCE =
+ Arrays.copyOf(new byte[] {1}, EncryptedChunk.NONCE_LENGTH_BYTES);
+ private static final byte[] TEST_KEY_DATA =
+ Arrays.copyOf(new byte[] {2}, EncryptedChunk.KEY_LENGTH_BYTES);
+ private static final byte[] TEST_DATA = {5, 4, 5, 7, 10, 12, 1, 2, 9};
+
+ @Mock private BackupWriter mMockBackupWriter;
+ private ChunkHash mTestKey;
+ private EncryptedChunk mTestChunk;
+ private EncryptedChunkEncoder mEncoder;
+
+ @Before
+ public void setUp() throws Exception {
+ mMockBackupWriter = mock(BackupWriter.class);
+ mTestKey = new ChunkHash(TEST_KEY_DATA);
+ mTestChunk = EncryptedChunk.create(mTestKey, TEST_NONCE, TEST_DATA);
+ mEncoder = new InlineLengthsEncryptedChunkEncoder();
+ }
+
+ @Test
+ public void writeChunkToWriter_writesLengthThenNonceThenData() throws Exception {
+ mEncoder.writeChunkToWriter(mMockBackupWriter, mTestChunk);
+
+ InOrder inOrder = inOrder(mMockBackupWriter);
+ inOrder.verify(mMockBackupWriter)
+ .writeBytes(
+ InlineLengthsEncryptedChunkEncoder.toByteArray(
+ TEST_NONCE.length + TEST_DATA.length));
+ inOrder.verify(mMockBackupWriter).writeBytes(TEST_NONCE);
+ inOrder.verify(mMockBackupWriter).writeBytes(TEST_DATA);
+ }
+
+ @Test
+ public void getEncodedLengthOfChunk_returnsSumOfNonceAndDataLengths() {
+ int encodedLength = mEncoder.getEncodedLengthOfChunk(mTestChunk);
+
+ assertThat(encodedLength).isEqualTo(Integer.BYTES + TEST_NONCE.length + TEST_DATA.length);
+ }
+
+ @Test
+ public void getChunkOrderingType_returnsExplicitStartsType() {
+ assertThat(mEncoder.getChunkOrderingType()).isEqualTo(ChunksMetadataProto.INLINE_LENGTHS);
+ }
+}
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java b/services/robotests/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java
new file mode 100644
index 0000000..b61dbe9
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2018 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.backup.encryption.chunking;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+
+import android.platform.test.annotations.Presubmit;
+import com.android.server.backup.encryption.chunk.ChunkHash;
+import com.android.server.backup.encryption.chunk.ChunksMetadataProto;
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderPackages;
+import java.util.Arrays;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.robolectric.annotation.Config;
+
+@RunWith(FrameworkRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, sdk = 26)
+@SystemLoaderPackages({"com.android.server.backup"})
+@Presubmit
+public class LengthlessEncryptedChunkEncoderTest {
+ private static final byte[] TEST_NONCE =
+ Arrays.copyOf(new byte[] {1}, EncryptedChunk.NONCE_LENGTH_BYTES);
+ private static final byte[] TEST_KEY_DATA =
+ Arrays.copyOf(new byte[] {2}, EncryptedChunk.KEY_LENGTH_BYTES);
+ private static final byte[] TEST_DATA = {5, 4, 5, 7, 10, 12, 1, 2, 9};
+
+ @Mock private BackupWriter mMockBackupWriter;
+ private ChunkHash mTestKey;
+ private EncryptedChunk mTestChunk;
+ private EncryptedChunkEncoder mEncoder;
+
+ @Before
+ public void setUp() throws Exception {
+ mMockBackupWriter = mock(BackupWriter.class);
+ mTestKey = new ChunkHash(TEST_KEY_DATA);
+ mTestChunk = EncryptedChunk.create(mTestKey, TEST_NONCE, TEST_DATA);
+ mEncoder = new LengthlessEncryptedChunkEncoder();
+ }
+
+ @Test
+ public void writeChunkToWriter_writesNonceThenData() throws Exception {
+ mEncoder.writeChunkToWriter(mMockBackupWriter, mTestChunk);
+
+ InOrder inOrder = inOrder(mMockBackupWriter);
+ inOrder.verify(mMockBackupWriter).writeBytes(TEST_NONCE);
+ inOrder.verify(mMockBackupWriter).writeBytes(TEST_DATA);
+ }
+
+ @Test
+ public void getEncodedLengthOfChunk_returnsSumOfNonceAndDataLengths() {
+ int encodedLength = mEncoder.getEncodedLengthOfChunk(mTestChunk);
+
+ assertThat(encodedLength).isEqualTo(TEST_NONCE.length + TEST_DATA.length);
+ }
+
+ @Test
+ public void getChunkOrderingType_returnsExplicitStartsType() throws Exception {
+ assertThat(mEncoder.getChunkOrderingType()).isEqualTo(ChunksMetadataProto.EXPLICIT_STARTS);
+ }
+}
diff --git a/services/tests/runtests.py b/services/tests/runtests.py
index 7980dc2..f19cc5d 100755
--- a/services/tests/runtests.py
+++ b/services/tests/runtests.py
@@ -22,8 +22,7 @@
'android.support.test.runner.AndroidJUnitRunner')
PACKAGE_WHITELIST = (
- 'android.net',
- 'com.android.server.connectivity',
+ "com.android.server",
)
COLOR_RED = '\033[0;31m'
@@ -37,14 +36,27 @@
COLOR_NONE)
return subprocess.check_call(shell_command, shell=True)
-
+# usage:
+# ${ANDROID_BUILD_TOP}/frameworks/base/services/tests/runtests.py : run tests in com.android.server
+# ${ANDROID_BUILD_TOP}/frameworks/base/services/tests/runtests.py -e package [package name, e.g. com.android.server]
+# ${ANDROID_BUILD_TOP}/frameworks/base/services/tests/runtests.py -e class [class name, e.g. com.android.server.MountServiceTests]
+#
+# The available INSTRUMENTED_PACKAGE_RUNNER may differ in different environments.
+# In this case, use "adb shell pm list instrumentation" to query available runners
+# and use the environment variable INSTRUMENTED_PACKAGE_RUNNER to overwrite
+# the default one, e.g.,
+# INSTRUMENTED_PACKAGE_RUNNER=com.android.frameworks.servicestests/androidx.test.runner.AndroidJUnitRunner \
+# ${ANDROID_BUILD_TOP}/frameworks/base/services/tests/runtests.py
+#
def main():
build_top = os.environ.get('ANDROID_BUILD_TOP', None)
out_dir = os.environ.get('OUT', None)
+ runner = os.environ.get('INSTRUMENTED_PACKAGE_RUNNER', None)
if build_top is None or out_dir is None:
print 'You need to source and lunch before you can use this script'
return 1
-
+ if runner is None:
+ runner = INSTRUMENTED_PACKAGE_RUNNER
print 'Building tests...'
run('make -j32 -C %s -f build/core/main.mk '
'MODULES-IN-frameworks-base-services-tests-servicestests' % build_top,
@@ -57,19 +69,19 @@
apk_path = (
'%s/data/app/FrameworksServicesTests/FrameworksServicesTests.apk' %
out_dir)
- run('adb install -r -g "%s"' % apk_path)
+ run('adb install -t -r -g "%s"' % apk_path)
print 'Running tests...'
if len(sys.argv) != 1:
run('adb shell am instrument -w %s "%s"' %
- (' '.join(sys.argv[1:]), INSTRUMENTED_PACKAGE_RUNNER))
+ (' '.join(sys.argv[1:]), runner))
return 0
# It would be nice if the activity manager accepted a list of packages, but
# in lieu of that...
for package in PACKAGE_WHITELIST:
run('adb shell am instrument -w -e package %s %s' %
- (package, INSTRUMENTED_PACKAGE_RUNNER))
+ (package, runner))
return 0
diff --git a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
index 2ec6830..4fbc587 100644
--- a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
@@ -73,6 +73,9 @@
private static final int IGNORED_ACTION = 13;
private static final int IGNORED_CODE = 1999;
private static final int IGNORED_REPEAT = 42;
+ private static final int IGNORED_META_STATE = 0;
+ private static final int IGNORED_DEVICE_ID = 0;
+ private static final int IGNORED_SCANCODE = 0;
private @Mock Context mContext;
private @Mock Resources mResources;
@@ -369,6 +372,50 @@
}
@Test
+ public void testInterceptPowerKeyDown_longpress() {
+ withCameraDoubleTapPowerEnableConfigValue(true);
+ withCameraDoubleTapPowerDisableSettingValue(0);
+ mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
+ withUserSetupCompleteValue(true);
+
+ long eventTime = INITIAL_EVENT_TIME_MILLIS;
+ KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+ IGNORED_REPEAT);
+ boolean interactive = true;
+ MutableBoolean outLaunched = new MutableBoolean(true);
+ boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+ outLaunched);
+ assertFalse(intercepted);
+ assertFalse(outLaunched.value);
+
+ final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+ eventTime += interval;
+ keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+ IGNORED_REPEAT, IGNORED_META_STATE, IGNORED_DEVICE_ID, IGNORED_SCANCODE,
+ KeyEvent.FLAG_LONG_PRESS);
+ outLaunched.value = false;
+ intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+ outLaunched);
+ assertFalse(intercepted);
+ assertFalse(outLaunched.value);
+
+ verify(mMetricsLogger, never())
+ .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
+
+ final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
+ verify(mMetricsLogger, times(1)).histogram(
+ eq("power_double_tap_interval"), intervalCaptor.capture());
+ List<Integer> intervals = intervalCaptor.getAllValues();
+ assertEquals((int) INITIAL_EVENT_TIME_MILLIS, intervals.get(0).intValue());
+
+ final ArgumentCaptor<Integer> tapCountCaptor = ArgumentCaptor.forClass(Integer.class);
+ verify(mMetricsLogger, times(1)).histogram(
+ eq("power_consecutive_short_tap_count"), tapCountCaptor.capture());
+ List<Integer> tapCounts = tapCountCaptor.getAllValues();
+ assertEquals(1, tapCounts.get(0).intValue());
+ }
+
+ @Test
public void
testInterceptPowerKeyDown_intervalInBoundsCameraPowerGestureOnInteractiveSetupIncomplete() {
withCameraDoubleTapPowerEnableConfigValue(true);
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityDisplayTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityDisplayTests.java
new file mode 100644
index 0000000..44981b3
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityDisplayTests.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
+
+import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
+
+import static org.junit.Assert.assertTrue;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for the {@link ActivityDisplay} class.
+ *
+ * Build/Install/Run:
+ * atest WmTests:ActivityDisplayTests
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class ActivityDisplayTests extends ActivityTestsBase {
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ setupActivityTaskManagerService();
+ }
+
+ /**
+ * This test simulates the picture-in-picture menu activity launches an activity to fullscreen
+ * stack. The fullscreen stack should be the top focused for resuming correctly.
+ */
+ @Test
+ public void testFullscreenStackCanBeFocusedWhenFocusablePinnedStackExists() {
+ // Create a pinned stack and move to front.
+ final ActivityStack pinnedStack = mSupervisor.getDefaultDisplay().createStack(
+ WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, ON_TOP);
+ final TaskRecord pinnedTask = new TaskBuilder(mService.mStackSupervisor)
+ .setStack(pinnedStack).build();
+ new ActivityBuilder(mService).setActivityFlags(FLAG_ALWAYS_FOCUSABLE)
+ .setTask(pinnedTask).build();
+ pinnedStack.moveToFront("movePinnedStackToFront");
+
+ // The focused stack should be the pinned stack.
+ assertTrue(pinnedStack.isFocusedStackOnDisplay());
+
+ // Create a fullscreen stack and move to front.
+ final ActivityStack fullscreenStack = createFullscreenStackWithSimpleActivityAt(
+ mSupervisor.getDefaultDisplay());
+ fullscreenStack.moveToFront("moveFullscreenStackToFront");
+
+ // The focused stack should be the fullscreen stack.
+ assertTrue(fullscreenStack.isFocusedStackOnDisplay());
+ }
+
+ /**
+ * Test {@link ActivityDisplay#mPreferredTopFocusableStack} will be cleared when the stack is
+ * removed or moved to back, and the focused stack will be according to z-order.
+ */
+ @Test
+ public void testStackShouldNotBeFocusedAfterMovingToBackOrRemoving() {
+ // Create a display which only contains 2 stacks.
+ final ActivityDisplay display = addNewActivityDisplayAt(ActivityDisplay.POSITION_TOP);
+ final ActivityStack stack1 = createFullscreenStackWithSimpleActivityAt(display);
+ final ActivityStack stack2 = createFullscreenStackWithSimpleActivityAt(display);
+
+ // Put stack1 and stack2 on top.
+ stack1.moveToFront("moveStack1ToFront");
+ stack2.moveToFront("moveStack2ToFront");
+ assertTrue(stack2.isFocusedStackOnDisplay());
+
+ // Stack1 should be focused after moving stack2 to back.
+ stack2.moveToBack("moveStack2ToBack", null /* task */);
+ assertTrue(stack1.isFocusedStackOnDisplay());
+
+ // Stack2 should be focused after removing stack1.
+ display.removeChild(stack1);
+ assertTrue(stack2.isFocusedStackOnDisplay());
+ }
+
+ private ActivityStack createFullscreenStackWithSimpleActivityAt(ActivityDisplay display) {
+ final ActivityStack fullscreenStack = display.createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, ON_TOP);
+ final TaskRecord fullscreenTask = new TaskBuilder(mService.mStackSupervisor)
+ .setStack(fullscreenStack).build();
+ new ActivityBuilder(mService).setTask(fullscreenTask).build();
+ return fullscreenStack;
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
index 0345a81..81a0934 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
@@ -33,10 +33,12 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -232,6 +234,7 @@
doReturn(displaySleeping).when(display).isSleeping();
doReturn(keyguardShowing).when(keyguard).isKeyguardOrAodShowing(anyInt());
+ doReturn(isFocusedStack).when(stack).isFocusedStackOnDisplay();
doReturn(isFocusedStack ? stack : null).when(display).getFocusedStack();
mSupervisor.applySleepTokensLocked(true);
verify(stack, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleepingLocked();
@@ -402,4 +405,32 @@
assertEquals(primaryStack.getBounds(), STACK_SIZE);
assertEquals(task.getBounds(), TASK_SIZE);
}
+
+ /**
+ * Verify if a stack is not at the topmost position, it should be able to resume its activity if
+ * the stack is the top focused.
+ */
+ @Test
+ public void testResumeActivityWhenNonTopmostStackIsTopFocused() throws Exception {
+ // Create a stack at bottom.
+ final ActivityDisplay display = mSupervisor.getDefaultDisplay();
+ final ActivityStack targetStack = spy(display.createStack(WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_STANDARD, false /* onTop */));
+ final TaskRecord task = new TaskBuilder(mSupervisor).setStack(targetStack).build();
+ final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build();
+ display.positionChildAtBottom(targetStack);
+
+ // Assume the stack is not at the topmost position (e.g. behind always-on-top stacks) but it
+ // is the current top focused stack.
+ assertFalse(targetStack.isTopStackOnDisplay());
+ doReturn(targetStack).when(mSupervisor).getTopDisplayFocusedStack();
+
+ // Use the stack as target to resume.
+ mSupervisor.resumeFocusedStacksTopActivitiesLocked(
+ targetStack, activity, null /* targetOptions */);
+
+ // Verify the target stack should resume its activity.
+ verify(targetStack, times(1)).resumeTopActivityUncheckedLocked(
+ eq(activity), eq(null /* targetOptions */));
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
index ab814ee..5fcd2aa 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
@@ -38,6 +38,7 @@
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
import android.content.pm.ActivityInfo;
import android.os.UserHandle;
@@ -71,8 +72,8 @@
setupActivityTaskManagerService();
mDefaultDisplay = mSupervisor.getDefaultDisplay();
- mStack = mDefaultDisplay.createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD,
- true /* onTop */);
+ mStack = spy(mDefaultDisplay.createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD,
+ true /* onTop */));
mTask = new TaskBuilder(mSupervisor).setStack(mStack).build();
}
@@ -720,7 +721,7 @@
doReturn(display).when(mSupervisor).getActivityDisplay(anyInt());
doReturn(keyguardGoingAway).when(keyguardController).isKeyguardGoingAway();
doReturn(displaySleeping).when(display).isSleeping();
- doReturn(focusedStack ? mStack : null).when(mSupervisor).getTopDisplayFocusedStack();
+ doReturn(focusedStack).when(mStack).isFocusedStackOnDisplay();
assertEquals(expected, mStack.shouldSleepActivities());
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index 22add01..2008861 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -168,6 +168,9 @@
// Makes sure the supervisor is using with the spy object.
atm.mStackSupervisor.setService(atm);
doReturn(mock(IPackageManager.class)).when(am).getPackageManager();
+ PackageManagerInternal mockPackageManager = mock(PackageManagerInternal.class);
+ doReturn(mockPackageManager).when(am).getPackageManagerInternalLocked();
+ doReturn(null).when(mockPackageManager).getDefaultHomeActivity(anyInt());
doNothing().when(am).grantEphemeralAccessLocked(anyInt(), any(), anyInt(), anyInt());
am.mWindowManager = prepareMockWindowManager();
atm.setWindowManager(am.mWindowManager);
@@ -175,10 +178,9 @@
// Put a home stack on the default display, so that we'll always have something focusable.
final TestActivityStackSupervisor supervisor =
(TestActivityStackSupervisor) atm.mStackSupervisor;
- supervisor.mHomeStack = supervisor.mDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_HOME, ON_TOP);
+ supervisor.mDisplay.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
final TaskRecord task = new TaskBuilder(atm.mStackSupervisor)
- .setStack(supervisor.mHomeStack).build();
+ .setStack(supervisor.getDefaultDisplay().getHomeStack()).build();
new ActivityBuilder(atm).setTask(task).build();
}
@@ -447,9 +449,6 @@
final ActivityStackSupervisor supervisor = spy(createTestSupervisor());
final KeyguardController keyguardController = mock(KeyguardController.class);
- // No home stack is set.
- doNothing().when(supervisor).moveHomeStackToFront(any());
- doReturn(true).when(supervisor).moveHomeStackTaskToTop(any());
// Invoked during {@link ActivityStack} creation.
doNothing().when(supervisor).updateUIDsPresentOnDisplay();
// Always keep things awake.
diff --git a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
index 70cfad1..1276f65 100644
--- a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
@@ -125,7 +125,6 @@
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
mStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- ((MyTestActivityStackSupervisor) mService.mStackSupervisor).setHomeStack(mHomeStack);
mCallbacksRecorder = new CallbacksRecorder();
mRecentTasks.registerCallback(mCallbacksRecorder);
QUIET_USER_INFO.flags = UserInfo.FLAG_MANAGED_PROFILE | UserInfo.FLAG_QUIET_MODE;
@@ -558,9 +557,8 @@
final MyTestActivityStackSupervisor supervisor =
(MyTestActivityStackSupervisor) mService.mStackSupervisor;
- final ActivityStack homeStack = new MyTestActivityStack(mDisplay, supervisor);
+ final ActivityStack homeStack = mDisplay.getHomeStack();
final ActivityStack aboveHomeStack = new MyTestActivityStack(mDisplay, supervisor);
- supervisor.setHomeStack(homeStack);
// Add a number of tasks (beyond the max) but ensure that nothing is trimmed because all
// the tasks belong in stacks above the home stack
@@ -579,9 +577,8 @@
final MyTestActivityStackSupervisor supervisor =
(MyTestActivityStackSupervisor) mService.mStackSupervisor;
final ActivityStack behindHomeStack = new MyTestActivityStack(mDisplay, supervisor);
- final ActivityStack homeStack = new MyTestActivityStack(mDisplay, supervisor);
+ final ActivityStack homeStack = mDisplay.getHomeStack();
final ActivityStack aboveHomeStack = new MyTestActivityStack(mDisplay, supervisor);
- supervisor.setHomeStack(homeStack);
// Add a number of tasks (beyond the max) but ensure that only the task in the stack behind
// the home stack is trimmed once a new task is added
@@ -601,9 +598,8 @@
final MyTestActivityStackSupervisor supervisor =
(MyTestActivityStackSupervisor) mService.mStackSupervisor;
- final ActivityStack homeStack = new MyTestActivityStack(mDisplay, supervisor);
+ final ActivityStack homeStack = mDisplay.getHomeStack();
final ActivityStack otherDisplayStack = new MyTestActivityStack(mOtherDisplay, supervisor);
- supervisor.setHomeStack(homeStack);
// Add a number of tasks (beyond the max) on each display, ensure that the tasks are not
// removed
@@ -870,7 +866,7 @@
@Override
public void initialize() {
super.initialize();
- mDisplay = TestActivityDisplay.create(this, DEFAULT_DISPLAY);
+ mDisplay = getActivityDisplay(DEFAULT_DISPLAY);
mOtherDisplay = TestActivityDisplay.create(this, DEFAULT_DISPLAY + 1);
addChild(mOtherDisplay, ActivityDisplay.POSITION_TOP);
addChild(mDisplay, ActivityDisplay.POSITION_TOP);
@@ -881,10 +877,6 @@
mRunningTasks = new TestRunningTasks();
return mRunningTasks;
}
-
- void setHomeStack(ActivityStack stack) {
- mHomeStack = stack;
- }
}
private class MyTestActivityStack extends TestActivityStack {
diff --git a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
index acd065e..e16f118 100644
--- a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
+++ b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
@@ -16,6 +16,10 @@
package com.android.server.policy;
+import static android.view.DisplayCutout.BOUNDS_POSITION_BOTTOM;
+import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
+import static android.view.DisplayCutout.BOUNDS_POSITION_RIGHT;
+import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_270;
@@ -179,8 +183,25 @@
transformPhysicalToLogicalCoordinates(rotation, DISPLAY_WIDTH, DISPLAY_HEIGHT, m);
m.mapRect(rectF);
+ int pos = -1;
+ switch (rotation) {
+ case ROTATION_0:
+ pos = BOUNDS_POSITION_TOP;
+ break;
+ case ROTATION_90:
+ pos = BOUNDS_POSITION_LEFT;
+ break;
+ case ROTATION_180:
+ pos = BOUNDS_POSITION_BOTTOM;
+ break;
+ case ROTATION_270:
+ pos = BOUNDS_POSITION_RIGHT;
+ break;
+ }
+
+
return DisplayCutout.fromBoundingRect((int) rectF.left, (int) rectF.top,
- (int) rectF.right, (int) rectF.bottom);
+ (int) rectF.right, (int) rectF.bottom, pos);
}
static class TestContextWrapper extends ContextWrapper {
diff --git a/services/tests/servicestests/src/com/android/server/textservices/LocaleUtilsTest.java b/services/tests/servicestests/src/com/android/server/textservices/LocaleUtilsTest.java
index 3766d24..ef945e6 100644
--- a/services/tests/servicestests/src/com/android/server/textservices/LocaleUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/textservices/LocaleUtilsTest.java
@@ -19,11 +19,17 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
import org.junit.Test;
+import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.Locale;
+@SmallTest
+@RunWith(AndroidJUnit4.class)
public class LocaleUtilsTest {
private static final Locale LOCALE_EN = new Locale("en");
private static final Locale LOCALE_EN_US = new Locale("en", "US");
diff --git a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
index 12be0b3..e6e08bb 100644
--- a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
@@ -90,7 +90,7 @@
private AppTransitionListener mListener;
MockAppTransition(Context context) {
- super(context, null);
+ super(context, sWm);
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
index b330304..3dcdd23 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
@@ -24,6 +24,8 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
+import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
import static android.view.DisplayCutout.fromBoundingRect;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
@@ -32,6 +34,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
+import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
@@ -303,7 +306,8 @@
createTapEvent(dm0.widthPixels / 2, dm0.heightPixels / 2, false));
// Check focus is on primary display.
- assertEquals(sWm.mCurrentFocus, dc0.findFocusedWindow());
+ assertEquals(sWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus,
+ dc0.findFocusedWindow());
// Tap on secondary display
DisplayMetrics dm1 = dc1.getDisplayMetrics();
@@ -313,7 +317,8 @@
createTapEvent(dm1.widthPixels / 2, dm1.heightPixels / 2, false));
// Check focus is on secondary.
- assertEquals(sWm.mCurrentFocus, dc1.findFocusedWindow());
+ assertEquals(sWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus,
+ dc1.findFocusedWindow());
}
@Test
@@ -321,34 +326,29 @@
// Create a focusable window and check that focus is calculated correctly
final WindowState window1 =
createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "window1");
- assertEquals(window1, sWm.mRoot.computeFocusedWindow());
+ updateFocusedWindow();
+ assertTrue(window1.isFocused());
+ assertEquals(window1, sWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
// Check that a new display doesn't affect focus
final DisplayContent dc = createNewDisplay();
- assertEquals(window1, sWm.mRoot.computeFocusedWindow());
+ updateFocusedWindow();
+ assertTrue(window1.isFocused());
+ assertEquals(window1, sWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
// Add a window to the second display, and it should be focused
final WindowState window2 = createWindow(null, TYPE_BASE_APPLICATION, dc, "window2");
- assertEquals(window2, sWm.mRoot.computeFocusedWindow());
+ updateFocusedWindow();
+ assertTrue(window1.isFocused());
+ assertTrue(window2.isFocused());
+ assertEquals(window2, sWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
// Move the first window to the to including parents, and make sure focus is updated
window1.getParent().positionChildAt(POSITION_TOP, window1, true);
- assertEquals(window1, sWm.mRoot.computeFocusedWindow());
- }
-
- @Test
- public void testKeyguard_preventsSecondaryDisplayFocus() throws Exception {
- final WindowState keyguard = createWindow(null, TYPE_STATUS_BAR,
- sWm.getDefaultDisplayContentLocked(), "keyguard");
- assertEquals(keyguard, sWm.mRoot.computeFocusedWindow());
-
- // Add a window to a second display, and it should be focused
- final DisplayContent dc = createNewDisplay();
- final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, dc, "win");
- assertEquals(win, sWm.mRoot.computeFocusedWindow());
-
- mWmRule.getWindowManagerPolicy().keyguardShowingAndNotOccluded = true;
- assertEquals(keyguard, sWm.mRoot.computeFocusedWindow());
+ updateFocusedWindow();
+ assertTrue(window1.isFocused());
+ assertTrue(window2.isFocused());
+ assertEquals(window1, sWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
}
/**
@@ -454,7 +454,7 @@
dc.mInitialDisplayHeight = 400;
Rect r = new Rect(80, 0, 120, 10);
final DisplayCutout cutout = new WmDisplayCutout(
- fromBoundingRect(r.left, r.top, r.right, r.bottom), null)
+ fromBoundingRect(r.left, r.top, r.right, r.bottom, BOUNDS_POSITION_TOP), null)
.computeSafeInsets(200, 400).getDisplayCutout();
dc.mInitialDisplayCutout = cutout;
@@ -484,7 +484,7 @@
final Rect r1 = new Rect(left, top, right, bottom);
final DisplayCutout cutout = new WmDisplayCutout(
- fromBoundingRect(r1.left, r1.top, r1.right, r1.bottom), null)
+ fromBoundingRect(r1.left, r1.top, r1.right, r1.bottom, BOUNDS_POSITION_TOP), null)
.computeSafeInsets(displayWidth, displayHeight).getDisplayCutout();
dc.mInitialDisplayCutout = cutout;
@@ -501,7 +501,7 @@
// | | -------------
final Rect r = new Rect(top, left, bottom, right);
assertEquals(new WmDisplayCutout(
- fromBoundingRect(r.left, r.top, r.right, r.bottom), null)
+ fromBoundingRect(r.left, r.top, r.right, r.bottom, BOUNDS_POSITION_LEFT), null)
.computeSafeInsets(displayHeight, displayWidth)
.getDisplayCutout(), dc.getDisplayInfo().displayCutout);
}
@@ -590,6 +590,12 @@
assertEquals(displayContent.mBaseDisplayDensity, expectedBaseDensity);
}
+ private void updateFocusedWindow() {
+ synchronized (sWm.mWindowMap) {
+ sWm.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false);
+ }
+ }
+
/**
* Create DisplayContent that does not update display base/initial values from device to keep
* the values set by test.
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
index 0886729..7cd1314 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
@@ -16,12 +16,12 @@
package com.android.server.wm;
+import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
import static android.view.DisplayCutout.fromBoundingRect;
import static android.view.WindowManager.LayoutParams.FILL_PARENT;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
import android.app.ActivityManager.TaskDescription;
import android.content.res.Configuration;
@@ -474,7 +474,8 @@
final Rect pf = new Rect(0, 0, 1000, 2000);
// Create a display cutout of size 50x50, aligned top-center
final WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
- fromBoundingRect(500, 0, 550, 50), pf.width(), pf.height());
+ fromBoundingRect(500, 0, 550, 50, BOUNDS_POSITION_TOP),
+ pf.width(), pf.height());
final WindowFrames windowFrames = w.getWindowFrames();
windowFrames.setFrames(pf, pf, pf, pf, pf, pf, pf, pf);
@@ -499,7 +500,8 @@
final Rect pf = new Rect(0, -500, 1000, 1500);
// Create a display cutout of size 50x50, aligned top-center
final WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
- fromBoundingRect(500, 0, 550, 50), pf.width(), pf.height());
+ fromBoundingRect(500, 0, 550, 50, BOUNDS_POSITION_TOP),
+ pf.width(), pf.height());
final WindowFrames windowFrames = w.getWindowFrames();
windowFrames.setFrames(pf, pf, pf, pf, pf, pf, pf, pf);
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
index b7cc9ce..3637baf 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
@@ -49,6 +49,7 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import android.graphics.Insets;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
@@ -404,8 +405,12 @@
WindowFrames wf = app.getWindowFrames();
wf.mParentFrame.set(7, 10, 185, 380);
wf.mDisplayFrame.set(wf.mParentFrame);
- final DisplayCutout cutout = new DisplayCutout(new Rect(0, 15, 0, 22),
- Arrays.asList(new Rect(95, 0, 105, 15), new Rect(95, 378, 105, 400)));
+ final DisplayCutout cutout = new DisplayCutout(
+ Insets.of(0, 15, 0, 22) /* safeInset */,
+ null /* boundLeft */,
+ new Rect(95, 0, 105, 15),
+ null /* boundRight */,
+ new Rect(95, 378, 105, 400));
wf.setDisplayCutout(new WmDisplayCutout(cutout, new Size(200, 400)));
app.computeFrameLw();
diff --git a/services/tests/servicestests/src/com/android/server/wm/utils/DisplayRotationUtilTest.java b/services/tests/servicestests/src/com/android/server/wm/utils/DisplayRotationUtilTest.java
new file mode 100644
index 0000000..ba8869b
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/utils/DisplayRotationUtilTest.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2018 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.wm.utils;
+
+import static android.view.DisplayCutout.BOUNDS_POSITION_BOTTOM;
+import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
+import static android.view.DisplayCutout.BOUNDS_POSITION_RIGHT;
+import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_180;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+import static com.android.server.wm.utils.DisplayRotationUtil.getBoundIndexFromRotation;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.junit.Assert.assertThat;
+
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+
+
+/**
+ * Tests for {@link DisplayRotationUtil}
+ *
+ * Run with: atest DisplayRotationUtilTest
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class DisplayRotationUtilTest {
+ private static Rect ZERO_RECT = new Rect();
+
+ @Test
+ public void testGetBoundIndexFromRotation_rot0() {
+ assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_LEFT, ROTATION_0),
+ equalTo(BOUNDS_POSITION_LEFT));
+ assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_TOP, ROTATION_0),
+ equalTo(BOUNDS_POSITION_TOP));
+ assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_RIGHT, ROTATION_0),
+ equalTo(BOUNDS_POSITION_RIGHT));
+ assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_BOTTOM, ROTATION_0),
+ equalTo(BOUNDS_POSITION_BOTTOM));
+ }
+
+ @Test
+ public void testGetBoundIndexFromRotation_rot90() {
+ assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_LEFT, ROTATION_90),
+ equalTo(BOUNDS_POSITION_BOTTOM));
+ assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_TOP, ROTATION_90),
+ equalTo(BOUNDS_POSITION_LEFT));
+ assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_RIGHT, ROTATION_90),
+ equalTo(BOUNDS_POSITION_TOP));
+ assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_BOTTOM, ROTATION_90),
+ equalTo(BOUNDS_POSITION_RIGHT));
+ }
+
+ @Test
+ public void testGetBoundIndexFromRotation_rot180() {
+ assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_LEFT, ROTATION_180),
+ equalTo(BOUNDS_POSITION_RIGHT));
+ assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_TOP, ROTATION_180),
+ equalTo(BOUNDS_POSITION_BOTTOM));
+ assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_RIGHT, ROTATION_180),
+ equalTo(BOUNDS_POSITION_LEFT));
+ assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_BOTTOM, ROTATION_180),
+ equalTo(BOUNDS_POSITION_TOP));
+ }
+
+ @Test
+ public void testGetBoundIndexFromRotation_rot270() {
+ assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_LEFT, ROTATION_270),
+ equalTo(BOUNDS_POSITION_TOP));
+ assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_TOP, ROTATION_270),
+ equalTo(BOUNDS_POSITION_RIGHT));
+ assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_RIGHT, ROTATION_270),
+ equalTo(BOUNDS_POSITION_BOTTOM));
+ assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_BOTTOM, ROTATION_270),
+ equalTo(BOUNDS_POSITION_LEFT));
+
+ }
+
+ @Test
+ public void testGetRotatedBounds_top_rot0() {
+ DisplayRotationUtil util = new DisplayRotationUtil();
+ Rect[] bounds = new Rect[] { ZERO_RECT, new Rect(50,0,150,10), ZERO_RECT, ZERO_RECT };
+ assertThat(util.getRotatedBounds(bounds, ROTATION_0, 200, 300),
+ equalTo(bounds));
+ }
+
+ @Test
+ public void testGetRotatedBounds_top_rot90() {
+ DisplayRotationUtil util = new DisplayRotationUtil();
+ Rect[] bounds = new Rect[] { ZERO_RECT, new Rect(50,0,150,10), ZERO_RECT, ZERO_RECT };
+ assertThat(util.getRotatedBounds(bounds, ROTATION_90, 200, 300),
+ equalTo(new Rect[] { new Rect(0, 50, 10, 150), ZERO_RECT, ZERO_RECT, ZERO_RECT }));
+ }
+
+ @Test
+ public void testGetRotatedBounds_top_rot180() {
+ DisplayRotationUtil util = new DisplayRotationUtil();
+ Rect[] bounds = new Rect[] { ZERO_RECT, new Rect(50,0,150,10), ZERO_RECT, ZERO_RECT };
+ assertThat(util.getRotatedBounds(bounds, ROTATION_180, 200, 300),
+ equalTo(new Rect[] { ZERO_RECT, ZERO_RECT, ZERO_RECT, new Rect(50, 290, 150, 300) }));
+ }
+
+ @Test
+ public void testGetRotatedBounds_top_rot270() {
+ DisplayRotationUtil util = new DisplayRotationUtil();
+ Rect[] bounds = new Rect[] { ZERO_RECT, new Rect(50,0,150,10), ZERO_RECT, ZERO_RECT };
+ assertThat(util.getRotatedBounds(bounds, ROTATION_270, 200, 300),
+ equalTo(new Rect[] { ZERO_RECT, ZERO_RECT, new Rect(290, 50, 300, 150), ZERO_RECT }));
+ }
+
+ @Test
+ public void testGetRotatedBounds_left_rot0() {
+ DisplayRotationUtil util = new DisplayRotationUtil();
+ Rect[] bounds = new Rect[] { new Rect(0, 50, 10, 150), ZERO_RECT, ZERO_RECT, ZERO_RECT };
+ assertThat(util.getRotatedBounds(bounds, ROTATION_0, 300, 200),
+ equalTo(bounds));
+ }
+
+ @Test
+ public void testGetRotatedBounds_left_rot90() {
+ DisplayRotationUtil util = new DisplayRotationUtil();
+ Rect[] bounds = new Rect[] { new Rect(0, 50, 10, 150), ZERO_RECT, ZERO_RECT, ZERO_RECT };
+ assertThat(util.getRotatedBounds(bounds, ROTATION_90, 300, 200),
+ equalTo(new Rect[]{ ZERO_RECT, ZERO_RECT, ZERO_RECT, new Rect(50, 290, 150, 300) }));
+ }
+
+ @Test
+ public void testGetRotatedBounds_left_rot180() {
+ DisplayRotationUtil util = new DisplayRotationUtil();
+ Rect[] bounds = new Rect[] { new Rect(0, 50, 10, 150), ZERO_RECT, ZERO_RECT, ZERO_RECT };
+ assertThat(util.getRotatedBounds(bounds, ROTATION_180, 300, 200),
+ equalTo(new Rect[]{ ZERO_RECT, ZERO_RECT, new Rect(290, 50, 300, 150), ZERO_RECT }));
+ }
+
+ @Test
+ public void testGetRotatedBounds_left_rot270() {
+ DisplayRotationUtil util = new DisplayRotationUtil();
+ Rect[] bounds = new Rect[] { new Rect(0, 50, 10, 150), ZERO_RECT, ZERO_RECT, ZERO_RECT };
+ assertThat(util.getRotatedBounds(bounds, ROTATION_270, 300, 200),
+ equalTo(new Rect[]{ ZERO_RECT, new Rect(50, 0, 150, 10), ZERO_RECT, ZERO_RECT }));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java b/services/tests/servicestests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java
index 9ce3dca..c5e35e7 100644
--- a/services/tests/servicestests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java
@@ -18,11 +18,19 @@
import static android.view.DisplayCutout.NO_CUTOUT;
+import static android.view.DisplayCutout.BOUNDS_POSITION_BOTTOM;
+import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
+import static android.view.DisplayCutout.BOUNDS_POSITION_RIGHT;
+import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
import static android.view.DisplayCutout.fromBoundingRect;
+import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertThat;
+
+import android.graphics.Insets;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.util.Size;
@@ -45,15 +53,17 @@
@SmallTest
@Presubmit
public class WmDisplayCutoutTest {
+ private static final Rect ZERO_RECT = new Rect();
private final DisplayCutout mCutoutTop = new DisplayCutout(
- new Rect(0, 100, 0, 0),
- Arrays.asList(new Rect(50, 0, 75, 100)));
+ Insets.of(0, 100, 0, 0),
+ null /* boundLeft */, new Rect(50, 0, 75, 100) /* boundTop */,
+ null /* boundRight */, null /* boundBottom */);
@Test
public void calculateRelativeTo_top() {
WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
- fromBoundingRect(0, 0, 100, 20), 200, 400)
+ fromBoundingRect(0, 0, 100, 20, BOUNDS_POSITION_TOP), 200, 400)
.calculateRelativeTo(new Rect(5, 5, 95, 195));
assertEquals(new Rect(0, 15, 0, 0), cutout.getDisplayCutout().getSafeInsets());
@@ -62,7 +72,7 @@
@Test
public void calculateRelativeTo_left() {
WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
- fromBoundingRect(0, 0, 20, 100), 400, 200)
+ fromBoundingRect(0, 0, 20, 100, BOUNDS_POSITION_LEFT), 400, 200)
.calculateRelativeTo(new Rect(5, 5, 195, 95));
assertEquals(new Rect(15, 0, 0, 0), cutout.getDisplayCutout().getSafeInsets());
@@ -71,7 +81,7 @@
@Test
public void calculateRelativeTo_bottom() {
WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
- fromBoundingRect(0, 180, 100, 200), 100, 200)
+ fromBoundingRect(0, 180, 100, 200, BOUNDS_POSITION_BOTTOM), 100, 200)
.calculateRelativeTo(new Rect(5, 5, 95, 195));
assertEquals(new Rect(0, 0, 0, 15), cutout.getDisplayCutout().getSafeInsets());
@@ -80,7 +90,7 @@
@Test
public void calculateRelativeTo_right() {
WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
- fromBoundingRect(180, 0, 200, 100), 200, 100)
+ fromBoundingRect(180, 0, 200, 100, BOUNDS_POSITION_RIGHT), 200, 100)
.calculateRelativeTo(new Rect(5, 5, 195, 95));
assertEquals(new Rect(0, 0, 15, 0), cutout.getDisplayCutout().getSafeInsets());
@@ -89,16 +99,17 @@
@Test
public void calculateRelativeTo_bounds() {
WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
- fromBoundingRect(0, 0, 100, 20), 200, 400)
+ fromBoundingRect(0, 0, 100, 20, BOUNDS_POSITION_TOP), 200, 400)
.calculateRelativeTo(new Rect(5, 10, 95, 180));
- assertEquals(new Rect(-5, -10, 95, 10), cutout.getDisplayCutout().getBounds().getBounds());
+ assertThat(cutout.getDisplayCutout().getBoundingRectTop(),
+ equalTo(new Rect(-5, -10, 95, 10)));
}
@Test
public void computeSafeInsets_top() {
WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
- fromBoundingRect(0, 0, 100, 20), 200, 400);
+ fromBoundingRect(0, 0, 100, 20, BOUNDS_POSITION_TOP), 200, 400);
assertEquals(new Rect(0, 20, 0, 0), cutout.getDisplayCutout().getSafeInsets());
}
@@ -106,7 +117,7 @@
@Test
public void computeSafeInsets_left() {
WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
- fromBoundingRect(0, 0, 20, 100), 400, 200);
+ fromBoundingRect(0, 0, 20, 100, BOUNDS_POSITION_LEFT), 400, 200);
assertEquals(new Rect(20, 0, 0, 0), cutout.getDisplayCutout().getSafeInsets());
}
@@ -114,7 +125,7 @@
@Test
public void computeSafeInsets_bottom() {
WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
- fromBoundingRect(0, 180, 100, 200), 100, 200);
+ fromBoundingRect(0, 180, 100, 200, BOUNDS_POSITION_BOTTOM), 100, 200);
assertEquals(new Rect(0, 0, 0, 20), cutout.getDisplayCutout().getSafeInsets());
}
@@ -122,7 +133,7 @@
@Test
public void computeSafeInsets_right() {
WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
- fromBoundingRect(180, 0, 200, 100), 200, 100);
+ fromBoundingRect(180, 0, 200, 100, BOUNDS_POSITION_RIGHT), 200, 100);
assertEquals(new Rect(0, 0, 20, 0), cutout.getDisplayCutout().getSafeInsets());
}
@@ -132,8 +143,7 @@
DisplayCutout cutout = WmDisplayCutout.computeSafeInsets(mCutoutTop, 1000,
2000).getDisplayCutout();
- assertEquals(mCutoutTop.getBounds().getBounds(),
- cutout.getBounds().getBounds());
+ assertEquals(mCutoutTop.getBoundingRects(), cutout.getBoundingRects());
}
@Test
diff --git a/startop/iorap/Android.bp b/startop/iorap/Android.bp
index 91f6aac..b3b0900 100644
--- a/startop/iorap/Android.bp
+++ b/startop/iorap/Android.bp
@@ -26,26 +26,3 @@
"**/*.java",
],
}
-
-android_test {
- name: "libiorap-java-tests",
- srcs: ["tests/src/**/*.kt"],
-
- static_libs: [
- // non-test dependencies
- "libiorap-java",
- // test android dependencies
- "platform-test-annotations",
- "android-support-test",
- // test framework dependencies
- "mockito-target-inline-minus-junit4",
- "truth-prebuilt",
- ],
-
- //sdk_version: "current",
- //certificate: "platform",
-
- libs: ["android.test.base"],
-
- test_suites: ["device-tests"],
-}
diff --git a/startop/iorap/tests/Android.bp b/startop/iorap/tests/Android.bp
new file mode 100644
index 0000000..7605784
--- /dev/null
+++ b/startop/iorap/tests/Android.bp
@@ -0,0 +1,40 @@
+// Copyright (C) 2018 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.
+
+// TODO: once b/80095087 is fixed, rewrite this back to android_test
+java_library {
+ name: "libiorap-java-test-lib",
+ srcs: ["src/**/*.kt"],
+
+ static_libs: [
+ // non-test dependencies
+ "libiorap-java",
+ // test android dependencies
+ "platform-test-annotations",
+ "android-support-test",
+ // test framework dependencies
+ "mockito-target-inline-minus-junit4",
+ // "mockito-target-minus-junit4",
+ // Mockito also requires JNI (see Android.mk)
+ // and android:debuggable=true (see AndroidManifest.xml)
+ "truth-prebuilt",
+ ],
+
+ // sdk_version: "current",
+ // certificate: "platform",
+
+ libs: ["android.test.base", "android.test.runner"],
+
+ // test_suites: ["device-tests"],
+}
diff --git a/startop/iorap/tests/Android.mk b/startop/iorap/tests/Android.mk
new file mode 100644
index 0000000..1b2aa46
--- /dev/null
+++ b/startop/iorap/tests/Android.mk
@@ -0,0 +1,46 @@
+# Copyright (C) 2018 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.
+
+# android_test does not support JNI libraries
+# TODO: once b/80095087 is fixed, rewrite this back to android_test
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_JACK_FLAGS := --multi-dex native
+LOCAL_DX_FLAGS := --multi-dex
+
+LOCAL_PACKAGE_NAME := libiorap-java-tests
+LOCAL_COMPATIBILITY_SUITE := device-tests
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ libiorap-java-test-lib
+
+LOCAL_MULTILIB := both
+
+LOCAL_JNI_SHARED_LIBRARIES := \
+ libdexmakerjvmtiagent \
+ libstaticjvmtiagent \
+ libmultiplejvmtiagentsinterferenceagent
+
+LOCAL_JAVA_LIBRARIES := \
+ android.test.base \
+ android.test.runner
+
+# Use private APIs
+LOCAL_CERTIFICATE := platform
+LOCAL_PRIVATE_PLATFORM_APIS := true
+
+include $(BUILD_PACKAGE)
diff --git a/startop/iorap/AndroidManifest.xml b/startop/iorap/tests/AndroidManifest.xml
similarity index 78%
rename from startop/iorap/AndroidManifest.xml
rename to startop/iorap/tests/AndroidManifest.xml
index 8e5fe97..99f4add 100644
--- a/startop/iorap/AndroidManifest.xml
+++ b/startop/iorap/tests/AndroidManifest.xml
@@ -25,7 +25,13 @@
android:name="android.support.test.runner.AndroidJUnitRunner"
android:targetPackage="com.google.android.startop.iorap.tests" />
- <application>
+ <!--
+ 'debuggable=true' is required to properly load mockito jvmti dependencies,
+ otherwise it gives the following error at runtime:
+
+ Openjdkjvmti plugin was loaded on a non-debuggable Runtime.
+ Plugin was loaded too late to change runtime state to DEBUGGABLE. -->
+ <application android:debuggable="true">
<uses-library android:name="android.test.runner" />
</application>
</manifest>
diff --git a/startop/iorap/tests/src/com/google/android/startop/iorap/IIorapIntegrationTest.kt b/startop/iorap/tests/src/com/google/android/startop/iorap/IIorapIntegrationTest.kt
new file mode 100644
index 0000000..4ba44a9
--- /dev/null
+++ b/startop/iorap/tests/src/com/google/android/startop/iorap/IIorapIntegrationTest.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2018 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.google.android.startop.iorap
+
+import android.net.Uri
+import android.os.ServiceManager
+import android.support.test.filters.MediumTest
+import org.junit.Test
+import org.junit.Ignore
+import org.mockito.Mockito.*
+
+// @Ignore("Test is disabled until iorapd is added to init and there's selinux policies for it")
+@MediumTest
+class IIorapIntegrationTest {
+ /**
+ * @throws ServiceManager.ServiceNotFoundException if iorapd service could not be found
+ */
+ private val iorapService : IIorap by lazy {
+ // TODO: connect to 'iorapd.stub' which doesn't actually do any work other than reply.
+ IIorap.Stub.asInterface(ServiceManager.getServiceOrThrow("iorapd"))
+
+ // Use 'adb shell setenforce 0' otherwise this whole test fails,
+ // because the servicemanager is not allowed to hand out the binder token for iorapd.
+
+ // TODO: implement the selinux policies for iorapd.
+ }
+
+ // A dummy binder stub implementation is required to use with mockito#spy.
+ // Mockito overrides the methods at runtime and tracks how methods were invoked.
+ open class DummyTaskListener : ITaskListener.Stub() {
+ // Note: make parameters nullable to avoid the kotlin IllegalStateExceptions
+ // from using the mockito matchers (eq, argThat, etc).
+ override fun onProgress(requestId: RequestId?, result: TaskResult?) {
+ }
+
+ override fun onComplete(requestId: RequestId?, result: TaskResult?) {
+ }
+ }
+
+ private fun testAnyMethod(func : (RequestId) -> Unit) {
+ val taskListener = spy(DummyTaskListener())!!
+
+ try {
+ iorapService.setTaskListener(taskListener)
+ // Note: Binder guarantees total order for oneway messages sent to the same binder
+ // interface, so we don't need any additional blocking here before sending later calls.
+
+ // Every new method call should have a unique request id.
+ val requestId = RequestId.nextValueForSequence()!!
+
+ // Apply the specific function under test.
+ func(requestId)
+
+ // Typical mockito behavior is to allow any-order callbacks, but we want to test order.
+ val inOrder = inOrder(taskListener)
+
+ // The "stub" behavior of iorapd is that every request immediately gets a response of
+ // BEGAN,ONGOING,COMPLETED
+ inOrder.verify(taskListener, timeout(100)).
+ onProgress(eq(requestId), argThat { it!!.state == TaskResult.STATE_BEGAN })
+ inOrder.verify(taskListener, timeout(100)).
+ onProgress(eq(requestId), argThat { it!!.state == TaskResult.STATE_ONGOING })
+ inOrder.verify(taskListener, timeout(100)).
+ onComplete(eq(requestId), argThat { it!!.state == TaskResult.STATE_COMPLETED })
+ inOrder.verifyNoMoreInteractions()
+
+ } finally {
+ iorapService.setTaskListener(null)
+ }
+ }
+
+ @Test
+ fun testOnPackageEvent() {
+ testAnyMethod { requestId : RequestId ->
+ iorapService.onPackageEvent(requestId,
+ PackageEvent.createReplaced(
+ Uri.parse("https://www.google.com"), "com.fake.package"))
+ }
+ }
+
+ @Test
+ fun testOnAppIntentEvent() {
+ testAnyMethod { requestId : RequestId ->
+ iorapService.onAppIntentEvent(requestId, AppIntentEvent.createDefaultIntentChanged(
+ ActivityInfo("dont care", "dont care"),
+ ActivityInfo("dont care 2", "dont care 2")))
+ }
+ }
+
+ @Test
+ fun testOnSystemServiceEvent() {
+ testAnyMethod { requestId : RequestId ->
+ iorapService.onSystemServiceEvent(requestId,
+ SystemServiceEvent(SystemServiceEvent.TYPE_START))
+ }
+ }
+
+ @Test
+ fun testOnSystemServiceUserEvent() {
+ testAnyMethod { requestId : RequestId ->
+ iorapService.onSystemServiceUserEvent(requestId,
+ SystemServiceUserEvent(SystemServiceUserEvent.TYPE_START_USER,0))
+ }
+ }
+}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 57b652e..9b5b700 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1633,11 +1633,21 @@
* When {@code false}, use default title for Enhanced 4G LTE Mode settings.
* When {@code true}, use the variant.
* @hide
+ * @deprecated use {@link #KEY_ENHANCED_4G_LTE_TITLE_VARIANT_INT}.
*/
+ @Deprecated
public static final String KEY_ENHANCED_4G_LTE_TITLE_VARIANT_BOOL =
"enhanced_4g_lte_title_variant_bool";
/**
+ * The index indicates the carrier specified title string of Enahnce 4G LTE Mode settings.
+ * Default value is 0, which indicates the default title string.
+ * @hide
+ */
+ public static final String KEY_ENHANCED_4G_LTE_TITLE_VARIANT_INT =
+ "enhanced_4g_lte_title_variant_int";
+
+ /**
* Indicates whether the carrier wants to notify the user when handover of an LTE video call to
* WIFI fails.
* <p>
@@ -2419,6 +2429,7 @@
sDefaults.putStringArray(KEY_IMS_REASONINFO_MAPPING_STRING_ARRAY, null);
sDefaults.putBoolean(KEY_ENHANCED_4G_LTE_TITLE_VARIANT_BOOL, false);
+ sDefaults.putInt(KEY_ENHANCED_4G_LTE_TITLE_VARIANT_INT, 0);
sDefaults.putBoolean(KEY_NOTIFY_VT_HANDOVER_TO_WIFI_FAILURE_BOOL, false);
sDefaults.putStringArray(KEY_FILTERED_CNAP_NAMES_STRING_ARRAY, null);
sDefaults.putBoolean(KEY_EDITABLE_WFC_ROAMING_MODE_BOOL, false);
diff --git a/telephony/java/android/telephony/DisconnectCause.java b/telephony/java/android/telephony/DisconnectCause.java
index ee5cdc2..d7169b2 100644
--- a/telephony/java/android/telephony/DisconnectCause.java
+++ b/telephony/java/android/telephony/DisconnectCause.java
@@ -319,6 +319,29 @@
*/
public static final int IMS_SIP_ALTERNATE_EMERGENCY_CALL = 71;
+ /**
+ * Indicates that a new outgoing call cannot be placed because there is already an outgoing
+ * call dialing out.
+ */
+ public static final int ALREADY_DIALING = 72;
+
+ /**
+ * Indicates that a new outgoing call cannot be placed while there is a ringing call.
+ */
+ public static final int CANT_CALL_WHILE_RINGING = 73;
+
+ /**
+ * Indicates that a new outgoing call cannot be placed because calling has been disabled using
+ * the ro.telephony.disable-call system property.
+ */
+ public static final int CALLING_DISABLED = 74;
+
+ /**
+ * Indicates that a new outgoing call cannot be placed because there is currently an ongoing
+ * foreground and background call.
+ */
+ public static final int TOO_MANY_ONGOING_CALLS = 75;
+
//*********************************************************************************************
// When adding a disconnect type:
// 1) Update toString() with the newly added disconnect type.
@@ -474,6 +497,14 @@
return "NORMAL_UNSPECIFIED";
case IMS_SIP_ALTERNATE_EMERGENCY_CALL:
return "IMS_SIP_ALTERNATE_EMERGENCY_CALL";
+ case ALREADY_DIALING:
+ return "ALREADY_DIALING";
+ case CANT_CALL_WHILE_RINGING:
+ return "CANT_CALL_WHILE_RINGING";
+ case CALLING_DISABLED:
+ return "CALLING_DISABLED";
+ case TOO_MANY_ONGOING_CALLS:
+ return "TOO_MANY_ONGOING_CALLS";
default:
return "INVALID: " + cause;
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index ea9ac39..4b8bf2b 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -168,7 +168,6 @@
/** @hide */
static public final int OTASP_SIM_UNPROVISIONED = 5;
-
/** @hide */
static public final int KEY_TYPE_EPDG = 1;
@@ -2925,7 +2924,7 @@
* of time the mode may be unknown.
*
* <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
- * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+ * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
*
* @return {@link PhoneConstants#LTE_ON_CDMA_UNKNOWN}, {@link PhoneConstants#LTE_ON_CDMA_FALSE}
* or {@link PhoneConstants#LTE_ON_CDMA_TRUE}
@@ -5930,7 +5929,7 @@
* Sets the network selection mode to automatic.
*
* <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
- * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+ * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
*
* <p>Requires Permission:
* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
@@ -5955,7 +5954,7 @@
* Perform a radio scan and return the list of available networks.
*
* <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
- * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+ * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
*
* <p> Note that this scan can take a long time (sometimes minutes) to happen.
*
@@ -6034,7 +6033,7 @@
* Ask the radio to connect to the input network and change selection mode to manual.
*
* <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
- * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+ * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
*
* <p>Requires Permission:
* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
@@ -6059,7 +6058,7 @@
* Ask the radio to connect to the input network and change selection mode to manual.
*
* <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
- * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+ * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
*
* <p>Requires Permission:
* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
@@ -6092,7 +6091,7 @@
* Get the network selection mode.
*
* <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
- * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+ * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
* @return the network selection mode.
*
@@ -6982,7 +6981,8 @@
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
- isDataRoamingEnabled = telephony.isDataRoamingEnabled(getSubId());
+ isDataRoamingEnabled = telephony.isDataRoamingEnabled(
+ getSubId(SubscriptionManager.getDefaultDataSubscriptionId()));
}
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelephony#isDataRoamingEnabled", e);
@@ -6994,7 +6994,7 @@
* Gets the roaming mode for CDMA phone.
*
* <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
- * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+ * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
*
* @return one of {@link #CDMA_ROAMING_MODE_RADIO_DEFAULT}, {@link #CDMA_ROAMING_MODE_HOME},
* {@link #CDMA_ROAMING_MODE_AFFILIATED}, {@link #CDMA_ROAMING_MODE_ANY}.
@@ -7019,7 +7019,7 @@
* Sets the roaming mode for CDMA phone to the given mode {@code mode}.
*
* <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
- * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+ * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
*
* @param mode should be one of {@link #CDMA_ROAMING_MODE_RADIO_DEFAULT},
* {@link #CDMA_ROAMING_MODE_HOME}, {@link #CDMA_ROAMING_MODE_AFFILIATED},
@@ -7088,7 +7088,8 @@
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
- telephony.setDataRoamingEnabled(getSubId(), isEnabled);
+ telephony.setDataRoamingEnabled(
+ getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), isEnabled);
}
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelephony#setDataRoamingEnabled", e);
@@ -7881,7 +7882,7 @@
* Returns the current {@link ServiceState} information.
*
* <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
- * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+ * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
*
* <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
* or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
@@ -8350,7 +8351,7 @@
* Checks if phone is in emergency callback mode.
*
* <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
- * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+ * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
*
* @return true if phone is in emergency callback mode.
* @hide
@@ -8381,6 +8382,29 @@
}
/**
+ * Checks if manual network selection is allowed.
+ *
+ * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+ * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * @return {@code true} if manual network selection is allowed, otherwise return {@code false}.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public boolean isManualNetworkSelectionAllowed() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.isManualNetworkSelectionAllowed(getSubId());
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#isManualNetworkSelectionAllowed", e);
+ }
+ return true;
+ }
+
+ /**
* Get the most recently available signal strength information.
*
* Get the most recent SignalStrength information reported by the modem. Due
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index c2c93da..8379f8c 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -1206,7 +1206,8 @@
/** @hide */
public static int getMvnoTypeIntFromString(String mvnoType) {
- Integer mvnoTypeInt = MVNO_TYPE_STRING_MAP.get(mvnoType);
+ String mvnoTypeString = TextUtils.isEmpty(mvnoType) ? mvnoType : mvnoType.toLowerCase();
+ Integer mvnoTypeInt = MVNO_TYPE_STRING_MAP.get(mvnoTypeString);
return mvnoTypeInt == null ? UNSPECIFIED_INT : mvnoTypeInt;
}
diff --git a/telephony/java/android/telephony/emergency/EmergencyNumber.aidl b/telephony/java/android/telephony/emergency/EmergencyNumber.aidl
new file mode 100644
index 0000000..bfb0a59
--- /dev/null
+++ b/telephony/java/android/telephony/emergency/EmergencyNumber.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2018, 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.telephony.emergency;
+
+parcelable EmergencyNumber;
diff --git a/telephony/java/android/telephony/emergency/EmergencyNumber.java b/telephony/java/android/telephony/emergency/EmergencyNumber.java
new file mode 100644
index 0000000..d6a08543
--- /dev/null
+++ b/telephony/java/android/telephony/emergency/EmergencyNumber.java
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) 2018 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.telephony.emergency;
+
+import android.annotation.IntDef;
+import android.hardware.radio.V1_3.EmergencyNumberSource;
+import android.hardware.radio.V1_3.EmergencyServiceCategory;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A parcelable class that wraps and retrieves the information of number, service category(s) and
+ * country code for a specific emergency number.
+ */
+public final class EmergencyNumber implements Parcelable {
+
+ private static final String LOG_TAG = "EmergencyNumber";
+
+ /**
+ * Defining Emergency Service Category as follows:
+ * - General emergency call, all categories;
+ * - Police;
+ * - Ambulance;
+ * - Fire Brigade;
+ * - Marine Guard;
+ * - Mountain Rescue;
+ * - Manually Initiated eCall (MIeC);
+ * - Automatically Initiated eCall (AIeC);
+ *
+ * Category UNSPECIFIED (General emergency call, all categories) indicates that no specific
+ * services are associated with this emergency number; if the emergency number is specified,
+ * it has one or more defined emergency service categories.
+ *
+ * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+ *
+ * @hide
+ */
+ @IntDef(flag = true, prefix = { "EMERGENCY_SERVICE_CATEGORY_" }, value = {
+ EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
+ EMERGENCY_SERVICE_CATEGORY_POLICE,
+ EMERGENCY_SERVICE_CATEGORY_AMBULANCE,
+ EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE,
+ EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD,
+ EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE,
+ EMERGENCY_SERVICE_CATEGORY_MIEC,
+ EMERGENCY_SERVICE_CATEGORY_AIEC
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface EmergencyServiceCategories {}
+
+ /**
+ * Emergency Service Category UNSPECIFIED (General emergency call, all categories) bit-field
+ * indicates that no specific services are associated with this emergency number; if the
+ * emergency number is specified, it has one or more defined emergency service categories.
+ *
+ * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+ */
+ public static final int EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED =
+ EmergencyServiceCategory.UNSPECIFIED;
+ /**
+ * Bit-field that indicates Emergency Service Category for Police.
+ *
+ * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+ */
+ public static final int EMERGENCY_SERVICE_CATEGORY_POLICE = EmergencyServiceCategory.POLICE;
+ /**
+ * Bit-field that indicates Emergency Service Category for Ambulance.
+ *
+ * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+ */
+ public static final int EMERGENCY_SERVICE_CATEGORY_AMBULANCE =
+ EmergencyServiceCategory.AMBULANCE;
+ /**
+ * Bit-field that indicates Emergency Service Category for Fire Brigade.
+ *
+ * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+ */
+ public static final int EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE =
+ EmergencyServiceCategory.FIRE_BRIGADE;
+ /**
+ * Bit-field that indicates Emergency Service Category for Marine Guard.
+ *
+ * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+ */
+ public static final int EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD =
+ EmergencyServiceCategory.MARINE_GUARD;
+ /**
+ * Bit-field that indicates Emergency Service Category for Mountain Rescue.
+ *
+ * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+ */
+ public static final int EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE =
+ EmergencyServiceCategory.MOUNTAIN_RESCUE;
+ /**
+ * Bit-field that indicates Emergency Service Category for Manually Initiated eCall (MIeC)
+ *
+ * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+ */
+ public static final int EMERGENCY_SERVICE_CATEGORY_MIEC = EmergencyServiceCategory.MIEC;
+ /**
+ * Bit-field that indicates Emergency Service Category for Automatically Initiated eCall (AIeC)
+ *
+ * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+ */
+ public static final int EMERGENCY_SERVICE_CATEGORY_AIEC = EmergencyServiceCategory.AIEC;
+
+ private static final Set<Integer> EMERGENCY_SERVICE_CATEGORY_SET;
+ static {
+ EMERGENCY_SERVICE_CATEGORY_SET = new HashSet<Integer>();
+ EMERGENCY_SERVICE_CATEGORY_SET.add(EMERGENCY_SERVICE_CATEGORY_POLICE);
+ EMERGENCY_SERVICE_CATEGORY_SET.add(EMERGENCY_SERVICE_CATEGORY_AMBULANCE);
+ EMERGENCY_SERVICE_CATEGORY_SET.add(EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE);
+ EMERGENCY_SERVICE_CATEGORY_SET.add(EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD);
+ EMERGENCY_SERVICE_CATEGORY_SET.add(EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE);
+ EMERGENCY_SERVICE_CATEGORY_SET.add(EMERGENCY_SERVICE_CATEGORY_MIEC);
+ EMERGENCY_SERVICE_CATEGORY_SET.add(EMERGENCY_SERVICE_CATEGORY_AIEC);
+ }
+
+ /**
+ * The source to tell where the corresponding @1.3::EmergencyNumber comes from.
+ *
+ * The emergency number has one or more defined emergency number sources.
+ *
+ * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+ *
+ * @hide
+ */
+ @IntDef(flag = true, prefix = { "EMERGENCY_NUMBER_SOURCE_" }, value = {
+ EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
+ EMERGENCY_NUMBER_SOURCE_SIM,
+ EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG,
+ EMERGENCY_NUMBER_SOURCE_DEFAULT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface EmergencyNumberSources {}
+
+ /**
+ * Bit-field which indicates the number is from the network signaling.
+ *
+ * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+ */
+ public static final int EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING =
+ EmergencyNumberSource.NETWORK_SIGNALING;
+ /**
+ * Bit-field which indicates the number is from the sim.
+ *
+ * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+ */
+ public static final int EMERGENCY_NUMBER_SOURCE_SIM = EmergencyNumberSource.SIM;
+ /** Bit-field which indicates the number is from the modem config. */
+ public static final int EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG =
+ EmergencyNumberSource.MODEM_CONFIG;
+ /**
+ * Bit-field which indicates the number is available as default.
+ *
+ * 112, 911 must always be available; additionally, 000, 08, 110, 999, 118 and 119 must be
+ * available when sim is not present.
+ *
+ * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+ */
+ public static final int EMERGENCY_NUMBER_SOURCE_DEFAULT = EmergencyNumberSource.DEFAULT;
+
+ private static final Set<Integer> EMERGENCY_NUMBER_SOURCE_SET;
+ static {
+ EMERGENCY_NUMBER_SOURCE_SET = new HashSet<Integer>();
+ EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING);
+ EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_SIM);
+ EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG);
+ EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_DEFAULT);
+ }
+
+ private final String mNumber;
+ private final String mCountryIso;
+ private final int mEmergencyServiceCategoryBitmask;
+ private final int mEmergencyNumberSourceBitmask;
+
+ /** @hide */
+ public EmergencyNumber(String number, String countryIso,
+ int emergencyServiceCategories,
+ int emergencyNumberSources) {
+ this.mNumber = number;
+ this.mCountryIso = countryIso;
+ this.mEmergencyServiceCategoryBitmask = emergencyServiceCategories;
+ this.mEmergencyNumberSourceBitmask = emergencyNumberSources;
+ }
+
+ /** @hide */
+ public EmergencyNumber(Parcel source) {
+ mNumber = source.readString();
+ mCountryIso = source.readString();
+ mEmergencyServiceCategoryBitmask = source.readInt();
+ mEmergencyNumberSourceBitmask = source.readInt();
+ }
+
+ /**
+ * Get the dialing number of the emergency number.
+ *
+ * The character in the number string is only the dial pad
+ * character('0'-'9', '*', or '#'). For example: 911.
+ *
+ * @return the dialing number.
+ */
+ public String getNumber() {
+ return mNumber;
+ }
+
+ /**
+ * Get the country code string (lowercase character) in ISO 3166 format of the emergency number.
+ *
+ * @return the country code string (lowercase character) in ISO 3166 format.
+ */
+ public String getCountryIso() {
+ return mCountryIso;
+ }
+
+ /**
+ * Returns the bitmask of emergency service categories {@link EmergencyServiceCategories} of
+ * the emergency number.
+ *
+ * @return bitmask of the emergency service categories {@link EmergencyServiceCategories}
+ */
+ public @EmergencyServiceCategories int getEmergencyServiceCategoryBitmask() {
+ return mEmergencyServiceCategoryBitmask;
+ }
+
+ /**
+ * Returns the emergency service categories {@link EmergencyServiceCategories} of the emergency
+ * number.
+ *
+ * @return a list of the emergency service categories {@link EmergencyServiceCategories}
+ */
+ public List<Integer> getEmergencyServiceCategories() {
+ List<Integer> categories = new ArrayList<>();
+ if (serviceUnspecified()) {
+ categories.add(EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED);
+ return categories;
+ }
+ for (Integer category : EMERGENCY_SERVICE_CATEGORY_SET) {
+ if (isInEmergencyServiceCategories(category)) {
+ categories.add(category);
+ }
+ }
+ return categories;
+ }
+
+ /**
+ * Checks if the emergency service category is unspecified for the emergency number
+ * {@link #EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED}.
+ *
+ * @return {@code true} if the emergency service category is unspecified for the emergency
+ * number {@link #EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED}; {@code false} otherwise.
+ */
+ private boolean serviceUnspecified() {
+ return mEmergencyServiceCategoryBitmask == EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED;
+ }
+
+ /**
+ * Checks if the emergency number is in the specified emergency service category(s)
+ * {@link EmergencyServiceCategories}.
+ *
+ * @return {@code true} if the emergency number is in the specified emergency service
+ * category(s) {@link EmergencyServiceCategories}; {@code false} otherwise.
+ *
+ * @param categories - emergency service categories {@link EmergencyServiceCategories}
+ */
+ public boolean isInEmergencyServiceCategories(@EmergencyServiceCategories int categories) {
+ if (categories == EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED) {
+ return serviceUnspecified();
+ }
+ return (mEmergencyServiceCategoryBitmask & categories) == categories;
+ }
+
+ /**
+ * Returns the bitmask of the sources {@link EmergencyNumberSources} of the emergency number.
+ *
+ * @return bitmask of the emergency number sources {@link EmergencyNumberSources}
+ */
+ public @EmergencyNumberSources int getEmergencyNumberSourceBitmask() {
+ return mEmergencyNumberSourceBitmask;
+ }
+
+ /**
+ * Returns a list of {@link EmergencyNumberSources} of the emergency number.
+ *
+ * @return a list of {@link EmergencyNumberSources}
+ */
+ public List<Integer> getEmergencyNumberSources() {
+ List<Integer> sources = new ArrayList<>();
+ for (Integer source : EMERGENCY_NUMBER_SOURCE_SET) {
+ if ((mEmergencyNumberSourceBitmask & source) == source) {
+ sources.add(source);
+ }
+ }
+ return sources;
+ }
+
+ /**
+ * Checks if the emergency number is from the specified emergency number source(s)
+ * {@link EmergencyNumberSources}.
+ *
+ * @return {@code true} if the emergency number is from the specified emergency number
+ * source(s) {@link EmergencyNumberSources}; {@code false} otherwise.
+ *
+ * @param sources - {@link EmergencyNumberSources}
+ */
+ public boolean isFromSources(@EmergencyNumberSources int sources) {
+ return (mEmergencyNumberSourceBitmask & sources) == sources;
+ }
+
+ @Override
+ /** @hide */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mNumber);
+ dest.writeString(mCountryIso);
+ dest.writeInt(mEmergencyServiceCategoryBitmask);
+ dest.writeInt(mEmergencyNumberSourceBitmask);
+ }
+
+ @Override
+ /** @hide */
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return "EmergencyNumber = " + "[Number]" + mNumber + " / [CountryIso]" + mCountryIso
+ + " / [ServiceCategories]"
+ + Integer.toBinaryString(mEmergencyServiceCategoryBitmask)
+ + " / [Sources]" + Integer.toBinaryString(mEmergencyNumberSourceBitmask);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!EmergencyNumber.class.isInstance(o)) {
+ return false;
+ }
+ return (o == this || toString().equals(o.toString()));
+ }
+
+ public static final Parcelable.Creator<EmergencyNumber> CREATOR =
+ new Parcelable.Creator<EmergencyNumber>() {
+ @Override
+ public EmergencyNumber createFromParcel(Parcel in) {
+ return new EmergencyNumber(in);
+ }
+
+ @Override
+ public EmergencyNumber[] newArray(int size) {
+ return new EmergencyNumber[size];
+ }
+ };
+}
diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index 5d6a8c1..89ef339 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -117,12 +117,14 @@
* @hide
*/
public static final String EXTRA_CONFERENCE = "conference";
+
/**
* Boolean extra property set on an {@link ImsCallProfile} to indicate that this call is an
* emergency call. The {@link ImsService} sets this on a call to indicate that the network has
* identified the call as an emergency call.
*/
- public static final String EXTRA_E_CALL = "e_call";
+ public static final String EXTRA_EMERGENCY_CALL = "e_call";
+
/**
* @hide
*/
diff --git a/telephony/java/android/telephony/ims/ImsCallSession.java b/telephony/java/android/telephony/ims/ImsCallSession.java
index a20d4f5..df903cc2 100644
--- a/telephony/java/android/telephony/ims/ImsCallSession.java
+++ b/telephony/java/android/telephony/ims/ImsCallSession.java
@@ -16,22 +16,16 @@
package android.telephony.ims;
-import android.annotation.CallbackExecutor;
-import android.annotation.NonNull;
-import android.annotation.SystemApi;
import android.os.Message;
import android.os.RemoteException;
import android.telephony.ims.aidl.IImsCallSessionListener;
-
-import java.util.Objects;
-import java.util.concurrent.Executor;
-
-import android.telephony.ims.stub.ImsCallSessionImplBase;
import android.util.Log;
import com.android.ims.internal.IImsCallSession;
import com.android.ims.internal.IImsVideoCallProvider;
+import java.util.Objects;
+
/**
* Provides the call initiation/termination, and media exchange between two IMS endpoints.
* It directly communicates with IMS service which implements the IMS protocol behavior.
@@ -42,7 +36,8 @@
private static final String TAG = "ImsCallSession";
/**
- * Defines IMS call session state. Please use {@link ImsCallSessionImplBase.State} definition.
+ * Defines IMS call session state. Please use
+ * {@link android.telephony.ims.stub.ImsCallSessionImplBase.State} definition.
* This is kept around for capability reasons.
*/
public static class State {
@@ -1027,9 +1022,9 @@
}
/**
- * Sends RTT Upgrade request
+ * Sends RTT Upgrade or downgrade request
*
- * @param to : expected profile
+ * @param to Profile with the RTT flag set to the desired value
*/
public void sendRttModifyRequest(ImsCallProfile to) {
if (mClosed) {
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index ca2bcff..32eb12b 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -808,6 +808,13 @@
*/
boolean isDataEnabled(int subId);
+ /**
+ * Checks if manual network selection is allowed.
+ *
+ * @return {@code true} if manual network selection is allowed, otherwise return {@code false}.
+ */
+ boolean isManualNetworkSelectionAllowed(int subId);
+
/**
* Get P-CSCF address from PCO after data connection is established or modified.
* @param apnType the apnType, "ims" for IMS APN, "emergency" for EMERGENCY APN
diff --git a/telephony/java/com/android/internal/telephony/SmsApplication.java b/telephony/java/com/android/internal/telephony/SmsApplication.java
index 39722c6..5ed283e 100644
--- a/telephony/java/com/android/internal/telephony/SmsApplication.java
+++ b/telephony/java/com/android/internal/telephony/SmsApplication.java
@@ -584,7 +584,8 @@
// We only make the change if the new package is valid
PackageManager packageManager = context.getPackageManager();
- Collection<SmsApplicationData> applications = getApplicationCollection(context);
+ Collection<SmsApplicationData> applications = getApplicationCollectionInternal(
+ context, userId);
SmsApplicationData oldAppData = oldPackageName != null ?
getApplicationForPackage(applications, oldPackageName) : null;
SmsApplicationData applicationData = getApplicationForPackage(applications, packageName);
diff --git a/tests/NativeProcessesMemoryTest/src/com/android/tests/nativeprocesses/NativeProcessesMemoryTest.java b/tests/NativeProcessesMemoryTest/src/com/android/tests/nativeprocesses/NativeProcessesMemoryTest.java
index c86f06e..51302ce 100644
--- a/tests/NativeProcessesMemoryTest/src/com/android/tests/nativeprocesses/NativeProcessesMemoryTest.java
+++ b/tests/NativeProcessesMemoryTest/src/com/android/tests/nativeprocesses/NativeProcessesMemoryTest.java
@@ -19,11 +19,15 @@
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.result.ByteArrayInputStreamSource;
-import com.android.tradefed.result.ITestInvocationListener;
import com.android.tradefed.result.LogDataType;
-import com.android.tradefed.result.TestDescription;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestLogData;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestMetrics;
import com.android.tradefed.testtype.IDeviceTest;
-import com.android.tradefed.testtype.IRemoteTest;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.Arrays;
@@ -58,7 +62,12 @@
* - memory usage of each native process (one measurement for each process)
* </pre>
*/
-public class NativeProcessesMemoryTest implements IDeviceTest, IRemoteTest {
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class NativeProcessesMemoryTest implements IDeviceTest {
+
+ @Rule public TestMetrics metrics = new TestMetrics();
+ @Rule public TestLogData logs = new TestLogData();
+
// the dumpsys process comes and go as we run this test, changing pids, so ignore it
private static final List<String> PROCESSES_TO_IGNORE = Arrays.asList("dumpsys");
@@ -68,38 +77,25 @@
private static final String SEPARATOR = ",";
private static final String LINE_SEPARATOR = "\\n";
- // name of this test run, used for reporting
- private static final String RUN_NAME = "NativeProcessesTest";
// key used to report the number of native processes
private static final String NUM_NATIVE_PROCESSES_KEY = "Num_native_processes";
- private final Map<String, String> mNativeProcessToMemory = new HashMap<String, String>();
// identity for summing over MemoryMetric
private final MemoryMetric mZero = new MemoryMetric(0, 0, 0);
private ITestDevice mTestDevice;
- private ITestInvocationListener mListener;
- public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
- mListener = listener;
+ @Test
+ public void run() throws DeviceNotAvailableException {
// showmap requires root, we enable it here for the rest of the test
- mTestDevice.enableAdbRoot();
-
- listener.testRunStarted(RUN_NAME, 1 /* testCount */);
-
- TestDescription testDescription = new TestDescription(getClass().getName(), "run");
- listener.testStarted(testDescription);
-
+ getDevice().enableAdbRoot();
// process name -> list of pids with that name
Map<String, List<String>> nativeProcesses = collectNativeProcesses();
sampleAndLogAllProcesses(nativeProcesses);
// want to also record the number of native processes
- mNativeProcessToMemory.put(
+ metrics.addTestMetric(
NUM_NATIVE_PROCESSES_KEY, Integer.toString(nativeProcesses.size()));
-
- listener.testEnded(testDescription, mNativeProcessToMemory);
- listener.testRunEnded(0, new HashMap<String, String>());
}
/** Samples memory of all processes and logs the memory use. */
@@ -148,7 +144,7 @@
*/
private Map<String, List<String>> collectNativeProcesses() throws DeviceNotAvailableException {
HashMap<String, List<String>> nativeProcesses = new HashMap<>();
- String memInfo = mTestDevice.executeShellCommand(DUMPSYS_MEMINFO_OOM_CMD);
+ String memInfo = getDevice().executeShellCommand(DUMPSYS_MEMINFO_OOM_CMD);
for (String line : memInfo.split(LINE_SEPARATOR)) {
String[] splits = line.split(SEPARATOR);
@@ -172,7 +168,7 @@
private void logShowmap(String label, String showmap) {
try (ByteArrayInputStreamSource source =
new ByteArrayInputStreamSource(showmap.getBytes())) {
- mListener.testLog(label + "_showmap", LogDataType.TEXT, source);
+ logs.addTestLog(label + "_showmap", LogDataType.TEXT, source);
}
}
@@ -183,7 +179,7 @@
private Optional<MemoryMetric> snapMemoryUsage(String processName, String pid)
throws DeviceNotAvailableException {
// TODO(zhin): copied from com.android.tests.sysmem.host.Metrics#sample(), extract?
- String showmap = mTestDevice.executeShellCommand("showmap " + pid);
+ String showmap = getDevice().executeShellCommand("showmap " + pid);
logShowmap(processName + "_" + pid, showmap);
// CHECKSTYLE:OFF Generated code
@@ -214,9 +210,9 @@
/** Logs a MemoryMetric of a process. */
private void logMemoryMetric(String processName, MemoryMetric memoryMetric) {
- mNativeProcessToMemory.put(processName + "_pss", Long.toString(memoryMetric.pss));
- mNativeProcessToMemory.put(processName + "_rss", Long.toString(memoryMetric.rss));
- mNativeProcessToMemory.put(processName + "_vss", Long.toString(memoryMetric.vss));
+ metrics.addTestMetric(processName + "_pss", Long.toString(memoryMetric.pss));
+ metrics.addTestMetric(processName + "_rss", Long.toString(memoryMetric.rss));
+ metrics.addTestMetric(processName + "_vss", Long.toString(memoryMetric.vss));
}
/** Container of memory numbers we want to log. */
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
index 99a5a69..9b919abf 100644
--- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -16,6 +16,8 @@
package com.android.server;
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
@@ -34,8 +36,10 @@
import android.net.IpSecConfig;
import android.net.IpSecManager;
import android.net.IpSecSpiResponse;
+import android.net.IpSecTransform;
import android.net.IpSecTransformResponse;
import android.net.IpSecTunnelInterfaceResponse;
+import android.net.IpSecUdpEncapResponse;
import android.net.LinkAddress;
import android.net.Network;
import android.net.NetworkUtils;
@@ -62,16 +66,17 @@
private static final int TEST_SPI = 0xD1201D;
- private final String mDestinationAddr;
private final String mSourceAddr;
+ private final String mDestinationAddr;
private final LinkAddress mLocalInnerAddress;
+ private final int mFamily;
@Parameterized.Parameters
public static Collection ipSecConfigs() {
return Arrays.asList(
new Object[][] {
- {"1.2.3.4", "8.8.4.4", "10.0.1.1/24"},
- {"2601::2", "2601::10", "2001:db8::1/64"}
+ {"1.2.3.4", "8.8.4.4", "10.0.1.1/24", AF_INET},
+ {"2601::2", "2601::10", "2001:db8::1/64", AF_INET6}
});
}
@@ -129,12 +134,14 @@
new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
private static final IpSecAlgorithm AEAD_ALGO =
new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 128);
+ private static final int REMOTE_ENCAP_PORT = 4500;
public IpSecServiceParameterizedTest(
- String sourceAddr, String destAddr, String localInnerAddr) {
+ String sourceAddr, String destAddr, String localInnerAddr, int family) {
mSourceAddr = sourceAddr;
mDestinationAddr = destAddr;
mLocalInnerAddress = new LinkAddress(localInnerAddr);
+ mFamily = family;
}
@Before
@@ -157,6 +164,8 @@
.thenReturn(AppOpsManager.MODE_IGNORED);
}
+ //TODO: Add a test to verify SPI.
+
@Test
public void testIpSecServiceReserveSpi() throws Exception {
when(mMockNetd.ipSecAllocateSpi(anyInt(), anyString(), eq(mDestinationAddr), eq(TEST_SPI)))
@@ -257,6 +266,47 @@
config.setAuthentication(AUTH_ALGO);
}
+ private void addEncapSocketToIpSecConfig(int resourceId, IpSecConfig config) throws Exception {
+ config.setEncapType(IpSecTransform.ENCAP_ESPINUDP);
+ config.setEncapSocketResourceId(resourceId);
+ config.setEncapRemotePort(REMOTE_ENCAP_PORT);
+ }
+
+ private void verifyTransformNetdCalledForCreatingSA(
+ IpSecConfig config, IpSecTransformResponse resp) throws Exception {
+ verifyTransformNetdCalledForCreatingSA(config, resp, 0);
+ }
+
+ private void verifyTransformNetdCalledForCreatingSA(
+ IpSecConfig config, IpSecTransformResponse resp, int encapSocketPort) throws Exception {
+ IpSecAlgorithm auth = config.getAuthentication();
+ IpSecAlgorithm crypt = config.getEncryption();
+ IpSecAlgorithm authCrypt = config.getAuthenticatedEncryption();
+
+ verify(mMockNetd, times(1))
+ .ipSecAddSecurityAssociation(
+ eq(mUid),
+ eq(config.getMode()),
+ eq(config.getSourceAddress()),
+ eq(config.getDestinationAddress()),
+ eq((config.getNetwork() != null) ? config.getNetwork().netId : 0),
+ eq(TEST_SPI),
+ eq(0),
+ eq(0),
+ eq((auth != null) ? auth.getName() : ""),
+ eq((auth != null) ? auth.getKey() : new byte[] {}),
+ eq((auth != null) ? auth.getTruncationLengthBits() : 0),
+ eq((crypt != null) ? crypt.getName() : ""),
+ eq((crypt != null) ? crypt.getKey() : new byte[] {}),
+ eq((crypt != null) ? crypt.getTruncationLengthBits() : 0),
+ eq((authCrypt != null) ? authCrypt.getName() : ""),
+ eq((authCrypt != null) ? authCrypt.getKey() : new byte[] {}),
+ eq((authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0),
+ eq(config.getEncapType()),
+ eq(encapSocketPort),
+ eq(config.getEncapRemotePort()));
+ }
+
@Test
public void testCreateTransform() throws Exception {
IpSecConfig ipSecConfig = new IpSecConfig();
@@ -267,28 +317,7 @@
mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
assertEquals(IpSecManager.Status.OK, createTransformResp.status);
- verify(mMockNetd)
- .ipSecAddSecurityAssociation(
- eq(mUid),
- anyInt(),
- anyString(),
- anyString(),
- anyInt(),
- eq(TEST_SPI),
- anyInt(),
- anyInt(),
- eq(IpSecAlgorithm.AUTH_HMAC_SHA256),
- eq(AUTH_KEY),
- anyInt(),
- eq(IpSecAlgorithm.CRYPT_AES_CBC),
- eq(CRYPT_KEY),
- anyInt(),
- eq(""),
- eq(new byte[] {}),
- eq(0),
- anyInt(),
- anyInt(),
- anyInt());
+ verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp);
}
@Test
@@ -302,28 +331,59 @@
mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
assertEquals(IpSecManager.Status.OK, createTransformResp.status);
- verify(mMockNetd)
- .ipSecAddSecurityAssociation(
- eq(mUid),
- anyInt(),
- anyString(),
- anyString(),
- anyInt(),
- eq(TEST_SPI),
- anyInt(),
- anyInt(),
- eq(""),
- eq(new byte[] {}),
- eq(0),
- eq(""),
- eq(new byte[] {}),
- eq(0),
- eq(IpSecAlgorithm.AUTH_CRYPT_AES_GCM),
- eq(AEAD_KEY),
- anyInt(),
- anyInt(),
- anyInt(),
- anyInt());
+ verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp);
+ }
+
+ @Test
+ public void testCreateTransportModeTransformWithEncap() throws Exception {
+ IpSecUdpEncapResponse udpSock = mIpSecService.openUdpEncapsulationSocket(0, new Binder());
+
+ IpSecConfig ipSecConfig = new IpSecConfig();
+ ipSecConfig.setMode(IpSecTransform.MODE_TRANSPORT);
+ addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
+ addAuthAndCryptToIpSecConfig(ipSecConfig);
+ addEncapSocketToIpSecConfig(udpSock.resourceId, ipSecConfig);
+
+ if (mFamily == AF_INET) {
+ IpSecTransformResponse createTransformResp =
+ mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
+ assertEquals(IpSecManager.Status.OK, createTransformResp.status);
+
+ verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp, udpSock.port);
+ } else {
+ try {
+ IpSecTransformResponse createTransformResp =
+ mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
+ fail("Expected IllegalArgumentException on attempt to use UDP Encap in IPv6");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+ }
+
+ @Test
+ public void testCreateTunnelModeTransformWithEncap() throws Exception {
+ IpSecUdpEncapResponse udpSock = mIpSecService.openUdpEncapsulationSocket(0, new Binder());
+
+ IpSecConfig ipSecConfig = new IpSecConfig();
+ ipSecConfig.setMode(IpSecTransform.MODE_TUNNEL);
+ addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
+ addAuthAndCryptToIpSecConfig(ipSecConfig);
+ addEncapSocketToIpSecConfig(udpSock.resourceId, ipSecConfig);
+
+ if (mFamily == AF_INET) {
+ IpSecTransformResponse createTransformResp =
+ mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
+ assertEquals(IpSecManager.Status.OK, createTransformResp.status);
+
+ verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp, udpSock.port);
+ } else {
+ try {
+ IpSecTransformResponse createTransformResp =
+ mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
+ fail("Expected IllegalArgumentException on attempt to use UDP Encap in IPv6");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
}
@Test
diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
index 9c4da1f..14312cf 100644
--- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
@@ -83,16 +83,6 @@
}
try {
- mWm.setFocusedApp(null, false);
- fail("IWindowManager.setFocusedApp did not throw SecurityException as"
- + " expected");
- } catch (SecurityException e) {
- // expected
- } catch (RemoteException e) {
- fail("Unexpected remote exception");
- }
-
- try {
mWm.prepareAppTransition(0, false);
fail("IWindowManager.prepareAppTransition did not throw SecurityException as"
+ " expected");
diff --git a/tools/aapt2/dump/DumpManifest.cpp b/tools/aapt2/dump/DumpManifest.cpp
index 2c356d1..b6a984f 100644
--- a/tools/aapt2/dump/DumpManifest.cpp
+++ b/tools/aapt2/dump/DumpManifest.cpp
@@ -16,6 +16,8 @@
#include "DumpManifest.h"
+#include <algorithm>
+
#include "LoadedApk.h"
#include "SdkConstants.h"
#include "ValueVisitor.h"
@@ -70,10 +72,14 @@
CATEGORY_ATTR = 0x010103e8,
BANNER_ATTR = 0x10103f2,
ISGAME_ATTR = 0x10103f4,
+ VERSION_ATTR = 0x01010519,
+ CERT_DIGEST_ATTR = 0x01010548,
REQUIRED_FEATURE_ATTR = 0x1010557,
REQUIRED_NOT_FEATURE_ATTR = 0x1010558,
COMPILE_SDK_VERSION_ATTR = 0x01010572,
COMPILE_SDK_VERSION_CODENAME_ATTR = 0x01010573,
+ VERSION_MAJOR_ATTR = 0x01010577,
+ PACKAGE_TYPE_ATTR = 0x01010587,
};
const std::string& kAndroidNamespace = "http://schemas.android.com/apk/res/android";
@@ -1318,6 +1324,70 @@
}
};
+/** Represents <static-library> elements. **/
+class StaticLibrary : public ManifestExtractor::Element {
+ public:
+ StaticLibrary() = default;
+ std::string name;
+ int version;
+ int versionMajor;
+
+ void Extract(xml::Element* element) override {
+ auto parent_stack = extractor()->parent_stack();
+ if (parent_stack.size() > 0 && ElementCast<Application>(parent_stack[0])) {
+ name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
+ version = GetAttributeIntegerDefault(FindAttribute(element, VERSION_ATTR), 0);
+ versionMajor = GetAttributeIntegerDefault(FindAttribute(element, VERSION_MAJOR_ATTR), 0);
+ }
+ }
+
+ void Print(text::Printer& printer) override {
+ printer.Print(StringPrintf(
+ "static-library: name='%s' version='%d' versionMajor='%d'\n",
+ name.data(), version, versionMajor));
+ }
+};
+
+/** Represents <uses-static-library> elements. **/
+class UsesStaticLibrary : public ManifestExtractor::Element {
+ public:
+ UsesStaticLibrary() = default;
+ std::string name;
+ int version;
+ int versionMajor;
+ std::vector<std::string> certDigests;
+
+ void Extract(xml::Element* element) override {
+ auto parent_stack = extractor()->parent_stack();
+ if (parent_stack.size() > 0 && ElementCast<Application>(parent_stack[0])) {
+ name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
+ version = GetAttributeIntegerDefault(FindAttribute(element, VERSION_ATTR), 0);
+ versionMajor = GetAttributeIntegerDefault(FindAttribute(element, VERSION_MAJOR_ATTR), 0);
+ AddCertDigest(element);
+ }
+ }
+
+ void AddCertDigest(xml::Element* element) {
+ std::string digest = GetAttributeStringDefault(FindAttribute(element, CERT_DIGEST_ATTR), "");
+ // We allow ":" delimiters in the SHA declaration as this is the format
+ // emitted by the certtool making it easy for developers to copy/paste.
+ digest.erase(std::remove(digest.begin(), digest.end(), ':'), digest.end());
+ if (!digest.empty()) {
+ certDigests.push_back(digest);
+ }
+ }
+
+ void Print(text::Printer& printer) override {
+ printer.Print(StringPrintf(
+ "uses-static-library: name='%s' version='%d' versionMajor='%d'",
+ name.data(), version, versionMajor));
+ for (size_t i = 0; i < certDigests.size(); i++) {
+ printer.Print(StringPrintf(" certDigest='%s'", certDigests[i].data()));
+ }
+ printer.Print("\n");
+ }
+};
+
/**
* Represents <meta-data> elements. These tags are only printed when a flag is passed in to
* explicitly enable meta data printing.
@@ -1326,29 +1396,29 @@
public:
MetaData() = default;
std::string name;
- const std::string* value;
+ std::string value;
const int* value_int;
- const std::string* resource;
+ std::string resource;
const int* resource_int;
void Extract(xml::Element* element) override {
name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
- value = GetAttributeString(FindAttribute(element, VALUE_ATTR));
+ value = GetAttributeStringDefault(FindAttribute(element, VALUE_ATTR), "");
value_int = GetAttributeInteger(FindAttribute(element, VALUE_ATTR));
- resource = GetAttributeString(FindAttribute(element, RESOURCE_ATTR));
+ resource = GetAttributeStringDefault(FindAttribute(element, RESOURCE_ATTR), "");
resource_int = GetAttributeInteger(FindAttribute(element, RESOURCE_ATTR));
}
void Print(text::Printer& printer) override {
if (extractor()->options_.include_meta_data && !name.empty()) {
printer.Print(StringPrintf("meta-data: name='%s' ", name.data()));
- if (value) {
- printer.Print(StringPrintf("value='%s' ", value->data()));
+ if (!value.empty()) {
+ printer.Print(StringPrintf("value='%s' ", value.data()));
} else if (value_int) {
printer.Print(StringPrintf("value='%d' ", *value_int));
} else {
- if (resource) {
- printer.Print(StringPrintf("resource='%s' ", resource->data()));
+ if (!resource.empty()) {
+ printer.Print(StringPrintf("resource='%s' ", resource.data()));
} else if (resource_int) {
printer.Print(StringPrintf("resource='%d' ", *resource_int));
}
@@ -1544,15 +1614,65 @@
class UsesPackage : public ManifestExtractor::Element {
public:
UsesPackage() = default;
+ const std::string* packageType = nullptr;
const std::string* name = nullptr;
+ int version;
+ int versionMajor;
+ std::vector<std::string> certDigests;
void Extract(xml::Element* element) override {
- name = GetAttributeString(FindAttribute(element, NAME_ATTR));
+ auto parent_stack = extractor()->parent_stack();
+ if (parent_stack.size() > 0 && ElementCast<Application>(parent_stack[0])) {
+ packageType = GetAttributeString(FindAttribute(element, PACKAGE_TYPE_ATTR));
+ name = GetAttributeString(FindAttribute(element, NAME_ATTR));
+ version = GetAttributeIntegerDefault(FindAttribute(element, VERSION_ATTR), 0);
+ versionMajor = GetAttributeIntegerDefault(FindAttribute(element, VERSION_MAJOR_ATTR), 0);
+ AddCertDigest(element);
+ }
+ }
+
+ void AddCertDigest(xml::Element* element) {
+ std::string digest = GetAttributeStringDefault(FindAttribute(element, CERT_DIGEST_ATTR), "");
+ // We allow ":" delimiters in the SHA declaration as this is the format
+ // emitted by the certtool making it easy for developers to copy/paste.
+ digest.erase(std::remove(digest.begin(), digest.end(), ':'), digest.end());
+ if (!digest.empty()) {
+ certDigests.push_back(digest);
+ }
}
void Print(text::Printer& printer) override {
if (name) {
- printer.Print(StringPrintf("uses-package:'%s'\n", name->data()));
+ if (packageType) {
+ printer.Print(StringPrintf(
+ "uses-typed-package: type='%s' name='%s' version='%d' versionMajor='%d'",
+ packageType->data(), name->data(), version, versionMajor));
+ for (size_t i = 0; i < certDigests.size(); i++) {
+ printer.Print(StringPrintf(" certDigest='%s'", certDigests[i].data()));
+ }
+ printer.Print("\n");
+ } else {
+ printer.Print(StringPrintf("uses-package:'%s'\n", name->data()));
+ }
+ }
+ }
+};
+
+/** Represents <additional-certificate> elements. **/
+class AdditionalCertificate : public ManifestExtractor::Element {
+ public:
+ AdditionalCertificate() = default;
+
+ void Extract(xml::Element* element) override {
+ auto parent_stack = extractor()->parent_stack();
+ if (parent_stack.size() > 0) {
+ if (ElementCast<UsesPackage>(parent_stack[0])) {
+ UsesPackage* uses = ElementCast<UsesPackage>(parent_stack[0]);
+ uses->AddCertDigest(element);
+ } else if (ElementCast<UsesStaticLibrary>(parent_stack[0])) {
+ UsesStaticLibrary* uses = ElementCast<UsesStaticLibrary>(parent_stack[0]);
+ uses->AddCertDigest(element);
+ }
}
}
};
@@ -1837,10 +1957,10 @@
&& offhost_apdu_action)) {
// Attempt to load the resource file
- if (!meta_data->resource) {
+ if (!meta_data->resource.empty()) {
return;
}
- auto resource = apk->LoadXml(*meta_data->resource, diag);
+ auto resource = apk->LoadXml(meta_data->resource, diag);
if (!resource) {
return;
}
@@ -2065,6 +2185,9 @@
{"uses-permission-sdk-23", std::is_base_of<UsesPermissionSdk23, T>::value},
{"uses-library", std::is_base_of<UsesLibrary, T>::value},
{"uses-package", std::is_base_of<UsesPackage, T>::value},
+ {"static-library", std::is_base_of<StaticLibrary, T>::value},
+ {"uses-static-library", std::is_base_of<UsesStaticLibrary, T>::value},
+ {"additional-certificate", std::is_base_of<AdditionalCertificate, T>::value},
{"uses-sdk", std::is_base_of<UsesSdkBadging, T>::value},
};
@@ -2110,7 +2233,10 @@
{"uses-permission", &CreateType<UsesPermission>},
{"uses-permission-sdk-23", &CreateType<UsesPermissionSdk23>},
{"uses-library", &CreateType<UsesLibrary>},
+ {"static-library", &CreateType<StaticLibrary>},
+ {"uses-static-library", &CreateType<UsesStaticLibrary>},
{"uses-package", &CreateType<UsesPackage>},
+ {"additional-certificate", &CreateType<AdditionalCertificate>},
{"uses-sdk", &CreateType<UsesSdkBadging>},
};
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index fa6538d..85bf6f2 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -393,6 +393,10 @@
uses_static_library_action.Action(RequiredAndroidAttribute("certDigest"));
uses_static_library_action["additional-certificate"];
+ xml::XmlNodeAction& uses_package_action = application_action["uses-package"];
+ uses_package_action.Action(RequiredNameIsJavaPackage);
+ uses_package_action["additional-certificate"];
+
if (options_.debug_mode) {
application_action.Action([&](xml::Element* el) -> bool {
xml::Attribute *attr = el->FindOrCreateAttribute(xml::kSchemaAndroid, "debuggable");
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 58c1300..7472278 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -513,7 +513,7 @@
/**
* @hide
* Universal name for app creating the configuration
- * see {#link {@link PackageManager#getNameForUid(int)}
+ * see {@link PackageManager#getNameForUid(int)}
*/
@SystemApi
public String creatorName;
@@ -521,7 +521,7 @@
/**
* @hide
* Universal name for app updating the configuration
- * see {#link {@link PackageManager#getNameForUid(int)}
+ * see {@link PackageManager#getNameForUid(int)}
*/
@SystemApi
public String lastUpdateName;