Merge "Rename ANAS to ANS"
diff --git a/Android.bp b/Android.bp
index f1e154c..13c6e85 100644
--- a/Android.bp
+++ b/Android.bp
@@ -152,9 +152,10 @@
":libcamera_client_framework_aidl",
"core/java/android/hardware/IConsumerIrService.aidl",
"core/java/android/hardware/ISerialManager.aidl",
+ "core/java/android/hardware/biometrics/IBiometricEnabledOnKeyguardCallback.aidl",
+ "core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl",
"core/java/android/hardware/biometrics/IBiometricService.aidl",
"core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl",
- "core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl",
"core/java/android/hardware/biometrics/IBiometricServiceLockoutResetCallback.aidl",
"core/java/android/hardware/display/IDisplayManager.aidl",
"core/java/android/hardware/display/IDisplayManagerCallback.aidl",
@@ -702,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 e6a31af..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;
@@ -15963,7 +15975,11 @@
package android.hardware.biometrics {
public class BiometricManager {
- method public boolean hasEnrolledBiometrics();
+ 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
}
public class BiometricPrompt {
@@ -36955,6 +36971,7 @@
field public static final java.lang.String ACTION_APPLICATION_DEVELOPMENT_SETTINGS = "android.settings.APPLICATION_DEVELOPMENT_SETTINGS";
field public static final java.lang.String ACTION_APPLICATION_SETTINGS = "android.settings.APPLICATION_SETTINGS";
field public static final java.lang.String ACTION_APP_NOTIFICATION_SETTINGS = "android.settings.APP_NOTIFICATION_SETTINGS";
+ field public static final java.lang.String ACTION_APP_SEARCH_SETTINGS = "android.settings.APP_SEARCH_SETTINGS";
field public static final java.lang.String ACTION_BATTERY_SAVER_SETTINGS = "android.settings.BATTERY_SAVER_SETTINGS";
field public static final java.lang.String ACTION_BLUETOOTH_SETTINGS = "android.settings.BLUETOOTH_SETTINGS";
field public static final java.lang.String ACTION_CAPTIONING_SETTINGS = "android.settings.CAPTIONING_SETTINGS";
@@ -43343,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 {
@@ -46641,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 041b497..e134554 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5330,9 +5330,12 @@
}
public class ServiceState implements android.os.Parcelable {
+ method public android.telephony.NetworkRegistrationState getNetworkRegistrationState(int, int);
method public java.util.List<android.telephony.NetworkRegistrationState> getNetworkRegistrationStates();
- method public java.util.List<android.telephony.NetworkRegistrationState> getNetworkRegistrationStates(int);
- method public android.telephony.NetworkRegistrationState getNetworkRegistrationStates(int, int);
+ method public deprecated java.util.List<android.telephony.NetworkRegistrationState> getNetworkRegistrationStates(int);
+ method public deprecated android.telephony.NetworkRegistrationState getNetworkRegistrationStates(int, int);
+ method public java.util.List<android.telephony.NetworkRegistrationState> getNetworkRegistrationStatesForDomain(int);
+ method public java.util.List<android.telephony.NetworkRegistrationState> getNetworkRegistrationStatesForTransportType(int);
}
public final class SmsManager {
@@ -5849,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 988ffc4..9d83afd 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -31,6 +31,7 @@
import "frameworks/base/core/proto/android/view/enums.proto";
import "frameworks/base/proto/src/stats_enums.proto";
import "frameworks/base/core/proto/android/service/procstats.proto";
+import "frameworks/base/core/proto/android/internal/powerprofile.proto";
/**
* The master atom class. This message defines all of the available
@@ -175,10 +176,12 @@
DirectoryUsage directory_usage = 10026;
AppSize app_size = 10027;
CategorySize category_size = 10028;
+ ProcStats proc_stats = 10029;
BatteryVoltage battery_voltage = 10030;
NumFingerprints num_fingerprints = 10031;
- ProcStats proc_stats = 10029;
DiskIo disk_io = 10032;
+ PowerProfile power_profile = 10033;
+ ProcStats proc_stats_pkg_proc = 10034;
}
// DO NOT USE field numbers above 100,000 in AOSP.
@@ -2665,3 +2668,11 @@
message ProcStats {
optional android.service.procstats.ProcessStatsSectionProto proc_stats_section = 1;
}
+
+/**
+ * power_profile.xml and other constants for power model calculations.
+ * Pulled from PowerProfile.java
+ */
+message PowerProfile {
+ optional com.android.internal.os.PowerProfileProto power_profile_proto = 1;
+}
\ No newline at end of file
diff --git a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp b/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
index 6d7bba0..3eb05a9 100644
--- a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
+++ b/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
@@ -54,7 +54,7 @@
vector<StatsLogEventWrapper> returned_value;
Status status = statsCompanionServiceCopy->pullData(mTagId, &returned_value);
if (!status.isOk()) {
- ALOGW("StatsCompanionServicePuller::pull failed to pull for %d", mTagId);
+ ALOGW("StatsCompanionServicePuller::pull failed for %d", mTagId);
return false;
}
data->clear();
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index fd86714..dab64ca 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -211,12 +211,18 @@
// 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},
{},
3 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::DISK_IO)}},
+ // PowerProfile constants for power model calculations.
+ {android::util::POWER_PROFILE,
+ {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::POWER_PROFILE)}},
};
StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
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/accounts/AbstractAccountAuthenticator.java b/core/java/android/accounts/AbstractAccountAuthenticator.java
index a3b3a9f..25cd342 100644
--- a/core/java/android/accounts/AbstractAccountAuthenticator.java
+++ b/core/java/android/accounts/AbstractAccountAuthenticator.java
@@ -17,7 +17,6 @@
package android.accounts;
import android.Manifest;
-import android.annotation.SystemApi;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -32,8 +31,8 @@
/**
* Abstract base class for creating AccountAuthenticators.
- * In order to be an authenticator one must extend this class, provider implementations for the
- * abstract methods and write a service that returns the result of {@link #getIBinder()}
+ * In order to be an authenticator one must extend this class, provide implementations for the
+ * abstract methods, and write a service that returns the result of {@link #getIBinder()}
* in the service's {@link android.app.Service#onBind(android.content.Intent)} when invoked
* with an intent with action {@link AccountManager#ACTION_AUTHENTICATOR_INTENT}. This service
* must specify the following intent filter and metadata tags in its AndroidManifest.xml file
@@ -974,7 +973,8 @@
*
* @param response to send the result back to the AccountManager, will never be null.
* @param account the account to check, will never be null
- * @param statusToken a String of token to check if update of credentials is suggested.
+ * @param statusToken a String of token which can be used to check the status of locally
+ * stored credentials and if update of credentials is suggested
* @return a Bundle result or null if the result is to be returned via the response. The result
* will contain either:
* <ul>
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/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 07a8504..3f07024 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -48,6 +48,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
@@ -722,10 +723,15 @@
public List<NotificationChannelGroup> getNotificationChannelGroups() {
INotificationManager service = getService();
try {
- return service.getNotificationChannelGroups(mContext.getPackageName()).getList();
+ final ParceledListSlice<NotificationChannelGroup> parceledList =
+ service.getNotificationChannelGroups(mContext.getPackageName());
+ if (parceledList != null) {
+ return parceledList.getList();
+ }
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
+ return new ArrayList<>();
}
/**
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index 67acfe9..16f6bda 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -21,13 +21,13 @@
import android.annotation.UnsupportedAppUsage;
import android.content.ComponentCallbacks2;
import android.content.ComponentName;
-import android.content.Intent;
-import android.content.ContextWrapper;
import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
import android.content.res.Configuration;
import android.os.Build;
-import android.os.RemoteException;
import android.os.IBinder;
+import android.os.RemoteException;
import android.util.Log;
import java.io.FileDescriptor;
@@ -391,7 +391,7 @@
* don't recreate until a future explicit call to
* {@link Context#startService Context.startService(Intent)}. The
* service will not receive a {@link #onStartCommand(Intent, int, int)}
- * call with a null Intent because it will not be re-started if there
+ * call with a null Intent because it will not be restarted if there
* are no pending Intents to deliver.
*
* <p>This mode makes sense for things that want to do some work as a
@@ -416,7 +416,7 @@
* redelivery until the service calls {@link #stopSelf(int)} with the
* start ID provided to {@link #onStartCommand}. The
* service will not receive a {@link #onStartCommand(Intent, int, int)}
- * call with a null Intent because it will will only be re-started if
+ * call with a null Intent because it will only be restarted if
* it is not finished processing all Intents sent to it (and any such
* pending events will be delivered at the point of restart).
*/
diff --git a/core/java/android/app/SmsAppService.java b/core/java/android/app/SmsAppService.java
index 3f2b025..3829d71 100644
--- a/core/java/android/app/SmsAppService.java
+++ b/core/java/android/app/SmsAppService.java
@@ -24,21 +24,42 @@
* it so that the process is always running, which allows the app to have a persistent connection
* to the server.
*
- * <p>The service must have {@link android.telephony.TelephonyManager#ACTION_SMS_APP_SERVICE}
+ * <p>The service must have an {@link android.telephony.TelephonyManager#ACTION_SMS_APP_SERVICE}
* action in the intent handler, and be protected with
* {@link android.Manifest.permission#BIND_SMS_APP_SERVICE}. However the service does not have to
* be exported.
*
- * <p>Apps can use
+ * <p>The service must be associated with a non-main process, meaning it must have an
+ * {@code android:process} tag in its manifest entry.
+ *
+ * <p>An app can use
* {@link android.content.pm.PackageManager#setComponentEnabledSetting(ComponentName, int, int)}
- * to disable/enable the service. Apps should use it to disable the service when it no longer needs
- * to be running.
+ * to disable or enable the service. An app should use it to disable the service when it no longer
+ * needs to be running.
*
* <p>When the owner process crashes, the service will be re-bound automatically after a
* back-off.
*
* <p>Note the process may still be killed if the system is under heavy memory pressure, in which
* case the process will be re-started later.
+ *
+ * <p>Example: First, define a subclass in the application:
+ * <pre>
+ * public class MySmsAppService extends SmsAppService {
+ * }
+ * </pre>
+ * Then, declare it in its {@code AndroidManifest.xml}:
+ * <pre>
+ * <service
+ * android:name=".MySmsAppService"
+ * android:exported="false"
+ * android:process=":persistent"
+ * android:permission="android.permission.BIND_SMS_APP_SERVICE">
+ * <intent-filter>
+ * <action android:name="android.telephony.action.SMS_APP_SERVICE" />
+ * </intent-filter>
+ * </service>
+ * </pre>
*/
public class SmsAppService extends Service {
private final ISmsAppService mImpl;
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/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index a64eead..a13a438 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -2104,7 +2104,11 @@
// a source of security issues.
final String encodedPath = uri.getEncodedPath();
if (encodedPath != null && encodedPath.indexOf("//") != -1) {
- return uri.buildUpon().encodedPath(encodedPath.replaceAll("//+", "/")).build();
+ final Uri normalized = uri.buildUpon()
+ .encodedPath(encodedPath.replaceAll("//+", "/")).build();
+ Log.w(TAG, "Normalized " + uri + " to " + normalized
+ + " to avoid possible security issues");
+ return normalized;
} else {
return uri;
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index f5431ca..24675d3 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -5785,52 +5785,32 @@
int AUTH = 16;
}
- /**
- * APK Signature Scheme v3 includes support for adding a proof-of-rotation record that
- * contains two pieces of information:
- * 1) the past signing certificates
- * 2) the flags that APK wants to assign to each of the past signing certificates.
- *
- * These flags, which have a one-to-one relationship for the {@code pastSigningCertificates}
- * collection, represent the second piece of information and are viewed as capabilities.
- * They are an APK's way of telling the platform: "this is how I want to trust my old certs,
- * please enforce that." This is useful for situation where this app itself is using its
- * signing certificate as an authorization mechanism, like whether or not to allow another
- * app to have its SIGNATURE permission. An app could specify whether to allow other apps
- * signed by its old cert 'X' to still get a signature permission it defines, for example.
- */
- @Nullable
- public final int[] pastSigningCertificatesFlags;
-
/** A representation of unknown signing details. Use instead of null. */
public static final SigningDetails UNKNOWN =
- new SigningDetails(null, SignatureSchemeVersion.UNKNOWN, null, null, null);
+ new SigningDetails(null, SignatureSchemeVersion.UNKNOWN, null, null);
@VisibleForTesting
public SigningDetails(Signature[] signatures,
@SignatureSchemeVersion int signatureSchemeVersion,
- ArraySet<PublicKey> keys, Signature[] pastSigningCertificates,
- int[] pastSigningCertificatesFlags) {
+ ArraySet<PublicKey> keys, Signature[] pastSigningCertificates) {
this.signatures = signatures;
this.signatureSchemeVersion = signatureSchemeVersion;
this.publicKeys = keys;
this.pastSigningCertificates = pastSigningCertificates;
- this.pastSigningCertificatesFlags = pastSigningCertificatesFlags;
}
public SigningDetails(Signature[] signatures,
@SignatureSchemeVersion int signatureSchemeVersion,
- Signature[] pastSigningCertificates, int[] pastSigningCertificatesFlags)
+ Signature[] pastSigningCertificates)
throws CertificateException {
this(signatures, signatureSchemeVersion, toSigningKeys(signatures),
- pastSigningCertificates, pastSigningCertificatesFlags);
+ pastSigningCertificates);
}
public SigningDetails(Signature[] signatures,
@SignatureSchemeVersion int signatureSchemeVersion)
throws CertificateException {
- this(signatures, signatureSchemeVersion,
- null, null);
+ this(signatures, signatureSchemeVersion, null);
}
public SigningDetails(SigningDetails orig) {
@@ -5844,17 +5824,14 @@
this.publicKeys = new ArraySet<>(orig.publicKeys);
if (orig.pastSigningCertificates != null) {
this.pastSigningCertificates = orig.pastSigningCertificates.clone();
- this.pastSigningCertificatesFlags = orig.pastSigningCertificatesFlags.clone();
} else {
this.pastSigningCertificates = null;
- this.pastSigningCertificatesFlags = null;
}
} else {
this.signatures = null;
this.signatureSchemeVersion = SignatureSchemeVersion.UNKNOWN;
this.publicKeys = null;
this.pastSigningCertificates = null;
- this.pastSigningCertificatesFlags = null;
}
}
@@ -5956,7 +5933,7 @@
if (Signature.areEffectiveMatch(
oldDetails.signatures[0],
pastSigningCertificates[i])
- && pastSigningCertificatesFlags[i] == flags) {
+ && pastSigningCertificates[i].getFlags() == flags) {
return true;
}
}
@@ -6006,7 +5983,7 @@
for (int i = 0; i < pastSigningCertificates.length - 1; i++) {
if (pastSigningCertificates[i].equals(signature)) {
if (flags == PAST_CERT_EXISTS
- || (flags & pastSigningCertificatesFlags[i]) == flags) {
+ || (flags & pastSigningCertificates[i].getFlags()) == flags) {
return true;
}
}
@@ -6090,7 +6067,7 @@
pastSigningCertificates[i].toByteArray());
if (Arrays.equals(sha256Certificate, digest)) {
if (flags == PAST_CERT_EXISTS
- || (flags & pastSigningCertificatesFlags[i]) == flags) {
+ || (flags & pastSigningCertificates[i].getFlags()) == flags) {
return true;
}
}
@@ -6127,7 +6104,6 @@
dest.writeInt(this.signatureSchemeVersion);
dest.writeArraySet(this.publicKeys);
dest.writeTypedArray(this.pastSigningCertificates, flags);
- dest.writeIntArray(this.pastSigningCertificatesFlags);
}
protected SigningDetails(Parcel in) {
@@ -6136,7 +6112,6 @@
this.signatureSchemeVersion = in.readInt();
this.publicKeys = (ArraySet<PublicKey>) in.readArraySet(boot);
this.pastSigningCertificates = in.createTypedArray(Signature.CREATOR);
- this.pastSigningCertificatesFlags = in.createIntArray();
}
public static final Creator<SigningDetails> CREATOR = new Creator<SigningDetails>() {
@@ -6175,9 +6150,6 @@
if (!Arrays.equals(pastSigningCertificates, that.pastSigningCertificates)) {
return false;
}
- if (!Arrays.equals(pastSigningCertificatesFlags, that.pastSigningCertificatesFlags)) {
- return false;
- }
return true;
}
@@ -6188,7 +6160,6 @@
result = 31 * result + signatureSchemeVersion;
result = 31 * result + (publicKeys != null ? publicKeys.hashCode() : 0);
result = 31 * result + Arrays.hashCode(pastSigningCertificates);
- result = 31 * result + Arrays.hashCode(pastSigningCertificatesFlags);
return result;
}
@@ -6199,7 +6170,6 @@
private Signature[] mSignatures;
private int mSignatureSchemeVersion = SignatureSchemeVersion.UNKNOWN;
private Signature[] mPastSigningCertificates;
- private int[] mPastSigningCertificatesFlags;
@UnsupportedAppUsage
public Builder() {
@@ -6226,34 +6196,12 @@
return this;
}
- /** set the flags for the {@code pastSigningCertificates} */
- @UnsupportedAppUsage
- public Builder setPastSigningCertificatesFlags(int[] pastSigningCertificatesFlags) {
- mPastSigningCertificatesFlags = pastSigningCertificatesFlags;
- return this;
- }
-
private void checkInvariants() {
// must have signatures and scheme version set
if (mSignatures == null) {
throw new IllegalStateException("SigningDetails requires the current signing"
+ " certificates.");
}
-
- // pastSigningCerts and flags must match up
- boolean pastMismatch = false;
- if (mPastSigningCertificates != null && mPastSigningCertificatesFlags != null) {
- if (mPastSigningCertificates.length != mPastSigningCertificatesFlags.length) {
- pastMismatch = true;
- }
- } else if (!(mPastSigningCertificates == null
- && mPastSigningCertificatesFlags == null)) {
- pastMismatch = true;
- }
- if (pastMismatch) {
- throw new IllegalStateException("SigningDetails must have a one to one mapping "
- + "between pastSigningCertificates and pastSigningCertificatesFlags");
- }
}
/** build a {@code SigningDetails} object */
@UnsupportedAppUsage
@@ -6261,7 +6209,7 @@
throws CertificateException {
checkInvariants();
return new SigningDetails(mSignatures, mSignatureSchemeVersion,
- mPastSigningCertificates, mPastSigningCertificatesFlags);
+ mPastSigningCertificates);
}
}
}
diff --git a/core/java/android/content/pm/Signature.java b/core/java/android/content/pm/Signature.java
index e58ca60..349bb69 100644
--- a/core/java/android/content/pm/Signature.java
+++ b/core/java/android/content/pm/Signature.java
@@ -45,6 +45,20 @@
private boolean mHaveHashCode;
private SoftReference<String> mStringRef;
private Certificate[] mCertificateChain;
+ /**
+ * APK Signature Scheme v3 includes support for adding a proof-of-rotation record that
+ * contains two pieces of information:
+ * 1) the past signing certificates
+ * 2) the flags that APK wants to assign to each of the past signing certificates.
+ *
+ * These flags represent the second piece of information and are viewed as capabilities.
+ * They are an APK's way of telling the platform: "this is how I want to trust my old certs,
+ * please enforce that." This is useful for situation where this app itself is using its
+ * signing certificate as an authorization mechanism, like whether or not to allow another
+ * app to have its SIGNATURE permission. An app could specify whether to allow other apps
+ * signed by its old cert 'X' to still get a signature permission it defines, for example.
+ */
+ private int mFlags;
/**
* Create Signature from an existing raw byte array.
@@ -109,6 +123,22 @@
}
/**
+ * Sets the flags representing the capabilities of the past signing certificate.
+ * @hide
+ */
+ public void setFlags(int flags) {
+ this.mFlags = flags;
+ }
+
+ /**
+ * Returns the flags representing the capabilities of the past signing certificate.
+ * @hide
+ */
+ public int getFlags() {
+ return mFlags;
+ }
+
+ /**
* Encode the Signature as ASCII text.
*/
public char[] toChars() {
@@ -328,4 +358,4 @@
return sPrime;
}
-}
+}
\ No newline at end of file
diff --git a/core/java/android/hardware/biometrics/BiometricAuthenticator.java b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
index c604ff1..1734b41 100644
--- a/core/java/android/hardware/biometrics/BiometricAuthenticator.java
+++ b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
@@ -201,6 +201,10 @@
throw new UnsupportedOperationException("Stub!");
}
+ default boolean hasEnrolledTemplates(int userId) {
+ throw new UnsupportedOperationException("Stub!");
+ }
+
/**
* This call warms up the hardware and starts scanning for valid biometrics. It terminates
* when {@link AuthenticationCallback#onAuthenticationError(int,
diff --git a/core/java/android/hardware/biometrics/BiometricConstants.java b/core/java/android/hardware/biometrics/BiometricConstants.java
index 6150be3..2a64c2e 100644
--- a/core/java/android/hardware/biometrics/BiometricConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricConstants.java
@@ -34,6 +34,13 @@
//
/**
+ * This was not added here since it would update BiometricPrompt API. But, is used in
+ * BiometricManager.
+ * @hide
+ */
+ int BIOMETRIC_ERROR_NONE = 0;
+
+ /**
* The hardware is unavailable. Try again later.
*/
int BIOMETRIC_ERROR_HW_UNAVAILABLE = 1;
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index eea5f9b..0faecb0 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -17,16 +17,44 @@
package android.hardware.biometrics;
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;
+import android.util.Slog;
/**
* A class that contains biometric utilities. For authentication, see {@link BiometricPrompt}.
*/
public class BiometricManager {
+ private static final String TAG = "BiometricManager";
+
+ /**
+ * No error detected.
+ */
+ public static final int ERROR_NONE = BiometricConstants.BIOMETRIC_ERROR_NONE;
+
+ /**
+ * The hardware is unavailable. Try again later.
+ */
+ public static final int ERROR_UNAVAILABLE = BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE;
+
+ /**
+ * The user does not have any biometrics enrolled.
+ */
+ 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;
@@ -41,16 +69,42 @@
}
/**
- * Determine if there is at least one biometric enrolled.
+ * Determine if biometrics can be used. In other words, determine if {@link BiometricPrompt}
+ * can be expected to be shown (hardware available, templates enrolled, user-enabled).
*
- * @return true if at least one biometric is enrolled, false otherwise
+ * @return Returns {@link #ERROR_NO_BIOMETRICS} if the user does not have any enrolled, or
+ * {@link #ERROR_UNAVAILABLE} if none are currently supported/enabled. Returns
+ * {@link #ERROR_NONE} if a biometric can currently be used (enrolled and available).
*/
@RequiresPermission(USE_BIOMETRIC)
- public boolean hasEnrolledBiometrics() {
- try {
- return mService.hasEnrolledBiometrics(mContext.getOpPackageName());
- } catch (RemoteException e) {
- return false;
+ public @BiometricError int canAuthenticate() {
+ if (mService != null) {
+ try {
+ return mService.canAuthenticate(mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ } else {
+ Slog.w(TAG, "hasEnrolledBiometrics(): Service not connected");
+ return ERROR_UNAVAILABLE;
+ }
+ }
+
+ /**
+ * Listens for changes to biometric eligibility on keyguard from user settings.
+ * @param callback
+ * @hide
+ */
+ @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+ public void registerEnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback) {
+ if (mService != null) {
+ try {
+ mService.registerEnabledOnKeyguardCallback(callback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ } else {
+ Slog.w(TAG, "registerEnabledOnKeyguardCallback(): Service not connected");
}
}
}
diff --git a/core/java/android/hardware/biometrics/IBiometricEnabledOnKeyguardCallback.aidl b/core/java/android/hardware/biometrics/IBiometricEnabledOnKeyguardCallback.aidl
new file mode 100644
index 0000000..d22e7e2
--- /dev/null
+++ b/core/java/android/hardware/biometrics/IBiometricEnabledOnKeyguardCallback.aidl
@@ -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.
+ */
+
+package android.hardware.biometrics;
+
+import android.hardware.biometrics.BiometricSourceType;
+
+/**
+ * @hide
+ */
+oneway interface IBiometricEnabledOnKeyguardCallback {
+ void onChanged(in BiometricSourceType type, boolean enabled);
+}
\ No newline at end of file
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
index 67c9346..27d25b8 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
@@ -15,9 +15,6 @@
*/
package android.hardware.biometrics;
-import android.os.Bundle;
-import android.os.UserHandle;
-
/**
* Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
* @hide
diff --git a/core/java/android/hardware/biometrics/IBiometricService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl
index fd9d572..51e4ecb 100644
--- a/core/java/android/hardware/biometrics/IBiometricService.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricService.aidl
@@ -17,6 +17,7 @@
package android.hardware.biometrics;
import android.os.Bundle;
+import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.biometrics.IBiometricPromptReceiver;
import android.hardware.biometrics.IBiometricServiceReceiver;
@@ -37,6 +38,9 @@
// Cancel authentication for the given sessionId
void cancelAuthentication(IBinder token, String opPackageName);
- // Returns true if the user has at least one enrolled biometric.
- boolean hasEnrolledBiometrics(String opPackageName);
+ // Checks if biometrics can be used.
+ int canAuthenticate(String opPackageName);
+
+ // Register callback for when keyguard biometric eligibility changes.
+ void registerEnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback);
}
\ No newline at end of file
diff --git a/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl b/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl
index 71abdd2..a6e3696 100644
--- a/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl
@@ -15,10 +15,6 @@
*/
package android.hardware.biometrics;
-import android.hardware.biometrics.BiometricSourceType;
-import android.os.Bundle;
-import android.os.UserHandle;
-
/**
* Communication channel from the BiometricService back to BiometricPrompt.
* @hide
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/hardware/display/DisplayViewport.java b/core/java/android/hardware/display/DisplayViewport.java
index 496f34c..df0d46b 100644
--- a/core/java/android/hardware/display/DisplayViewport.java
+++ b/core/java/android/hardware/display/DisplayViewport.java
@@ -16,9 +16,14 @@
package android.hardware.display;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
import android.graphics.Rect;
import android.text.TextUtils;
+import java.lang.annotation.Retention;
+
/**
* Describes how the pixels of physical display device reflects the content of
* a logical display.
@@ -35,6 +40,10 @@
public static final int VIEWPORT_INTERNAL = 1;
public static final int VIEWPORT_EXTERNAL = 2;
public static final int VIEWPORT_VIRTUAL = 3;
+ @IntDef(prefix = { "VIEWPORT_" }, value = {
+ VIEWPORT_INTERNAL, VIEWPORT_EXTERNAL, VIEWPORT_VIRTUAL})
+ @Retention(SOURCE)
+ public @interface ViewportType {};
// True if this viewport is valid.
public boolean valid;
@@ -62,6 +71,8 @@
// The ID used to uniquely identify this display.
public String uniqueId;
+ public @ViewportType int type;
+
public void copyFrom(DisplayViewport viewport) {
valid = viewport.valid;
displayId = viewport.displayId;
@@ -71,6 +82,7 @@
deviceWidth = viewport.deviceWidth;
deviceHeight = viewport.deviceHeight;
uniqueId = viewport.uniqueId;
+ type = viewport.type;
}
/**
@@ -100,7 +112,8 @@
&& physicalFrame.equals(other.physicalFrame)
&& deviceWidth == other.deviceWidth
&& deviceHeight == other.deviceHeight
- && TextUtils.equals(uniqueId, other.uniqueId);
+ && TextUtils.equals(uniqueId, other.uniqueId)
+ && type == other.type;
}
@Override
@@ -115,13 +128,15 @@
result += prime * result + deviceWidth;
result += prime * result + deviceHeight;
result += prime * result + uniqueId.hashCode();
+ result += prime * result + type;
return result;
}
// For debugging purposes.
@Override
public String toString() {
- return "DisplayViewport{valid=" + valid
+ return "DisplayViewport{type=" + typeToString(type)
+ + ", valid=" + valid
+ ", displayId=" + displayId
+ ", uniqueId='" + uniqueId + "'"
+ ", orientation=" + orientation
@@ -131,4 +146,20 @@
+ ", deviceHeight=" + deviceHeight
+ "}";
}
+
+ /**
+ * Human-readable viewport type.
+ */
+ public static String typeToString(@ViewportType int viewportType) {
+ switch (viewportType) {
+ case VIEWPORT_INTERNAL:
+ return "INTERNAL";
+ case VIEWPORT_EXTERNAL:
+ return "EXTERNAL";
+ case VIEWPORT_VIRTUAL:
+ return "VIRTUAL";
+ default:
+ return "UNKNOWN (" + viewportType + ")";
+ }
+ }
}
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 873a24a..7aec43e 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -408,6 +408,7 @@
@RequiresPermission(allOf = {
USE_BIOMETRIC_INTERNAL,
INTERACT_ACROSS_USERS})
+ @Override
public boolean hasEnrolledTemplates(int userId) {
if (mService != null) {
try {
diff --git a/core/java/android/hardware/face/IFaceServiceReceiver.aidl b/core/java/android/hardware/face/IFaceServiceReceiver.aidl
index 16fb690..b88574b 100644
--- a/core/java/android/hardware/face/IFaceServiceReceiver.aidl
+++ b/core/java/android/hardware/face/IFaceServiceReceiver.aidl
@@ -16,8 +16,6 @@
package android.hardware.face;
import android.hardware.face.Face;
-import android.os.Bundle;
-import android.os.UserHandle;
/**
* Communication channel from the FaceService back to FaceAuthenticationManager.
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index a4f3ce1..bf2280d 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -637,6 +637,14 @@
}
/**
+ * @hide
+ */
+ @Override
+ public boolean hasEnrolledTemplates(int userId) {
+ return hasEnrolledFingerprints(userId);
+ }
+
+ /**
* Determine if there is at least one fingerprint enrolled.
*
* @return true if at least one fingerprint is enrolled, false otherwise
diff --git a/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
index 370383f..cf1c94e 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
@@ -16,8 +16,6 @@
package android.hardware.fingerprint;
import android.hardware.fingerprint.Fingerprint;
-import android.os.Bundle;
-import android.os.UserHandle;
/**
* Communication channel from the FingerprintService back to FingerprintManager.
diff --git a/core/java/android/hardware/input/InputManagerInternal.java b/core/java/android/hardware/input/InputManagerInternal.java
index c4d7e40..d8da548 100644
--- a/core/java/android/hardware/input/InputManagerInternal.java
+++ b/core/java/android/hardware/input/InputManagerInternal.java
@@ -40,8 +40,7 @@
* Called by the display manager to set information about the displays as needed
* by the input system. The input system must copy this information to retain it.
*/
- public abstract void setDisplayViewports(DisplayViewport defaultViewport,
- DisplayViewport externalTouchViewport, List<DisplayViewport> virtualTouchViewports);
+ public abstract void setDisplayViewports(List<DisplayViewport> viewports);
/**
* Called by the power manager to tell the input manager whether it should start
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/net/UrlQuerySanitizer.java b/core/java/android/net/UrlQuerySanitizer.java
index d2073b4..5b67406 100644
--- a/core/java/android/net/UrlQuerySanitizer.java
+++ b/core/java/android/net/UrlQuerySanitizer.java
@@ -287,7 +287,7 @@
/**
* Sanitize a value.
* <ol>
- * <li>If script URLs are not OK, the will be removed.
+ * <li>If script URLs are not OK, they will be removed.
* <li>If neither spaces nor other white space is OK, then
* white space will be trimmed from the beginning and end of
* the URL. (Just the actual white space characters are trimmed, not
@@ -563,7 +563,7 @@
}
/**
- * Constructs a UrlQuerySanitizer and parse a URL.
+ * Constructs a UrlQuerySanitizer and parses a URL.
* This constructor is provided for convenience when the
* default parsing behavior is acceptable.
* <p>
@@ -644,7 +644,7 @@
}
/**
- * An array list of all of the parameter value pairs in the sanitized
+ * An array list of all of the parameter-value pairs in the sanitized
* query, in the order they appeared in the query. May contain duplicate
* parameters.
* <p class="note"><b>Note:</b> Do not modify this list. Treat it as a read-only list.</p>
@@ -656,7 +656,7 @@
/**
* Check if a parameter exists in the current sanitized query.
* @param parameter the unencoded name of a parameter.
- * @return true if the paramater exists in the current sanitized queary.
+ * @return true if the parameter exists in the current sanitized queary.
*/
public boolean hasParameter(String parameter) {
return mEntries.containsKey(parameter);
@@ -766,7 +766,7 @@
* the value. If all goes well then addSanitizedValue is called with
* the unescaped parameter and the sanitized unescaped value.
* @param parameter an escaped parameter
- * @param value an unsanitzied escaped value
+ * @param value an unsanitized escaped value
*/
protected void parseEntry(String parameter, String value) {
String unescapedParameter = unescape(parameter);
@@ -812,7 +812,7 @@
/**
* Get the effective value sanitizer for a parameter. Like getValueSanitizer,
* except if there is no value sanitizer registered for a parameter, and
- * unregistered paramaters are allowed, then the default value sanitizer is
+ * unregistered parameters are allowed, then the default value sanitizer is
* returned.
* @param parameter an unescaped parameter
* @return the effective value sanitizer for a parameter.
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 3c43fd18..483b764 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -219,12 +219,11 @@
* services to store files relating to the user. This directory will be
* automatically deleted when the user is removed.
*
- * @deprecated This directory is valid and still exists, but callers should
- * <em>strongly</em> consider switching to
- * {@link #getDataSystemCeDirectory(int)} which is protected
- * with user credentials or
- * {@link #getDataSystemDeDirectory(int)} which supports fast
- * user wipe.
+ * @deprecated This directory is valid and still exists, but but callers
+ * should <em>strongly</em> consider switching to using either
+ * {@link #getDataSystemCeDirectory(int)} or
+ * {@link #getDataSystemDeDirectory(int)}, both of which support
+ * fast user wipe.
* @hide
*/
@Deprecated
@@ -292,12 +291,42 @@
return buildPath(getDataDirectory(), "system_ce");
}
- /** {@hide} */
+ /**
+ * Return the "credential encrypted" system directory for a user. This is
+ * for use by system services to store files relating to the user. This
+ * directory supports fast user wipe, and will be automatically deleted when
+ * the user is removed.
+ * <p>
+ * Data stored under this path is "credential encrypted", which uses an
+ * encryption key that is entangled with user credentials, such as a PIN or
+ * password. The contents will only be available once the user has been
+ * unlocked, as reported by {@code SystemService.onUnlockUser()}.
+ * <p>
+ * New code should <em>strongly</em> prefer storing sensitive data in these
+ * credential encrypted areas.
+ *
+ * @hide
+ */
public static File getDataSystemCeDirectory(int userId) {
return buildPath(getDataDirectory(), "system_ce", String.valueOf(userId));
}
- /** {@hide} */
+ /**
+ * Return the "device encrypted" system directory for a user. This is for
+ * use by system services to store files relating to the user. This
+ * directory supports fast user wipe, and will be automatically deleted when
+ * the user is removed.
+ * <p>
+ * Data stored under this path is "device encrypted", which uses an
+ * encryption key that is tied to the physical device. The contents will
+ * only be available once the device has finished a {@code dm-verity}
+ * protected boot.
+ * <p>
+ * New code should <em>strongly</em> avoid storing sensitive data in these
+ * device encrypted areas.
+ *
+ * @hide
+ */
public static File getDataSystemDeDirectory(int userId) {
return buildPath(getDataDirectory(), "system_de", String.valueOf(userId));
}
diff --git a/core/java/android/os/StatsLogEventWrapper.java b/core/java/android/os/StatsLogEventWrapper.java
index 051ab75..72e1ab9 100644
--- a/core/java/android/os/StatsLogEventWrapper.java
+++ b/core/java/android/os/StatsLogEventWrapper.java
@@ -70,11 +70,17 @@
}
};
+ /**
+ * Write a int value.
+ */
public void writeInt(int val) {
mTypes.add(EVENT_TYPE_INT);
mValues.add(val);
}
+ /**
+ * Write a long value.
+ */
public void writeLong(long val) {
mTypes.add(EVENT_TYPE_LONG);
mValues.add(val);
@@ -89,12 +95,23 @@
mValues.add(val == null ? "" : val);
}
+ /**
+ * Write a float value.
+ */
public void writeFloat(float val) {
mTypes.add(EVENT_TYPE_FLOAT);
mValues.add(val);
}
/**
+ * Write a double value.
+ */
+ public void writeDouble(double val) {
+ mTypes.add(EVENT_TYPE_DOUBLE);
+ mValues.add(val);
+ }
+
+ /**
* Write a storage value.
*/
public void writeStorage(byte[] val) {
@@ -102,6 +119,9 @@
mValues.add(val);
}
+ /**
+ * Write a boolean value.
+ */
public void writeBoolean(boolean val) {
mTypes.add(EVENT_TYPE_INT);
mValues.add(val ? 1 : 0);
@@ -134,6 +154,9 @@
case EVENT_TYPE_FLOAT:
out.writeFloat((float) mValues.get(i));
break;
+ case EVENT_TYPE_DOUBLE:
+ out.writeDouble((double) mValues.get(i));
+ break;
case EVENT_TYPE_STRING:
out.writeString((String) mValues.get(i));
break;
diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java
index fb34a52..bdc776d 100644
--- a/core/java/android/os/SystemProperties.java
+++ b/core/java/android/os/SystemProperties.java
@@ -208,13 +208,18 @@
return;
}
ArrayList<Runnable> callbacks = new ArrayList<Runnable>(sChangeCallbacks);
- for (int i=0; i<callbacks.size(); i++) {
- try {
- callbacks.get(i).run();
- } catch (Throwable t) {
- Log.wtf(TAG, "Exception in SystemProperties change callback", t);
- // Ignore and try to go on.
+ final long token = Binder.clearCallingIdentity();
+ try {
+ for (int i = 0; i < callbacks.size(); i++) {
+ try {
+ callbacks.get(i).run();
+ } catch (Throwable t) {
+ Log.wtf(TAG, "Exception in SystemProperties change callback", t);
+ // Ignore and try to go on.
+ }
}
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
}
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 80dbfe5..3f264b7 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -497,6 +497,16 @@
"android.settings.BLUETOOTH_SETTINGS";
/**
+ * Activity action: Show Settings app search UI when this action is available for device.
+ * <p>
+ * Input: Nothing.
+ * <p>
+ * Output: Nothing.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_APP_SEARCH_SETTINGS = "android.settings.APP_SEARCH_SETTINGS";
+
+ /**
* Activity Action: Show settings to allow configuration of Assist Gesture.
* <p>
* In some cases, a matching Activity may not exist, so ensure you
diff --git a/core/java/android/util/JsonReader.java b/core/java/android/util/JsonReader.java
index 7d1c6c4..50f63f8 100644
--- a/core/java/android/util/JsonReader.java
+++ b/core/java/android/util/JsonReader.java
@@ -16,13 +16,15 @@
package android.util;
+import libcore.internal.StringPool;
+
import java.io.Closeable;
import java.io.EOFException;
import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;
-import libcore.internal.StringPool;
+
/**
* Reads a JSON (<a href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>)
@@ -295,7 +297,7 @@
/**
* Consumes the next token from the JSON stream and asserts that it is the
- * end of the current array.
+ * end of the current object.
*/
public void endObject() throws IOException {
expect(JsonToken.END_OBJECT);
diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java
index a299b11..ac4ea75 100644
--- a/core/java/android/util/apk/ApkSignatureVerifier.java
+++ b/core/java/android/util/apk/ApkSignatureVerifier.java
@@ -81,19 +81,17 @@
Certificate[][] signerCerts = new Certificate[][] { vSigner.certs };
Signature[] signerSigs = convertToSignatures(signerCerts);
Signature[] pastSignerSigs = null;
- int[] pastSignerSigsFlags = null;
if (vSigner.por != null) {
// populate proof-of-rotation information
pastSignerSigs = new Signature[vSigner.por.certs.size()];
- pastSignerSigsFlags = new int[vSigner.por.flagsList.size()];
for (int i = 0; i < pastSignerSigs.length; i++) {
pastSignerSigs[i] = new Signature(vSigner.por.certs.get(i).getEncoded());
- pastSignerSigsFlags[i] = vSigner.por.flagsList.get(i);
+ pastSignerSigs[i].setFlags(vSigner.por.flagsList.get(i));
}
}
return new PackageParser.SigningDetails(
signerSigs, SignatureSchemeVersion.SIGNING_BLOCK_V3,
- pastSignerSigs, pastSignerSigsFlags);
+ pastSignerSigs);
} catch (SignatureNotFoundException e) {
// not signed with v3, try older if allowed
if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V3) {
@@ -323,19 +321,17 @@
Certificate[][] signerCerts = new Certificate[][] { vSigner.certs };
Signature[] signerSigs = convertToSignatures(signerCerts);
Signature[] pastSignerSigs = null;
- int[] pastSignerSigsFlags = null;
if (vSigner.por != null) {
// populate proof-of-rotation information
pastSignerSigs = new Signature[vSigner.por.certs.size()];
- pastSignerSigsFlags = new int[vSigner.por.flagsList.size()];
for (int i = 0; i < pastSignerSigs.length; i++) {
pastSignerSigs[i] = new Signature(vSigner.por.certs.get(i).getEncoded());
- pastSignerSigsFlags[i] = vSigner.por.flagsList.get(i);
+ pastSignerSigs[i].setFlags(vSigner.por.flagsList.get(i));
}
}
return new PackageParser.SigningDetails(
signerSigs, SignatureSchemeVersion.SIGNING_BLOCK_V3,
- pastSignerSigs, pastSignerSigsFlags);
+ pastSignerSigs);
} catch (SignatureNotFoundException e) {
// not signed with v3, try older if allowed
if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V3) {
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/android/view/View.java b/core/java/android/view/View.java
index b2944d6..cddd83c 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -14639,9 +14639,10 @@
* Recomputes the matrix if necessary.
*
* @return True if the transform matrix is the identity matrix, false otherwise.
+ * @hide
*/
@UnsupportedAppUsage
- final boolean hasIdentityMatrix() {
+ public final boolean hasIdentityMatrix() {
return mRenderNode.hasIdentityMatrix();
}
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 7abe19e79..d4c7069 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -803,10 +803,6 @@
return true;
}
}
- if (sVerbose) {
- Log.v(TAG, "not ignoring notifyViewEntered(flags=" + flags + ", view=" + id
- + ", state " + getStateAsStringLocked() + ", enteredIds=" + mEnteredIds);
- }
return false;
}
@@ -845,6 +841,9 @@
ensureServiceClientAddedIfNeededLocked();
if (!mEnabled) {
+ if (sVerbose) {
+ Log.v(TAG, "ignoring notifyViewEntered(" + id + "): disabled");
+ }
if (mCallback != null) {
callback = mCallback;
}
@@ -995,6 +994,9 @@
ensureServiceClientAddedIfNeededLocked();
if (!mEnabled) {
+ if (sVerbose) {
+ Log.v(TAG, "ignoring notifyViewEntered(" + id + "): disabled");
+ }
if (mCallback != null) {
callback = mCallback;
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 9d74c98..8027dd7 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -101,6 +101,7 @@
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
+import android.view.ViewParent;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -4800,6 +4801,24 @@
return glyphHeight > magnifierContentHeight;
}
+ private boolean viewIsMatrixTransformed() {
+ if (mMagnifierAnimator.mMagnifierIsShowing) {
+ // Do not check again when the magnifier is currently showing.
+ return false;
+ }
+ if (!mTextView.hasIdentityMatrix()) {
+ return true;
+ }
+ ViewParent viewParent = mTextView.getParent();
+ while (viewParent != null) {
+ if (viewParent instanceof View && !((View) viewParent).hasIdentityMatrix()) {
+ return true;
+ }
+ viewParent = viewParent.getParent();
+ }
+ return false;
+ }
+
/**
* Computes the position where the magnifier should be shown, relative to
* {@code mTextView}, and writes them to {@code showPosInView}. Also decides
@@ -4928,6 +4947,7 @@
final PointF showPosInView = new PointF();
final boolean shouldShow = !tooLargeTextForMagnifier()
+ && !viewIsMatrixTransformed()
&& obtainMagnifierShowCoordinates(event, showPosInView);
if (shouldShow) {
// Make the cursor visible and stop blinking.
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/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index c0c358d..856712f 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -436,6 +436,12 @@
}
public void setSamplingInterval(int samplingInterval) {
+ if (samplingInterval <= 0) {
+ Slog.w(TAG, "Ignored invalid sampling interval (value must be positive): "
+ + samplingInterval);
+ return;
+ }
+
synchronized (mLock) {
if (samplingInterval != mPeriodicSamplingInterval) {
mPeriodicSamplingInterval = samplingInterval;
diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java
index 997b722..8338d78 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.XmlUtils;
@@ -501,4 +502,181 @@
public double getBatteryCapacity() {
return getAveragePower(POWER_BATTERY_CAPACITY);
}
+
+ /**
+ * Dump power constants into PowerProfileProto
+ */
+ public void writeToProto(ProtoOutputStream proto) {
+ // cpu.suspend
+ writePowerConstantToProto(proto, POWER_CPU_SUSPEND, PowerProfileProto.CPU_SUSPEND);
+
+ // cpu.idle
+ writePowerConstantToProto(proto, POWER_CPU_IDLE, PowerProfileProto.CPU_IDLE);
+
+ // cpu.active
+ writePowerConstantToProto(proto, POWER_CPU_ACTIVE, PowerProfileProto.CPU_ACTIVE);
+
+ // cpu.clusters.cores
+ // cpu.cluster_power.cluster
+ // cpu.core_speeds.cluster
+ // cpu.core_power.cluster
+ for (int cluster = 0; cluster < mCpuClusters.length; cluster++) {
+ final long token = proto.start(PowerProfileProto.CPU_CLUSTER);
+ proto.write(PowerProfileProto.CpuCluster.ID, cluster);
+ proto.write(PowerProfileProto.CpuCluster.CLUSTER_POWER,
+ sPowerItemMap.get(mCpuClusters[cluster].clusterPowerKey));
+ proto.write(PowerProfileProto.CpuCluster.CORES, mCpuClusters[cluster].numCpus);
+ for (Double speed : sPowerArrayMap.get(mCpuClusters[cluster].freqKey)) {
+ proto.write(PowerProfileProto.CpuCluster.SPEED, speed);
+ }
+ for (Double corePower : sPowerArrayMap.get(mCpuClusters[cluster].corePowerKey)) {
+ proto.write(PowerProfileProto.CpuCluster.CORE_POWER, corePower);
+ }
+ proto.end(token);
+ }
+
+ // wifi.scan
+ writePowerConstantToProto(proto, POWER_WIFI_SCAN, PowerProfileProto.WIFI_SCAN);
+
+ // wifi.on
+ writePowerConstantToProto(proto, POWER_WIFI_ON, PowerProfileProto.WIFI_ON);
+
+ // wifi.active
+ writePowerConstantToProto(proto, POWER_WIFI_ACTIVE, PowerProfileProto.WIFI_ACTIVE);
+
+ // wifi.controller.idle
+ writePowerConstantToProto(proto, POWER_WIFI_CONTROLLER_IDLE,
+ PowerProfileProto.WIFI_CONTROLLER_IDLE);
+
+ // wifi.controller.rx
+ writePowerConstantToProto(proto, POWER_WIFI_CONTROLLER_RX,
+ PowerProfileProto.WIFI_CONTROLLER_RX);
+
+ // wifi.controller.tx
+ writePowerConstantToProto(proto, POWER_WIFI_CONTROLLER_TX,
+ PowerProfileProto.WIFI_CONTROLLER_TX);
+
+ // wifi.controller.tx_levels
+ writePowerConstantArrayToProto(proto, POWER_WIFI_CONTROLLER_TX_LEVELS,
+ PowerProfileProto.WIFI_CONTROLLER_TX_LEVELS);
+
+ // wifi.controller.voltage
+ writePowerConstantToProto(proto, POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE,
+ PowerProfileProto.WIFI_CONTROLLER_OPERATING_VOLTAGE);
+
+ // bluetooth.controller.idle
+ writePowerConstantToProto(proto, POWER_BLUETOOTH_CONTROLLER_IDLE,
+ PowerProfileProto.BLUETOOTH_CONTROLLER_IDLE);
+
+ // bluetooth.controller.rx
+ writePowerConstantToProto(proto, POWER_BLUETOOTH_CONTROLLER_RX,
+ PowerProfileProto.BLUETOOTH_CONTROLLER_RX);
+
+ // bluetooth.controller.tx
+ writePowerConstantToProto(proto, POWER_BLUETOOTH_CONTROLLER_TX,
+ PowerProfileProto.BLUETOOTH_CONTROLLER_TX);
+
+ // bluetooth.controller.voltage
+ writePowerConstantToProto(proto, POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE,
+ PowerProfileProto.BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE);
+
+ // modem.controller.sleep
+ writePowerConstantToProto(proto, POWER_MODEM_CONTROLLER_SLEEP,
+ PowerProfileProto.MODEM_CONTROLLER_SLEEP);
+
+ // modem.controller.idle
+ writePowerConstantToProto(proto, POWER_MODEM_CONTROLLER_IDLE,
+ PowerProfileProto.MODEM_CONTROLLER_IDLE);
+
+ // modem.controller.rx
+ writePowerConstantToProto(proto, POWER_MODEM_CONTROLLER_RX,
+ PowerProfileProto.MODEM_CONTROLLER_RX);
+
+ // modem.controller.tx
+ writePowerConstantArrayToProto(proto, POWER_MODEM_CONTROLLER_TX,
+ PowerProfileProto.MODEM_CONTROLLER_TX);
+
+ // modem.controller.voltage
+ writePowerConstantToProto(proto, POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE,
+ PowerProfileProto.MODEM_CONTROLLER_OPERATING_VOLTAGE);
+
+ // gps.on
+ writePowerConstantToProto(proto, POWER_GPS_ON, PowerProfileProto.GPS_ON);
+
+ // gps.signalqualitybased
+ writePowerConstantArrayToProto(proto, POWER_GPS_SIGNAL_QUALITY_BASED,
+ PowerProfileProto.GPS_SIGNAL_QUALITY_BASED);
+
+ // gps.voltage
+ writePowerConstantToProto(proto, POWER_GPS_OPERATING_VOLTAGE,
+ PowerProfileProto.GPS_OPERATING_VOLTAGE);
+
+ // bluetooth.on
+ writePowerConstantToProto(proto, POWER_BLUETOOTH_ON, PowerProfileProto.BLUETOOTH_ON);
+
+ // bluetooth.active
+ writePowerConstantToProto(proto, POWER_BLUETOOTH_ACTIVE,
+ PowerProfileProto.BLUETOOTH_ACTIVE);
+
+ // bluetooth.at
+ writePowerConstantToProto(proto, POWER_BLUETOOTH_AT_CMD,
+ PowerProfileProto.BLUETOOTH_AT_CMD);
+
+ // ambient.on
+ writePowerConstantToProto(proto, POWER_AMBIENT_DISPLAY, PowerProfileProto.AMBIENT_DISPLAY);
+
+ // screen.on
+ writePowerConstantToProto(proto, POWER_SCREEN_ON, PowerProfileProto.SCREEN_ON);
+
+ // radio.on
+ writePowerConstantToProto(proto, POWER_RADIO_ON, PowerProfileProto.RADIO_ON);
+
+ // radio.scanning
+ writePowerConstantToProto(proto, POWER_RADIO_SCANNING, PowerProfileProto.RADIO_SCANNING);
+
+ // radio.active
+ writePowerConstantToProto(proto, POWER_RADIO_ACTIVE, PowerProfileProto.RADIO_ACTIVE);
+
+ // screen.full
+ writePowerConstantToProto(proto, POWER_SCREEN_FULL, PowerProfileProto.SCREEN_FULL);
+
+ // audio
+ writePowerConstantToProto(proto, POWER_AUDIO, PowerProfileProto.AUDIO);
+
+ // video
+ writePowerConstantToProto(proto, POWER_VIDEO, PowerProfileProto.VIDEO);
+
+ // camera.flashlight
+ writePowerConstantToProto(proto, POWER_FLASHLIGHT, PowerProfileProto.FLASHLIGHT);
+
+ // memory.bandwidths
+ writePowerConstantToProto(proto, POWER_MEMORY, PowerProfileProto.MEMORY);
+
+ // camera.avg
+ writePowerConstantToProto(proto, POWER_CAMERA, PowerProfileProto.CAMERA);
+
+ // wifi.batchedscan
+ writePowerConstantToProto(proto, POWER_WIFI_BATCHED_SCAN,
+ PowerProfileProto.WIFI_BATCHED_SCAN);
+
+ // battery.capacity
+ writePowerConstantToProto(proto, POWER_BATTERY_CAPACITY,
+ PowerProfileProto.BATTERY_CAPACITY);
+ }
+
+ // Writes items in sPowerItemMap to proto if exists.
+ private void writePowerConstantToProto(ProtoOutputStream proto, String key, long fieldId) {
+ if (sPowerItemMap.containsKey(key)) {
+ proto.write(fieldId, sPowerItemMap.get(key));
+ }
+ }
+
+ // Writes items in sPowerArrayMap to proto if exists.
+ private void writePowerConstantArrayToProto(ProtoOutputStream proto, String key, long fieldId) {
+ if (sPowerArrayMap.containsKey(key)) {
+ for (Double d : sPowerArrayMap.get(key)) {
+ proto.write(fieldId, d);
+ }
+ }
+ }
}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 762b430..8e24d10 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -209,6 +209,8 @@
"com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp",
"hwbinder/EphemeralStorage.cpp",
"fd_utils.cpp",
+ "android_hardware_input_InputWindowHandle.cpp",
+ "android_hardware_input_InputApplicationHandle.cpp",
],
include_dirs: [
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 897f6fa5..2635969 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -7,15 +7,9 @@
#include "SkImageEncoder.h"
#include "SkImageInfo.h"
#include "SkColor.h"
-#include "SkColorPriv.h"
#include "SkColorSpace.h"
-#include "SkColorSpaceXform.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>
@@ -559,13 +553,10 @@
if (!p3.tryAllocPixels(info)) {
return JNI_FALSE;
}
- auto xform = SkColorSpaceXform::New(skbitmap.colorSpace(), info.colorSpace());
- if (!xform) {
- return JNI_FALSE;
- }
- if (!xform->apply(SkColorSpaceXform::kRGBA_8888_ColorFormat, p3.getPixels(),
- SkColorSpaceXform::kRGBA_F16_ColorFormat, skbitmap.getPixels(),
- info.width() * info.height(), kUnpremul_SkAlphaType)) {
+
+ SkPixmap pm;
+ SkAssertResult(p3.peekPixels(&pm)); // should always work if tryAllocPixels() did.
+ if (!skbitmap.readPixels(pm)) {
return JNI_FALSE;
}
skbitmap = p3;
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/jni/android_hardware_display_DisplayViewport.cpp b/core/jni/android_hardware_display_DisplayViewport.cpp
index ab8e685..05f6556 100644
--- a/core/jni/android_hardware_display_DisplayViewport.cpp
+++ b/core/jni/android_hardware_display_DisplayViewport.cpp
@@ -40,6 +40,7 @@
jfieldID deviceWidth;
jfieldID deviceHeight;
jfieldID uniqueId;
+ jfieldID type;
} gDisplayViewportClassInfo;
static struct {
@@ -64,6 +65,9 @@
viewport->uniqueId = ScopedUtfChars(env, uniqueId).c_str();
}
+ viewport->type = static_cast<ViewportType>(env->GetIntField(viewportObj,
+ gDisplayViewportClassInfo.type));
+
jobject logicalFrameObj =
env->GetObjectField(viewportObj, gDisplayViewportClassInfo.logicalFrame);
viewport->logicalLeft = env->GetIntField(logicalFrameObj, gRectClassInfo.left);
@@ -108,6 +112,9 @@
gDisplayViewportClassInfo.uniqueId = GetFieldIDOrDie(env,
gDisplayViewportClassInfo.clazz, "uniqueId", "Ljava/lang/String;");
+ gDisplayViewportClassInfo.type = GetFieldIDOrDie(env,
+ gDisplayViewportClassInfo.clazz, "type", "I");
+
clazz = FindClassOrDie(env, "android/graphics/Rect");
gRectClassInfo.left = GetFieldIDOrDie(env, clazz, "left", "I");
gRectClassInfo.top = GetFieldIDOrDie(env, clazz, "top", "I");
diff --git a/services/core/jni/com_android_server_input_InputApplicationHandle.cpp b/core/jni/android_hardware_input_InputApplicationHandle.cpp
similarity index 98%
rename from services/core/jni/com_android_server_input_InputApplicationHandle.cpp
rename to core/jni/android_hardware_input_InputApplicationHandle.cpp
index 514b6e1..8ace8da 100644
--- a/services/core/jni/com_android_server_input_InputApplicationHandle.cpp
+++ b/core/jni/android_hardware_input_InputApplicationHandle.cpp
@@ -21,7 +21,7 @@
#include <android_runtime/AndroidRuntime.h>
#include <utils/threads.h>
-#include "com_android_server_input_InputApplicationHandle.h"
+#include "android_hardware_input_InputApplicationHandle.h"
namespace android {
diff --git a/services/core/jni/com_android_server_input_InputApplicationHandle.h b/core/jni/android_hardware_input_InputApplicationHandle.h
similarity index 95%
rename from services/core/jni/com_android_server_input_InputApplicationHandle.h
rename to core/jni/android_hardware_input_InputApplicationHandle.h
index c9af711..7115611 100644
--- a/services/core/jni/com_android_server_input_InputApplicationHandle.h
+++ b/core/jni/android_hardware_input_InputApplicationHandle.h
@@ -17,7 +17,9 @@
#ifndef _ANDROID_SERVER_INPUT_APPLICATION_HANDLE_H
#define _ANDROID_SERVER_INPUT_APPLICATION_HANDLE_H
-#include <inputflinger/InputApplication.h>
+#include <string>
+
+#include <input/InputApplication.h>
#include <nativehelper/JNIHelp.h>
#include "jni.h"
diff --git a/services/core/jni/com_android_server_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp
similarity index 98%
rename from services/core/jni/com_android_server_input_InputWindowHandle.cpp
rename to core/jni/android_hardware_input_InputWindowHandle.cpp
index c13aa38..f4829ad 100644
--- a/services/core/jni/com_android_server_input_InputWindowHandle.cpp
+++ b/core/jni/android_hardware_input_InputWindowHandle.cpp
@@ -25,8 +25,8 @@
#include <android/graphics/Region.h>
#include <ui/Region.h>
-#include "com_android_server_input_InputWindowHandle.h"
-#include "com_android_server_input_InputApplicationHandle.h"
+#include "android_hardware_input_InputWindowHandle.h"
+#include "android_hardware_input_InputApplicationHandle.h"
namespace android {
diff --git a/services/core/jni/com_android_server_input_InputWindowHandle.h b/core/jni/android_hardware_input_InputWindowHandle.h
similarity index 96%
rename from services/core/jni/com_android_server_input_InputWindowHandle.h
rename to core/jni/android_hardware_input_InputWindowHandle.h
index 44d4620..2be267e 100644
--- a/services/core/jni/com_android_server_input_InputWindowHandle.h
+++ b/core/jni/android_hardware_input_InputWindowHandle.h
@@ -17,7 +17,7 @@
#ifndef _ANDROID_SERVER_INPUT_WINDOW_HANDLE_H
#define _ANDROID_SERVER_INPUT_WINDOW_HANDLE_H
-#include <inputflinger/InputWindow.h>
+#include <input/InputWindow.h>
#include <nativehelper/JNIHelp.h>
#include "jni.h"
diff --git a/core/jni/android_view_InputChannel.cpp b/core/jni/android_view_InputChannel.cpp
index 0a90b97..2f17907 100644
--- a/core/jni/android_view_InputChannel.cpp
+++ b/core/jni/android_view_InputChannel.cpp
@@ -202,17 +202,9 @@
if (parcel) {
bool isInitialized = parcel->readInt32();
if (isInitialized) {
- String8 name = parcel->readString8();
- int rawFd = parcel->readFileDescriptor();
- int dupFd = dup(rawFd);
- if (dupFd < 0) {
- ALOGE("Error %d dup channel fd %d.", errno, rawFd);
- jniThrowRuntimeException(env,
- "Could not read input channel file descriptors from parcel.");
- return;
- }
+ InputChannel* inputChannel = new InputChannel();
+ inputChannel->read(*parcel);
- InputChannel* inputChannel = new InputChannel(name.string(), dupFd);
NativeInputChannel* nativeInputChannel = new NativeInputChannel(inputChannel);
android_view_InputChannel_setNativeInputChannel(env, obj, nativeInputChannel);
@@ -230,8 +222,7 @@
sp<InputChannel> inputChannel = nativeInputChannel->getInputChannel();
parcel->writeInt32(1);
- parcel->writeString8(String8(inputChannel->getName().c_str()));
- parcel->writeDupFileDescriptor(inputChannel->getFd());
+ inputChannel->write(*parcel);
} else {
parcel->writeInt32(0);
}
diff --git a/core/proto/android/internal/powerprofile.proto b/core/proto/android/internal/powerprofile.proto
new file mode 100644
index 0000000..9dd5e7e
--- /dev/null
+++ b/core/proto/android/internal/powerprofile.proto
@@ -0,0 +1,111 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+package com.android.internal.os;
+
+option java_multiple_files = true;
+
+// next: 41
+message PowerProfileProto {
+ optional double cpu_suspend = 1;
+
+ optional double cpu_idle = 2;
+
+ optional double cpu_active = 3;
+
+ message CpuCluster {
+ optional int32 id = 1;
+ optional double cluster_power = 2;
+ optional int32 cores = 3;
+ repeated int64 speed = 4;
+ repeated double core_power = 5;
+ }
+
+ repeated CpuCluster cpu_cluster = 41;
+
+ optional double wifi_scan = 4;
+
+ optional double wifi_on = 5;
+
+ optional double wifi_active = 6;
+
+ optional double wifi_controller_idle = 7;
+
+ optional double wifi_controller_rx = 8;
+
+ optional double wifi_controller_tx = 9;
+
+ repeated double wifi_controller_tx_levels = 10;
+
+ optional double wifi_controller_operating_voltage = 11;
+
+ optional double bluetooth_controller_idle = 12;
+
+ optional double bluetooth_controller_rx = 13;
+
+ optional double bluetooth_controller_tx = 14;
+
+ optional double bluetooth_controller_operating_voltage = 15;
+
+ optional double modem_controller_sleep = 16;
+
+ optional double modem_controller_idle = 17;
+
+ optional double modem_controller_rx = 18;
+
+ repeated double modem_controller_tx = 19;
+
+ optional double modem_controller_operating_voltage = 20;
+
+ optional double gps_on = 21;
+
+ repeated double gps_signal_quality_based = 22;
+
+ optional double gps_operating_voltage = 23;
+
+ optional double bluetooth_on = 24;
+
+ optional double bluetooth_active = 25;
+
+ optional double bluetooth_at_cmd = 26;
+
+ optional double ambient_display = 27;
+
+ optional double screen_on = 29;
+
+ optional double radio_on = 30;
+
+ optional double radio_scanning = 31;
+
+ optional double radio_active = 32;
+
+ optional double screen_full = 33;
+
+ optional double audio = 34;
+
+ optional double video = 35;
+
+ optional double flashlight = 36;
+
+ optional double memory = 37;
+
+ optional double camera = 38;
+
+ optional double wifi_batched_scan = 39;
+
+ optional double battery_capacity = 40;
+}
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 f654ce2..f3f012d 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1100,7 +1100,7 @@
<!-- Allows a calling application which manages it own calls through the self-managed
{@link android.telecom.ConnectionService} APIs. See
- {@link android.telecom.PhoneAccount#CAPABILITY_SELF_MANAGED for more information on the
+ {@link android.telecom.PhoneAccount#CAPABILITY_SELF_MANAGED} for more information on the
self-managed ConnectionService APIs.
<p>Protection level: normal
-->
@@ -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 45cf0f0..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>
@@ -32,6 +32,8 @@
<item name="panelColorBackground">@color/material_grey_800</item>
</style>
+ <style name="Theme.DeviceDefault.QuickSettings.Dialog" parent="Theme.DeviceDefault.Dialog" />
+
<style name="TextAppearance.Material.Notification">
<item name="textColor">@color/notification_secondary_text_color_dark</item>
<item name="textSize">@dimen/notification_text_size</item>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index e0db946..cdb65ed 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4184,9 +4184,9 @@
row is full. The rowCount attribute may be used similarly in the vertical case.
The default is horizontal. -->
<attr name="orientation" />
- <!-- The maxmimum number of rows to create when automatically positioning children. -->
+ <!-- The maximum number of rows to create when automatically positioning children. -->
<attr name="rowCount" format="integer" />
- <!-- The maxmimum number of columns to create when automatically positioning children. -->
+ <!-- The maximum number of columns to create when automatically positioning children. -->
<attr name="columnCount" format="integer" />
<!-- When set to true, tells GridLayout to use default margins when none are specified
in a view's layout parameters.
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 0daf5a7..8b73b67 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1527,7 +1527,7 @@
<bool name="config_checkWallpaperAtBoot">true</bool>
<!-- Class name of WallpaperManagerService. -->
- <string name="config_wallpaperManagerServiceName">com.android.server.wallpaper.WallpaperManagerService</string>
+ <string name="config_wallpaperManagerServiceName" translatable="false">com.android.server.wallpaper.WallpaperManagerService</string>
<!-- Enables the TimeZoneRuleManager service. This is the master switch for the updateable time
zone update mechanism. -->
@@ -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/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index c30af84..f928de9 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -23,6 +23,7 @@
#include "SkDrawShadowInfo.h"
#include "SkImage.h"
#include "SkImageFilter.h"
+#include "SkLatticeIter.h"
#include "SkMath.h"
#include "SkPicture.h"
#include "SkRSXform.h"
@@ -280,7 +281,8 @@
struct DrawImage final : Op {
static const auto kType = Type::DrawImage;
- DrawImage(sk_sp<const SkImage>&& image, SkScalar x, SkScalar y, const SkPaint* paint, BitmapPalette palette)
+ DrawImage(sk_sp<const SkImage>&& image, SkScalar x, SkScalar y, const SkPaint* paint,
+ BitmapPalette palette)
: image(std::move(image)), x(x), y(y), palette(palette) {
if (paint) {
this->paint = *paint;
@@ -312,7 +314,8 @@
struct DrawImageRect final : Op {
static const auto kType = Type::DrawImageRect;
DrawImageRect(sk_sp<const SkImage>&& image, const SkRect* src, const SkRect& dst,
- const SkPaint* paint, SkCanvas::SrcRectConstraint constraint, BitmapPalette palette)
+ const SkPaint* paint, SkCanvas::SrcRectConstraint constraint,
+ BitmapPalette palette)
: image(std::move(image)), dst(dst), constraint(constraint), palette(palette) {
this->src = src ? *src : SkRect::MakeIWH(this->image->width(), this->image->height());
if (paint) {
@@ -331,8 +334,14 @@
struct DrawImageLattice final : Op {
static const auto kType = Type::DrawImageLattice;
DrawImageLattice(sk_sp<const SkImage>&& image, int xs, int ys, int fs, const SkIRect& src,
- const SkRect& dst, const SkPaint* paint)
- : image(std::move(image)), xs(xs), ys(ys), fs(fs), src(src), dst(dst) {
+ const SkRect& dst, const SkPaint* paint, BitmapPalette palette)
+ : image(std::move(image))
+ , xs(xs)
+ , ys(ys)
+ , fs(fs)
+ , src(src)
+ , dst(dst)
+ , palette(palette) {
if (paint) {
this->paint = *paint;
}
@@ -342,6 +351,7 @@
SkIRect src;
SkRect dst;
SkPaint paint;
+ BitmapPalette palette;
void draw(SkCanvas* c, const SkMatrix&) const {
auto xdivs = pod<int>(this, 0), ydivs = pod<int>(this, xs * sizeof(int));
auto colors = (0 == fs) ? nullptr : pod<SkColor>(this, (xs + ys) * sizeof(int));
@@ -511,16 +521,13 @@
tree->getPaintFor(&paint, tree->stagingProperties());
}
- void draw(SkCanvas* canvas, const SkMatrix&) const {
- mRoot->draw(canvas, mBounds, paint);
- }
+ void draw(SkCanvas* canvas, const SkMatrix&) const { mRoot->draw(canvas, mBounds, paint); }
sp<VectorDrawableRoot> mRoot;
SkRect mBounds;
SkPaint paint;
BitmapPalette palette;
};
-
}
template <typename T, typename... Args>
@@ -647,14 +654,15 @@
this->push<DrawImageRect>(0, std::move(image), src, dst, paint, constraint, palette);
}
void DisplayListData::drawImageLattice(sk_sp<const SkImage> image, const SkCanvas::Lattice& lattice,
- const SkRect& dst, const SkPaint* paint) {
+ const SkRect& dst, const SkPaint* paint,
+ BitmapPalette palette) {
int xs = lattice.fXCount, ys = lattice.fYCount;
int fs = lattice.fRectTypes ? (xs + 1) * (ys + 1) : 0;
size_t bytes = (xs + ys) * sizeof(int) + fs * sizeof(SkCanvas::Lattice::RectType) +
fs * sizeof(SkColor);
SkASSERT(lattice.fBounds);
void* pod = this->push<DrawImageLattice>(bytes, std::move(image), xs, ys, fs, *lattice.fBounds,
- dst, paint);
+ dst, paint, palette);
copy_v(pod, lattice.fXDivs, xs, lattice.fYDivs, ys, lattice.fColors, fs, lattice.fRectTypes,
fs);
}
@@ -779,22 +787,26 @@
template <class T>
constexpr color_transform_fn colorTransformForOp() {
- if constexpr(has_paint<T> && has_palette<T>) {
- // It's a bitmap
- return [](const void* opRaw, ColorTransform transform) {
- // TODO: We should be const. Or not. Or just use a different map
- // Unclear, but this is the quick fix
- const T* op = reinterpret_cast<const T*>(opRaw);
- transformPaint(transform, const_cast<SkPaint*>(&(op->paint)), op->palette);
- };
- } else if constexpr(has_paint<T>) {
- return [](const void* opRaw, ColorTransform transform) {
- // TODO: We should be const. Or not. Or just use a different map
- // Unclear, but this is the quick fix
- const T* op = reinterpret_cast<const T*>(opRaw);
- transformPaint(transform, const_cast<SkPaint*>(&(op->paint)));
- };
- } else {
+ if
+ constexpr(has_paint<T> && has_palette<T>) {
+ // It's a bitmap
+ return [](const void* opRaw, ColorTransform transform) {
+ // TODO: We should be const. Or not. Or just use a different map
+ // Unclear, but this is the quick fix
+ const T* op = reinterpret_cast<const T*>(opRaw);
+ transformPaint(transform, const_cast<SkPaint*>(&(op->paint)), op->palette);
+ };
+ }
+ else if
+ constexpr(has_paint<T>) {
+ return [](const void* opRaw, ColorTransform transform) {
+ // TODO: We should be const. Or not. Or just use a different map
+ // Unclear, but this is the quick fix
+ const T* op = reinterpret_cast<const T*>(opRaw);
+ transformPaint(transform, const_cast<SkPaint*>(&(op->paint)));
+ };
+ }
+ else {
return nullptr;
}
}
@@ -931,11 +943,12 @@
}
void RecordingCanvas::onDrawBitmapRect(const SkBitmap& bm, const SkRect* src, const SkRect& dst,
const SkPaint* paint, SrcRectConstraint constraint) {
- fDL->drawImageRect(SkImage::MakeFromBitmap(bm), src, dst, paint, constraint, BitmapPalette::Unknown);
+ fDL->drawImageRect(SkImage::MakeFromBitmap(bm), src, dst, paint, constraint,
+ BitmapPalette::Unknown);
}
void RecordingCanvas::onDrawBitmapLattice(const SkBitmap& bm, const SkCanvas::Lattice& lattice,
const SkRect& dst, const SkPaint* paint) {
- fDL->drawImageLattice(SkImage::MakeFromBitmap(bm), lattice, dst, paint);
+ fDL->drawImageLattice(SkImage::MakeFromBitmap(bm), lattice, dst, paint, BitmapPalette::Unknown);
}
void RecordingCanvas::drawImage(const sk_sp<SkImage>& image, SkScalar x, SkScalar y,
@@ -943,11 +956,34 @@
fDL->drawImage(image, x, y, paint, palette);
}
-void RecordingCanvas::drawImageRect(const sk_sp<SkImage>& image, const SkRect& src, const SkRect& dst,
- const SkPaint* paint, SrcRectConstraint constraint, BitmapPalette palette) {
+void RecordingCanvas::drawImageRect(const sk_sp<SkImage>& image, const SkRect& src,
+ const SkRect& dst, const SkPaint* paint,
+ SrcRectConstraint constraint, BitmapPalette palette) {
fDL->drawImageRect(image, &src, dst, paint, constraint, palette);
}
+void RecordingCanvas::drawImageLattice(const sk_sp<SkImage>& image, const Lattice& lattice,
+ const SkRect& dst, const SkPaint* paint,
+ BitmapPalette palette) {
+ if (!image || dst.isEmpty()) {
+ return;
+ }
+
+ SkIRect bounds;
+ Lattice latticePlusBounds = lattice;
+ if (!latticePlusBounds.fBounds) {
+ bounds = SkIRect::MakeWH(image->width(), image->height());
+ latticePlusBounds.fBounds = &bounds;
+ }
+
+ if (SkLatticeIter::Valid(image->width(), image->height(), latticePlusBounds)) {
+ fDL->drawImageLattice(image, latticePlusBounds, dst, paint, palette);
+ } else {
+ fDL->drawImageRect(image, nullptr, dst, paint, SrcRectConstraint::kFast_SrcRectConstraint,
+ palette);
+ }
+}
+
void RecordingCanvas::onDrawImage(const SkImage* img, SkScalar x, SkScalar y,
const SkPaint* paint) {
fDL->drawImage(sk_ref_sp(img), x, y, paint, BitmapPalette::Unknown);
@@ -962,7 +998,7 @@
}
void RecordingCanvas::onDrawImageLattice(const SkImage* img, const SkCanvas::Lattice& lattice,
const SkRect& dst, const SkPaint* paint) {
- fDL->drawImageLattice(sk_ref_sp(img), lattice, dst, paint);
+ fDL->drawImageLattice(sk_ref_sp(img), lattice, dst, paint, BitmapPalette::Unknown);
}
void RecordingCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
@@ -975,8 +1011,8 @@
fDL->drawPoints(mode, count, pts, paint);
}
void RecordingCanvas::onDrawVerticesObject(const SkVertices* vertices,
- const SkVertices::Bone bones[], int boneCount,
- SkBlendMode mode, const SkPaint& paint) {
+ const SkVertices::Bone bones[], int boneCount,
+ SkBlendMode mode, const SkPaint& paint) {
fDL->drawVertices(vertices, bones, boneCount, mode, paint);
}
void RecordingCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xforms[],
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 80c80ca..099e0be 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -110,7 +110,7 @@
void drawImageRect(sk_sp<const SkImage>, const SkRect*, const SkRect&, const SkPaint*,
SkCanvas::SrcRectConstraint, BitmapPalette palette);
void drawImageLattice(sk_sp<const SkImage>, const SkCanvas::Lattice&, const SkRect&,
- const SkPaint*);
+ const SkPaint*, BitmapPalette);
void drawPatch(const SkPoint[12], const SkColor[4], const SkPoint[4], SkBlendMode,
const SkPaint&);
@@ -185,11 +185,13 @@
void onDrawBitmapRect(const SkBitmap&, const SkRect*, const SkRect&, const SkPaint*,
SrcRectConstraint) override;
- void drawImage(const sk_sp<SkImage>& image, SkScalar left, SkScalar top,
- const SkPaint* paint, BitmapPalette pallete);
+ void drawImage(const sk_sp<SkImage>& image, SkScalar left, SkScalar top, const SkPaint* paint,
+ BitmapPalette pallete);
void drawImageRect(const sk_sp<SkImage>& image, const SkRect& src, const SkRect& dst,
const SkPaint* paint, SrcRectConstraint constraint, BitmapPalette palette);
+ void drawImageLattice(const sk_sp<SkImage>& image, const Lattice& lattice, const SkRect& dst,
+ const SkPaint* paint, BitmapPalette palette);
void onDrawImage(const SkImage*, SkScalar, SkScalar, const SkPaint*) override;
void onDrawImageLattice(const SkImage*, const Lattice&, const SkRect&, const SkPaint*) override;
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index d9a7cc3..d2a8f02 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -282,25 +282,45 @@
mStagingDisplayList = nullptr;
if (mDisplayList) {
mDisplayList->syncContents();
+ handleForceDark(info);
+ }
+}
- if (CC_UNLIKELY(info && !info->disableForceDark)) {
- auto usage = usageHint();
- if (mDisplayList->hasText()) {
- usage = UsageHint::Foreground;
- }
- if (usage == UsageHint::Unknown) {
- if (mDisplayList->mChildNodes.size() > 1) {
- usage = UsageHint::Background;
- } else if (mDisplayList->mChildNodes.size() == 1 &&
- mDisplayList->mChildNodes.front().getRenderNode()->usageHint() !=
- UsageHint::Background) {
- usage = UsageHint::Background;
- }
- }
- mDisplayList->mDisplayList.applyColorTransform(
- usage == UsageHint::Background ? ColorTransform::Dark : ColorTransform::Light);
+void RenderNode::handleForceDark(android::uirenderer::TreeInfo *info) {
+ if (CC_LIKELY(!info || info->disableForceDark)) {
+ return;
+ }
+ auto usage = usageHint();
+ const auto& children = mDisplayList->mChildNodes;
+ if (mDisplayList->hasText()) {
+ usage = UsageHint::Foreground;
+ }
+ if (usage == UsageHint::Unknown) {
+ if (children.size() > 1) {
+ usage = UsageHint::Background;
+ } else if (children.size() == 1 &&
+ children.front().getRenderNode()->usageHint() !=
+ UsageHint::Background) {
+ usage = UsageHint::Background;
}
}
+ if (children.size() > 1) {
+ // Crude overlap check
+ SkRect drawn = SkRect::MakeEmpty();
+ for (auto iter = children.rbegin(); iter != children.rend(); ++iter) {
+ const auto& child = iter->getRenderNode();
+ // We use stagingProperties here because we haven't yet sync'd the children
+ SkRect bounds = SkRect::MakeXYWH(child->stagingProperties().getX(), child->stagingProperties().getY(),
+ child->stagingProperties().getWidth(), child->stagingProperties().getHeight());
+ if (bounds.contains(drawn)) {
+ // This contains everything drawn after it, so make it a background
+ child->setUsageHint(UsageHint::Background);
+ }
+ drawn.join(bounds);
+ }
+ }
+ mDisplayList->mDisplayList.applyColorTransform(
+ usage == UsageHint::Background ? ColorTransform::Dark : ColorTransform::Light);
}
void RenderNode::pushStagingDisplayListChanges(TreeObserver& observer, TreeInfo& info) {
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index 211dd2d..be0b46b 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -220,6 +220,7 @@
void syncProperties();
void syncDisplayList(TreeObserver& observer, TreeInfo* info);
+ void handleForceDark(TreeInfo* info);
void prepareTreeImpl(TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer);
void pushStagingPropertiesChanges(TreeInfo& info);
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/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index dbbe9f3..6cf04bf 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -470,10 +470,10 @@
void Tree::getPaintFor(SkPaint* outPaint, const TreeProperties &prop) const {
// HWUI always draws VD with bilinear filtering.
outPaint->setFilterQuality(kLow_SkFilterQuality);
- if (prop.getRootAlpha() < 1.0f || prop.getColorFilter() != nullptr) {
+ if (prop.getColorFilter() != nullptr) {
outPaint->setColorFilter(sk_ref_sp(prop.getColorFilter()));
- outPaint->setAlpha(prop.getRootAlpha() * 255);
}
+ outPaint->setAlpha(prop.getRootAlpha() * 255);
}
Bitmap& Tree::getBitmapUpdateIfDirty() {
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/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index fac07d7..3fa73a4 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -245,8 +245,9 @@
}
sk_sp<SkColorFilter> colorFilter;
sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
- mRecorder.drawImageLattice(image.get(), lattice, dst,
- filterBitmap(std::move(filteredPaint), std::move(colorFilter)));
+ mRecorder.drawImageLattice(image, lattice, dst,
+ filterBitmap(std::move(filteredPaint), std::move(colorFilter)),
+ bitmap.palette());
if (!bitmap.isImmutable() && image.get() && !image->unique() && !dst.isEmpty()) {
mDisplayList->mMutableImages.push_back(image.get());
}
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index a2d8119..2ca110f 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -126,6 +126,12 @@
mVkSurface = mVkManager.createSurface(surface, colorMode);
}
+ if (colorMode == ColorMode::SRGB) {
+ mSurfaceColorType = SkColorType::kN32_SkColorType;
+ } else if (colorMode == ColorMode::WideColorGamut) {
+ mSurfaceColorType = SkColorType::kRGBA_F16_SkColorType;
+ }
+
return mVkSurface != nullptr;
}
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 83e9db3..b0d4505 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -78,7 +78,7 @@
0, // applicationVersion
"android framework", // pEngineName
0, // engineVerison
- VK_MAKE_VERSION(1, 0, 0), // apiVersion
+ VK_MAKE_VERSION(1, 1, 0), // apiVersion
};
std::vector<const char*> instanceExtensions;
@@ -133,6 +133,7 @@
GET_INST_PROC(DestroyInstance);
GET_INST_PROC(EnumeratePhysicalDevices);
+ GET_INST_PROC(GetPhysicalDeviceProperties);
GET_INST_PROC(GetPhysicalDeviceQueueFamilyProperties);
GET_INST_PROC(GetPhysicalDeviceFeatures2);
GET_INST_PROC(CreateDevice);
@@ -164,6 +165,13 @@
return false;
}
+ VkPhysicalDeviceProperties physDeviceProperties;
+ mGetPhysicalDeviceProperties(mPhysicalDevice, &physDeviceProperties);
+ if (physDeviceProperties.apiVersion < VK_MAKE_VERSION(1, 1, 0)) {
+ this->destroy();
+ return false;
+ }
+
// query to get the initial queue props size
uint32_t queueCount;
mGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &queueCount, nullptr);
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index 7c59b6d..6702649 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -176,6 +176,7 @@
VkPtr<PFN_vkDestroyInstance> mDestroyInstance;
VkPtr<PFN_vkEnumeratePhysicalDevices> mEnumeratePhysicalDevices;
+ VkPtr<PFN_vkGetPhysicalDeviceProperties> mGetPhysicalDeviceProperties;
VkPtr<PFN_vkGetPhysicalDeviceQueueFamilyProperties> mGetPhysicalDeviceQueueFamilyProperties;
VkPtr<PFN_vkGetPhysicalDeviceFeatures2> mGetPhysicalDeviceFeatures2;
VkPtr<PFN_vkCreateDevice> mCreateDevice;
diff --git a/libs/services/include/android/os/StatsLogEventWrapper.h b/libs/services/include/android/os/StatsLogEventWrapper.h
index 52cb75e..f60c338 100644
--- a/libs/services/include/android/os/StatsLogEventWrapper.h
+++ b/libs/services/include/android/os/StatsLogEventWrapper.h
@@ -58,6 +58,11 @@
type = FLOAT;
}
+ StatsLogValue(double v) {
+ double_value = v;
+ type = DOUBLE;
+ }
+
StatsLogValue(const std::string& v) {
str_value = v;
type = STRING;
diff --git a/libs/services/src/os/StatsLogEventWrapper.cpp b/libs/services/src/os/StatsLogEventWrapper.cpp
index 04c4629..a1a6d9f 100644
--- a/libs/services/src/os/StatsLogEventWrapper.cpp
+++ b/libs/services/src/os/StatsLogEventWrapper.cpp
@@ -85,6 +85,9 @@
case StatsLogValue::FLOAT:
mElements.push_back(StatsLogValue(in->readFloat()));
break;
+ case StatsLogValue::DOUBLE:
+ mElements.push_back(StatsLogValue(in->readDouble()));
+ break;
case StatsLogValue::STORAGE:
mElements.push_back(StatsLogValue());
mElements.back().setType(StatsLogValue::STORAGE);
diff --git a/location/java/android/location/Criteria.java b/location/java/android/location/Criteria.java
index a6099be..74eb445 100644
--- a/location/java/android/location/Criteria.java
+++ b/location/java/android/location/Criteria.java
@@ -21,9 +21,9 @@
/**
* A class indicating the application criteria for selecting a
- * location provider. Providers maybe ordered according to accuracy,
- * power usage, ability to report altitude, speed,
- * and bearing, and monetary cost.
+ * location provider. Providers may be ordered according to accuracy,
+ * power usage, ability to report altitude, speed, bearing, and monetary
+ * cost.
*/
public class Criteria implements Parcelable {
/**
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/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 452ba0f..2a575b6 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -473,7 +473,7 @@
* .setSampleRate(32000)
* .setChannelMask(AudioFormat.CHANNEL_IN_MONO)
* .build())
- * .setBufferSize(2*minBuffSize)
+ * .setBufferSizeInBytes(2*minBuffSize)
* .build();
* </pre>
* <p>
diff --git a/media/java/android/media/Image.java b/media/java/android/media/Image.java
index dff5e9a..26b9b8c 100644
--- a/media/java/android/media/Image.java
+++ b/media/java/android/media/Image.java
@@ -75,7 +75,7 @@
/**
* Get the format for this image. This format determines the number of
* ByteBuffers needed to represent the image, and the general layout of the
- * pixel data in each in ByteBuffer.
+ * pixel data in each ByteBuffer.
*
* <p>
* The format is one of the values from
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 7492aa6..8665f28 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -30,6 +30,8 @@
import android.view.Surface;
import android.view.SurfaceHolder;
+import dalvik.system.CloseGuard;
+
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InputStream;
@@ -45,425 +47,202 @@
/**
* @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
, AudioRouting {
+ private final CloseGuard mGuard = CloseGuard.get();
+
/**
* Create a MediaPlayer2 object.
*
* @return A MediaPlayer2 object created
*/
public static final MediaPlayer2 create() {
- // TODO: load MediaUpdate APK
return new MediaPlayer2Impl();
}
@@ -512,7 +291,9 @@
* @hide
*/
// add hidden empty constructor so it doesn't show in SDK
- public MediaPlayer2() { }
+ public MediaPlayer2() {
+ mGuard.open("close");
+ }
/**
* Returns a {@link MediaPlayerBase} implementation which runs based on
@@ -545,7 +326,22 @@
*/
// This is a synchronous call.
@Override
- public abstract void close();
+ public void close() {
+ synchronized (mGuard) {
+ mGuard.close();
+ }
+ }
+
+ // Have to declare protected for finalize() since it is protected
+ // in the base class Object.
+ @Override
+ protected void finalize() throws Throwable {
+ if (mGuard != null) {
+ mGuard.warnIfOpen();
+ }
+
+ close();
+ }
/**
* Starts or resumes playback. If playback had previously been paused,
diff --git a/media/java/android/media/MediaPlayer2Impl.java b/media/java/android/media/MediaPlayer2Impl.java
index dfcbabe..678cb9a 100644
--- a/media/java/android/media/MediaPlayer2Impl.java
+++ b/media/java/android/media/MediaPlayer2Impl.java
@@ -53,9 +53,6 @@
import com.android.framework.protobuf.InvalidProtocolBufferException;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.Preconditions;
-
-import dalvik.system.CloseGuard;
import libcore.io.IoBridge;
@@ -79,7 +76,6 @@
import java.util.List;
import java.util.Map;
import java.util.Scanner;
-import java.util.Set;
import java.util.UUID;
import java.util.Vector;
import java.util.concurrent.Executor;
@@ -105,7 +101,6 @@
private boolean mScreenOnWhilePlaying;
private boolean mStayAwake;
private int mStreamType = AudioManager.USE_DEFAULT_STREAM_TYPE;
- private final CloseGuard mGuard = CloseGuard.get();
private final Object mSrcLock = new Object();
//--- guarded by |mSrcLock| start
@@ -145,6 +140,9 @@
@GuardedBy("mTaskLock")
private Task mCurrentTask;
+ @GuardedBy("this")
+ private boolean mReleased;
+
/**
* Default constructor.
* <p>When done with the MediaPlayer2Impl, you should call {@link #close()},
@@ -159,7 +157,6 @@
mTimeProvider = new TimeProvider(this);
mOpenSubtitleSources = new Vector<InputStream>();
- mGuard.open("close");
/* Native setup requires a weak reference to our object.
* It's easier to create it here than in C++.
@@ -197,9 +194,8 @@
*/
@Override
public void close() {
- synchronized (mGuard) {
- release();
- }
+ super.close();
+ release();
}
/**
@@ -353,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);
@@ -379,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);
@@ -693,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:
@@ -1293,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);
}
});
@@ -1357,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);
}
});
@@ -1390,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);
}
});
@@ -2447,15 +2443,14 @@
// in the base class Object.
@Override
protected void finalize() throws Throwable {
- if (mGuard != null) {
- mGuard.warnIfOpen();
- }
-
- close();
+ super.finalize();
native_finalize();
}
- private void release() {
+ private synchronized void release() {
+ if (mReleased) {
+ return;
+ }
stayAwake(false);
updateSurfaceScreenOn();
synchronized (mEventCbLock) {
@@ -2478,6 +2473,7 @@
resetDrmState();
_release();
+ mReleased = true;
}
private native void _release();
@@ -3030,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 {
@@ -3673,7 +3675,7 @@
supportedSchemes = new UUID[supportedDRMsCount];
for (int i = 0; i < supportedDRMsCount; i++) {
byte[] uuid = new byte[16];
- in.next().getBytesValue().copyTo(uuid, uuid.length);
+ in.next().getBytesValue().copyTo(uuid, 0);
supportedSchemes[i] = bytesToUUID(uuid);
@@ -3806,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 {
@@ -4684,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/jni/OWNERS b/media/jni/OWNERS
new file mode 100644
index 0000000..bb91d4b
--- /dev/null
+++ b/media/jni/OWNERS
@@ -0,0 +1,2 @@
+# extra for MTP related files
+per-file android_mtp_*.cpp=marcone@google.com,jsharkey@android.com,jameswei@google.com,rmojumder@google.com
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/media/tests/MtpTests/OWNERS b/media/tests/MtpTests/OWNERS
new file mode 100644
index 0000000..1928ba8
--- /dev/null
+++ b/media/tests/MtpTests/OWNERS
@@ -0,0 +1,7 @@
+set noparent
+
+marcone@google.com
+jsharkey@android.com
+jameswei@google.com
+rmojumder@google.com
+
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/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUidLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUidLoader.java
index 95efb4c..cc970b9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUidLoader.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUidLoader.java
@@ -36,10 +36,12 @@
private final List<NetworkCycleDataForUid> mData;
private final int mUid;
+ private final boolean mRetrieveDetail;
private NetworkCycleDataForUidLoader(Builder builder) {
super(builder);
mUid = builder.mUid;
+ mRetrieveDetail = builder.mRetrieveDetail;
mData = new ArrayList<NetworkCycleDataForUid>();
}
@@ -50,13 +52,15 @@
mNetworkType, mSubId, start, end, mUid);
final long total = getTotalUsage(stats);
if (total > 0L) {
- final long foreground = getForegroundUsage(start, end);
final NetworkCycleDataForUid.Builder builder = new NetworkCycleDataForUid.Builder();
- builder.setBackgroundUsage(total - foreground)
- .setForegroundUsage(foreground)
- .setStartTime(start)
+ builder.setStartTime(start)
.setEndTime(end)
.setTotalUsage(total);
+ if (mRetrieveDetail) {
+ final long foreground = getForegroundUsage(start, end);
+ builder.setBackgroundUsage(total - foreground)
+ .setForegroundUsage(foreground);
+ }
mData.add(builder.build());
}
} catch (Exception e) {
@@ -88,6 +92,7 @@
extends NetworkCycleDataLoader.Builder<T> {
private int mUid;
+ private boolean mRetrieveDetail = true;
public Builder(Context context) {
super(context);
@@ -97,6 +102,11 @@
mUid = uid;
return this;
}
+
+ public Builder<T> setRetrieveDetail(boolean retrieveDetail) {
+ mRetrieveDetail = retrieveDetail;
+ return this;
+ }
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java
index cc936d2..b1c2c3a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java
@@ -22,6 +22,7 @@
import android.app.usage.NetworkStats;
import android.app.usage.NetworkStatsManager;
import android.content.Context;
+import android.net.ConnectivityManager;
import android.net.INetworkStatsService;
import android.net.INetworkStatsSession;
import android.net.NetworkPolicy;
@@ -34,6 +35,8 @@
import android.text.format.DateUtils;
import android.util.Pair;
+import com.android.settingslib.NetworkPolicyEditor;
+
import java.time.ZonedDateTime;
import java.util.Iterator;
@@ -55,7 +58,6 @@
protected NetworkCycleDataLoader(Builder<?> builder) {
super(builder.mContext);
- mPolicy = builder.mPolicy;
mSubId = builder.mSubId;
mNetworkType = builder.mNetworkType;
mNetworkTemplate = builder.mNetworkTemplate;
@@ -63,6 +65,10 @@
builder.mContext.getSystemService(Context.NETWORK_STATS_SERVICE);
mNetworkStatsService = INetworkStatsService.Stub.asInterface(
ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
+ final NetworkPolicyEditor policyEditor =
+ new NetworkPolicyEditor(NetworkPolicyManager.from(builder.mContext));
+ policyEditor.read();
+ mPolicy = policyEditor.getPolicy(mNetworkTemplate);
}
@Override
@@ -115,7 +121,8 @@
long cycleEnd = historyEnd;
while (cycleEnd > historyStart) {
- final long cycleStart = cycleEnd - (DateUtils.WEEK_IN_MILLIS * 4);
+ final long cycleStart = Math.max(
+ historyStart, cycleEnd - (DateUtils.WEEK_IN_MILLIS * 4));
recordUsage(cycleStart, cycleEnd);
cycleEnd = cycleStart;
}
@@ -154,7 +161,6 @@
public static abstract class Builder<T extends NetworkCycleDataLoader> {
private final Context mContext;
- private NetworkPolicy mPolicy;
private String mSubId;
private int mNetworkType;
private NetworkTemplate mNetworkTemplate;
@@ -163,27 +169,38 @@
mContext = context;
}
- public Builder<T> setNetworkPolicy(NetworkPolicy policy) {
- mPolicy = policy;
- return this;
- }
-
public Builder<T> setSubscriberId(String subId) {
mSubId = subId;
return this;
}
- public Builder<T> setNetworkType(int networkType) {
- mNetworkType = networkType;
- return this;
- }
-
public Builder<T> setNetworkTemplate(NetworkTemplate template) {
mNetworkTemplate = template;
+ setNetworkType();
return this;
}
public abstract T build();
+
+ private void setNetworkType() {
+ if (mNetworkTemplate != null) {
+ final int matchRule = mNetworkTemplate.getMatchRule();
+ switch (matchRule) {
+ case NetworkTemplate.MATCH_MOBILE:
+ case NetworkTemplate.MATCH_MOBILE_WILDCARD:
+ mNetworkType = ConnectivityManager.TYPE_MOBILE;
+ break;
+ case NetworkTemplate.MATCH_WIFI:
+ mNetworkType = ConnectivityManager.TYPE_WIFI;
+ break;
+ case NetworkTemplate.MATCH_ETHERNET:
+ mNetworkType = ConnectivityManager.TYPE_ETHERNET;
+ break;
+ default:
+ mNetworkType = ConnectivityManager.TYPE_MOBILE;
+ }
+ }
+ }
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java
index 3dc110d..cad88b1 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java
@@ -22,6 +22,8 @@
import android.app.usage.NetworkStatsManager;
import android.content.Context;
import android.net.ConnectivityManager;
+import android.net.NetworkPolicy;
+import android.net.NetworkPolicyManager;
import android.os.RemoteException;
import android.text.format.DateUtils;
@@ -39,6 +41,8 @@
@Mock
private NetworkStatsManager mNetworkStatsManager;
@Mock
+ private NetworkPolicyManager mNetworkPolicyManager;
+ @Mock
private Context mContext;
private NetworkCycleChartDataLoader mLoader;
@@ -48,6 +52,9 @@
MockitoAnnotations.initMocks(this);
when(mContext.getSystemService(Context.NETWORK_STATS_SERVICE))
.thenReturn(mNetworkStatsManager);
+ when(mContext.getSystemService(Context.NETWORK_POLICY_SERVICE))
+ .thenReturn(mNetworkPolicyManager);
+ when(mNetworkPolicyManager.getNetworkPolicies()).thenReturn(new NetworkPolicy[0]);
}
@Test
@@ -57,7 +64,7 @@
final int networkType = ConnectivityManager.TYPE_MOBILE;
final String subId = "TestSubscriber";
mLoader = NetworkCycleChartDataLoader.builder(mContext)
- .setNetworkType(networkType).setSubscriberId(subId).build();
+ .setSubscriberId(subId).build();
mLoader.recordUsage(start, end);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java
index 53fe451..2314f27 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java
@@ -16,12 +16,21 @@
package com.android.settingslib.net;
+import static android.app.usage.NetworkStats.Bucket.STATE_FOREGROUND;
+import static android.net.NetworkStats.TAG_NONE;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.usage.NetworkStatsManager;
import android.content.Context;
import android.net.ConnectivityManager;
+import android.net.NetworkPolicy;
+import android.net.NetworkPolicyManager;
import android.text.format.DateUtils;
import com.android.settingslib.SettingsLibRobolectricTestRunner;
@@ -38,6 +47,8 @@
@Mock
private NetworkStatsManager mNetworkStatsManager;
@Mock
+ private NetworkPolicyManager mNetworkPolicyManager;
+ @Mock
private Context mContext;
private NetworkCycleDataForUidLoader mLoader;
@@ -47,20 +58,42 @@
MockitoAnnotations.initMocks(this);
when(mContext.getSystemService(Context.NETWORK_STATS_SERVICE))
.thenReturn(mNetworkStatsManager);
+ when(mContext.getSystemService(Context.NETWORK_POLICY_SERVICE))
+ .thenReturn(mNetworkPolicyManager);
+ when(mNetworkPolicyManager.getNetworkPolicies()).thenReturn(new NetworkPolicy[0]);
}
@Test
- public void recordUsage_shouldQueryNetworkDetailsForUid() {
+ public void recordUsage_shouldQueryNetworkDetailsForUidAndForegroundState() {
final long end = System.currentTimeMillis();
final long start = end - (DateUtils.WEEK_IN_MILLIS * 4);
final int networkType = ConnectivityManager.TYPE_MOBILE;
final String subId = "TestSubscriber";
final int uid = 1;
- mLoader = NetworkCycleDataForUidLoader.builder(mContext)
- .setUid(uid).setNetworkType(networkType).setSubscriberId(subId).build();
+ mLoader = spy(NetworkCycleDataForUidLoader.builder(mContext)
+ .setUid(uid).setSubscriberId(subId).build());
+ doReturn(1024L).when(mLoader).getTotalUsage(any());
mLoader.recordUsage(start, end);
verify(mNetworkStatsManager).queryDetailsForUid(networkType, subId, start, end, uid);
+ verify(mNetworkStatsManager).queryDetailsForUidTagState(
+ networkType, subId, start, end, uid, TAG_NONE, STATE_FOREGROUND);
+ }
+
+ @Test
+ public void recordUsage_retrieveDetailIsFalse_shouldNotQueryNetworkForegroundState() {
+ final long end = System.currentTimeMillis();
+ final long start = end - (DateUtils.WEEK_IN_MILLIS * 4);
+ final int networkType = ConnectivityManager.TYPE_MOBILE;
+ final String subId = "TestSubscriber";
+ final int uid = 1;
+ mLoader = spy(NetworkCycleDataForUidLoader.builder(mContext)
+ .setRetrieveDetail(false).setUid(uid).setSubscriberId(subId).build());
+ doReturn(1024L).when(mLoader).getTotalUsage(any());
+
+ mLoader.recordUsage(start, end);
+ verify(mNetworkStatsManager, never()).queryDetailsForUidTagState(
+ networkType, subId, start, end, uid, TAG_NONE, STATE_FOREGROUND);
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
index be7f1bb..9d60a97 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
@@ -30,6 +30,7 @@
import android.net.INetworkStatsService;
import android.net.INetworkStatsSession;
import android.net.NetworkPolicy;
+import android.net.NetworkPolicyManager;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
import android.os.RemoteException;
@@ -55,6 +56,8 @@
@Mock
private NetworkStatsManager mNetworkStatsManager;
@Mock
+ private NetworkPolicyManager mNetworkPolicyManager;
+ @Mock
private Context mContext;
@Mock
private NetworkPolicy mPolicy;
@@ -62,9 +65,6 @@
private Iterator<Range<ZonedDateTime>> mIterator;
@Mock
private INetworkStatsService mNetworkStatsService;
- @Mock
- private NetworkCycleDataLoader.Builder mBuilder;
-
private NetworkCycleDataTestLoader mLoader;
@@ -73,7 +73,10 @@
MockitoAnnotations.initMocks(this);
when(mContext.getSystemService(Context.NETWORK_STATS_SERVICE))
.thenReturn(mNetworkStatsManager);
+ when(mContext.getSystemService(Context.NETWORK_POLICY_SERVICE))
+ .thenReturn(mNetworkPolicyManager);
when(mPolicy.cycleIterator()).thenReturn(mIterator);
+ when(mNetworkPolicyManager.getNetworkPolicies()).thenReturn(new NetworkPolicy[0]);
}
@Test
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/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 2b51aaa..e1c71fa 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1081,7 +1081,7 @@
<string name="clear_all_notifications_text">Clear all</string>
<!-- The text for the manage notifications link. [CHAR LIMIT=40] -->
- <string name="manage_notifications_text">Manage notifications</string>
+ <string name="manage_notifications_text">Manage</string>
<!-- The text to show in the notifications shade when dnd is suppressing notifications. [CHAR LIMIT=100] -->
<string name="dnd_suppressing_shade_text">Notifications paused by Do Not Disturb</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 8442dd1..6446367 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -279,7 +279,7 @@
<item name="android:fontFamily">sans-serif</item>
</style>
- <style name="BaseBrightnessDialogContainer">
+ <style name="BaseBrightnessDialogContainer" parent="@style/Theme.SystemUI">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
</style>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
index d38cc0f..69aea2c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
@@ -24,8 +24,6 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import sun.misc.Resource;
-
public class NavigationBarCompat {
/**
* Touch slopes and thresholds for quick step operations. Drag slop is the point where the
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index f1b53fe..c7685f8 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -41,7 +41,6 @@
import android.app.trust.TrustManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -49,13 +48,14 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.database.ContentObserver;
+import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricSourceType;
+import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback;
import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
import android.media.AudioManager;
-import android.net.Uri;
import android.os.BatteryManager;
import android.os.CancellationSignal;
import android.os.Handler;
@@ -250,51 +250,6 @@
private static final int HW_UNAVAILABLE_TIMEOUT = 3000; // ms
private static final int HW_UNAVAILABLE_RETRY_MAX = 3;
- private class SettingObserver extends ContentObserver {
- private final Uri FACE_UNLOCK_KEYGUARD_ENABLED =
- Settings.Secure.getUriFor(Settings.Secure.FACE_UNLOCK_KEYGUARD_ENABLED);
-
- private final ContentResolver mContentResolver;
-
- /**
- * Creates a content observer.
- *
- * @param handler The handler to run {@link #onChange} on, or null if none.
- */
- public SettingObserver(Handler handler) {
- super(handler);
- mContentResolver = mContext.getContentResolver();
- updateContentObserver();
- }
-
- public void updateContentObserver() {
- mContentResolver.unregisterContentObserver(this);
- mContentResolver.registerContentObserver(FACE_UNLOCK_KEYGUARD_ENABLED,
- false /* notifyForDescendents */,
- this,
- UserHandle.USER_CURRENT);
-
- // Update the value immediately
- onChange(true /* selfChange */, FACE_UNLOCK_KEYGUARD_ENABLED);
- }
-
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- if (FACE_UNLOCK_KEYGUARD_ENABLED.equals(uri)) {
- mFaceSettingEnabledForUser =
- Settings.Secure.getIntForUser(
- mContentResolver,
- Settings.Secure.FACE_UNLOCK_KEYGUARD_ENABLED,
- 1 /* default */,
- UserHandle.USER_CURRENT) != 0;
- updateBiometricListeningState();
- }
- }
- }
-
- private final SettingObserver mSettingObserver;
- private boolean mFaceSettingEnabledForUser;
-
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
@@ -400,6 +355,18 @@
}
};
+ private boolean mFaceSettingEnabledForUser;
+ private BiometricManager mBiometricManager;
+ private IBiometricEnabledOnKeyguardCallback mBiometricEnabledCallback =
+ new IBiometricEnabledOnKeyguardCallback.Stub() {
+ @Override
+ public void onChanged(BiometricSourceType type, boolean enabled) throws RemoteException {
+ if (type == BiometricSourceType.FACE) {
+ mFaceSettingEnabledForUser = enabled;
+ }
+ }
+ };
+
private OnSubscriptionsChangedListener mSubscriptionListener =
new OnSubscriptionsChangedListener() {
@Override
@@ -1165,7 +1132,7 @@
private CancellationSignal mFingerprintCancelSignal;
private CancellationSignal mFaceCancelSignal;
private FingerprintManager mFpm;
- private FaceManager mFaceAuthenticationManager;
+ private FaceManager mFaceManager;
/**
* When we receive a
@@ -1434,7 +1401,6 @@
mSubscriptionManager = SubscriptionManager.from(context);
mDeviceProvisioned = isDeviceProvisionedInSettingsDb();
mStrongAuthTracker = new StrongAuthTracker(context);
- mSettingObserver = new SettingObserver(mHandler);
// Since device can't be un-provisioned, we only need to register a content observer
// to update mDeviceProvisioned when we are...
@@ -1504,17 +1470,21 @@
if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
mFpm = (FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE);
}
-
if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FACE)) {
- mFaceAuthenticationManager =
- (FaceManager) context.getSystemService(Context.FACE_SERVICE);
+ mFaceManager = (FaceManager) context.getSystemService(Context.FACE_SERVICE);
}
+
+ if (mFpm != null || mFaceManager != null) {
+ mBiometricManager = context.getSystemService(BiometricManager.class);
+ mBiometricManager.registerEnabledOnKeyguardCallback(mBiometricEnabledCallback);
+ }
+
updateBiometricListeningState();
if (mFpm != null) {
mFpm.addLockoutResetCallback(mFingerprintLockoutResetCallback);
}
- if (mFaceAuthenticationManager != null) {
- mFaceAuthenticationManager.addLockoutResetCallback(mFaceLockoutResetCallback);
+ if (mFaceManager != null) {
+ mFaceManager.addLockoutResetCallback(mFaceLockoutResetCallback);
}
ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
@@ -1629,7 +1599,7 @@
mFaceCancelSignal.cancel();
}
mFaceCancelSignal = new CancellationSignal();
- mFaceAuthenticationManager.authenticate(null, mFaceCancelSignal, 0,
+ mFaceManager.authenticate(null, mFaceCancelSignal, 0,
mFaceAuthenticationCallback, null);
setFaceRunningState(BIOMETRIC_STATE_RUNNING);
}
@@ -1641,9 +1611,9 @@
}
public boolean isUnlockWithFacePossible(int userId) {
- return mFaceAuthenticationManager != null && mFaceAuthenticationManager.isHardwareDetected()
+ return mFaceManager != null && mFaceManager.isHardwareDetected()
&& !isFaceDisabled(userId)
- && mFaceAuthenticationManager.hasEnrolledTemplates(userId);
+ && mFaceManager.hasEnrolledTemplates(userId);
}
private void stopListeningForFingerprint() {
@@ -1765,7 +1735,6 @@
* Handle {@link #MSG_USER_SWITCH_COMPLETE}
*/
private void handleUserSwitchComplete(int userId) {
- mSettingObserver.updateContentObserver();
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -2437,7 +2406,7 @@
pw.println(" strongAuthFlags=" + Integer.toHexString(strongAuthFlags));
pw.println(" trustManaged=" + getUserTrustIsManaged(userId));
}
- if (mFaceAuthenticationManager != null && mFaceAuthenticationManager.isHardwareDetected()) {
+ if (mFaceManager != null && mFaceManager.isHardwareDetected()) {
final int userId = ActivityManager.getCurrentUser();
final int strongAuthFlags = mStrongAuthTracker.getStrongAuthForUser(userId);
pw.println(" Face authentication state (user=" + userId + ")");
@@ -2449,6 +2418,7 @@
pw.println(" possible=" + isUnlockWithFacePossible(userId));
pw.println(" strongAuthFlags=" + Integer.toHexString(strongAuthFlags));
pw.println(" trustManaged=" + getUserTrustIsManaged(userId));
+ pw.println(" enabledByUser=" + mFaceSettingEnabledForUser);
}
}
}
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 bde7f1b..512cd82 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -90,6 +90,7 @@
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.util.EmergencyDialerConstants;
import com.android.systemui.volume.SystemUIInterpolators.LogAccelerateInterpolator;
@@ -102,7 +103,8 @@
* is provisioned.
*/
class GlobalActionsDialog implements DialogInterface.OnDismissListener,
- DialogInterface.OnClickListener, DialogInterface.OnShowListener {
+ DialogInterface.OnClickListener, DialogInterface.OnShowListener,
+ ConfigurationController.ConfigurationListener {
static public final String SYSTEM_DIALOG_REASON_KEY = "reason";
static public final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions";
@@ -197,6 +199,8 @@
mEmergencyAffordanceManager = new EmergencyAffordanceManager(context);
mScreenshotHelper = new ScreenshotHelper(context);
+
+ Dependency.get(ConfigurationController.class).addCallback(this);
}
/**
@@ -417,6 +421,15 @@
|| state == SOME_AUTH_REQUIRED_AFTER_USER_REQUEST);
}
+ @Override
+ public void onUiModeChanged() {
+ mContext.getTheme().applyStyle(mContext.getThemeResId(), true);
+ }
+
+ public void destroy() {
+ Dependency.get(ConfigurationController.class).removeCallback(this);
+ }
+
private final class PowerAction extends SinglePressAction implements LongPressAction {
private PowerAction() {
super(R.drawable.ic_lock_power_off,
@@ -1530,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/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
index 1489c21..0394998 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
@@ -61,6 +61,10 @@
@Override
public void destroy() {
SysUiServiceProvider.getComponent(mContext, CommandQueue.class).removeCallbacks(this);
+ if (mGlobalActions != null) {
+ mGlobalActions.destroy();
+ mGlobalActions = null;
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 0b9067e..568a039 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -301,9 +301,10 @@
// mark if we've already shown a warning this cycle. This will prevent the notification
// trigger from spamming users by only showing low/critical warnings once per cycle
if (hybridEnabled) {
- if (mTimeRemaining < mEnhancedEstimates.getSevereWarningThreshold()
- || mBatteryLevel < mLowBatteryReminderLevels[1]) {
+ if (mTimeRemaining <= mEnhancedEstimates.getSevereWarningThreshold()
+ || mBatteryLevel <= mLowBatteryReminderLevels[1]) {
mSevereWarningShownThisChargeCycle = true;
+ mLowWarningShownThisChargeCycle = true;
} else {
mLowWarningShownThisChargeCycle = true;
}
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/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index 556786a..6f847c8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -253,7 +253,8 @@
final int availableWidth = getMeasuredWidth() - getPaddingStart() - getPaddingEnd();
final int leftoverWithespace = availableWidth - maxTiles * mCellWidth;
- final int smallestHorizontalMarginNeeded = leftoverWithespace / (maxTiles - 1);
+ final int smallestHorizontalMarginNeeded;
+ smallestHorizontalMarginNeeded = leftoverWithespace / Math.max(1, maxTiles - 1);
if (smallestHorizontalMarginNeeded > 0){
mCellMarginHorizontal = smallestHorizontalMarginNeeded;
diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
index 6918a63..2ae53b5 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
@@ -46,11 +46,7 @@
window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
window.requestFeature(Window.FEATURE_NO_TITLE);
- // Use a dialog theme as the activity theme, but inflate the content as
- // the QS content.
- ContextThemeWrapper themedContext = new ContextThemeWrapper(this,
- com.android.internal.R.style.Theme_DeviceDefault_QuickSettings);
- View v = LayoutInflater.from(themedContext).inflate(
+ View v = LayoutInflater.from(this).inflate(
R.layout.quick_settings_brightness_dialog, null);
setContentView(v);
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 3a4c218..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";
@@ -733,7 +731,11 @@
mQsExpandImmediate = true;
mNotificationStackScroller.setShouldShowShelfOnly(true);
}
- expand(true /* animate */);
+ if (isFullyCollapsed()){
+ expand(true /* animate */);
+ } else {
+ flingSettings(0 /* velocity */, FLING_EXPAND);
+ }
}
public void expandWithoutQs() {
@@ -1780,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/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
index ef51bf0..8d2552f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
@@ -55,6 +55,7 @@
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final ArrayList<Callback> mCallbacks = new ArrayList<>();
+ private final Object mCallbacksLock = new Object();
private final Context mContext;
private final GlobalSetting mModeSetting;
private final GlobalSetting mConfigSetting;
@@ -114,12 +115,16 @@
@Override
public void addCallback(Callback callback) {
- mCallbacks.add(callback);
+ synchronized (mCallbacksLock) {
+ mCallbacks.add(callback);
+ }
}
@Override
public void removeCallback(Callback callback) {
- mCallbacks.remove(callback);
+ synchronized (mCallbacksLock) {
+ mCallbacks.remove(callback);
+ }
}
@Override
@@ -183,28 +188,40 @@
}
private void fireNextAlarmChanged() {
- Utils.safeForeach(mCallbacks, c -> c.onNextAlarmChanged());
+ synchronized (mCallbacksLock) {
+ Utils.safeForeach(mCallbacks, c -> c.onNextAlarmChanged());
+ }
}
private void fireEffectsSuppressorChanged() {
- Utils.safeForeach(mCallbacks, c -> c.onEffectsSupressorChanged());
+ synchronized (mCallbacksLock) {
+ Utils.safeForeach(mCallbacks, c -> c.onEffectsSupressorChanged());
+ }
}
private void fireZenChanged(int zen) {
- Utils.safeForeach(mCallbacks, c -> c.onZenChanged(zen));
+ synchronized (mCallbacksLock) {
+ Utils.safeForeach(mCallbacks, c -> c.onZenChanged(zen));
+ }
}
private void fireZenAvailableChanged(boolean available) {
- Utils.safeForeach(mCallbacks, c -> c.onZenAvailableChanged(available));
+ synchronized (mCallbacksLock) {
+ Utils.safeForeach(mCallbacks, c -> c.onZenAvailableChanged(available));
+ }
}
private void fireManualRuleChanged(ZenRule rule) {
- Utils.safeForeach(mCallbacks, c -> c.onManualRuleChanged(rule));
+ synchronized (mCallbacksLock) {
+ Utils.safeForeach(mCallbacks, c -> c.onManualRuleChanged(rule));
+ }
}
@VisibleForTesting
protected void fireConfigChanged(ZenModeConfig config) {
- Utils.safeForeach(mCallbacks, c -> c.onConfigChanged(config));
+ synchronized (mCallbacksLock) {
+ Utils.safeForeach(mCallbacks, c -> c.onConfigChanged(config));
+ }
}
@VisibleForTesting
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/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
index a9d49f9..b44630a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
@@ -65,10 +65,12 @@
private static final long ONE_HOUR_MILLIS = Duration.ofHours(1).toMillis();
public static final int BELOW_WARNING_BUCKET = -1;
public static final long BELOW_HYBRID_THRESHOLD = TimeUnit.HOURS.toMillis(2);
+ public static final long BELOW_SEVERE_HYBRID_THRESHOLD = TimeUnit.MINUTES.toMillis(30);
public static final long ABOVE_HYBRID_THRESHOLD = TimeUnit.HOURS.toMillis(4);
private static final long ABOVE_CHARGE_CYCLE_THRESHOLD = Duration.ofHours(8).toMillis();
private static final int OLD_BATTERY_LEVEL_NINE = 9;
private static final int OLD_BATTERY_LEVEL_10 = 10;
+ private static final long VERY_BELOW_SEVERE_HYBRID_THRESHOLD = TimeUnit.MINUTES.toMillis(15);
private HardwarePropertiesManager mHardProps;
private WarningsUI mMockWarnings;
private PowerUI mPowerUI;
@@ -467,6 +469,35 @@
}
@Test
+ public void testSevereWarning_countsAsLowAndSevere_WarningOnlyShownOnce() {
+ mPowerUI.start();
+ when(mEnhancedEstimates.isHybridNotificationEnabled()).thenReturn(true);
+ when(mEnhancedEstimates.getLowWarningThreshold()).thenReturn(PowerUI.THREE_HOURS_IN_MILLIS);
+ when(mEnhancedEstimates.getSevereWarningThreshold()).thenReturn(ONE_HOUR_MILLIS);
+ when(mEnhancedEstimates.getEstimate())
+ .thenReturn(new Estimate(BELOW_SEVERE_HYBRID_THRESHOLD, true));
+ mPowerUI.mBatteryStatus = BatteryManager.BATTERY_HEALTH_GOOD;
+
+ // reduce battery level to handle time based trigger -> level trigger interactions
+ mPowerUI.mBatteryLevel = 5;
+ boolean shouldShow =
+ mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
+ ABOVE_WARNING_BUCKET, BELOW_SEVERE_HYBRID_THRESHOLD,
+ POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD);
+ assertTrue(shouldShow);
+
+ // actually run the end to end since it handles changing the internal state.
+ mPowerUI.maybeShowBatteryWarning(OLD_BATTERY_LEVEL_10, UNPLUGGED, UNPLUGGED,
+ ABOVE_WARNING_BUCKET, ABOVE_WARNING_BUCKET);
+
+ shouldShow =
+ mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
+ ABOVE_WARNING_BUCKET, VERY_BELOW_SEVERE_HYBRID_THRESHOLD,
+ POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD);
+ assertFalse(shouldShow);
+ }
+
+ @Test
public void testMaybeShowBatteryWarning_onlyQueriesEstimateOnBatteryLevelChangeOrNull() {
mPowerUI.start();
Estimate estimate = new Estimate(BELOW_HYBRID_THRESHOLD, true);
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/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index ad2f82c..af33cbc 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -142,6 +142,8 @@
static final boolean RECORD_DEVICE_IDLE_ALARMS = false;
static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
+ static final int TICK_HISTORY_DEPTH = 10;
+
// Indices into the APP_STANDBY_MIN_DELAYS and KEYS_APP_STANDBY_DELAY arrays
static final int ACTIVE_INDEX = 0;
static final int WORKING_INDEX = 1;
@@ -176,21 +178,25 @@
private long mNextNonWakeUpSetAt;
private long mLastWakeup;
private long mLastTrigger;
+
private long mLastTickSet;
- private long mLastTickIssued; // elapsed
private long mLastTickReceived;
private long mLastTickAdded;
private long mLastTickRemoved;
+ // ring buffer of recent TIME_TICK issuance, in the elapsed timebase
+ private final long[] mTickHistory = new long[TICK_HISTORY_DEPTH];
+ private int mNextTickHistory;
+
private final Injector mInjector;
int mBroadcastRefCount = 0;
PowerManager.WakeLock mWakeLock;
- boolean mLastWakeLockUnimportantForLogging;
ArrayList<Alarm> mPendingNonWakeupAlarms = new ArrayList<>();
ArrayList<InFlight> mInFlight = new ArrayList<>();
AlarmHandler mHandler;
ClockReceiver mClockReceiver;
final DeliveryTracker mDeliveryTracker = new DeliveryTracker();
- PendingIntent mTimeTickSender;
+ Intent mTimeTickIntent;
+ IAlarmListener mTimeTickTrigger;
PendingIntent mDateChangeSender;
Random mRandom;
boolean mInteractive = true;
@@ -509,7 +515,7 @@
end = clampPositive(seed.maxWhenElapsed);
flags = seed.flags;
alarms.add(seed);
- if (seed.operation == mTimeTickSender) {
+ if (seed.listener == mTimeTickTrigger) {
mLastTickAdded = mInjector.getCurrentTimeMillis();
}
}
@@ -534,7 +540,7 @@
index = 0 - index - 1;
}
alarms.add(index, alarm);
- if (alarm.operation == mTimeTickSender) {
+ if (alarm.listener == mTimeTickTrigger) {
mLastTickAdded = mInjector.getCurrentTimeMillis();
}
if (DEBUG_BATCH) {
@@ -572,7 +578,7 @@
if (alarm.alarmClock != null) {
mNextAlarmClockMayChange = true;
}
- if (alarm.operation == mTimeTickSender) {
+ if (alarm.listener == mTimeTickTrigger) {
mLastTickRemoved = mInjector.getCurrentTimeMillis();
}
} else {
@@ -690,8 +696,7 @@
Alarm a = alarms.get(i);
final int alarmPrio;
- if (a.operation != null
- && Intent.ACTION_TIME_TICK.equals(a.operation.getIntent().getAction())) {
+ if (a.listener == mTimeTickTrigger) {
alarmPrio = PRIO_TICK;
} else if (a.wakeup) {
alarmPrio = PRIO_WAKEUP;
@@ -823,7 +828,7 @@
}
final int batchSize = alarms.size();
for (int j = 0; j < batchSize; j++) {
- if (alarms.get(j).operation == mTimeTickSender) {
+ if (alarms.get(j).listener == mTimeTickTrigger) {
return true;
}
}
@@ -1111,10 +1116,7 @@
updateNextAlarmClockLocked();
// And send a TIME_TICK right now, since it is important to get the UI updated.
- try {
- mTimeTickSender.send();
- } catch (PendingIntent.CanceledException e) {
- }
+ mHandler.post(() -> getContext().sendBroadcastAsUser(mTimeTickIntent, UserHandle.ALL));
}
static final class InFlight {
@@ -1312,12 +1314,36 @@
}
mWakeLock = mInjector.getAlarmWakeLock();
- mTimeTickSender = PendingIntent.getBroadcastAsUser(getContext(), 0,
- new Intent(Intent.ACTION_TIME_TICK).addFlags(
- Intent.FLAG_RECEIVER_REGISTERED_ONLY
- | Intent.FLAG_RECEIVER_FOREGROUND
- | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS), 0,
- UserHandle.ALL);
+ mTimeTickIntent = new Intent(Intent.ACTION_TIME_TICK).addFlags(
+ Intent.FLAG_RECEIVER_REGISTERED_ONLY
+ | Intent.FLAG_RECEIVER_FOREGROUND
+ | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
+
+ mTimeTickTrigger = new IAlarmListener.Stub() {
+ @Override
+ public void doAlarm(final IAlarmCompleteListener callback) throws RemoteException {
+ if (DEBUG_BATCH) {
+ Slog.v(TAG, "Received TIME_TICK alarm; rescheduling");
+ }
+
+ // Via handler because dispatch invokes this within its lock. OnAlarmListener
+ // takes care of this automatically, but we're using the direct internal
+ // interface here rather than that client-side wrapper infrastructure.
+ mHandler.post(() -> {
+ getContext().sendBroadcastAsUser(mTimeTickIntent, UserHandle.ALL);
+
+ try {
+ callback.alarmComplete(this);
+ } catch (RemoteException e) { /* local method call */ }
+ });
+
+ synchronized (mLock) {
+ mLastTickReceived = mInjector.getCurrentTimeMillis();
+ }
+ mClockReceiver.scheduleTimeTickEvent();
+ }
+ };
+
Intent intent = new Intent(Intent.ACTION_DATE_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
| Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
@@ -1438,12 +1464,9 @@
}
}
- void removeImpl(PendingIntent operation) {
- if (operation == null) {
- return;
- }
+ void removeImpl(PendingIntent operation, IAlarmListener listener) {
synchronized (mLock) {
- removeLocked(operation, null);
+ removeLocked(operation, listener);
}
}
@@ -1887,9 +1910,9 @@
pw.println(" App Standby Parole: " + mAppStandbyParole);
pw.println();
- final long nowRTC = mInjector.getCurrentTimeMillis();
final long nowELAPSED = mInjector.getElapsedRealtime();
final long nowUPTIME = SystemClock.uptimeMillis();
+ final long nowRTC = mInjector.getCurrentTimeMillis();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
pw.print(" nowRTC="); pw.print(nowRTC);
@@ -1899,13 +1922,27 @@
pw.print(" mLastTimeChangeClockTime="); pw.print(mLastTimeChangeClockTime);
pw.print("="); pw.println(sdf.format(new Date(mLastTimeChangeClockTime)));
pw.print(" mLastTimeChangeRealtime="); pw.println(mLastTimeChangeRealtime);
- pw.print(" mLastTickIssued=");
- pw.println(sdf.format(new Date(nowRTC - (nowELAPSED - mLastTickIssued))));
pw.print(" mLastTickReceived="); pw.println(sdf.format(new Date(mLastTickReceived)));
pw.print(" mLastTickSet="); pw.println(sdf.format(new Date(mLastTickSet)));
pw.print(" mLastTickAdded="); pw.println(sdf.format(new Date(mLastTickAdded)));
pw.print(" mLastTickRemoved="); pw.println(sdf.format(new Date(mLastTickRemoved)));
+ if (RECORD_ALARMS_IN_HISTORY) {
+ pw.println();
+ pw.println(" Recent TIME_TICK history:");
+ int i = mNextTickHistory;
+ do {
+ i--;
+ if (i < 0) i = TICK_HISTORY_DEPTH - 1;
+ final long time = mTickHistory[i];
+ pw.print(" ");
+ pw.println((time > 0)
+ ? sdf.format(new Date(nowRTC - (nowELAPSED - time)))
+ : "-");
+ } while (i != mNextTickHistory);
+ pw.println();
+ }
+
SystemServiceManager ssm = LocalServices.getService(SystemServiceManager.class);
if (ssm != null) {
pw.println();
@@ -3640,8 +3677,8 @@
}
// StatsLog requires currentTimeMillis(), which == nowRTC to within usecs.
StatsLog.write(StatsLog.WALL_CLOCK_TIME_SHIFTED, nowRTC);
- removeImpl(mTimeTickSender);
- removeImpl(mDateChangeSender);
+ removeImpl(null, mTimeTickTrigger);
+ removeImpl(mDateChangeSender, null);
rebatchAllAlarms();
mClockReceiver.scheduleTimeTickEvent();
mClockReceiver.scheduleDateChangedEvent();
@@ -3764,14 +3801,8 @@
void setWakelockWorkSource(PendingIntent pi, WorkSource ws, int type, String tag,
int knownUid, boolean first) {
try {
- final boolean unimportant = pi == mTimeTickSender;
- mWakeLock.setUnimportantForLogging(unimportant);
- if (first || mLastWakeLockUnimportantForLogging) {
- mWakeLock.setHistoryTag(tag);
- } else {
- mWakeLock.setHistoryTag(null);
- }
- mLastWakeLockUnimportantForLogging = unimportant;
+ mWakeLock.setHistoryTag(first ? tag : null);
+
if (ws != null) {
mWakeLock.setWorkSource(ws);
return;
@@ -3828,7 +3859,7 @@
if (alarm.repeatInterval > 0) {
// This IntentSender is no longer valid, but this
// is a repeating alarm, so toss the hoser.
- removeImpl(alarm.operation);
+ removeImpl(alarm.operation, null);
}
}
}
@@ -3886,22 +3917,13 @@
class ClockReceiver extends BroadcastReceiver {
public ClockReceiver() {
IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_TIME_TICK);
filter.addAction(Intent.ACTION_DATE_CHANGED);
getContext().registerReceiver(this, filter);
}
@Override
public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(Intent.ACTION_TIME_TICK)) {
- if (DEBUG_BATCH) {
- Slog.v(TAG, "Received TIME_TICK alarm; rescheduling");
- }
- synchronized (mLock) {
- mLastTickReceived = mInjector.getCurrentTimeMillis();
- }
- scheduleTimeTickEvent();
- } else if (intent.getAction().equals(Intent.ACTION_DATE_CHANGED)) {
+ if (intent.getAction().equals(Intent.ACTION_DATE_CHANGED)) {
// Since the kernel does not keep track of DST, we need to
// reset the TZ information at the beginning of each day
// based off of the current Zone gmt offset + userspace tracked
@@ -3923,7 +3945,7 @@
final WorkSource workSource = null; // Let system take blame for time tick events.
setImpl(ELAPSED_REALTIME, mInjector.getElapsedRealtime() + tickEventDelay, 0,
- 0, mTimeTickSender, null, null, AlarmManager.FLAG_STANDALONE, workSource,
+ 0, null, mTimeTickTrigger, null, AlarmManager.FLAG_STANDALONE, workSource,
null, Process.myUid(), "android");
// Finally, remember when we set the tick alarm
@@ -4333,10 +4355,6 @@
// PendingIntent alarm
mSendCount++;
- if (alarm.priorityClass.priority == PRIO_TICK) {
- mLastTickIssued = nowELAPSED;
- }
-
try {
alarm.operation.send(getContext(), 0,
mBackgroundIntent.putExtra(
@@ -4344,13 +4362,10 @@
mDeliveryTracker, mHandler, null,
allowWhileIdle ? mIdleOptions : null);
} catch (PendingIntent.CanceledException e) {
- if (alarm.operation == mTimeTickSender) {
- Slog.wtf(TAG, "mTimeTickSender canceled");
- }
if (alarm.repeatInterval > 0) {
// This IntentSender is no longer valid, but this
// is a repeating alarm, so toss it
- removeImpl(alarm.operation);
+ removeImpl(alarm.operation, null);
}
// No actual delivery was possible, so the delivery tracker's
// 'finished' callback won't be invoked. We also don't need
@@ -4362,6 +4377,16 @@
} else {
// Direct listener callback alarm
mListenerCount++;
+
+ if (RECORD_ALARMS_IN_HISTORY) {
+ if (alarm.listener == mTimeTickTrigger) {
+ mTickHistory[mNextTickHistory++] = nowELAPSED;
+ if (mNextTickHistory >= TICK_HISTORY_DEPTH) {
+ mNextTickHistory = 0;
+ }
+ }
+ }
+
try {
if (DEBUG_LISTENER_CALLBACK) {
Slog.v(TAG, "Alarm to uid=" + alarm.uid
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 a34c2b9..376bc0d 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -77,6 +77,7 @@
import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.os.AtomicFile;
import com.android.internal.os.BackgroundThread;
@@ -104,6 +105,126 @@
/**
* 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 {
@@ -148,21 +269,29 @@
private boolean mScreenLocked;
/** Device is currently active. */
- private static final int STATE_ACTIVE = 0;
+ @VisibleForTesting
+ static final int STATE_ACTIVE = 0;
/** Device is inactive (screen off, no motion) and we are waiting to for idle. */
- private static final int STATE_INACTIVE = 1;
+ @VisibleForTesting
+ static final int STATE_INACTIVE = 1;
/** Device is past the initial inactive period, and waiting for the next idle period. */
- private static final int STATE_IDLE_PENDING = 2;
+ @VisibleForTesting
+ static final int STATE_IDLE_PENDING = 2;
/** Device is currently sensing motion. */
- private static final int STATE_SENSING = 3;
+ @VisibleForTesting
+ static final int STATE_SENSING = 3;
/** Device is currently finding location (and may still be sensing). */
- private static final int STATE_LOCATING = 4;
+ @VisibleForTesting
+ static final int STATE_LOCATING = 4;
/** Device is in the idle state, trying to stay asleep as much as possible. */
- private static final int STATE_IDLE = 5;
+ @VisibleForTesting
+ static final int STATE_IDLE = 5;
/** Device is in the idle state, but temporarily out of idle to do regular maintenance. */
- private static final int STATE_IDLE_MAINTENANCE = 6;
+ @VisibleForTesting
+ static final int STATE_IDLE_MAINTENANCE = 6;
- private static String stateToString(int state) {
+ @VisibleForTesting
+ static String stateToString(int state) {
switch (state) {
case STATE_ACTIVE: return "ACTIVE";
case STATE_INACTIVE: return "INACTIVE";
@@ -176,21 +305,30 @@
}
/** Device is currently active. */
- private static final int LIGHT_STATE_ACTIVE = 0;
+ @VisibleForTesting
+ static final int LIGHT_STATE_ACTIVE = 0;
/** Device is inactive (screen off) and we are waiting to for the first light idle. */
- private static final int LIGHT_STATE_INACTIVE = 1;
+ @VisibleForTesting
+ static final int LIGHT_STATE_INACTIVE = 1;
/** Device is about to go idle for the first time, wait for current work to complete. */
- private static final int LIGHT_STATE_PRE_IDLE = 3;
+ @VisibleForTesting
+ static final int LIGHT_STATE_PRE_IDLE = 3;
/** Device is in the light idle state, trying to stay asleep as much as possible. */
- private static final int LIGHT_STATE_IDLE = 4;
+ @VisibleForTesting
+ static final int LIGHT_STATE_IDLE = 4;
/** Device is in the light idle state, we want to go in to idle maintenance but are
* waiting for network connectivity before doing so. */
- private static final int LIGHT_STATE_WAITING_FOR_NETWORK = 5;
+ @VisibleForTesting
+ static final int LIGHT_STATE_WAITING_FOR_NETWORK = 5;
/** Device is in the light idle state, but temporarily out of idle to do regular maintenance. */
- private static final int LIGHT_STATE_IDLE_MAINTENANCE = 6;
+ @VisibleForTesting
+ static final int LIGHT_STATE_IDLE_MAINTENANCE = 6;
/** Device light idle state is overriden, now applying deep doze state. */
- private static final int LIGHT_STATE_OVERRIDE = 7;
- private static String lightStateToString(int state) {
+ @VisibleForTesting
+ static final int LIGHT_STATE_OVERRIDE = 7;
+
+ @VisibleForTesting
+ static String lightStateToString(int state) {
switch (state) {
case LIGHT_STATE_ACTIVE: return "ACTIVE";
case LIGHT_STATE_INACTIVE: return "INACTIVE";
@@ -382,6 +520,8 @@
public void onAlarm() {
if (mState == STATE_SENSING) {
synchronized (DeviceIdleController.this) {
+ // Restart the device idle progression in case the device moved but the screen
+ // didn't turn on.
becomeInactiveIfAppropriateLocked();
}
}
@@ -422,11 +562,16 @@
}
};
- private final class MotionListener extends TriggerEventListener
+ @VisibleForTesting
+ final class MotionListener extends TriggerEventListener
implements SensorEventListener {
boolean active = false;
+ public boolean isActive() {
+ return active;
+ }
+
@Override
public void onTrigger(TriggerEvent event) {
synchronized (DeviceIdleController.this) {
@@ -472,7 +617,7 @@
active = false;
}
}
- private final MotionListener mMotionListener = new MotionListener();
+ @VisibleForTesting final MotionListener mMotionListener = new MotionListener();
private final LocationListener mGenericLocationListener = new LocationListener() {
@Override
@@ -594,7 +739,7 @@
public float LIGHT_IDLE_FACTOR;
/**
- * This is the maximum time we will run in idle maintenence mode.
+ * This is the maximum time we will run in idle maintenance mode.
* @see Settings.Global#DEVICE_IDLE_CONSTANTS
* @see #KEY_LIGHT_MAX_IDLE_TIMEOUT
*/
@@ -1360,6 +1505,45 @@
}
}
+ static class Injector {
+ private final Context mContext;
+
+ Injector(Context ctx) {
+ mContext = ctx;
+ }
+
+ AlarmManager getAlarmManager() {
+ return mContext.getSystemService(AlarmManager.class);
+ }
+
+ AnyMotionDetector getAnyMotionDetector(Handler handler, SensorManager sm,
+ AnyMotionDetector.DeviceIdleCallback callback, float angleThreshold) {
+ return new AnyMotionDetector(getPowerManager(), handler, sm, callback, angleThreshold);
+ }
+
+ AppStateTracker getAppStateTracker(Context ctx, Looper looper) {
+ return new AppStateTracker(ctx, looper);
+ }
+
+ ConnectivityService getConnectivityService() {
+ return (ConnectivityService) ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
+ }
+
+ LocationManager getLocationManager() {
+ return mContext.getSystemService(LocationManager.class);
+ }
+
+ MyHandler getHandler(DeviceIdleController ctlr) {
+ return ctlr.new MyHandler(BackgroundThread.getHandler().getLooper());
+ }
+
+ PowerManager getPowerManager() {
+ return mContext.getSystemService(PowerManager.class);
+ }
+ }
+
+ private final Injector mInjector;
+
private ActivityTaskManagerInternal.ScreenObserver mScreenObserver =
new ActivityTaskManagerInternal.ScreenObserver() {
@Override
@@ -1373,14 +1557,19 @@
}
};
- public DeviceIdleController(Context context) {
+ @VisibleForTesting DeviceIdleController(Context context, Injector injector) {
super(context);
+ mInjector = injector;
mConfigFile = new AtomicFile(new File(getSystemDir(), "deviceidle.xml"));
- mHandler = new MyHandler(BackgroundThread.getHandler().getLooper());
- mAppStateTracker = new AppStateTracker(context, FgThread.get().getLooper());
+ mHandler = mInjector.getHandler(this);
+ mAppStateTracker = mInjector.getAppStateTracker(context, FgThread.get().getLooper());
LocalServices.addService(AppStateTracker.class, mAppStateTracker);
}
+ public DeviceIdleController(Context context) {
+ this(context, new Injector(context));
+ }
+
boolean isAppOnWhitelistInternal(int appid) {
synchronized (this) {
return Arrays.binarySearch(mPowerSaveWhitelistAllAppIdArray, appid) >= 0;
@@ -1459,20 +1648,19 @@
public void onBootPhase(int phase) {
if (phase == PHASE_SYSTEM_SERVICES_READY) {
synchronized (this) {
- mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
+ mAlarmManager = mInjector.getAlarmManager();
mBatteryStats = BatteryStatsService.getService();
mLocalActivityManager = getLocalService(ActivityManagerInternal.class);
mLocalActivityTaskManager = getLocalService(ActivityTaskManagerInternal.class);
mLocalPowerManager = getLocalService(PowerManagerInternal.class);
- mPowerManager = getContext().getSystemService(PowerManager.class);
+ mPowerManager = mInjector.getPowerManager();
mActiveIdleWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"deviceidle_maint");
mActiveIdleWakeLock.setReferenceCounted(false);
mGoingIdleWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"deviceidle_going_idle");
mGoingIdleWakeLock.setReferenceCounted(true);
- mConnectivityService = (ConnectivityService)ServiceManager.getService(
- Context.CONNECTIVITY_SERVICE);
+ mConnectivityService = mInjector.getConnectivityService();
mNetworkPolicyManager = INetworkPolicyManager.Stub.asInterface(
ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
mNetworkPolicyManagerInternal = getLocalService(NetworkPolicyManagerInternal.class);
@@ -1495,8 +1683,7 @@
if (getContext().getResources().getBoolean(
com.android.internal.R.bool.config_autoPowerModePrefetchLocation)) {
- mLocationManager = (LocationManager) getContext().getSystemService(
- Context.LOCATION_SERVICE);
+ mLocationManager = mInjector.getLocationManager();
mLocationRequest = new LocationRequest()
.setQuality(LocationRequest.ACCURACY_FINE)
.setInterval(0)
@@ -1506,9 +1693,8 @@
float angleThreshold = getContext().getResources().getInteger(
com.android.internal.R.integer.config_autoPowerModeThresholdAngle) / 100f;
- mAnyMotionDetector = new AnyMotionDetector(
- (PowerManager) getContext().getSystemService(Context.POWER_SERVICE),
- mHandler, mSensorManager, this, angleThreshold);
+ mAnyMotionDetector = mInjector.getAnyMotionDetector(mHandler, mSensorManager, this,
+ angleThreshold);
mAppStateTracker.onSystemServicesReady();
@@ -2005,6 +2191,11 @@
}
}
+ @VisibleForTesting
+ boolean isScreenOn() {
+ return mScreenOn;
+ }
+
void updateInteractivityLocked() {
// The interactivity state from the power manager tells us whether the display is
// in a state that we need to keep things running so they will update at a normal
@@ -2024,6 +2215,11 @@
}
}
+ @VisibleForTesting
+ boolean isCharging() {
+ return mCharging;
+ }
+
void updateChargingLocked(boolean charging) {
if (DEBUG) Slog.i(TAG, "updateChargingLocked: charging=" + charging);
if (!charging && mCharging) {
@@ -2071,6 +2267,18 @@
}
}
+ /** Must only be used in tests. */
+ @VisibleForTesting
+ void setDeepEnabledForTest(boolean enabled) {
+ mDeepEnabled = enabled;
+ }
+
+ /** Must only be used in tests. */
+ @VisibleForTesting
+ void setLightEnabledForTest(boolean enabled) {
+ mLightEnabled = enabled;
+ }
+
void becomeInactiveIfAppropriateLocked() {
if (DEBUG) Slog.d(TAG, "becomeInactiveIfAppropriateLocked()");
if ((!mScreenOn && !mCharging) || mForceIdle) {
@@ -2093,7 +2301,7 @@
}
}
- void resetIdleManagementLocked() {
+ private void resetIdleManagementLocked() {
mNextIdlePendingDelay = 0;
mNextIdleDelay = 0;
mNextLightIdleDelay = 0;
@@ -2104,7 +2312,7 @@
mAnyMotionDetector.stop();
}
- void resetLightIdleManagementLocked() {
+ private void resetLightIdleManagementLocked() {
cancelLightAlarmLocked();
}
@@ -2117,6 +2325,11 @@
}
}
+ @VisibleForTesting
+ int getLightState() {
+ return mLightState;
+ }
+
void stepLightIdleStateLocked(String reason) {
if (mLightState == LIGHT_STATE_OVERRIDE) {
// If we are already in deep device idle mode, then
@@ -2200,6 +2413,18 @@
}
}
+ /** Must only be used in tests. */
+ @VisibleForTesting
+ void setLocationManagerForTest(LocationManager lm) {
+ mLocationManager = lm;
+ }
+
+ @VisibleForTesting
+ int getState() {
+ return mState;
+ }
+
+ @VisibleForTesting
void stepIdleStateLocked(String reason) {
if (DEBUG) Slog.d(TAG, "stepIdleStateLocked: mState=" + mState);
EventLogTags.writeDeviceIdleStep();
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 9688d26..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;
@@ -50,7 +52,6 @@
import static android.view.Display.INVALID_DISPLAY;
import static android.view.Display.TYPE_VIRTUAL;
import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
-
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOCUS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_IDLE;
@@ -97,7 +98,6 @@
import static com.android.server.am.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT;
import static com.android.server.am.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
import static com.android.server.am.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT;
-
import static java.lang.Integer.MAX_VALUE;
import android.Manifest;
@@ -132,6 +132,7 @@
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
@@ -156,6 +157,7 @@
import android.service.voice.IVoiceInteractionSession;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.IntArray;
import android.util.MergedConfiguration;
@@ -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. */
@@ -445,7 +443,7 @@
// The default minimal size that will be used if the activity doesn't specify its minimal size.
// It will be calculated when the default display gets added.
- int mDefaultMinSizeOfResizeableTask = -1;
+ int mDefaultMinSizeOfResizeableTaskDp = -1;
// Whether tasks have moved and we need to rank the tasks before next OOM scoring
private boolean mTaskLayersChanged = true;
@@ -689,11 +687,12 @@
mDefaultDisplay = activityDisplay;
}
addChild(activityDisplay, ActivityDisplay.POSITION_TOP);
- calculateDefaultMinimalSizeOfResizeableTasks(activityDisplay);
}
+ 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);
}
}
@@ -4319,7 +4327,6 @@
// The display hasn't been added to ActivityManager yet, create a new record now.
activityDisplay = new ActivityDisplay(this, display);
addChild(activityDisplay, ActivityDisplay.POSITION_BOTTOM);
- calculateDefaultMinimalSizeOfResizeableTasks(activityDisplay);
mWindowManager.onDisplayAdded(displayId);
return activityDisplay;
}
@@ -4337,10 +4344,13 @@
mActivityDisplays.remove(activityDisplay);
}
- private void calculateDefaultMinimalSizeOfResizeableTasks(ActivityDisplay display) {
- mDefaultMinSizeOfResizeableTask =
- mService.mContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.default_minimal_size_resizable_task);
+ private void calculateDefaultMinimalSizeOfResizeableTasks() {
+ final Resources res = mService.mContext.getResources();
+ final float minimalSize = res.getDimension(
+ com.android.internal.R.dimen.default_minimal_size_resizable_task);
+ final DisplayMetrics dm = res.getDisplayMetrics();
+
+ mDefaultMinSizeOfResizeableTaskDp = (int) (minimalSize / dm.density);
}
private void handleDisplayRemoved(int displayId) {
@@ -4836,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
@@ -4879,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/PersistentConnection.java b/services/core/java/com/android/server/am/PersistentConnection.java
index 080fa2a..3490b1d 100644
--- a/services/core/java/com/android/server/am/PersistentConnection.java
+++ b/services/core/java/com/android/server/am/PersistentConnection.java
@@ -59,6 +59,8 @@
* know what to do when the service component has gone missing, for example. If the user of this
* class wants to restore the connection, then it should call {@link #unbind()} and {@link #bind}
* explicitly.
+ *
+ * atest ${ANDROID_BUILD_TOP}/frameworks/base/services/tests/mockingservicestests/src/com/android/server/am/PersistentConnectionTest.java
*/
public abstract class PersistentConnection<T> {
private final Object mLock = new Object();
@@ -76,6 +78,7 @@
private final long mRebindBackoffMs;
private final double mRebindBackoffIncrease;
private final long mRebindMaxBackoffMs;
+ private final long mResetBackoffDelay;
private long mReconnectTime;
@@ -109,6 +112,9 @@
@GuardedBy("mLock")
private int mNumBindingDied;
+ @GuardedBy("mLock")
+ private long mLastConnectedTime;
+
private final ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
@@ -127,7 +133,10 @@
mNumConnected++;
mIsConnected = true;
+ mLastConnectedTime = injectUptimeMillis();
mService = asInterface(service);
+
+ scheduleStableCheckLocked();
}
}
@@ -140,6 +149,9 @@
mNumDisconnected++;
cleanUpConnectionLocked();
+
+ // Note we won't increase the rebind timeout here, because we don't explicitly
+ // rebind in this case.
}
}
@@ -168,7 +180,8 @@
public PersistentConnection(@NonNull String tag, @NonNull Context context,
@NonNull Handler handler, int userId, @NonNull ComponentName componentName,
- long rebindBackoffSeconds, double rebindBackoffIncrease, long rebindMaxBackoffSeconds) {
+ long rebindBackoffSeconds, double rebindBackoffIncrease, long rebindMaxBackoffSeconds,
+ long resetBackoffDelay) {
mTag = tag;
mContext = context;
mHandler = handler;
@@ -178,6 +191,7 @@
mRebindBackoffMs = rebindBackoffSeconds * 1000;
mRebindBackoffIncrease = rebindBackoffIncrease;
mRebindMaxBackoffMs = rebindMaxBackoffSeconds * 1000;
+ mResetBackoffDelay = resetBackoffDelay * 1000;
mNextBackoffMs = mRebindBackoffMs;
}
@@ -242,6 +256,42 @@
}
}
+ /** Return the next back-off time */
+ public long getNextBackoffMs() {
+ synchronized (mLock) {
+ return mNextBackoffMs;
+ }
+ }
+
+ /** Return the number of times the connected callback called. */
+ public int getNumConnected() {
+ synchronized (mLock) {
+ return mNumConnected;
+ }
+ }
+
+ /** Return the number of times the disconnected callback called. */
+ public int getNumDisconnected() {
+ synchronized (mLock) {
+ return mNumDisconnected;
+ }
+ }
+
+ /** Return the number of times the binding died callback called. */
+ public int getNumBindingDied() {
+ synchronized (mLock) {
+ return mNumBindingDied;
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void resetBackoffLocked() {
+ if (mNextBackoffMs != mRebindBackoffMs) {
+ mNextBackoffMs = mRebindBackoffMs;
+ Log.i(mTag, "Backoff reset to " + mNextBackoffMs);
+ }
+ }
+
@GuardedBy("mLock")
public final void bindInnerLocked(boolean resetBackoff) {
unscheduleRebindLocked();
@@ -251,9 +301,10 @@
}
mBound = true;
+ unscheduleStableCheckLocked();
+
if (resetBackoff) {
- // Note this is the only place we reset the backoff time.
- mNextBackoffMs = mRebindBackoffMs;
+ resetBackoffLocked();
}
final Intent service = new Intent().setComponent(mComponentName);
@@ -287,6 +338,8 @@
private void cleanUpConnectionLocked() {
mIsConnected = false;
mService = null;
+
+ unscheduleStableCheckLocked();
}
/**
@@ -297,6 +350,7 @@
mShouldBeBound = false;
unbindLocked();
+ unscheduleStableCheckLocked();
}
}
@@ -338,6 +392,33 @@
}
}
+ private final Runnable mStableCheck = this::stableConnectionCheck;
+
+ private void stableConnectionCheck() {
+ synchronized (mLock) {
+ final long now = injectUptimeMillis();
+ final long timeRemaining = (mLastConnectedTime + mResetBackoffDelay) - now;
+ if (DEBUG) {
+ Log.d(mTag, "stableConnectionCheck: bound=" + mBound + " connected=" + mIsConnected
+ + " remaining=" + timeRemaining);
+ }
+ if (mBound && mIsConnected && timeRemaining <= 0) {
+ resetBackoffLocked();
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void unscheduleStableCheckLocked() {
+ injectRemoveCallbacks(mStableCheck);
+ }
+
+ @GuardedBy("mLock")
+ private void scheduleStableCheckLocked() {
+ unscheduleStableCheckLocked();
+ injectPostAtTime(mStableCheck, injectUptimeMillis() + mResetBackoffDelay);
+ }
+
/** Must be implemented by a subclass to convert an {@link IBinder} to a stub. */
protected abstract T asInterface(IBinder binder);
@@ -367,6 +448,10 @@
pw.print(mNumDisconnected);
pw.print(" Died: ");
pw.print(mNumBindingDied);
+ if (mIsConnected) {
+ pw.print(" Duration: ");
+ TimeUtils.formatDuration((injectUptimeMillis() - mLastConnectedTime), pw);
+ }
pw.println();
}
}
@@ -407,6 +492,11 @@
}
@VisibleForTesting
+ Runnable getStableCheckRunnableForTest() {
+ return mStableCheck;
+ }
+
+ @VisibleForTesting
boolean shouldBeBoundForTest() {
return mShouldBeBound;
}
diff --git a/services/core/java/com/android/server/am/PersisterQueue.java b/services/core/java/com/android/server/am/PersisterQueue.java
new file mode 100644
index 0000000..60ea0fa
--- /dev/null
+++ b/services/core/java/com/android/server/am/PersisterQueue.java
@@ -0,0 +1,277 @@
+/*
+ * 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 android.os.Process;
+import android.os.SystemClock;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.function.Predicate;
+
+/**
+ * The common threading logic for persisters to use so that they can run in the same threads.
+ * Methods in this class are synchronized on its instance, so caller could also synchronize on
+ * its instance to perform modifications in items.
+ */
+class PersisterQueue {
+ private static final String TAG = "PersisterQueue";
+ private static final boolean DEBUG = false;
+
+ /** When not flushing don't write out files faster than this */
+ private static final long INTER_WRITE_DELAY_MS = 500;
+
+ /**
+ * When not flushing delay this long before writing the first file out. This gives the next task
+ * being launched a chance to load its resources without this occupying IO bandwidth.
+ */
+ private static final long PRE_TASK_DELAY_MS = 3000;
+
+ /** The maximum number of entries to keep in the queue before draining it automatically. */
+ private static final int MAX_WRITE_QUEUE_LENGTH = 6;
+
+ /** Special value for mWriteTime to mean don't wait, just write */
+ private static final long FLUSH_QUEUE = -1;
+
+ /** An {@link WriteQueueItem} that doesn't do anything. Used to trigger {@link
+ * Listener#onPreProcessItem}. */
+ static final WriteQueueItem EMPTY_ITEM = () -> { };
+
+ private final long mInterWriteDelayMs;
+ private final long mPreTaskDelayMs;
+ private final LazyTaskWriterThread mLazyTaskWriterThread;
+ private final ArrayList<WriteQueueItem> mWriteQueue = new ArrayList<>();
+
+ private final ArrayList<Listener> mListeners = new ArrayList<>();
+
+ /**
+ * Value determines write delay mode as follows: < 0 We are Flushing. No delays between writes
+ * until the image queue is drained and all tasks needing persisting are written to disk. There
+ * is no delay between writes. == 0 We are Idle. Next writes will be delayed by
+ * #PRE_TASK_DELAY_MS. > 0 We are Actively writing. Next write will be at this time. Subsequent
+ * writes will be delayed by #INTER_WRITE_DELAY_MS.
+ */
+ private long mNextWriteTime = 0;
+
+ PersisterQueue() {
+ this(INTER_WRITE_DELAY_MS, PRE_TASK_DELAY_MS);
+ }
+
+ /** Used for tests to reduce waiting time. */
+ @VisibleForTesting
+ PersisterQueue(long interWriteDelayMs, long preTaskDelayMs) {
+ if (interWriteDelayMs < 0 || preTaskDelayMs < 0) {
+ throw new IllegalArgumentException("Both inter-write delay and pre-task delay need to"
+ + "be non-negative. inter-write delay: " + interWriteDelayMs
+ + "ms pre-task delay: " + preTaskDelayMs);
+ }
+ mInterWriteDelayMs = interWriteDelayMs;
+ mPreTaskDelayMs = preTaskDelayMs;
+ mLazyTaskWriterThread = new LazyTaskWriterThread("LazyTaskWriterThread");
+ }
+
+ synchronized void startPersisting() {
+ if (!mLazyTaskWriterThread.isAlive()) {
+ mLazyTaskWriterThread.start();
+ }
+ }
+
+ /** Stops persisting thread. Should only be used in tests. */
+ @VisibleForTesting
+ void stopPersisting() throws InterruptedException {
+ if (!mLazyTaskWriterThread.isAlive()) {
+ return;
+ }
+
+ synchronized (this) {
+ mLazyTaskWriterThread.interrupt();
+ }
+ mLazyTaskWriterThread.join();
+ }
+
+ synchronized void addItem(WriteQueueItem item, boolean flush) {
+ mWriteQueue.add(item);
+
+ if (flush || mWriteQueue.size() > MAX_WRITE_QUEUE_LENGTH) {
+ mNextWriteTime = FLUSH_QUEUE;
+ } else if (mNextWriteTime == 0) {
+ mNextWriteTime = SystemClock.uptimeMillis() + mPreTaskDelayMs;
+ }
+ notify();
+ }
+
+ synchronized <T extends WriteQueueItem> T findLastItem(Predicate<T> predicate, Class<T> clazz) {
+ for (int i = mWriteQueue.size() - 1; i >= 0; --i) {
+ WriteQueueItem writeQueueItem = mWriteQueue.get(i);
+ if (clazz.isInstance(writeQueueItem)) {
+ T item = clazz.cast(writeQueueItem);
+ if (predicate.test(item)) {
+ return item;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ synchronized <T extends WriteQueueItem> void removeItems(Predicate<T> predicate,
+ Class<T> clazz) {
+ for (int i = mWriteQueue.size() - 1; i >= 0; --i) {
+ WriteQueueItem writeQueueItem = mWriteQueue.get(i);
+ if (clazz.isInstance(writeQueueItem)) {
+ T item = clazz.cast(writeQueueItem);
+ if (predicate.test(item)) {
+ if (DEBUG) Slog.d(TAG, "Removing " + item + " from write queue.");
+ mWriteQueue.remove(i);
+ }
+ }
+ }
+ }
+
+ synchronized void flush() {
+ mNextWriteTime = FLUSH_QUEUE;
+ notifyAll();
+ do {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ }
+ } while (mNextWriteTime == FLUSH_QUEUE);
+ }
+
+ void yieldIfQueueTooDeep() {
+ boolean stall = false;
+ synchronized (this) {
+ if (mNextWriteTime == FLUSH_QUEUE) {
+ stall = true;
+ }
+ }
+ if (stall) {
+ Thread.yield();
+ }
+ }
+
+ void addListener(Listener listener) {
+ mListeners.add(listener);
+ }
+
+ private void processNextItem() throws InterruptedException {
+ // This part is extracted into a method so that the GC can clearly see the end of the
+ // scope of the variable 'item'. If this part was in the loop in LazyTaskWriterThread, the
+ // last item it processed would always "leak".
+ // See https://b.corp.google.com/issues/64438652#comment7
+
+ // If mNextWriteTime, then don't delay between each call to saveToXml().
+ final WriteQueueItem item;
+ synchronized (this) {
+ if (mNextWriteTime != FLUSH_QUEUE) {
+ // The next write we don't have to wait so long.
+ mNextWriteTime = SystemClock.uptimeMillis() + mInterWriteDelayMs;
+ if (DEBUG) {
+ Slog.d(TAG, "Next write time may be in " + mInterWriteDelayMs
+ + " msec. (" + mNextWriteTime + ")");
+ }
+ }
+
+ while (mWriteQueue.isEmpty()) {
+ if (mNextWriteTime != 0) {
+ mNextWriteTime = 0; // idle.
+ notify(); // May need to wake up flush().
+ }
+ // Make sure we exit this thread correctly when interrupted before going to
+ // indefinite wait.
+ if (Thread.currentThread().isInterrupted()) {
+ throw new InterruptedException();
+ }
+ if (DEBUG) Slog.d(TAG, "LazyTaskWriter: waiting indefinitely.");
+ wait();
+ // Invariant: mNextWriteTime is either FLUSH_QUEUE or PRE_WRITE_DELAY_MS
+ // from now.
+ }
+ item = mWriteQueue.remove(0);
+
+ long now = SystemClock.uptimeMillis();
+ if (DEBUG) {
+ Slog.d(TAG, "LazyTaskWriter: now=" + now + " mNextWriteTime=" + mNextWriteTime
+ + " mWriteQueue.size=" + mWriteQueue.size());
+ }
+ while (now < mNextWriteTime) {
+ if (DEBUG) {
+ Slog.d(TAG, "LazyTaskWriter: waiting " + (mNextWriteTime - now));
+ }
+ wait(mNextWriteTime - now);
+ now = SystemClock.uptimeMillis();
+ }
+
+ // Got something to do.
+ }
+
+ item.process();
+ }
+
+ interface WriteQueueItem {
+ void process();
+ }
+
+ interface Listener {
+ /**
+ * Called before {@link PersisterQueue} tries to process next item.
+ *
+ * Note if the queue is empty, this callback will be called before the indefinite wait. This
+ * will be called once when {@link PersisterQueue} starts the internal thread before the
+ * indefinite wait.
+ *
+ * This callback is called w/o locking the instance of {@link PersisterQueue}.
+ *
+ * @param queueEmpty {@code true} if the queue is empty, which indicates {@link
+ * PersisterQueue} is likely to enter indefinite wait; or {@code false} if there is still
+ * item to process.
+ */
+ void onPreProcessItem(boolean queueEmpty);
+ }
+
+ private class LazyTaskWriterThread extends Thread {
+
+ private LazyTaskWriterThread(String name) {
+ super(name);
+ }
+
+ @Override
+ public void run() {
+ Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+ try {
+ while (true) {
+ final boolean probablyDone;
+ synchronized (PersisterQueue.this) {
+ probablyDone = mWriteQueue.isEmpty();
+ }
+
+ for (int i = mListeners.size() - 1; i >= 0; --i) {
+ mListeners.get(i).onPreProcessItem(probablyDone);
+ }
+
+ processNextItem();
+ }
+ } catch (InterruptedException e) {
+ Slog.e(TAG, "Persister thread is exiting. Should never happen in prod, but"
+ + "it's OK in tests.");
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index e11e003..4d0b1da 100644
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ b/services/core/java/com/android/server/am/RecentTasks.java
@@ -30,8 +30,9 @@
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
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;
@@ -55,7 +56,6 @@
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
-import android.graphics.Rect;
import android.os.Bundle;
import android.os.Environment;
import android.os.IBinder;
@@ -431,7 +431,7 @@
void onSystemReadyLocked() {
loadRecentsComponent(mService.mContext.getResources());
mTasks.clear();
- mTaskPersister.startPersisting();
+ mTaskPersister.onSystemReady();
}
Bitmap getTaskDescriptionIcon(String path) {
@@ -1244,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) {
@@ -1252,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/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java
index 481bb2b..fc5dfb4 100644
--- a/services/core/java/com/android/server/am/TaskPersister.java
+++ b/services/core/java/com/android/server/am/TaskPersister.java
@@ -16,13 +16,14 @@
package com.android.server.am;
+import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
+
import android.annotation.NonNull;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Debug;
import android.os.Environment;
import android.os.FileUtils;
-import android.os.Process;
import android.os.SystemClock;
import android.util.ArraySet;
import android.util.AtomicFile;
@@ -34,6 +35,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;
+
import libcore.io.IoUtils;
import org.xmlpull.v1.XmlPullParser;
@@ -54,32 +56,18 @@
import java.util.Comparator;
import java.util.List;
-import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
-
-public class TaskPersister {
+/**
+ * Persister that saves recent tasks into disk.
+ */
+public class TaskPersister implements PersisterQueue.Listener {
static final String TAG = "TaskPersister";
static final boolean DEBUG = false;
-
- /** When not flushing don't write out files faster than this */
- private static final long INTER_WRITE_DELAY_MS = 500;
-
- /**
- * When not flushing delay this long before writing the first file out. This gives the next task
- * being launched a chance to load its resources without this occupying IO bandwidth.
- */
- private static final long PRE_TASK_DELAY_MS = 3000;
-
- /** The maximum number of entries to keep in the queue before draining it automatically. */
- private static final int MAX_WRITE_QUEUE_LENGTH = 6;
-
- /** Special value for mWriteTime to mean don't wait, just write */
- private static final long FLUSH_QUEUE = -1;
+ static final String IMAGE_EXTENSION = ".png";
private static final String TASKS_DIRNAME = "recent_tasks";
private static final String TASK_FILENAME_SUFFIX = "_task.xml";
private static final String IMAGES_DIRNAME = "recent_images";
private static final String PERSISTED_TASK_IDS_FILENAME = "persisted_taskIds.txt";
- static final String IMAGE_EXTENSION = ".png";
private static final String TAG_TASK = "task";
@@ -90,39 +78,9 @@
private final File mTaskIdsDir;
// To lock file operations in TaskPersister
private final Object mIoLock = new Object();
+ private final PersisterQueue mPersisterQueue;
- /**
- * Value determines write delay mode as follows: < 0 We are Flushing. No delays between writes
- * until the image queue is drained and all tasks needing persisting are written to disk. There
- * is no delay between writes. == 0 We are Idle. Next writes will be delayed by
- * #PRE_TASK_DELAY_MS. > 0 We are Actively writing. Next write will be at this time. Subsequent
- * writes will be delayed by #INTER_WRITE_DELAY_MS.
- */
- private long mNextWriteTime = 0;
-
- private final LazyTaskWriterThread mLazyTaskWriterThread;
-
- private static class WriteQueueItem {}
-
- private static class TaskWriteQueueItem extends WriteQueueItem {
- final TaskRecord mTask;
-
- TaskWriteQueueItem(TaskRecord task) {
- mTask = task;
- }
- }
-
- private static class ImageWriteQueueItem extends WriteQueueItem {
- final String mFilePath;
- Bitmap mImage;
-
- ImageWriteQueueItem(String filePath, Bitmap image) {
- mFilePath = filePath;
- mImage = image;
- }
- }
-
- ArrayList<WriteQueueItem> mWriteQueue = new ArrayList<WriteQueueItem>();
+ private final ArraySet<Integer> mTmpTaskIds = new ArraySet<>();
TaskPersister(File systemDir, ActivityStackSupervisor stackSupervisor,
ActivityTaskManagerService service, RecentTasks recentTasks) {
@@ -145,7 +103,8 @@
mStackSupervisor = stackSupervisor;
mService = service;
mRecentTasks = recentTasks;
- mLazyTaskWriterThread = new LazyTaskWriterThread("LazyTaskWriterThread");
+ mPersisterQueue = new PersisterQueue();
+ mPersisterQueue.addListener(this);
}
@VisibleForTesting
@@ -154,42 +113,21 @@
mStackSupervisor = null;
mService = null;
mRecentTasks = null;
- mLazyTaskWriterThread = new LazyTaskWriterThread("LazyTaskWriterThreadTest");
+ mPersisterQueue = new PersisterQueue();
+ mPersisterQueue.addListener(this);
}
- void startPersisting() {
- if (!mLazyTaskWriterThread.isAlive()) {
- mLazyTaskWriterThread.start();
- }
+ void onSystemReady() {
+ mPersisterQueue.startPersisting();
}
private void removeThumbnails(TaskRecord task) {
- final String taskString = Integer.toString(task.taskId);
- for (int queueNdx = mWriteQueue.size() - 1; queueNdx >= 0; --queueNdx) {
- final WriteQueueItem item = mWriteQueue.get(queueNdx);
- if (item instanceof ImageWriteQueueItem) {
- final File thumbnailFile = new File(((ImageWriteQueueItem) item).mFilePath);
- if (thumbnailFile.getName().startsWith(taskString)) {
- if (DEBUG) {
- Slog.d(TAG, "Removing " + ((ImageWriteQueueItem) item).mFilePath +
- " from write queue");
- }
- mWriteQueue.remove(queueNdx);
- }
- }
- }
- }
-
- private void yieldIfQueueTooDeep() {
- boolean stall = false;
- synchronized (this) {
- if (mNextWriteTime == FLUSH_QUEUE) {
- stall = true;
- }
- }
- if (stall) {
- Thread.yield();
- }
+ mPersisterQueue.removeItems(
+ item -> {
+ File file = new File(item.mFilePath);
+ return file.getName().startsWith(Integer.toString(task.taskId));
+ },
+ ImageWriteQueueItem.class);
}
@NonNull
@@ -251,84 +189,51 @@
}
void wakeup(TaskRecord task, boolean flush) {
- synchronized (this) {
+ synchronized (mPersisterQueue) {
if (task != null) {
- int queueNdx;
- for (queueNdx = mWriteQueue.size() - 1; queueNdx >= 0; --queueNdx) {
- final WriteQueueItem item = mWriteQueue.get(queueNdx);
- if (item instanceof TaskWriteQueueItem &&
- ((TaskWriteQueueItem) item).mTask == task) {
- if (!task.inRecents) {
- // This task is being removed.
- removeThumbnails(task);
- }
- break;
- }
+ final TaskWriteQueueItem item = mPersisterQueue.findLastItem(
+ queueItem -> task == queueItem.mTask, TaskWriteQueueItem.class);
+ if (item != null && !task.inRecents) {
+ removeThumbnails(task);
}
- if (queueNdx < 0 && task.isPersistable) {
- mWriteQueue.add(new TaskWriteQueueItem(task));
+
+ if (item == null && task.isPersistable) {
+ mPersisterQueue.addItem(new TaskWriteQueueItem(task, mService), flush);
}
} else {
// Dummy. Ensures removeObsoleteFiles is called when LazyTaskThreadWriter is
// notified.
- mWriteQueue.add(new WriteQueueItem());
+ mPersisterQueue.addItem(PersisterQueue.EMPTY_ITEM, flush);
}
- if (flush || mWriteQueue.size() > MAX_WRITE_QUEUE_LENGTH) {
- mNextWriteTime = FLUSH_QUEUE;
- } else if (mNextWriteTime == 0) {
- mNextWriteTime = SystemClock.uptimeMillis() + PRE_TASK_DELAY_MS;
+ if (DEBUG) {
+ Slog.d(TAG, "wakeup: task=" + task + " flush=" + flush + " Callers="
+ + Debug.getCallers(4));
}
- if (DEBUG) Slog.d(TAG, "wakeup: task=" + task + " flush=" + flush + " mNextWriteTime="
- + mNextWriteTime + " mWriteQueue.size=" + mWriteQueue.size()
- + " Callers=" + Debug.getCallers(4));
- notifyAll();
}
- yieldIfQueueTooDeep();
+ mPersisterQueue.yieldIfQueueTooDeep();
}
void flush() {
- synchronized (this) {
- mNextWriteTime = FLUSH_QUEUE;
- notifyAll();
- do {
- try {
- wait();
- } catch (InterruptedException e) {
- }
- } while (mNextWriteTime == FLUSH_QUEUE);
- }
+ mPersisterQueue.flush();
}
void saveImage(Bitmap image, String filePath) {
- synchronized (this) {
- int queueNdx;
- for (queueNdx = mWriteQueue.size() - 1; queueNdx >= 0; --queueNdx) {
- final WriteQueueItem item = mWriteQueue.get(queueNdx);
- if (item instanceof ImageWriteQueueItem) {
- ImageWriteQueueItem imageWriteQueueItem = (ImageWriteQueueItem) item;
- if (imageWriteQueueItem.mFilePath.equals(filePath)) {
- // replace the Bitmap with the new one.
- imageWriteQueueItem.mImage = image;
- break;
- }
- }
- }
- if (queueNdx < 0) {
- mWriteQueue.add(new ImageWriteQueueItem(filePath, image));
- }
- if (mWriteQueue.size() > MAX_WRITE_QUEUE_LENGTH) {
- mNextWriteTime = FLUSH_QUEUE;
- } else if (mNextWriteTime == 0) {
- mNextWriteTime = SystemClock.uptimeMillis() + PRE_TASK_DELAY_MS;
+ synchronized (mPersisterQueue) {
+ final ImageWriteQueueItem item = mPersisterQueue.findLastItem(
+ queueItem -> queueItem.mFilePath.equals(filePath), ImageWriteQueueItem.class);
+ if (item != null) {
+ // replace the Bitmap with the new one.
+ item.mImage = image;
+ } else {
+ mPersisterQueue.addItem(new ImageWriteQueueItem(filePath, image),
+ /* flush */ false);
}
if (DEBUG) Slog.d(TAG, "saveImage: filePath=" + filePath + " now=" +
- SystemClock.uptimeMillis() + " mNextWriteTime=" +
- mNextWriteTime + " Callers=" + Debug.getCallers(4));
- notifyAll();
+ SystemClock.uptimeMillis() + " Callers=" + Debug.getCallers(4));
}
- yieldIfQueueTooDeep();
+ mPersisterQueue.yieldIfQueueTooDeep();
}
Bitmap getTaskDescriptionIcon(String filePath) {
@@ -340,41 +245,10 @@
return restoreImage(filePath);
}
- Bitmap getImageFromWriteQueue(String filePath) {
- synchronized (this) {
- for (int queueNdx = mWriteQueue.size() - 1; queueNdx >= 0; --queueNdx) {
- final WriteQueueItem item = mWriteQueue.get(queueNdx);
- if (item instanceof ImageWriteQueueItem) {
- ImageWriteQueueItem imageWriteQueueItem = (ImageWriteQueueItem) item;
- if (imageWriteQueueItem.mFilePath.equals(filePath)) {
- return imageWriteQueueItem.mImage;
- }
- }
- }
- return null;
- }
- }
-
- private StringWriter saveToXml(TaskRecord task) throws IOException, XmlPullParserException {
- if (DEBUG) Slog.d(TAG, "saveToXml: task=" + task);
- final XmlSerializer xmlSerializer = new FastXmlSerializer();
- StringWriter stringWriter = new StringWriter();
- xmlSerializer.setOutput(stringWriter);
-
- if (DEBUG) xmlSerializer.setFeature(
- "http://xmlpull.org/v1/doc/features.html#indent-output", true);
-
- // save task
- xmlSerializer.startDocument(null, true);
-
- xmlSerializer.startTag(null, TAG_TASK);
- task.saveToXml(xmlSerializer);
- xmlSerializer.endTag(null, TAG_TASK);
-
- xmlSerializer.endDocument();
- xmlSerializer.flush();
-
- return stringWriter;
+ private Bitmap getImageFromWriteQueue(String filePath) {
+ final ImageWriteQueueItem item = mPersisterQueue.findLastItem(
+ queueItem -> queueItem.mFilePath.equals(filePath), ImageWriteQueueItem.class);
+ return item != null ? item.mImage : null;
}
private String fileToString(File file) {
@@ -534,6 +408,26 @@
return tasks;
}
+ @Override
+ public void onPreProcessItem(boolean queueEmpty) {
+ // We can't lock mService while locking the queue, but we don't want to
+ // call removeObsoleteFiles before every item, only the last time
+ // before going to sleep. The risk is that we call removeObsoleteFiles()
+ // successively.
+ if (queueEmpty) {
+ if (DEBUG) Slog.d(TAG, "Looking for obsolete files.");
+ mTmpTaskIds.clear();
+ synchronized (mService.mGlobalLock) {
+ if (DEBUG) Slog.d(TAG, "mRecents=" + mRecentTasks);
+ mRecentTasks.getPersistableTaskIds(mTmpTaskIds);
+ mService.mWindowManager.removeObsoleteTaskFiles(mTmpTaskIds,
+ mRecentTasks.usersWithRecentsLoadedLocked());
+ }
+ removeObsoleteFiles(mTmpTaskIds);
+ }
+ writeTaskIdsFiles();
+ }
+
private static void removeObsoleteFiles(ArraySet<Integer> persistentTaskIds, File[] files) {
if (DEBUG) Slog.d(TAG, "removeObsoleteFiles: persistentTaskIds=" + persistentTaskIds +
" files=" + files);
@@ -631,143 +525,117 @@
return parentDir.exists() || parentDir.mkdirs();
}
- private class LazyTaskWriterThread extends Thread {
+ private static class TaskWriteQueueItem implements PersisterQueue.WriteQueueItem {
+ private final ActivityTaskManagerService mService;
+ private final TaskRecord mTask;
- LazyTaskWriterThread(String name) {
- super(name);
+ TaskWriteQueueItem(TaskRecord task, ActivityTaskManagerService service) {
+ mTask = task;
+ mService = service;
+ }
+
+ private StringWriter saveToXml(TaskRecord task) throws IOException, XmlPullParserException {
+ if (DEBUG) Slog.d(TAG, "saveToXml: task=" + task);
+ final XmlSerializer xmlSerializer = new FastXmlSerializer();
+ StringWriter stringWriter = new StringWriter();
+ xmlSerializer.setOutput(stringWriter);
+
+ if (DEBUG) {
+ xmlSerializer.setFeature(
+ "http://xmlpull.org/v1/doc/features.html#indent-output", true);
+ }
+
+ // save task
+ xmlSerializer.startDocument(null, true);
+
+ xmlSerializer.startTag(null, TAG_TASK);
+ task.saveToXml(xmlSerializer);
+ xmlSerializer.endTag(null, TAG_TASK);
+
+ xmlSerializer.endDocument();
+ xmlSerializer.flush();
+
+ return stringWriter;
}
@Override
- public void run() {
- Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
- ArraySet<Integer> persistentTaskIds = new ArraySet<>();
- while (true) {
- // We can't lock mService while holding TaskPersister.this, but we don't want to
- // call removeObsoleteFiles every time through the loop, only the last time before
- // going to sleep. The risk is that we call removeObsoleteFiles() successively.
- final boolean probablyDone;
- synchronized (TaskPersister.this) {
- probablyDone = mWriteQueue.isEmpty();
- }
- if (probablyDone) {
- if (DEBUG) Slog.d(TAG, "Looking for obsolete files.");
- persistentTaskIds.clear();
- synchronized (mService.mGlobalLock) {
- if (DEBUG) Slog.d(TAG, "mRecents=" + mRecentTasks);
- mRecentTasks.getPersistableTaskIds(persistentTaskIds);
- mService.mWindowManager.removeObsoleteTaskFiles(persistentTaskIds,
- mRecentTasks.usersWithRecentsLoadedLocked());
+ public void process() {
+ // Write out one task.
+ StringWriter stringWriter = null;
+ TaskRecord task = mTask;
+ if (DEBUG) Slog.d(TAG, "Writing task=" + task);
+ synchronized (mService.mGlobalLock) {
+ if (task.inRecents) {
+ // Still there.
+ try {
+ if (DEBUG) Slog.d(TAG, "Saving task=" + task);
+ stringWriter = saveToXml(task);
+ } catch (IOException e) {
+ } catch (XmlPullParserException e) {
}
- removeObsoleteFiles(persistentTaskIds);
}
- writeTaskIdsFiles();
-
- processNextItem();
+ }
+ if (stringWriter != null) {
+ // Write out xml file while not holding mService lock.
+ FileOutputStream file = null;
+ AtomicFile atomicFile = null;
+ try {
+ atomicFile = new AtomicFile(new File(
+ getUserTasksDir(task.userId),
+ String.valueOf(task.taskId) + TASK_FILENAME_SUFFIX));
+ file = atomicFile.startWrite();
+ file.write(stringWriter.toString().getBytes());
+ file.write('\n');
+ atomicFile.finishWrite(file);
+ } catch (IOException e) {
+ if (file != null) {
+ atomicFile.failWrite(file);
+ }
+ Slog.e(TAG,
+ "Unable to open " + atomicFile + " for persisting. " + e);
+ }
}
}
- private void processNextItem() {
- // This part is extracted into a method so that the GC can clearly see the end of the
- // scope of the variable 'item'. If this part was in the loop above, the last item
- // it processed would always "leak".
- // See https://b.corp.google.com/issues/64438652#comment7
+ @Override
+ public String toString() {
+ return "TaskWriteQueueItem{task=" + mTask + "}";
+ }
+ }
- // If mNextWriteTime, then don't delay between each call to saveToXml().
- final WriteQueueItem item;
- synchronized (TaskPersister.this) {
- if (mNextWriteTime != FLUSH_QUEUE) {
- // The next write we don't have to wait so long.
- mNextWriteTime = SystemClock.uptimeMillis() + INTER_WRITE_DELAY_MS;
- if (DEBUG) Slog.d(TAG, "Next write time may be in " +
- INTER_WRITE_DELAY_MS + " msec. (" + mNextWriteTime + ")");
- }
+ private static class ImageWriteQueueItem implements PersisterQueue.WriteQueueItem {
+ final String mFilePath;
+ Bitmap mImage;
- while (mWriteQueue.isEmpty()) {
- if (mNextWriteTime != 0) {
- mNextWriteTime = 0; // idle.
- TaskPersister.this.notifyAll(); // wake up flush() if needed.
- }
- try {
- if (DEBUG) Slog.d(TAG, "LazyTaskWriter: waiting indefinitely.");
- TaskPersister.this.wait();
- } catch (InterruptedException e) {
- }
- // Invariant: mNextWriteTime is either FLUSH_QUEUE or PRE_WRITE_DELAY_MS
- // from now.
- }
- item = mWriteQueue.remove(0);
+ ImageWriteQueueItem(String filePath, Bitmap image) {
+ mFilePath = filePath;
+ mImage = image;
+ }
- long now = SystemClock.uptimeMillis();
- if (DEBUG) Slog.d(TAG, "LazyTaskWriter: now=" + now + " mNextWriteTime=" +
- mNextWriteTime + " mWriteQueue.size=" + mWriteQueue.size());
- while (now < mNextWriteTime) {
- try {
- if (DEBUG) Slog.d(TAG, "LazyTaskWriter: waiting " +
- (mNextWriteTime - now));
- TaskPersister.this.wait(mNextWriteTime - now);
- } catch (InterruptedException e) {
- }
- now = SystemClock.uptimeMillis();
- }
-
- // Got something to do.
+ @Override
+ public void process() {
+ final String filePath = mFilePath;
+ if (!createParentDirectory(filePath)) {
+ Slog.e(TAG, "Error while creating images directory for file: " + filePath);
+ return;
}
-
- if (item instanceof ImageWriteQueueItem) {
- ImageWriteQueueItem imageWriteQueueItem = (ImageWriteQueueItem) item;
- final String filePath = imageWriteQueueItem.mFilePath;
- if (!createParentDirectory(filePath)) {
- Slog.e(TAG, "Error while creating images directory for file: " + filePath);
- return;
- }
- final Bitmap bitmap = imageWriteQueueItem.mImage;
- if (DEBUG) Slog.d(TAG, "writing bitmap: filename=" + filePath);
- FileOutputStream imageFile = null;
- try {
- imageFile = new FileOutputStream(new File(filePath));
- bitmap.compress(Bitmap.CompressFormat.PNG, 100, imageFile);
- } catch (Exception e) {
- Slog.e(TAG, "saveImage: unable to save " + filePath, e);
- } finally {
- IoUtils.closeQuietly(imageFile);
- }
- } else if (item instanceof TaskWriteQueueItem) {
- // Write out one task.
- StringWriter stringWriter = null;
- TaskRecord task = ((TaskWriteQueueItem) item).mTask;
- if (DEBUG) Slog.d(TAG, "Writing task=" + task);
- synchronized (mService.mGlobalLock) {
- if (task.inRecents) {
- // Still there.
- try {
- if (DEBUG) Slog.d(TAG, "Saving task=" + task);
- stringWriter = saveToXml(task);
- } catch (IOException e) {
- } catch (XmlPullParserException e) {
- }
- }
- }
- if (stringWriter != null) {
- // Write out xml file while not holding mService lock.
- FileOutputStream file = null;
- AtomicFile atomicFile = null;
- try {
- atomicFile = new AtomicFile(new File(
- getUserTasksDir(task.userId),
- String.valueOf(task.taskId) + TASK_FILENAME_SUFFIX));
- file = atomicFile.startWrite();
- file.write(stringWriter.toString().getBytes());
- file.write('\n');
- atomicFile.finishWrite(file);
- } catch (IOException e) {
- if (file != null) {
- atomicFile.failWrite(file);
- }
- Slog.e(TAG,
- "Unable to open " + atomicFile + " for persisting. " + e);
- }
- }
+ final Bitmap bitmap = mImage;
+ if (DEBUG) Slog.d(TAG, "writing bitmap: filename=" + filePath);
+ FileOutputStream imageFile = null;
+ try {
+ imageFile = new FileOutputStream(new File(filePath));
+ bitmap.compress(Bitmap.CompressFormat.PNG, 100, imageFile);
+ } catch (Exception e) {
+ Slog.e(TAG, "saveImage: unable to save " + filePath, e);
+ } finally {
+ IoUtils.closeQuietly(imageFile);
}
}
+
+ @Override
+ public String toString() {
+ return "ImageWriteQueueItem{path=" + mFilePath
+ + ", image=(" + mImage.getWidth() + "x" + mImage.getHeight() + ")}";
+ }
}
}
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index ef8cb1c..05b0d59 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -45,7 +45,6 @@
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
import static android.view.Display.DEFAULT_DISPLAY;
-
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS;
@@ -75,7 +74,6 @@
import static com.android.server.am.TaskRecordProto.REAL_ACTIVITY;
import static com.android.server.am.TaskRecordProto.RESIZE_MODE;
import static com.android.server.am.TaskRecordProto.STACK_ID;
-
import static java.lang.Integer.MAX_VALUE;
import android.annotation.IntDef;
@@ -1686,11 +1684,19 @@
// so that the user can not render the task too small to manipulate. We don't need
// to do this for the pinned stack as the bounds are controlled by the system.
if (!inPinnedWindowingMode()) {
+ final int defaultMinSizeDp =
+ mService.mStackSupervisor.mDefaultMinSizeOfResizeableTaskDp;
+ final ActivityDisplay display =
+ mService.mStackSupervisor.getActivityDisplay(mStack.mDisplayId);
+ final float density =
+ (float) display.getConfiguration().densityDpi / DisplayMetrics.DENSITY_DEFAULT;
+ final int defaultMinSize = (int) (defaultMinSizeDp * density);
+
if (minWidth == INVALID_MIN_SIZE) {
- minWidth = mService.mStackSupervisor.mDefaultMinSizeOfResizeableTask;
+ minWidth = defaultMinSize;
}
if (minHeight == INVALID_MIN_SIZE) {
- minHeight = mService.mStackSupervisor.mDefaultMinSizeOfResizeableTask;
+ minHeight = defaultMinSize;
}
}
final boolean adjustWidth = minWidth > bounds.width();
diff --git a/services/core/java/com/android/server/appbinding/AppBindingConstants.java b/services/core/java/com/android/server/appbinding/AppBindingConstants.java
index c2655a2..7184769 100644
--- a/services/core/java/com/android/server/appbinding/AppBindingConstants.java
+++ b/services/core/java/com/android/server/appbinding/AppBindingConstants.java
@@ -15,6 +15,7 @@
*/
package com.android.server.appbinding;
+import android.content.Context;
import android.util.KeyValueListParser;
import android.util.Slog;
@@ -24,7 +25,7 @@
/**
* Constants that are configurable via the global settings for {@link AppBindingService}.
*/
-class AppBindingConstants {
+public class AppBindingConstants {
private static final String TAG = AppBindingService.TAG;
private static final String SERVICE_RECONNECT_BACKOFF_SEC_KEY =
@@ -36,6 +37,15 @@
private static final String SERVICE_RECONNECT_MAX_BACKOFF_SEC_KEY =
"service_reconnect_max_backoff_sec";
+ 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";
+
public final String sourceSettings;
/**
@@ -54,6 +64,21 @@
*/
public final long SERVICE_RECONNECT_MAX_BACKOFF_SEC;
+ /**
+ * If a connection lasts more than this duration, we reset the re-connect back-off time.
+ */
+ 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;
+
private AppBindingConstants(String settings) {
sourceSettings = settings;
@@ -67,13 +92,22 @@
}
long serviceReconnectBackoffSec = parser.getLong(
- SERVICE_RECONNECT_BACKOFF_SEC_KEY, TimeUnit.HOURS.toSeconds(1));
+ SERVICE_RECONNECT_BACKOFF_SEC_KEY, 10);
double serviceReconnectBackoffIncrease = parser.getFloat(
SERVICE_RECONNECT_BACKOFF_INCREASE_KEY, 2f);
long serviceReconnectMaxBackoffSec = parser.getLong(
- SERVICE_RECONNECT_MAX_BACKOFF_SEC_KEY, TimeUnit.DAYS.toSeconds(1));
+ 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);
+
+ long serviceStableConnectionThresholdSec = parser.getLong(
+ SERVICE_STABLE_CONNECTION_THRESHOLD_SEC_KEY, TimeUnit.MINUTES.toSeconds(2));
// Set minimum: 5 seconds.
serviceReconnectBackoffSec = Math.max(5, serviceReconnectBackoffSec);
@@ -89,6 +123,9 @@
SERVICE_RECONNECT_BACKOFF_SEC = serviceReconnectBackoffSec;
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;
}
/**
@@ -103,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: ");
@@ -116,5 +154,17 @@
pw.print(prefix);
pw.print(" SERVICE_RECONNECT_MAX_BACKOFF_SEC: ");
pw.println(SERVICE_RECONNECT_MAX_BACKOFF_SEC);
+
+ pw.print(prefix);
+ pw.print(" SERVICE_STABLE_CONNECTION_THRESHOLD_SEC: ");
+ 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 c5cb2a4..3131255 100644
--- a/services/core/java/com/android/server/appbinding/AppBindingService.java
+++ b/services/core/java/com/android/server/appbinding/AppBindingService.java
@@ -21,6 +21,7 @@
import android.app.AppGlobals;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -58,10 +59,11 @@
*
* <p>As of android Q, we only use it for the default SMS app.
*
- * TODO Unit tests
- * TODO How do we handle force stop??
- * TODO Change OOM adjustment to 200 or so
- * TODO Only allow it when the service is associated with a secondary process.
+ * Relevant tests:
+ * atest CtsAppBindingHostTestCases
+ *
+ * TODO Maybe handle force-stop differently. Right now we just get "binder died" and re-bind
+ * after a timeout. b/116813347
*/
public class AppBindingService extends Binder {
public static final String TAG = "AppBindingService";
@@ -91,17 +93,25 @@
public IPackageManager getIPackageManager() {
return AppGlobals.getPackageManager();
}
+
+ public String getGlobalSettingString(ContentResolver resolver, String key) {
+ return Settings.Global.getString(resolver, key);
+ }
}
/**
* {@link SystemService} for this service.
*/
- public static final class Lifecycle extends SystemService {
+ public static class Lifecycle extends SystemService {
final AppBindingService mService;
public Lifecycle(Context context) {
+ this(context, new Injector());
+ }
+
+ Lifecycle(Context context, Injector injector) {
super(context);
- mService = new AppBindingService(new Injector(), context);
+ mService = new AppBindingService(injector, context);
}
@Override
@@ -171,7 +181,6 @@
packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
- packageFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
packageFilter.addDataScheme("package");
packageFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
@@ -197,7 +206,7 @@
};
private void refreshConstants() {
- final String newSetting = Settings.Global.getString(
+ final String newSetting = mInjector.getGlobalSettingString(
mContext.getContentResolver(), Global.APP_BINDING_CONSTANTS);
synchronized (mLock) {
@@ -215,6 +224,9 @@
final BroadcastReceiver mPackageUserMonitor = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
+ if (DEBUG) {
+ Slog.d(TAG, "Broadcast received: " + intent);
+ }
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
if (userId == UserHandle.USER_NULL) {
Slog.w(TAG, "Intent broadcast does not contain user handle: " + intent);
@@ -410,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;
}
@@ -454,14 +466,15 @@
super(TAG, context, handler, userId, componentName,
constants.SERVICE_RECONNECT_BACKOFF_SEC,
constants.SERVICE_RECONNECT_BACKOFF_INCREASE,
- constants.SERVICE_RECONNECT_MAX_BACKOFF_SEC);
+ constants.SERVICE_RECONNECT_MAX_BACKOFF_SEC,
+ constants.SERVICE_STABLE_CONNECTION_THRESHOLD_SEC);
mFinder = finder;
mConstants = constants;
}
@Override
protected int getBindFlags() {
- return Context.BIND_FOREGROUND_SERVICE;
+ return mFinder.getBindFlags(mConstants);
}
@Override
@@ -535,9 +548,21 @@
pw.print(conn.isBound() ? "bound" : "not-bound");
pw.print(",");
pw.print(conn.isConnected() ? "connected" : "not-connected");
+ pw.print(",#con=");
+ pw.print(conn.getNumConnected());
+ pw.print(",#dis=");
+ pw.print(conn.getNumDisconnected());
+ pw.print(",#died=");
+ pw.print(conn.getNumBindingDied());
+ pw.print(",backoff=");
+ pw.print(conn.getNextBackoffMs());
pw.println();
}
forAllAppsLocked((app) -> app.dumpSimple(pw));
}
}
+
+ AppBindingConstants getConstantsForTest() {
+ return mConstants;
+ }
}
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 68c5e496..a075c50 100644
--- a/services/core/java/com/android/server/appbinding/finders/AppServiceFinder.java
+++ b/services/core/java/com/android/server/appbinding/finders/AppServiceFinder.java
@@ -24,10 +24,12 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.IInterface;
+import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.server.appbinding.AppBindingConstants;
import com.android.server.appbinding.AppBindingService;
import com.android.server.appbinding.AppBindingUtils;
@@ -51,13 +53,13 @@
private final Object mLock = new Object();
@GuardedBy("mLock")
- private final SparseArray<String> mTargetPackages = new SparseArray(1);
+ private final SparseArray<String> mTargetPackages = new SparseArray(4);
@GuardedBy("mLock")
- private final SparseArray<ServiceInfo> mTargetServices = new SparseArray(1);
+ private final SparseArray<ServiceInfo> mTargetServices = new SparseArray(4);
@GuardedBy("mLock")
- private final SparseArray<String> mLastMessages = new SparseArray(1);
+ private final SparseArray<String> mLastMessages = new SparseArray(4);
public AppServiceFinder(Context context,
BiConsumer<AppServiceFinder, Integer> listener,
@@ -67,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();
@@ -80,6 +87,7 @@
synchronized (mLock) {
mTargetPackages.delete(userId);
mTargetServices.delete(userId);
+ mLastMessages.delete(userId);
}
}
@@ -87,10 +95,19 @@
* 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) {
@@ -118,11 +135,18 @@
final String message = errorMessage.toString();
mLastMessages.put(userId, message);
if (DEBUG) {
+ // This log is optional because findService() already did Log.e().
Slog.w(TAG, getAppDescription() + " package " + targetPackage + " u" + userId
+ " " + message);
}
return null;
}
+ final String error = validateService(service);
+ if (error != null) {
+ mLastMessages.put(userId, error);
+ Log.e(TAG, error);
+ return null;
+ }
final String message = "Valid service found";
mLastMessages.put(userId, message);
@@ -156,6 +180,17 @@
@NonNull
protected abstract String getServicePermission();
+ /**
+ * Subclass can implement it to decide whether to accept a service (by returning null) or not
+ * (by returning an error message.)
+ */
+ protected String validateService(ServiceInfo service) {
+ return null;
+ }
+
+ /** Return the bind flags for this service. */
+ public abstract int getBindFlags(AppBindingConstants constants);
+
/** Dumpsys support. */
public void dump(String prefix, PrintWriter pw) {
pw.print(prefix);
@@ -165,24 +200,25 @@
synchronized (mLock) {
for (int i = 0; i < mTargetPackages.size(); i++) {
+ final int userId = mTargetPackages.keyAt(i);
pw.print(prefix);
pw.print(" User: ");
- pw.print(mTargetPackages.keyAt(i));
+ pw.print(userId);
pw.println();
pw.print(prefix);
pw.print(" Package: ");
- pw.print(mTargetPackages.valueAt(i));
+ pw.print(mTargetPackages.get(userId));
pw.println();
pw.print(prefix);
pw.print(" Service: ");
- pw.print(mTargetServices.valueAt(i));
+ pw.print(mTargetServices.get(userId));
pw.println();
pw.print(prefix);
pw.print(" Message: ");
- pw.print(mLastMessages.valueAt(i));
+ pw.print(mLastMessages.get(userId));
pw.println();
}
}
@@ -192,16 +228,17 @@
public void dumpSimple(PrintWriter pw) {
synchronized (mLock) {
for (int i = 0; i < mTargetPackages.size(); i++) {
+ final int userId = mTargetPackages.keyAt(i);
pw.print("finder,");
pw.print(getAppDescription());
pw.print(",");
- pw.print(mTargetPackages.keyAt(i)); // User-id
+ pw.print(userId);
pw.print(",");
- pw.print(mTargetPackages.valueAt(i));
+ pw.print(mTargetPackages.get(userId));
pw.print(",");
- pw.print(mTargetServices.valueAt(i));
+ pw.print(mTargetServices.get(userId));
pw.print(",");
- pw.print(mLastMessages.valueAt(i));
+ pw.print(mLastMessages.get(userId));
pw.println();
}
}
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 c908bd9..fcc28f8 100644
--- a/services/core/java/com/android/server/appbinding/finders/SmsAppServiceFinder.java
+++ b/services/core/java/com/android/server/appbinding/finders/SmsAppServiceFinder.java
@@ -26,12 +26,17 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ServiceInfo;
import android.os.Handler;
import android.os.IBinder;
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;
import java.util.function.BiConsumer;
@@ -46,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]";
}
@@ -74,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
@@ -84,6 +101,22 @@
/* permission= */ null, mHandler);
}
+ @Override
+ protected String validateService(ServiceInfo service) {
+ final String packageName = service.packageName;
+ final String process = service.processName;
+
+ if (process == null || TextUtils.equals(packageName, process)) {
+ return "Service must not run on the main process";
+ }
+ return null; // Null means accept this service.
+ }
+
+ @Override
+ public int getBindFlags(AppBindingConstants constants) {
+ return constants.SMS_APP_BIND_FLAGS;
+ }
+
private final BroadcastReceiver mSmsAppChangedWatcher = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
index aa4d34e..8a72a6d 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
@@ -195,7 +195,7 @@
// ERROR_CANCELED message.
return true;
}
- if (mBundle != null) {
+ if (mBundle != null && error != BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED) {
try {
mStatusBarService.onBiometricError(getErrorString(error, vendorCode));
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 87cf9c4..3d2f567 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -17,13 +17,20 @@
package com.android.server.biometrics;
import static android.Manifest.permission.USE_BIOMETRIC;
+import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.Manifest.permission.USE_FINGERPRINT;
+import android.app.ActivityManager;
import android.app.AppOpsManager;
+import android.app.UserSwitchObserver;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.database.ContentObserver;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.BiometricSourceType;
+import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.biometrics.IBiometricPromptReceiver;
import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.IBiometricServiceReceiver;
@@ -31,8 +38,10 @@
import android.hardware.face.IFaceService;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.IFingerprintService;
+import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
+import android.os.DeadObjectException;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -40,19 +49,21 @@
import android.os.ServiceManager;
import android.os.UserHandle;
import android.provider.Settings;
+import android.util.Pair;
import android.util.Slog;
import com.android.internal.R;
import com.android.server.SystemService;
import java.util.ArrayList;
+import java.util.List;
/**
* System service that arbitrates the modality for BiometricPrompt to use.
*/
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.
@@ -87,6 +98,8 @@
private final boolean mHasFeatureFingerprint;
private final boolean mHasFeatureIris;
private final boolean mHasFeatureFace;
+ private final SettingObserver mSettingObserver;
+ private final List<EnabledOnKeyguardCallback> mEnabledOnKeyguardCallbacks;
private IFingerprintService mFingerprintService;
private IFaceService mFaceService;
@@ -120,6 +133,107 @@
}
}
+ private final class SettingObserver extends ContentObserver {
+ private final Uri FACE_UNLOCK_KEYGUARD_ENABLED =
+ Settings.Secure.getUriFor(Settings.Secure.FACE_UNLOCK_KEYGUARD_ENABLED);
+ private final Uri FACE_UNLOCK_APP_ENABLED =
+ Settings.Secure.getUriFor(Settings.Secure.FACE_UNLOCK_APP_ENABLED);
+
+ private final ContentResolver mContentResolver;
+ private boolean mFaceEnabledOnKeyguard;
+ private boolean mFaceEnabledForApps;
+
+ /**
+ * Creates a content observer.
+ *
+ * @param handler The handler to run {@link #onChange} on, or null if none.
+ */
+ SettingObserver(Handler handler) {
+ super(handler);
+ mContentResolver = getContext().getContentResolver();
+ updateContentObserver();
+ }
+
+ void updateContentObserver() {
+ mContentResolver.unregisterContentObserver(this);
+ mContentResolver.registerContentObserver(FACE_UNLOCK_KEYGUARD_ENABLED,
+ false /* notifyForDescendents */,
+ this /* observer */,
+ UserHandle.USER_CURRENT);
+ mContentResolver.registerContentObserver(FACE_UNLOCK_APP_ENABLED,
+ false /* notifyForDescendents */,
+ this /* observer */,
+ UserHandle.USER_CURRENT);
+
+ // Update the value immediately
+ onChange(true /* selfChange */, FACE_UNLOCK_KEYGUARD_ENABLED);
+ onChange(true /* selfChange */, FACE_UNLOCK_APP_ENABLED);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ if (FACE_UNLOCK_KEYGUARD_ENABLED.equals(uri)) {
+ mFaceEnabledOnKeyguard =
+ Settings.Secure.getIntForUser(
+ mContentResolver,
+ Settings.Secure.FACE_UNLOCK_KEYGUARD_ENABLED,
+ 1 /* default */,
+ UserHandle.USER_CURRENT) != 0;
+
+ List<EnabledOnKeyguardCallback> callbacks = mEnabledOnKeyguardCallbacks;
+ for (int i = 0; i < callbacks.size(); i++) {
+ callbacks.get(i).notify(BiometricSourceType.FACE, mFaceEnabledOnKeyguard);
+ }
+ } else if (FACE_UNLOCK_APP_ENABLED.equals(uri)) {
+ mFaceEnabledForApps =
+ Settings.Secure.getIntForUser(
+ mContentResolver,
+ Settings.Secure.FACE_UNLOCK_APP_ENABLED,
+ 1 /* default */,
+ UserHandle.USER_CURRENT) != 0;
+ }
+ }
+
+ boolean getFaceEnabledOnKeyguard() {
+ return mFaceEnabledOnKeyguard;
+ }
+
+ boolean getFaceEnabledForApps() {
+ return mFaceEnabledForApps;
+ }
+ }
+
+ private final class EnabledOnKeyguardCallback implements IBinder.DeathRecipient {
+
+ private final IBiometricEnabledOnKeyguardCallback mCallback;
+
+ EnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback) {
+ mCallback = callback;
+ try {
+ mCallback.asBinder().linkToDeath(EnabledOnKeyguardCallback.this, 0);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Unable to linkToDeath", e);
+ }
+ }
+
+ void notify(BiometricSourceType sourceType, boolean enabled) {
+ try {
+ mCallback.onChanged(sourceType, enabled);
+ } catch (DeadObjectException e) {
+ Slog.w(TAG, "Death while invoking notify", e);
+ mEnabledOnKeyguardCallbacks.remove(this);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to invoke onChanged", e);
+ }
+ }
+
+ @Override
+ public void binderDied() {
+ Slog.e(TAG, "Enabled callback binder died");
+ mEnabledOnKeyguardCallbacks.remove(this);
+ }
+ }
+
/**
* This is just a pass-through service that wraps Fingerprint, Iris, Face services. This service
* should not carry any state. The reality is we need to keep a tiny amount of state so that
@@ -131,7 +245,7 @@
public void authenticate(IBinder token, long sessionId, int userId,
IBiometricServiceReceiver receiver, int flags, String opPackageName,
Bundle bundle, IBiometricPromptReceiver dialogReceiver) throws RemoteException {
- // Check the USE_BIOMETRIC permission here. In the BiometricService, check do the
+ // Check the USE_BIOMETRIC permission here. In the BiometricServiceBase, check do the
// AppOps and foreground check.
checkPermission();
@@ -146,8 +260,38 @@
final int callingUserId = UserHandle.getCallingUserId();
mHandler.post(() -> {
- mCurrentModality = checkAndGetBiometricModality(receiver);
+ final Pair<Integer, Integer> result = checkAndGetBiometricModality(callingUserId);
+ final int modality = result.first;
+ final int error = result.second;
+ // Check for errors, notify callback, and return
+ if (error != BiometricConstants.BIOMETRIC_ERROR_NONE) {
+ try {
+ final String hardwareUnavailable =
+ getContext().getString(R.string.biometric_error_hw_unavailable);
+ switch (error) {
+ case BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT:
+ receiver.onError(0 /* deviceId */, error, hardwareUnavailable);
+ break;
+ case BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE:
+ receiver.onError(0 /* deviceId */, error, hardwareUnavailable);
+ break;
+ case BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS:
+ receiver.onError(0 /* deviceId */, error,
+ getErrorString(modality, error, 0 /* vendorCode */));
+ break;
+ default:
+ Slog.e(TAG, "Unhandled error");
+ break;
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to send error", e);
+ }
+ return;
+ }
+
+ // Actually start authentication
+ mCurrentModality = modality;
try {
// No polymorphism :(
if (mCurrentModality == BIOMETRIC_FINGERPRINT) {
@@ -157,18 +301,9 @@
} else if (mCurrentModality == BIOMETRIC_IRIS) {
Slog.w(TAG, "Unsupported modality");
} else if (mCurrentModality == BIOMETRIC_FACE) {
- // If the user disabled face for apps, return ERROR_HW_UNAVAILABLE
- if (isFaceEnabledForApps()) {
- receiver.onError(0 /* deviceId */,
- BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
- FaceManager.getErrorString(getContext(),
- BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
- 0 /* vendorCode */));
- } else {
- mFaceService.authenticateFromService(true /* requireConfirmation */,
- token, sessionId, userId, receiver, flags, opPackageName,
- bundle, dialogReceiver, callingUid, callingPid, callingUserId);
- }
+ mFaceService.authenticateFromService(true /* requireConfirmation */,
+ token, sessionId, userId, receiver, flags, opPackageName,
+ bundle, dialogReceiver, callingUid, callingPid, callingUserId);
} else {
Slog.w(TAG, "Unsupported modality");
}
@@ -178,15 +313,6 @@
});
}
- private boolean isFaceEnabledForApps() {
- // TODO: maybe cache this and eliminate duplicated code with KeyguardUpdateMonitor
- return Settings.Secure.getIntForUser(
- getContext().getContentResolver(),
- Settings.Secure.FACE_UNLOCK_APP_ENABLED,
- 1 /* default */,
- UserHandle.USER_CURRENT) == 0;
- }
-
@Override // Binder call
public void cancelAuthentication(IBinder token, String opPackageName)
throws RemoteException {
@@ -221,31 +347,47 @@
}
@Override // Binder call
- public boolean hasEnrolledBiometrics(String opPackageName) {
+ public int canAuthenticate(String opPackageName) {
checkPermission();
+ checkAppOp(opPackageName, Binder.getCallingUid());
- if (mAppOps.noteOp(AppOpsManager.OP_USE_BIOMETRIC, Binder.getCallingUid(),
- opPackageName) != AppOpsManager.MODE_ALLOWED) {
- Slog.w(TAG, "Rejecting " + opPackageName + "; permission denied");
- throw new SecurityException("Permission denied");
- }
-
+ final int userId = UserHandle.getCallingUserId();
final long ident = Binder.clearCallingIdentity();
- boolean hasEnrolled = false;
+ int error;
try {
- // Note: On devices with multi-modal authentication, the selection logic will need
- // to be updated.
- for (int i = 0; i < mAuthenticators.size(); i++) {
- if (mAuthenticators.get(i).getAuthenticator().hasEnrolledTemplates()) {
- hasEnrolled = true;
- break;
- }
- }
+ final Pair<Integer, Integer> result = checkAndGetBiometricModality(userId);
+ error = result.second;
} finally {
Binder.restoreCallingIdentity(ident);
}
- return hasEnrolled;
+ return error;
}
+
+ @Override
+ public void registerEnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback)
+ throws RemoteException {
+ checkInternalPermission();
+ mEnabledOnKeyguardCallbacks.add(new EnabledOnKeyguardCallback(callback));
+ try {
+ callback.onChanged(BiometricSourceType.FACE,
+ mSettingObserver.getFaceEnabledOnKeyguard());
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Remote exception", e);
+ }
+ }
+ }
+
+ private void checkAppOp(String opPackageName, int callingUid) {
+ if (mAppOps.noteOp(AppOpsManager.OP_USE_BIOMETRIC, callingUid,
+ opPackageName) != AppOpsManager.MODE_ALLOWED) {
+ Slog.w(TAG, "Rejecting " + opPackageName + "; permission denied");
+ throw new SecurityException("Permission denied");
+ }
+ }
+
+ private void checkInternalPermission() {
+ getContext().enforceCallingPermission(USE_BIOMETRIC_INTERNAL,
+ "Must have MANAGE_BIOMETRIC permission");
}
private void checkPermission() {
@@ -270,11 +412,26 @@
mAppOps = context.getSystemService(AppOpsManager.class);
mHandler = new Handler(Looper.getMainLooper());
+ mEnabledOnKeyguardCallbacks = new ArrayList<>();
+ mSettingObserver = new SettingObserver(mHandler);
final PackageManager pm = context.getPackageManager();
mHasFeatureFingerprint = pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT);
mHasFeatureIris = pm.hasSystemFeature(PackageManager.FEATURE_IRIS);
mHasFeatureFace = pm.hasSystemFeature(PackageManager.FEATURE_FACE);
+
+ try {
+ ActivityManager.getService().registerUserSwitchObserver(
+ new UserSwitchObserver() {
+ @Override
+ public void onUserSwitchComplete(int newUserId) {
+ mSettingObserver.updateContentObserver();
+ }
+ }, BiometricService.class.getName()
+ );
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to register user switch observer", e);
+ }
}
@Override
@@ -305,65 +462,94 @@
* Checks if there are any available biometrics, and returns the modality. This method also
* returns errors through the callback (no biometric feature, hardware not detected, no
* templates enrolled, etc). This service must not start authentication if errors are sent.
+ *
+ * @Returns A pair [Modality, Error] with Modality being one of {@link #BIOMETRIC_NONE},
+ * {@link #BIOMETRIC_FINGERPRINT}, {@link #BIOMETRIC_IRIS}, {@link #BIOMETRIC_FACE}
+ * and the error containing one of the {@link BiometricConstants} errors.
*/
- private int checkAndGetBiometricModality(IBiometricServiceReceiver receiver) {
+ private Pair<Integer, Integer> checkAndGetBiometricModality(int callingUid) {
int modality = BIOMETRIC_NONE;
- final String hardwareUnavailable =
- getContext().getString(R.string.biometric_error_hw_unavailable);
// No biometric features, send error
if (mAuthenticators.isEmpty()) {
- try {
- receiver.onError(0 /* deviceId */,
- BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT,
- hardwareUnavailable);
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to send error", e);
- }
- return BIOMETRIC_NONE;
+ return new Pair<>(BIOMETRIC_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT);
}
- // Find first authenticator that's both detected and enrolled
+ // Assuming that authenticators are listed in priority-order, the rest of this function
+ // will go through and find the first authenticator that's available, enrolled, and enabled.
+ // The tricky part is returning the correct error. Error strings that are modality-specific
+ // should also respect the priority-order.
+
+ // Find first authenticator that's detected, enrolled, and enabled.
boolean isHardwareDetected = false;
boolean hasTemplatesEnrolled = false;
+ boolean enabledForApps = false;
+
+ int firstHwAvailable = BIOMETRIC_NONE;
for (int i = 0; i < mAuthenticators.size(); i++) {
- int featureId = mAuthenticators.get(i).getType();
+ modality = mAuthenticators.get(i).getType();
BiometricAuthenticator authenticator = mAuthenticators.get(i).getAuthenticator();
if (authenticator.isHardwareDetected()) {
isHardwareDetected = true;
- if (authenticator.hasEnrolledTemplates()) {
+ if (firstHwAvailable == BIOMETRIC_NONE) {
+ // Store the first one since we want to return the error in correct priority
+ // order.
+ firstHwAvailable = modality;
+ }
+ if (authenticator.hasEnrolledTemplates(callingUid)) {
hasTemplatesEnrolled = true;
- modality = featureId;
- break;
+ if (isEnabledForApp(modality)) {
+ // TODO(b/110907543): When face settings (and other settings) have both a
+ // user toggle as well as a work profile settings page, this needs to be
+ // updated to reflect the correct setting.
+ enabledForApps = true;
+ break;
+ }
}
}
}
// Check error conditions
if (!isHardwareDetected) {
- try {
- receiver.onError(0 /* deviceId */,
- BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
- hardwareUnavailable);
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to send error", e);
- }
- return BIOMETRIC_NONE;
- }
- if (!hasTemplatesEnrolled) {
- try {
- receiver.onError(0 /* deviceId */,
- BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS,
- FaceManager.getErrorString(getContext(),
- BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS,
- 0 /* vendorCode */));
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to send error", e);
- }
- return BIOMETRIC_NONE;
+ return new Pair<>(BIOMETRIC_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
+ } else if (!hasTemplatesEnrolled) {
+ // Return the modality here so the correct error string can be sent. This error is
+ // preferred over !enabledForApps
+ return new Pair<>(firstHwAvailable, BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS);
+ } else if (!enabledForApps) {
+ return new Pair<>(BIOMETRIC_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
}
- return modality;
+ return new Pair<>(modality, BiometricConstants.BIOMETRIC_ERROR_NONE);
+ }
+
+ private boolean isEnabledForApp(int modality) {
+ switch(modality) {
+ case BIOMETRIC_FINGERPRINT:
+ return true;
+ case BIOMETRIC_IRIS:
+ return true;
+ case BIOMETRIC_FACE:
+ return mSettingObserver.getFaceEnabledForApps();
+ default:
+ Slog.w(TAG, "Unsupported modality: " + modality);
+ return false;
+ }
+ }
+
+ private String getErrorString(int type, int error, int vendorCode) {
+ switch (type) {
+ case BIOMETRIC_FINGERPRINT:
+ return FingerprintManager.getErrorString(getContext(), error, vendorCode);
+ case BIOMETRIC_IRIS:
+ Slog.w(TAG, "Modality not supported");
+ return null; // not supported
+ case BIOMETRIC_FACE:
+ return FaceManager.getErrorString(getContext(), error, vendorCode);
+ default:
+ Slog.w(TAG, "Unable to get error string for modality: " + type);
+ return null;
+ }
}
private BiometricAuthenticator getAuthenticator(int type) {
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/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index 2405925..7bfe9ce 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -19,7 +19,6 @@
import android.graphics.Rect;
import android.hardware.display.DisplayViewport;
import android.os.IBinder;
-import android.view.Display;
import android.view.Surface;
import android.view.SurfaceControl;
@@ -224,6 +223,8 @@
DisplayDeviceInfo info = getDisplayDeviceInfoLocked();
viewport.deviceWidth = isRotated ? info.height : info.width;
viewport.deviceHeight = isRotated ? info.width : info.height;
+
+ viewport.uniqueId = info.uniqueId;
}
/**
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/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 0eff7f5..e70460a 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -17,15 +17,14 @@
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_OWN_CONTENT_ONLY;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE;
-import static android.hardware.display.DisplayManager
- .VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.DumpUtils;
-import com.android.internal.util.IndentingPrintWriter;
+import static android.hardware.display.DisplayViewport.VIEWPORT_EXTERNAL;
+import static android.hardware.display.DisplayViewport.VIEWPORT_INTERNAL;
+import static android.hardware.display.DisplayViewport.VIEWPORT_VIRTUAL;
import android.Manifest;
import android.annotation.NonNull;
@@ -45,8 +44,8 @@
import android.hardware.display.Curve;
import android.hardware.display.DisplayManagerGlobal;
import android.hardware.display.DisplayManagerInternal;
-import android.hardware.display.DisplayViewport;
import android.hardware.display.DisplayManagerInternal.DisplayTransactionListener;
+import android.hardware.display.DisplayViewport;
import android.hardware.display.IDisplayManager;
import android.hardware.display.IDisplayManagerCallback;
import android.hardware.display.IVirtualDisplayCallback;
@@ -83,14 +82,17 @@
import android.view.Surface;
import android.view.SurfaceControl;
-import com.android.internal.util.Preconditions;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.DumpUtils;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.server.AnimationThread;
import com.android.server.DisplayThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.UiThread;
-import com.android.server.wm.WindowManagerInternal;
import com.android.server.wm.SurfaceAnimationThread;
+import com.android.server.wm.WindowManagerInternal;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -256,9 +258,8 @@
// Viewports of the default display and the display that should receive touch
// input from an external source. Used by the input system.
- private final DisplayViewport mDefaultViewport = new DisplayViewport();
- private final DisplayViewport mExternalTouchViewport = new DisplayViewport();
- private final ArrayList<DisplayViewport> mVirtualTouchViewports = new ArrayList<>();
+ @GuardedBy("mSyncRoot")
+ private final ArrayList<DisplayViewport> mViewports = new ArrayList<>();
// Persistent data store for all internal settings maintained by the display manager service.
private final PersistentDataStore mPersistentDataStore = new PersistentDataStore();
@@ -272,9 +273,7 @@
// Temporary viewports, used when sending new viewport information to the
// input system. May be used outside of the lock but only on the handler thread.
- private final DisplayViewport mTempDefaultViewport = new DisplayViewport();
- private final DisplayViewport mTempExternalTouchViewport = new DisplayViewport();
- private final ArrayList<DisplayViewport> mTempVirtualTouchViewports = new ArrayList<>();
+ private final ArrayList<DisplayViewport> mTempViewports = new ArrayList<>();
// The default color mode for default displays. Overrides the usual
// Display.Display.COLOR_MODE_DEFAULT for displays with the
@@ -1255,9 +1254,7 @@
}
private void clearViewportsLocked() {
- mDefaultViewport.valid = false;
- mExternalTouchViewport.valid = false;
- mVirtualTouchViewports.clear();
+ mViewports.clear();
}
private void configureDisplayLocked(SurfaceControl.Transaction t, DisplayDevice device) {
@@ -1287,40 +1284,89 @@
}
display.configureDisplayLocked(t, device, info.state == Display.STATE_OFF);
- // Update the viewports if needed.
- if (!mDefaultViewport.valid
- && (info.flags & DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0) {
- setViewportLocked(mDefaultViewport, display, device);
+ // Update the corresponding viewport.
+ DisplayViewport internalViewport = getInternalViewportLocked();
+ if ((info.flags & DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0) {
+ populateViewportLocked(internalViewport, display, device);
}
- if (!mExternalTouchViewport.valid
- && info.touch == DisplayDeviceInfo.TOUCH_EXTERNAL) {
- setViewportLocked(mExternalTouchViewport, display, device);
+ DisplayViewport externalViewport = getExternalViewportLocked();
+ if (info.touch == DisplayDeviceInfo.TOUCH_EXTERNAL) {
+ populateViewportLocked(externalViewport, display, device);
+ } else if (!externalViewport.valid) {
+ // TODO (b/116850516) move this logic into InputReader
+ externalViewport.copyFrom(internalViewport);
+ externalViewport.type = DisplayViewport.VIEWPORT_EXTERNAL;
}
if (info.touch == DisplayDeviceInfo.TOUCH_VIRTUAL && !TextUtils.isEmpty(info.uniqueId)) {
- final DisplayViewport viewport = getVirtualTouchViewportLocked(info.uniqueId);
- setViewportLocked(viewport, display, device);
+ final DisplayViewport viewport = getVirtualViewportLocked(info.uniqueId);
+ populateViewportLocked(viewport, display, device);
}
}
- /** Gets the virtual device viewport or creates it if not yet created. */
- private DisplayViewport getVirtualTouchViewportLocked(@NonNull String uniqueId) {
+ /** Get the virtual device viewport that has the specified uniqueId.
+ * If such viewport does not exist, create it. */
+ private DisplayViewport getVirtualViewportLocked(@NonNull String uniqueId) {
DisplayViewport viewport;
- final int count = mVirtualTouchViewports.size();
+ final int count = mViewports.size();
for (int i = 0; i < count; i++) {
- viewport = mVirtualTouchViewports.get(i);
+ viewport = mViewports.get(i);
if (uniqueId.equals(viewport.uniqueId)) {
+ if (viewport.type != VIEWPORT_VIRTUAL) {
+ Slog.wtf(TAG, "Found a viewport with uniqueId '" + uniqueId
+ + "' but it has type " + DisplayViewport.typeToString(viewport.type)
+ + " (expected VIRTUAL)");
+ continue;
+ }
return viewport;
}
}
viewport = new DisplayViewport();
viewport.uniqueId = uniqueId;
- mVirtualTouchViewports.add(viewport);
+ viewport.type = VIEWPORT_VIRTUAL;
+ mViewports.add(viewport);
return viewport;
}
- private static void setViewportLocked(DisplayViewport viewport,
+ private DisplayViewport getInternalViewportLocked() {
+ return getViewportByTypeLocked(VIEWPORT_INTERNAL);
+ }
+
+ private DisplayViewport getExternalViewportLocked() {
+ return getViewportByTypeLocked(VIEWPORT_EXTERNAL);
+ }
+
+ /**
+ * Get internal or external viewport. Create it if does not currently exist.
+ * @param viewportType - either INTERNAL or EXTERNAL
+ * @return the viewport with the requested type
+ */
+ private DisplayViewport getViewportByTypeLocked(int viewportType) {
+ // Only allow a single INTERNAL or EXTERNAL viewport, which makes this function possible.
+ // TODO (b/116824030) allow multiple EXTERNAL viewports and remove this function.
+ // Creates the viewport if none exists.
+ if (viewportType != VIEWPORT_INTERNAL && viewportType != VIEWPORT_EXTERNAL) {
+ Slog.wtf(TAG, "Cannot call getViewportByTypeLocked for type "
+ + DisplayViewport.typeToString(viewportType));
+ return null;
+ }
+ DisplayViewport viewport;
+ final int count = mViewports.size();
+ for (int i = 0; i < count; i++) {
+ viewport = mViewports.get(i);
+ if (viewport.type == viewportType) {
+ return viewport;
+ }
+ }
+
+ viewport = new DisplayViewport();
+ viewport.type = viewportType;
+ mViewports.add(viewport);
+ return viewport;
+ }
+
+ private static void populateViewportLocked(DisplayViewport viewport,
LogicalDisplay display, DisplayDevice device) {
viewport.valid = true;
viewport.displayId = display.getDisplayIdLocked();
@@ -1400,9 +1446,7 @@
pw.println(" mPendingTraversal=" + mPendingTraversal);
pw.println(" mGlobalDisplayState=" + Display.stateToString(mGlobalDisplayState));
pw.println(" mNextNonDefaultDisplayId=" + mNextNonDefaultDisplayId);
- pw.println(" mDefaultViewport=" + mDefaultViewport);
- pw.println(" mExternalTouchViewport=" + mExternalTouchViewport);
- pw.println(" mVirtualTouchViewports=" + mVirtualTouchViewports);
+ pw.println(" mViewports=" + mViewports);
pw.println(" mDefaultDisplayDefaultColorMode=" + mDefaultDisplayDefaultColorMode);
pw.println(" mSingleDisplayDemoMode=" + mSingleDisplayDemoMode);
pw.println(" mWifiDisplayScanRequestCount=" + mWifiDisplayScanRequestCount);
@@ -1522,18 +1566,19 @@
break;
case MSG_UPDATE_VIEWPORT: {
+ final boolean changed;
synchronized (mSyncRoot) {
- mTempDefaultViewport.copyFrom(mDefaultViewport);
- mTempExternalTouchViewport.copyFrom(mExternalTouchViewport);
- if (!mTempVirtualTouchViewports.equals(mVirtualTouchViewports)) {
- mTempVirtualTouchViewports.clear();
- for (DisplayViewport d : mVirtualTouchViewports) {
- mTempVirtualTouchViewports.add(d.makeCopy());
- }
+ changed = !mTempViewports.equals(mViewports);
+ if (changed) {
+ mTempViewports.clear();
+ for (DisplayViewport d : mViewports) {
+ mTempViewports.add(d.makeCopy());
+ }
}
}
- mInputManagerInternal.setDisplayViewports(mTempDefaultViewport,
- mTempExternalTouchViewport, mTempVirtualTouchViewports);
+ if (changed) {
+ mInputManagerInternal.setDisplayViewports(mTempViewports);
+ }
break;
}
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 6111c23..5aa585f 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -17,15 +17,14 @@
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_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_SECURE;
-import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT;
-import static android.hardware.display.DisplayManager
- .VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL;
+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;
import android.hardware.display.IVirtualDisplayCallback;
@@ -33,10 +32,10 @@
import android.media.projection.IMediaProjectionCallback;
import android.os.Handler;
import android.os.IBinder;
-import android.os.SystemProperties;
import android.os.IBinder.DeathRecipient;
import android.os.Message;
import android.os.RemoteException;
+import android.os.SystemProperties;
import android.util.ArrayMap;
import android.util.Slog;
import android.view.Display;
@@ -60,7 +59,8 @@
static final boolean DEBUG = false;
// Unique id prefix for virtual displays
- private static final String UNIQUE_ID_PREFIX = "virtual:";
+ @VisibleForTesting
+ static final String UNIQUE_ID_PREFIX = "virtual:";
private final ArrayMap<IBinder, VirtualDisplayDevice> mVirtualDisplayDevices =
new ArrayMap<IBinder, VirtualDisplayDevice>();
@@ -366,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 0f28439..4913e8b 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -16,9 +16,6 @@
package com.android.server.input;
-import static android.hardware.display.DisplayViewport.VIEWPORT_INTERNAL;
-import static android.hardware.display.DisplayViewport.VIEWPORT_EXTERNAL;
-
import android.annotation.NonNull;
import android.app.IInputForwarder;
import android.app.Notification;
@@ -188,13 +185,8 @@
private static native long nativeInit(InputManagerService service,
Context context, MessageQueue messageQueue);
private static native void nativeStart(long ptr);
- private static native void nativeSetVirtualDisplayViewports(long ptr,
+ private static native void nativeSetDisplayViewports(long ptr,
DisplayViewport[] viewports);
- private static native void nativeSetDisplayViewport(long ptr, int viewportType,
- int displayId, int rotation,
- int logicalLeft, int logicalTop, int logicalRight, int logicalBottom,
- int physicalLeft, int physicalTop, int physicalRight, int physicalBottom,
- int deviceWidth, int deviceHeight, String uniqueId);
private static native int nativeGetScanCodeState(long ptr,
int deviceId, int sourceMask, int scanCode);
@@ -217,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);
@@ -409,31 +402,8 @@
nativeReloadDeviceAliases(mPtr);
}
- private void setDisplayViewportsInternal(DisplayViewport defaultViewport,
- DisplayViewport externalTouchViewport,
- List<DisplayViewport> virtualTouchViewports) {
- if (defaultViewport.valid) {
- setDisplayViewport(VIEWPORT_INTERNAL, defaultViewport);
- }
-
- if (externalTouchViewport.valid) {
- setDisplayViewport(VIEWPORT_EXTERNAL, externalTouchViewport);
- } else if (defaultViewport.valid) {
- setDisplayViewport(VIEWPORT_EXTERNAL, defaultViewport);
- }
-
- nativeSetVirtualDisplayViewports(mPtr,
- virtualTouchViewports.toArray(new DisplayViewport[0]));
- }
-
- private void setDisplayViewport(int viewportType, DisplayViewport viewport) {
- nativeSetDisplayViewport(mPtr, viewportType,
- viewport.displayId, viewport.orientation,
- viewport.logicalFrame.left, viewport.logicalFrame.top,
- viewport.logicalFrame.right, viewport.logicalFrame.bottom,
- viewport.physicalFrame.left, viewport.physicalFrame.top,
- viewport.physicalFrame.right, viewport.physicalFrame.bottom,
- viewport.deviceWidth, viewport.deviceHeight, viewport.uniqueId);
+ private void setDisplayViewportsInternal(List<DisplayViewport> viewports) {
+ nativeSetDisplayViewports(mPtr, viewports.toArray(new DisplayViewport[0]));
}
/**
@@ -1462,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
@@ -1491,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) {
@@ -2203,11 +2181,8 @@
private final class LocalService extends InputManagerInternal {
@Override
- public void setDisplayViewports(DisplayViewport defaultViewport,
- DisplayViewport externalTouchViewport,
- List<DisplayViewport> virtualTouchViewports) {
- setDisplayViewportsInternal(defaultViewport, externalTouchViewport,
- virtualTouchViewports);
+ public void setDisplayViewports(List<DisplayViewport> viewports) {
+ setDisplayViewportsInternal(viewports);
}
@Override
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/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 5005ea7..f9d49d7 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -16,6 +16,7 @@
package com.android.server.notification;
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
import static android.app.NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED;
import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED;
@@ -2001,7 +2002,8 @@
return mInternalService;
}
- private final IBinder mService = new INotificationManager.Stub() {
+ @VisibleForTesting
+ final IBinder mService = new INotificationManager.Stub() {
// Toasts
// ============================================================================
@@ -2015,21 +2017,30 @@
}
if (pkg == null || callback == null) {
- Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
+ Slog.e(TAG, "Not enqueuing toast. pkg=" + pkg + " callback=" + callback);
return ;
}
- final boolean isSystemToast = isCallerSystemOrPhone() || ("android".equals(pkg));
- final boolean isPackageSuspended =
- isPackageSuspendedForUser(pkg, Binder.getCallingUid());
- if (ENABLE_BLOCKED_TOASTS && !isSystemToast &&
- (!areNotificationsEnabledForPackage(pkg, Binder.getCallingUid())
- || isPackageSuspended)) {
- Slog.e(TAG, "Suppressing toast from package " + pkg
- + (isPackageSuspended
- ? " due to package suspended by administrator."
- : " by user request."));
- return;
+ final int callingUid = Binder.getCallingUid();
+ final boolean isSystemToast = isCallerSystemOrPhone()
+ || PackageManagerService.PLATFORM_PACKAGE_NAME.equals(pkg);
+ final boolean isPackageSuspended = isPackageSuspendedForUser(pkg, callingUid);
+ final boolean notificationsDisabledForPackage = !areNotificationsEnabledForPackage(pkg,
+ callingUid);
+
+ long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ final boolean appIsForeground = mActivityManager.getUidImportance(callingUid)
+ == IMPORTANCE_FOREGROUND;
+ if (ENABLE_BLOCKED_TOASTS && !isSystemToast && ((notificationsDisabledForPackage
+ && !appIsForeground) || isPackageSuspended)) {
+ Slog.e(TAG, "Suppressing toast from package " + pkg
+ + (isPackageSuspended ? " due to package suspended."
+ : " by user request."));
+ return;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
}
synchronized (mToastQueue) {
diff --git a/services/core/java/com/android/server/pm/PackageSignatures.java b/services/core/java/com/android/server/pm/PackageSignatures.java
index 471729e..6bce788 100644
--- a/services/core/java/com/android/server/pm/PackageSignatures.java
+++ b/services/core/java/com/android/server/pm/PackageSignatures.java
@@ -16,18 +16,18 @@
package com.android.server.pm;
-import com.android.internal.util.XmlUtils;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
import android.annotation.NonNull;
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
import android.content.pm.Signature;
import android.util.Log;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
import java.io.IOException;
import java.security.cert.CertificateException;
import java.util.ArrayList;
@@ -61,23 +61,22 @@
serializer.attribute(null, "count", Integer.toString(mSigningDetails.signatures.length));
serializer.attribute(null, "schemeVersion",
Integer.toString(mSigningDetails.signatureSchemeVersion));
- writeCertsListXml(serializer, writtenSignatures, mSigningDetails.signatures, null);
+ writeCertsListXml(serializer, writtenSignatures, mSigningDetails.signatures, false);
// if we have past signer certificate information, write it out
if (mSigningDetails.pastSigningCertificates != null) {
serializer.startTag(null, "pastSigs");
serializer.attribute(null, "count",
Integer.toString(mSigningDetails.pastSigningCertificates.length));
- writeCertsListXml(
- serializer, writtenSignatures, mSigningDetails.pastSigningCertificates,
- mSigningDetails.pastSigningCertificatesFlags);
+ writeCertsListXml(serializer, writtenSignatures,
+ mSigningDetails.pastSigningCertificates, true);
serializer.endTag(null, "pastSigs");
}
serializer.endTag(null, tagName);
}
private void writeCertsListXml(XmlSerializer serializer, ArrayList<Signature> writtenSignatures,
- Signature[] signatures, int[] flags) throws IOException {
+ Signature[] signatures, boolean isPastSigs) throws IOException {
for (int i=0; i<signatures.length; i++) {
serializer.startTag(null, "cert");
final Signature sig = signatures[i];
@@ -96,8 +95,10 @@
serializer.attribute(null, "index", Integer.toString(numWritten));
serializer.attribute(null, "key", sig.toCharsString());
}
- if (flags != null) {
- serializer.attribute(null, "flags", Integer.toString(flags[i]));
+ // The flags attribute is only written for previous signatures to represent the
+ // capabilities the developer wants to grant to the previous signing certificates.
+ if (isPastSigs) {
+ serializer.attribute(null, "flags", Integer.toString(sig.getFlags()));
}
serializer.endTag(null, "cert");
}
@@ -114,6 +115,7 @@
"Error in package manager settings: <sigs> has"
+ " no count at " + parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
+ return;
}
final int count = Integer.parseInt(countStr);
@@ -128,16 +130,11 @@
signatureSchemeVersion = Integer.parseInt(schemeVersionStr);
}
builder.setSignatureSchemeVersion(signatureSchemeVersion);
- Signature[] signatures = new Signature[count];
- int pos = readCertsListXml(parser, readSignatures, signatures, null, builder);
+ ArrayList<Signature> signatureList = new ArrayList<>();
+ int pos = readCertsListXml(parser, readSignatures, signatureList, count, false, builder);
+ Signature[] signatures = signatureList.toArray(new Signature[signatureList.size()]);
builder.setSignatures(signatures);
if (pos < count) {
- // Should never happen -- there is an error in the written
- // settings -- but if it does we don't want to generate
- // a bad array.
- Signature[] newSigs = new Signature[pos];
- System.arraycopy(signatures, 0, newSigs, 0, pos);
- builder = builder.setSignatures(newSigs);
PackageManagerService.reportSettingsProblem(Log.WARN,
"Error in package manager settings: <sigs> count does not match number of "
+ " <cert> entries" + parser.getPositionDescription());
@@ -154,9 +151,9 @@
}
private int readCertsListXml(XmlPullParser parser, ArrayList<Signature> readSignatures,
- Signature[] signatures, int[] flags, PackageParser.SigningDetails.Builder builder)
+ ArrayList<Signature> signatures, int count, boolean isPastSigs,
+ PackageParser.SigningDetails.Builder builder)
throws IOException, XmlPullParserException {
- int count = signatures.length;
int pos = 0;
int outerDepth = parser.getDepth();
@@ -174,6 +171,7 @@
if (pos < count) {
String index = parser.getAttributeValue(null, "index");
if (index != null) {
+ boolean signatureParsed = false;
try {
int idx = Integer.parseInt(index);
String key = parser.getAttributeValue(null, "key");
@@ -181,7 +179,8 @@
if (idx >= 0 && idx < readSignatures.size()) {
Signature sig = readSignatures.get(idx);
if (sig != null) {
- signatures[pos] = readSignatures.get(idx);
+ signatures.add(sig);
+ signatureParsed = true;
} else {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Error in package manager settings: <cert> "
@@ -195,12 +194,15 @@
+ parser.getPositionDescription());
}
} else {
- while (readSignatures.size() <= idx) {
+ // Create the signature first to prevent adding null entries to the
+ // output List if the key value is invalid.
+ Signature sig = new Signature(key);
+ while (readSignatures.size() < idx) {
readSignatures.add(null);
}
- Signature sig = new Signature(key);
- readSignatures.set(idx, sig);
- signatures[pos] = sig;
+ readSignatures.add(sig);
+ signatures.add(sig);
+ signatureParsed = true;
}
} catch (NumberFormatException e) {
PackageManagerService.reportSettingsProblem(Log.WARN,
@@ -215,11 +217,22 @@
+ e.getMessage());
}
- if (flags != null) {
+ if (isPastSigs) {
String flagsStr = parser.getAttributeValue(null, "flags");
if (flagsStr != null) {
try {
- flags[pos] = Integer.parseInt(flagsStr);
+ int flagsValue = Integer.parseInt(flagsStr);
+ // only modify the flags if the signature of the previous signer
+ // was successfully parsed above
+ if (signatureParsed) {
+ signatures.get(signatures.size() - 1).setFlags(flagsValue);
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: signature not "
+ + "available at index "
+ + pos + " to set flags at "
+ + parser.getPositionDescription());
+ }
} catch (NumberFormatException e) {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Error in package manager settings: <cert> "
@@ -246,7 +259,7 @@
pos++;
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals("pastSigs")) {
- if (flags == null) {
+ if (!isPastSigs) {
// we haven't encountered pastSigs yet, go ahead
String countStr = parser.getAttributeValue(null, "count");
if (countStr == null) {
@@ -254,32 +267,23 @@
"Error in package manager settings: <pastSigs> has"
+ " no count at " + parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
+ continue;
}
try {
final int pastSigsCount = Integer.parseInt(countStr);
- Signature[] pastSignatures = new Signature[pastSigsCount];
- int[] pastSignaturesFlags = new int[pastSigsCount];
- int pastSigsPos = readCertsListXml(parser, readSignatures, pastSignatures,
- pastSignaturesFlags, builder);
- builder = builder
- .setPastSigningCertificates(pastSignatures)
- .setPastSigningCertificatesFlags(pastSignaturesFlags);
+ ArrayList<Signature> pastSignatureList = new ArrayList<>();
+ int pastSigsPos = readCertsListXml(parser, readSignatures,
+ pastSignatureList,
+ pastSigsCount, true, builder);
+ Signature[] pastSignatures = pastSignatureList.toArray(
+ new Signature[pastSignatureList.size()]);
+ builder = builder.setPastSigningCertificates(pastSignatures);
if (pastSigsPos < pastSigsCount) {
- // Should never happen -- there is an error in the written
- // settings -- but if it does we don't want to generate
- // a bad array.
- Signature[] newSigs = new Signature[pastSigsPos];
- System.arraycopy(pastSignatures, 0, newSigs, 0, pastSigsPos);
- int[] newFlags = new int[pastSigsPos];
- System.arraycopy(pastSignaturesFlags, 0, newFlags, 0, pastSigsPos);
- builder = builder
- .setPastSigningCertificates(newSigs)
- .setPastSigningCertificatesFlags(newFlags);
PackageManagerService.reportSettingsProblem(Log.WARN,
"Error in package manager settings: <pastSigs> count does not "
- + "match number of <cert> entries "
- + parser.getPositionDescription());
+ + "match number of <cert> entries "
+ + parser.getPositionDescription());
}
} catch (NumberFormatException e) {
PackageManagerService.reportSettingsProblem(Log.WARN,
@@ -326,7 +330,8 @@
buf.append(Integer.toHexString(
mSigningDetails.pastSigningCertificates[i].hashCode()));
buf.append(" flags: ");
- buf.append(Integer.toHexString(mSigningDetails.pastSigningCertificatesFlags[i]));
+ buf.append(
+ Integer.toHexString(mSigningDetails.pastSigningCertificates[i].getFlags()));
}
}
buf.append("]}");
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 2557f46..21bf488 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -1655,11 +1655,9 @@
}
final boolean keyguardShowing = isKeyguardShowingAndNotOccluded();
mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned());
- if (keyguardShowing) {
- // since it took two seconds of long press to bring this up,
- // poke the wake lock so they have some time to see the dialog.
- mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
- }
+ // since it took two seconds of long press to bring this up,
+ // poke the wake lock so they have some time to see the dialog.
+ mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
}
boolean isDeviceProvisioned() {
@@ -4777,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 d0de940..1abaaf2 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -15,6 +15,8 @@
*/
package com.android.server.stats;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
import android.annotation.Nullable;
import android.app.ActivityManagerInternal;
import android.app.AlarmManager;
@@ -67,6 +69,7 @@
import android.util.Log;
import android.util.Slog;
import android.util.StatsLog;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.procstats.IProcessStats;
@@ -1256,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) {
@@ -1279,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;
}
@@ -1295,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) {
@@ -1336,12 +1340,28 @@
}
}
+ private void pullPowerProfile(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ PowerProfile powerProfile = new PowerProfile(mContext);
+ checkNotNull(powerProfile);
+
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
+ wallClockNanos);
+ ProtoOutputStream proto = new ProtoOutputStream();
+ powerProfile.writeToProto(proto);
+ proto.flush();
+ e.writeStorage(proto.getBytes());
+ pulledData.add(e);
+ }
+
private void pullDiskIo(int tagId, long elapsedNanos, final long wallClockNanos,
List<StatsLogEventWrapper> pulledData) {
mStoragedUidIoStatsReader.readAbsolute((uid, fgCharsRead, fgCharsWrite, fgBytesRead,
fgBytesWrite, bgCharsRead, bgCharsWrite, bgBytesRead, bgBytesWrite,
fgFsync, bgFsync) -> {
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
+ wallClockNanos);
e.writeInt(uid);
e.writeLong(fgCharsRead);
e.writeLong(fgCharsWrite);
@@ -1471,13 +1491,22 @@
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: {
pullDiskIo(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+ case StatsLog.POWER_PROFILE: {
+ pullPowerProfile(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
default:
Slog.w(TAG, "No such tagId data as " + tagId);
return null;
@@ -1539,7 +1568,8 @@
public void onStart() {
mStatsCompanionService = new StatsCompanionService(getContext());
try {
- publishBinderService(Context.STATS_COMPANION_SERVICE, mStatsCompanionService);
+ publishBinderService(Context.STATS_COMPANION_SERVICE,
+ mStatsCompanionService);
if (DEBUG) Slog.d(TAG, "Published " + Context.STATS_COMPANION_SERVICE);
} catch (Exception e) {
Slog.e(TAG, "Failed to publishBinderService", e);
@@ -1564,18 +1594,22 @@
}
/**
- * Tells statsd that statscompanion is ready. If the binder call returns, link to statsd.
+ * Tells statsd that statscompanion is ready. If the binder call returns, link to
+ * statsd.
*/
private void sayHiToStatsd() {
synchronized (sStatsdLock) {
if (sStatsd != null) {
Slog.e(TAG, "Trying to fetch statsd, but it was already fetched",
- new IllegalStateException("sStatsd is not null when being fetched"));
+ new IllegalStateException(
+ "sStatsd is not null when being fetched"));
return;
}
sStatsd = fetchStatsdService();
if (sStatsd == null) {
- Slog.i(TAG, "Could not yet find statsd to tell it that StatsCompanion is alive.");
+ Slog.i(TAG,
+ "Could not yet find statsd to tell it that StatsCompanion is "
+ + "alive.");
return;
}
if (DEBUG) Slog.d(TAG, "Saying hi to statsd");
@@ -1593,10 +1627,12 @@
filter.addAction(Intent.ACTION_PACKAGE_ADDED);
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addDataScheme("package");
- mContext.registerReceiverAsUser(mAppUpdateReceiver, UserHandle.ALL, filter, null,
+ mContext.registerReceiverAsUser(mAppUpdateReceiver, UserHandle.ALL, filter,
+ null,
null);
- // Setup receiver for user initialize (which happens once for a new user) and
+ // Setup receiver for user initialize (which happens once for a new user)
+ // and
// if a user is removed.
filter = new IntentFilter(Intent.ACTION_USER_INITIALIZE);
filter.addAction(Intent.ACTION_USER_REMOVED);
@@ -1610,7 +1646,8 @@
mShutdownEventReceiver, UserHandle.ALL, filter, null, null);
final long token = Binder.clearCallingIdentity();
try {
- // Pull the latest state of UID->app name, version mapping when statsd starts.
+ // Pull the latest state of UID->app name, version mapping when
+ // statsd starts.
informAllUidsLocked(mContext);
} finally {
restoreCallingIdentity(token);
@@ -1672,7 +1709,8 @@
if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) return;
synchronized (sStatsdLock) {
- writer.println("Number of configuration files deleted: " + mDeletedFiles.size());
+ writer.println(
+ "Number of configuration files deleted: " + mDeletedFiles.size());
if (mDeletedFiles.size() > 0) {
writer.println(" timestamp, deleted file name");
}
@@ -1680,7 +1718,8 @@
SystemClock.currentThreadTimeMillis() - SystemClock.elapsedRealtime();
for (Long elapsedMillis : mDeletedFiles.keySet()) {
long deletionMillis = lastBootMillis + elapsedMillis;
- writer.println(" " + deletionMillis + ", " + mDeletedFiles.get(elapsedMillis));
+ writer.println(
+ " " + deletionMillis + ", " + mDeletedFiles.get(elapsedMillis));
}
}
}
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/Android.bp b/services/core/jni/Android.bp
index 157b634..045f4eb 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -8,6 +8,7 @@
"-Wall",
"-Werror",
"-Wno-unused-parameter",
+ "-Wthread-safety",
"-DEGL_EGLEXT_PROTOTYPES",
"-DGL_GLEXT_PROTOTYPES",
@@ -29,9 +30,7 @@
"com_android_server_devicepolicy_CryptoTestHelper.cpp",
"com_android_server_HardwarePropertiesManagerService.cpp",
"com_android_server_hdmi_HdmiCecController.cpp",
- "com_android_server_input_InputApplicationHandle.cpp",
"com_android_server_input_InputManagerService.cpp",
- "com_android_server_input_InputWindowHandle.cpp",
"com_android_server_lights_LightsService.cpp",
"com_android_server_location_GnssLocationProvider.cpp",
"com_android_server_locksettings_SyntheticPasswordManager.cpp",
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 42ade38..c66d03c 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -60,10 +60,12 @@
#include <nativehelper/ScopedUtfChars.h>
#include "com_android_server_power_PowerManagerService.h"
-#include "com_android_server_input_InputApplicationHandle.h"
-#include "com_android_server_input_InputWindowHandle.h"
+#include "android_hardware_input_InputApplicationHandle.h"
+#include "android_hardware_input_InputWindowHandle.h"
#include "android_hardware_display_DisplayViewport.h"
+#include <vector>
+
#define INDENT " "
using android::base::StringPrintf;
@@ -144,8 +146,8 @@
static jobject getInputApplicationHandleObjLocalRef(JNIEnv* env,
const sp<InputApplicationHandle>& inputApplicationHandle) {
- if (inputApplicationHandle == NULL) {
- return NULL;
+ if (inputApplicationHandle == nullptr) {
+ return nullptr;
}
return static_cast<NativeInputApplicationHandle*>(inputApplicationHandle.get())->
getInputApplicationHandleObjLocalRef(env);
@@ -153,8 +155,8 @@
static jobject getInputWindowHandleObjLocalRef(JNIEnv* env,
const sp<InputWindowHandle>& inputWindowHandle) {
- if (inputWindowHandle == NULL) {
- return NULL;
+ if (inputWindowHandle == nullptr) {
+ return nullptr;
}
return static_cast<NativeInputWindowHandle*>(inputWindowHandle.get())->
getInputWindowHandleObjLocalRef(env);
@@ -182,6 +184,15 @@
loadSystemIconAsSpriteWithPointerIcon(env, contextObj, style, &pointerIcon, outSpriteIcon);
}
+static void updatePointerControllerFromViewport(
+ sp<PointerController> controller, const DisplayViewport* const viewport) {
+ if (controller != nullptr && viewport != nullptr) {
+ const int32_t width = viewport->logicalRight - viewport->logicalLeft;
+ const int32_t height = viewport->logicalBottom - viewport->logicalTop;
+ controller->setDisplayViewport(width, height, viewport->orientation);
+ }
+}
+
enum {
WM_ACTION_PASS_TO_USER = 1,
};
@@ -203,15 +214,15 @@
void dump(std::string& dump);
- void setVirtualDisplayViewports(JNIEnv* env, jobjectArray viewportObjArray);
- void setDisplayViewport(int32_t viewportType, const DisplayViewport& viewport);
+ void setDisplayViewports(JNIEnv* env, jobjectArray viewportObjArray);
status_t registerInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel,
const sp<InputWindowHandle>& inputWindowHandle, bool monitor);
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);
@@ -277,9 +288,7 @@
Mutex mLock;
struct Locked {
// Display size information.
- DisplayViewport internalViewport;
- DisplayViewport externalViewport;
- Vector<DisplayViewport> virtualViewports;
+ std::vector<DisplayViewport> viewports;
// System UI visibility.
int32_t systemUiVisibility;
@@ -304,7 +313,7 @@
// Input devices to be disabled
SortedVector<int32_t> disabledInputDevices;
- } mLocked;
+ } mLocked GUARDED_BY(mLock);
std::atomic<bool> mInteractive;
@@ -384,8 +393,17 @@
return false;
}
-void NativeInputManager::setVirtualDisplayViewports(JNIEnv* env, jobjectArray viewportObjArray) {
- Vector<DisplayViewport> viewports;
+static const DisplayViewport* findInternalViewport(const std::vector<DisplayViewport>& viewports) {
+ for (const DisplayViewport& v : viewports) {
+ if (v.type == ViewportType::VIEWPORT_INTERNAL) {
+ return &v;
+ }
+ }
+ return nullptr;
+}
+
+void NativeInputManager::setDisplayViewports(JNIEnv* env, jobjectArray viewportObjArray) {
+ std::vector<DisplayViewport> viewports;
if (viewportObjArray) {
jsize length = env->GetArrayLength(viewportObjArray);
@@ -397,57 +415,32 @@
DisplayViewport viewport;
android_hardware_display_DisplayViewport_toNative(env, viewportObj, &viewport);
- ALOGI("Viewport [%d] to add: %s", (int) length, viewport.uniqueId.c_str());
- viewports.push(viewport);
+ ALOGI("Viewport [%d] to add: %s", (int) i, viewport.uniqueId.c_str());
+ viewports.push_back(viewport);
env->DeleteLocalRef(viewportObj);
}
}
+ const DisplayViewport* newInternalViewport = findInternalViewport(viewports);
{
AutoMutex _l(mLock);
- mLocked.virtualViewports = viewports;
+ const DisplayViewport* oldInternalViewport = findInternalViewport(mLocked.viewports);
+ // 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 || (*oldInternalViewport != *newInternalViewport));
+ if (internalViewportChanged) {
+ sp<PointerController> controller = mLocked.pointerController.promote();
+ updatePointerControllerFromViewport(controller, newInternalViewport);
+ }
+ mLocked.viewports = viewports;
}
mInputManager->getReader()->requestRefreshConfiguration(
InputReaderConfiguration::CHANGE_DISPLAY_INFO);
}
-void NativeInputManager::setDisplayViewport(int32_t type, const DisplayViewport& viewport) {
- bool changed = false;
- {
- AutoMutex _l(mLock);
-
- ViewportType viewportType = static_cast<ViewportType>(type);
- DisplayViewport* v = NULL;
- if (viewportType == ViewportType::VIEWPORT_EXTERNAL) {
- v = &mLocked.externalViewport;
- } else if (viewportType == ViewportType::VIEWPORT_INTERNAL) {
- v = &mLocked.internalViewport;
- }
-
- if (v != NULL && *v != viewport) {
- changed = true;
- *v = viewport;
-
- if (viewportType == ViewportType::VIEWPORT_INTERNAL) {
- sp<PointerController> controller = mLocked.pointerController.promote();
- if (controller != NULL) {
- controller->setDisplayViewport(
- viewport.logicalRight - viewport.logicalLeft,
- viewport.logicalBottom - viewport.logicalTop,
- viewport.orientation);
- }
- }
- }
- }
-
- if (changed) {
- mInputManager->getReader()->requestRefreshConfiguration(
- InputReaderConfiguration::CHANGE_DISPLAY_INFO);
- }
-}
-
status_t NativeInputManager::registerInputChannel(JNIEnv* /* env */,
const sp<InputChannel>& inputChannel,
const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
@@ -479,7 +472,7 @@
jsize length = env->GetArrayLength(excludedDeviceNames);
for (jsize i = 0; i < length; i++) {
jstring item = jstring(env->GetObjectArrayElement(excludedDeviceNames, i));
- const char* deviceNameChars = env->GetStringUTFChars(item, NULL);
+ const char* deviceNameChars = env->GetStringUTFChars(item, nullptr);
outConfig->excludedDeviceNames.push_back(deviceNameChars);
env->ReleaseStringUTFChars(item, deviceNameChars);
env->DeleteLocalRef(item);
@@ -526,11 +519,7 @@
outConfig->pointerCapture = mLocked.pointerCapture;
- outConfig->setPhysicalDisplayViewport(ViewportType::VIEWPORT_INTERNAL,
- mLocked.internalViewport);
- outConfig->setPhysicalDisplayViewport(ViewportType::VIEWPORT_EXTERNAL,
- mLocked.externalViewport);
- outConfig->setVirtualDisplayViewports(mLocked.virtualViewports);
+ outConfig->setDisplayViewports(mLocked.viewports);
outConfig->disabledDevices = mLocked.disabledInputDevices;
} // release lock
@@ -541,25 +530,22 @@
AutoMutex _l(mLock);
sp<PointerController> controller = mLocked.pointerController.promote();
- if (controller == NULL) {
+ if (controller == nullptr) {
ensureSpriteControllerLocked();
controller = new PointerController(this, mLooper, mLocked.spriteController);
mLocked.pointerController = controller;
- DisplayViewport& v = mLocked.internalViewport;
- controller->setDisplayViewport(
- v.logicalRight - v.logicalLeft,
- v.logicalBottom - v.logicalTop,
- v.orientation);
+ const DisplayViewport* internalViewport = findInternalViewport(mLocked.viewports);
+ updatePointerControllerFromViewport(controller, internalViewport);
updateInactivityTimeoutLocked(controller);
}
return controller;
}
-void NativeInputManager::ensureSpriteControllerLocked() {
- if (mLocked.spriteController == NULL) {
+void NativeInputManager::ensureSpriteControllerLocked() REQUIRES(mLock) {
+ if (mLocked.spriteController == nullptr) {
JNIEnv* env = jniEnv();
jint layer = env->CallIntMethod(mServiceObj, gServiceClassInfo.getPointerLayer);
if (checkAndClearExceptionFromCallback(env, "getPointerLayer")) {
@@ -575,7 +561,7 @@
size_t count = inputDevices.size();
jobjectArray inputDevicesObjArray = env->NewObjectArray(
- count, gInputDeviceClassInfo.clazz, NULL);
+ count, gInputDeviceClassInfo.clazz, nullptr);
if (inputDevicesObjArray) {
bool error = false;
for (size_t i = 0; i < count; i++) {
@@ -750,7 +736,7 @@
sp<InputWindowHandle> windowHandle =
android_server_InputWindowHandle_getHandle(env, windowHandleObj);
- if (windowHandle != NULL) {
+ if (windowHandle != nullptr) {
windowHandles.push(windowHandle);
}
env->DeleteLocalRef(windowHandleObj);
@@ -786,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) {
@@ -803,13 +794,14 @@
mLocked.systemUiVisibility = visibility;
sp<PointerController> controller = mLocked.pointerController.promote();
- if (controller != NULL) {
+ if (controller != nullptr) {
updateInactivityTimeoutLocked(controller);
}
}
}
-void NativeInputManager::updateInactivityTimeoutLocked(const sp<PointerController>& controller) {
+void NativeInputManager::updateInactivityTimeoutLocked(const sp<PointerController>& controller)
+ REQUIRES(mLock) {
bool lightsOut = mLocked.systemUiVisibility & ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN;
controller->setInactivityTimeout(lightsOut
? PointerController::INACTIVITY_TIMEOUT_SHORT
@@ -894,7 +886,7 @@
void NativeInputManager::setPointerIconType(int32_t iconId) {
AutoMutex _l(mLock);
sp<PointerController> controller = mLocked.pointerController.promote();
- if (controller != NULL) {
+ if (controller != nullptr) {
controller->updatePointerIcon(iconId);
}
}
@@ -902,7 +894,7 @@
void NativeInputManager::reloadPointerIcons() {
AutoMutex _l(mLock);
sp<PointerController> controller = mLocked.pointerController.promote();
- if (controller != NULL) {
+ if (controller != nullptr) {
controller->reloadPointerResources();
}
}
@@ -910,7 +902,7 @@
void NativeInputManager::setCustomPointerIcon(const SpriteIcon& icon) {
AutoMutex _l(mLock);
sp<PointerController> controller = mLocked.pointerController.promote();
- if (controller != NULL) {
+ if (controller != nullptr) {
controller->setCustomPointerIcon(icon);
}
}
@@ -1122,7 +1114,7 @@
gServiceClassInfo.dispatchUnhandledKey,
inputWindowHandleObj, keyEventObj, policyFlags);
if (checkAndClearExceptionFromCallback(env, "dispatchUnhandledKey")) {
- fallbackKeyEventObj = NULL;
+ fallbackKeyEventObj = nullptr;
}
android_view_KeyEvent_recycle(env, keyEventObj);
env->DeleteLocalRef(keyEventObj);
@@ -1235,7 +1227,7 @@
static jlong nativeInit(JNIEnv* env, jclass /* clazz */,
jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
- if (messageQueue == NULL) {
+ if (messageQueue == nullptr) {
jniThrowRuntimeException(env, "MessageQueue is not initialized.");
return 0;
}
@@ -1255,37 +1247,10 @@
}
}
-static void nativeSetVirtualDisplayViewports(JNIEnv* env, jclass /* clazz */, jlong ptr,
+static void nativeSetDisplayViewports(JNIEnv* env, jclass /* clazz */, jlong ptr,
jobjectArray viewportObjArray) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- im->setVirtualDisplayViewports(env, viewportObjArray);
-}
-
-static void nativeSetDisplayViewport(JNIEnv* env, jclass /* clazz */, jlong ptr,
- jint viewportType, jint displayId, jint orientation,
- jint logicalLeft, jint logicalTop, jint logicalRight, jint logicalBottom,
- jint physicalLeft, jint physicalTop, jint physicalRight, jint physicalBottom,
- jint deviceWidth, jint deviceHeight, jstring uniqueId) {
- NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
-
- DisplayViewport v;
- v.displayId = displayId;
- v.orientation = orientation;
- v.logicalLeft = logicalLeft;
- v.logicalTop = logicalTop;
- v.logicalRight = logicalRight;
- v.logicalBottom = logicalBottom;
- v.physicalLeft = physicalLeft;
- v.physicalTop = physicalTop;
- v.physicalRight = physicalRight;
- v.physicalBottom = physicalBottom;
- v.deviceWidth = deviceWidth;
- v.deviceHeight = deviceHeight;
- if (uniqueId != nullptr) {
- v.uniqueId = ScopedUtfChars(env, uniqueId).c_str();
- }
-
- im->setDisplayViewport(viewportType, v);
+ im->setDisplayViewports(env, viewportObjArray);
}
static jint nativeGetScanCodeState(JNIEnv* /* env */, jclass /* clazz */,
@@ -1316,8 +1281,8 @@
jlong ptr, jint deviceId, jint sourceMask, jintArray keyCodes, jbooleanArray outFlags) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- int32_t* codes = env->GetIntArrayElements(keyCodes, NULL);
- uint8_t* flags = env->GetBooleanArrayElements(outFlags, NULL);
+ int32_t* codes = env->GetIntArrayElements(keyCodes, nullptr);
+ uint8_t* flags = env->GetBooleanArrayElements(outFlags, nullptr);
jsize numCodes = env->GetArrayLength(keyCodes);
jboolean result;
if (numCodes == env->GetArrayLength(keyCodes)) {
@@ -1356,7 +1321,7 @@
sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
inputChannelObj);
- if (inputChannel == NULL) {
+ if (inputChannel == nullptr) {
throwInputChannelNotInitialized(env);
return;
}
@@ -1385,12 +1350,12 @@
sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
inputChannelObj);
- if (inputChannel == NULL) {
+ if (inputChannel == nullptr) {
throwInputChannelNotInitialized(env);
return;
}
- android_view_InputChannel_setDisposeCallback(env, inputChannelObj, NULL, NULL);
+ android_view_InputChannel_setDisposeCallback(env, inputChannelObj, nullptr, nullptr);
status_t status = im->unregisterInputChannel(env, inputChannel);
if (status && status != BAD_VALUE) { // ignore already unregistered channel
@@ -1454,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,
@@ -1490,7 +1462,7 @@
sp<InputChannel> toChannel =
android_view_InputChannel_getInputChannel(env, toChannelObj);
- if (fromChannel == NULL || toChannel == NULL) {
+ if (fromChannel == nullptr || toChannel == nullptr) {
return JNI_FALSE;
}
@@ -1543,7 +1515,7 @@
}
jlong* patternMillis = static_cast<jlong*>(env->GetPrimitiveArrayCritical(
- patternObj, NULL));
+ patternObj, nullptr));
nsecs_t pattern[patternSize];
for (size_t i = 0; i < patternSize; i++) {
pattern[i] = max(jlong(0), min(patternMillis[i],
@@ -1656,10 +1628,8 @@
(void*) nativeInit },
{ "nativeStart", "(J)V",
(void*) nativeStart },
- { "nativeSetVirtualDisplayViewports", "(J[Landroid/hardware/display/DisplayViewport;)V",
- (void*) nativeSetVirtualDisplayViewports },
- { "nativeSetDisplayViewport", "(JIIIIIIIIIIIIILjava/lang/String;)V",
- (void*) nativeSetDisplayViewport },
+ { "nativeSetDisplayViewports", "(J[Landroid/hardware/display/DisplayViewport;)V",
+ (void*) nativeSetDisplayViewports },
{ "nativeGetScanCodeState", "(JIII)I",
(void*) nativeGetScanCodeState },
{ "nativeGetKeyCodeState", "(JIII)I",
@@ -1681,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/Android.bp b/services/devicepolicy/Android.bp
index 0505204..47790ce 100644
--- a/services/devicepolicy/Android.bp
+++ b/services/devicepolicy/Android.bp
@@ -3,7 +3,6 @@
srcs: ["java/**/*.java"],
libs: [
- "conscrypt",
"services.core",
],
}
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/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java
index a8b9b0c..85ca52e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java
@@ -66,7 +66,8 @@
super(TAG, mContext, mHandler, userId, componentName,
mConstants.DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC,
mConstants.DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE,
- mConstants.DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC);
+ mConstants.DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC,
+ mConstants.DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC);
}
@Override
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java
index 616c669..71fea02 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java
@@ -30,14 +30,17 @@
public class DevicePolicyConstants {
private static final String TAG = DevicePolicyManagerService.LOG_TAG;
- private static final String DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC_KEY
- = "das_died_service_reconnect_backoff_sec";
+ private static final String DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC_KEY =
+ "das_died_service_reconnect_backoff_sec";
- private static final String DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE_KEY
- = "das_died_service_reconnect_backoff_increase";
+ private static final String DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE_KEY =
+ "das_died_service_reconnect_backoff_increase";
- private static final String DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC_KEY
- = "das_died_service_reconnect_max_backoff_sec";
+ private static final String DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC_KEY =
+ "das_died_service_reconnect_max_backoff_sec";
+
+ private static final String DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC_KEY =
+ "das_died_service_stable_connection_threshold_sec";
/**
* The back-off before re-connecting, when a service binding died, due to the owner
@@ -55,6 +58,11 @@
*/
public final long DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC;
+ /**
+ * If a connection lasts more than this duration, we reset the re-connect back-off time.
+ */
+ public final long DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC;
+
private DevicePolicyConstants(String settings) {
final KeyValueListParser parser = new KeyValueListParser(',');
@@ -75,6 +83,10 @@
long dasDiedServiceReconnectMaxBackoffSec = parser.getLong(
DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC_KEY, TimeUnit.DAYS.toSeconds(1));
+ long dasDiedServiceStableConnectionThresholdSec = parser.getLong(
+ DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC_KEY,
+ TimeUnit.MINUTES.toSeconds(2));
+
// Set minimum: 5 seconds.
dasDiedServiceReconnectBackoffSec = Math.max(5, dasDiedServiceReconnectBackoffSec);
@@ -89,7 +101,8 @@
DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC = dasDiedServiceReconnectBackoffSec;
DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE = dasDiedServiceReconnectBackoffIncrease;
DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC = dasDiedServiceReconnectMaxBackoffSec;
-
+ DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC =
+ dasDiedServiceStableConnectionThresholdSec;
}
public static DevicePolicyConstants loadFromString(String settings) {
@@ -102,14 +115,18 @@
pw.print(prefix);
pw.print(" DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC: ");
- pw.println( DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC);
+ pw.println(DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC);
pw.print(prefix);
pw.print(" DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE: ");
- pw.println( DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE);
+ pw.println(DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE);
pw.print(prefix);
pw.print(" DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC: ");
- pw.println( DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC);
+ pw.println(DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC);
+
+ pw.print(prefix);
+ pw.print(" DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC: ");
+ pw.println(DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC);
}
}
diff --git a/services/net/java/android/net/dhcp/DhcpPacket.java b/services/net/java/android/net/dhcp/DhcpPacket.java
index 77a3e21..6ba7d94 100644
--- a/services/net/java/android/net/dhcp/DhcpPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpPacket.java
@@ -1,5 +1,8 @@
package android.net.dhcp;
+import static android.net.util.NetworkConstants.IPV4_MAX_MTU;
+import static android.net.util.NetworkConstants.IPV4_MIN_MTU;
+
import android.annotation.Nullable;
import android.net.DhcpResults;
import android.net.LinkAddress;
@@ -381,6 +384,26 @@
}
/**
+ * Returns whether a parameter is included in the parameter request list option of this packet.
+ *
+ * <p>If there is no parameter request list option in the packet, false is returned.
+ *
+ * @param paramId ID of the parameter, such as {@link #DHCP_MTU} or {@link #DHCP_HOST_NAME}.
+ */
+ public boolean hasRequestedParam(byte paramId) {
+ if (mRequestedParams == null) {
+ return false;
+ }
+
+ for (byte reqParam : mRequestedParams) {
+ if (reqParam == paramId) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
* Creates a new L3 packet (including IP header) containing the
* DHCP udp packet. This method relies upon the delegated method
* finishPacket() to insert the per-packet contents.
@@ -696,7 +719,11 @@
addTlv(buf, DHCP_ROUTER, mGateways);
addTlv(buf, DHCP_DNS_SERVER, mDnsServers);
addTlv(buf, DHCP_DOMAIN_NAME, mDomainName);
+ addTlv(buf, DHCP_HOST_NAME, mHostName);
addTlv(buf, DHCP_VENDOR_INFO, mVendorInfo);
+ if (mMtu != null && Short.toUnsignedInt(mMtu) >= IPV4_MIN_MTU) {
+ addTlv(buf, DHCP_MTU, mMtu);
+ }
}
/**
@@ -1259,7 +1286,8 @@
boolean broadcast, Inet4Address serverIpAddr, Inet4Address relayIp,
Inet4Address yourIp, byte[] mac, Integer timeout, Inet4Address netMask,
Inet4Address bcAddr, List<Inet4Address> gateways, List<Inet4Address> dnsServers,
- Inet4Address dhcpServerIdentifier, String domainName, boolean metered) {
+ Inet4Address dhcpServerIdentifier, String domainName, String hostname, boolean metered,
+ short mtu) {
DhcpPacket pkt = new DhcpOfferPacket(
transactionId, (short) 0, broadcast, serverIpAddr, relayIp,
INADDR_ANY /* clientIp */, yourIp, mac);
@@ -1267,9 +1295,11 @@
pkt.mDnsServers = dnsServers;
pkt.mLeaseTime = timeout;
pkt.mDomainName = domainName;
+ pkt.mHostName = hostname;
pkt.mServerIdentifier = dhcpServerIdentifier;
pkt.mSubnetMask = netMask;
pkt.mBroadcastAddress = bcAddr;
+ pkt.mMtu = mtu;
if (metered) {
pkt.mVendorInfo = VENDOR_INFO_ANDROID_METERED;
}
@@ -1283,7 +1313,8 @@
boolean broadcast, Inet4Address serverIpAddr, Inet4Address relayIp, Inet4Address yourIp,
Inet4Address requestClientIp, byte[] mac, Integer timeout, Inet4Address netMask,
Inet4Address bcAddr, List<Inet4Address> gateways, List<Inet4Address> dnsServers,
- Inet4Address dhcpServerIdentifier, String domainName, boolean metered) {
+ Inet4Address dhcpServerIdentifier, String domainName, String hostname, boolean metered,
+ short mtu) {
DhcpPacket pkt = new DhcpAckPacket(
transactionId, (short) 0, broadcast, serverIpAddr, relayIp, requestClientIp, yourIp,
mac);
@@ -1291,9 +1322,11 @@
pkt.mDnsServers = dnsServers;
pkt.mLeaseTime = timeout;
pkt.mDomainName = domainName;
+ pkt.mHostName = hostname;
pkt.mSubnetMask = netMask;
pkt.mServerIdentifier = dhcpServerIdentifier;
pkt.mBroadcastAddress = bcAddr;
+ pkt.mMtu = mtu;
if (metered) {
pkt.mVendorInfo = VENDOR_INFO_ANDROID_METERED;
}
diff --git a/services/net/java/android/net/dhcp/DhcpServer.java b/services/net/java/android/net/dhcp/DhcpServer.java
index 2b3d577..cee6fa9 100644
--- a/services/net/java/android/net/dhcp/DhcpServer.java
+++ b/services/net/java/android/net/dhcp/DhcpServer.java
@@ -20,6 +20,7 @@
import static android.net.NetworkUtils.getPrefixMaskAsInet4Address;
import static android.net.TrafficStats.TAG_SYSTEM_DHCP_SERVER;
import static android.net.dhcp.DhcpPacket.DHCP_CLIENT;
+import static android.net.dhcp.DhcpPacket.DHCP_HOST_NAME;
import static android.net.dhcp.DhcpPacket.DHCP_SERVER;
import static android.net.dhcp.DhcpPacket.ENCAP_BOOTP;
import static android.net.dhcp.DhcpPacket.INFINITE_LEASE;
@@ -46,6 +47,7 @@
import android.os.SystemClock;
import android.system.ErrnoException;
import android.system.Os;
+import android.text.TextUtils;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.HexDump;
@@ -350,6 +352,19 @@
return isEmpty(request.mClientIp) && (request.mBroadcast || isEmpty(lease.getNetAddr()));
}
+ /**
+ * Get the hostname from a lease if non-empty and requested in the incoming request.
+ * @param request The incoming request.
+ * @return The hostname, or null if not requested or empty.
+ */
+ @Nullable
+ private static String getHostnameIfRequested(@NonNull DhcpPacket request,
+ @NonNull DhcpLease lease) {
+ return request.hasRequestedParam(DHCP_HOST_NAME) && !TextUtils.isEmpty(lease.getHostname())
+ ? lease.getHostname()
+ : null;
+ }
+
private boolean transmitOffer(@NonNull DhcpPacket request, @NonNull DhcpLease lease,
@NonNull MacAddress clientMac) {
final boolean broadcastFlag = getBroadcastFlag(request, lease);
@@ -358,12 +373,14 @@
getPrefixMaskAsInet4Address(mServingParams.serverAddr.getPrefixLength());
final Inet4Address broadcastAddr = getBroadcastAddress(
mServingParams.getServerInet4Addr(), mServingParams.serverAddr.getPrefixLength());
+ final String hostname = getHostnameIfRequested(request, lease);
final ByteBuffer offerPacket = DhcpPacket.buildOfferPacket(
ENCAP_BOOTP, request.mTransId, broadcastFlag, mServingParams.getServerInet4Addr(),
request.mRelayIp, lease.getNetAddr(), request.mClientMac, timeout, prefixMask,
broadcastAddr, new ArrayList<>(mServingParams.defaultRouters),
new ArrayList<>(mServingParams.dnsServers),
- mServingParams.getServerInet4Addr(), null /* domainName */, mServingParams.metered);
+ mServingParams.getServerInet4Addr(), null /* domainName */, hostname,
+ mServingParams.metered, (short) mServingParams.linkMtu);
return transmitOfferOrAckPacket(offerPacket, request, lease, clientMac, broadcastFlag);
}
@@ -374,13 +391,15 @@
// transmitOffer above
final boolean broadcastFlag = getBroadcastFlag(request, lease);
final int timeout = getLeaseTimeout(lease);
+ final String hostname = getHostnameIfRequested(request, lease);
final ByteBuffer ackPacket = DhcpPacket.buildAckPacket(ENCAP_BOOTP, request.mTransId,
broadcastFlag, mServingParams.getServerInet4Addr(), request.mRelayIp,
lease.getNetAddr(), request.mClientIp, request.mClientMac, timeout,
mServingParams.getPrefixMaskAsAddress(), mServingParams.getBroadcastAddress(),
new ArrayList<>(mServingParams.defaultRouters),
new ArrayList<>(mServingParams.dnsServers),
- mServingParams.getServerInet4Addr(), null /* domainName */, mServingParams.metered);
+ mServingParams.getServerInet4Addr(), null /* domainName */, hostname,
+ mServingParams.metered, (short) mServingParams.linkMtu);
return transmitOfferOrAckPacket(ackPacket, request, lease, clientMac, broadcastFlag);
}
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/robotests/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java b/services/robotests/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
index 112e1e3..b771039 100644
--- a/services/robotests/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
+++ b/services/robotests/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
@@ -183,7 +183,6 @@
new Signature[] {new Signature("1234"), new Signature("5678")},
SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
File manifestFile = createFile(BACKUP_MANIFEST_FILENAME);
diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
new file mode 100644
index 0000000..6665070
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
@@ -0,0 +1,543 @@
+/*
+ * 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;
+
+import static androidx.test.InstrumentationRegistry.getContext;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.server.DeviceIdleController.LIGHT_STATE_ACTIVE;
+import static com.android.server.DeviceIdleController.LIGHT_STATE_IDLE;
+import static com.android.server.DeviceIdleController.LIGHT_STATE_IDLE_MAINTENANCE;
+import static com.android.server.DeviceIdleController.LIGHT_STATE_INACTIVE;
+import static com.android.server.DeviceIdleController.LIGHT_STATE_OVERRIDE;
+import static com.android.server.DeviceIdleController.LIGHT_STATE_PRE_IDLE;
+import static com.android.server.DeviceIdleController.LIGHT_STATE_WAITING_FOR_NETWORK;
+import static com.android.server.DeviceIdleController.STATE_ACTIVE;
+import static com.android.server.DeviceIdleController.STATE_IDLE;
+import static com.android.server.DeviceIdleController.STATE_IDLE_MAINTENANCE;
+import static com.android.server.DeviceIdleController.STATE_IDLE_PENDING;
+import static com.android.server.DeviceIdleController.STATE_INACTIVE;
+import static com.android.server.DeviceIdleController.STATE_LOCATING;
+import static com.android.server.DeviceIdleController.STATE_SENSING;
+import static com.android.server.DeviceIdleController.lightStateToString;
+import static com.android.server.DeviceIdleController.stateToString;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+
+import android.app.ActivityManagerInternal;
+import android.app.AlarmManager;
+import android.app.IActivityManager;
+import android.content.Context;
+import android.hardware.SensorManager;
+import android.location.LocationManager;
+import android.location.LocationProvider;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.PowerManagerInternal;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.net.NetworkPolicyManagerInternal;
+import com.android.server.wm.ActivityTaskManagerInternal;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+/**
+ * Tests for {@link com.android.server.DeviceIdleController}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class DeviceIdleControllerTest {
+ private DeviceIdleController mDeviceIdleController;
+ private AnyMotionDetectorForTest mAnyMotionDetector;
+ private AppStateTrackerForTest mAppStateTracker;
+
+ private MockitoSession mMockingSession;
+ @Mock
+ private PowerManager mPowerManager;
+ @Mock
+ private PowerManager.WakeLock mWakeLock;
+ @Mock
+ private AlarmManager mAlarmManager;
+ @Mock
+ private LocationManager mLocationManager;
+ @Mock
+ private IActivityManager mIActivityManager;
+
+ class InjectorForTest extends DeviceIdleController.Injector {
+
+ InjectorForTest(Context ctx) {
+ super(ctx);
+ }
+
+ @Override
+ AlarmManager getAlarmManager() {
+ return mAlarmManager;
+ }
+
+ @Override
+ AnyMotionDetector getAnyMotionDetector(Handler handler, SensorManager sm,
+ AnyMotionDetector.DeviceIdleCallback callback, float angleThreshold) {
+ return mAnyMotionDetector;
+ }
+
+ @Override
+ AppStateTracker getAppStateTracker(Context ctx, Looper loop) {
+ return mAppStateTracker;
+ }
+
+ @Override
+ ConnectivityService getConnectivityService() {
+ return null;
+ }
+
+ @Override
+ LocationManager getLocationManager() {
+ return mLocationManager;
+ }
+
+ @Override
+ DeviceIdleController.MyHandler getHandler(DeviceIdleController ctlr) {
+ return mock(DeviceIdleController.MyHandler.class);
+ }
+
+ @Override
+ PowerManager getPowerManager() {
+ return mPowerManager;
+ }
+ }
+
+ private class AnyMotionDetectorForTest extends AnyMotionDetector {
+ boolean isMonitoring = false;
+
+ AnyMotionDetectorForTest() {
+ super(mPowerManager, mock(Handler.class), mock(SensorManager.class),
+ mock(DeviceIdleCallback.class), 0.5f);
+ }
+
+ @Override
+ public void checkForAnyMotion() {
+ isMonitoring = true;
+ }
+
+ @Override
+ public void stop() {
+ isMonitoring = false;
+ }
+ }
+
+ private class AppStateTrackerForTest extends AppStateTracker {
+ AppStateTrackerForTest(Context ctx, Looper looper) {
+ super(ctx, looper);
+ }
+
+ @Override
+ public void onSystemServicesReady() {
+ // Do nothing.
+ }
+
+ @Override
+ IActivityManager injectIActivityManager() {
+ return mIActivityManager;
+ }
+ }
+
+ @Before
+ public void setUp() {
+ mMockingSession = mockitoSession()
+ .initMocks(this)
+ .strictness(Strictness.LENIENT)
+ .mockStatic(LocalServices.class)
+ .startMocking();
+ doReturn(mock(ActivityManagerInternal.class))
+ .when(() -> LocalServices.getService(ActivityManagerInternal.class));
+ doReturn(mock(ActivityTaskManagerInternal.class))
+ .when(() -> LocalServices.getService(ActivityTaskManagerInternal.class));
+ doReturn(mock(PowerManagerInternal.class))
+ .when(() -> LocalServices.getService(PowerManagerInternal.class));
+ doReturn(mock(NetworkPolicyManagerInternal.class))
+ .when(() -> LocalServices.getService(NetworkPolicyManagerInternal.class));
+ when(mPowerManager.newWakeLock(anyInt(), anyString())).thenReturn(mWakeLock);
+ doNothing().when(mWakeLock).acquire();
+ mAppStateTracker = new AppStateTrackerForTest(getContext(), Looper.getMainLooper());
+ mAnyMotionDetector = new AnyMotionDetectorForTest();
+ mDeviceIdleController = new DeviceIdleController(getContext(),
+ new InjectorForTest(getContext()));
+ spyOn(mDeviceIdleController);
+ doNothing().when(mDeviceIdleController).publishBinderService(any(), any());
+ mDeviceIdleController.onStart();
+ mDeviceIdleController.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
+ mDeviceIdleController.setDeepEnabledForTest(true);
+ mDeviceIdleController.setLightEnabledForTest(true);
+ }
+
+ @After
+ public void tearDown() {
+ if (mMockingSession != null) {
+ mMockingSession.finishMocking();
+ }
+ // DeviceIdleController adds this to LocalServices in the constructor, so we have to remove
+ // it after each test, otherwise, subsequent tests will fail.
+ LocalServices.removeServiceForTest(AppStateTracker.class);
+ }
+
+ @Test
+ public void testUpdateInteractivityLocked() {
+ doReturn(false).when(mPowerManager).isInteractive();
+ mDeviceIdleController.updateInteractivityLocked();
+ assertFalse(mDeviceIdleController.isScreenOn());
+
+ // Make sure setting false when screen is already off doesn't change anything.
+ doReturn(false).when(mPowerManager).isInteractive();
+ mDeviceIdleController.updateInteractivityLocked();
+ assertFalse(mDeviceIdleController.isScreenOn());
+
+ // Test changing from screen off to screen on.
+ doReturn(true).when(mPowerManager).isInteractive();
+ mDeviceIdleController.updateInteractivityLocked();
+ assertTrue(mDeviceIdleController.isScreenOn());
+
+ // Make sure setting true when screen is already on doesn't change anything.
+ doReturn(true).when(mPowerManager).isInteractive();
+ mDeviceIdleController.updateInteractivityLocked();
+ assertTrue(mDeviceIdleController.isScreenOn());
+
+ // Test changing from screen on to screen off.
+ doReturn(false).when(mPowerManager).isInteractive();
+ mDeviceIdleController.updateInteractivityLocked();
+ assertFalse(mDeviceIdleController.isScreenOn());
+ }
+
+ @Test
+ public void testUpdateChargingLocked() {
+ mDeviceIdleController.updateChargingLocked(false);
+ assertFalse(mDeviceIdleController.isCharging());
+
+ // Make sure setting false when charging is already off doesn't change anything.
+ mDeviceIdleController.updateChargingLocked(false);
+ assertFalse(mDeviceIdleController.isCharging());
+
+ // Test changing from charging off to charging on.
+ mDeviceIdleController.updateChargingLocked(true);
+ assertTrue(mDeviceIdleController.isCharging());
+
+ // Make sure setting true when charging is already on doesn't change anything.
+ mDeviceIdleController.updateChargingLocked(true);
+ assertTrue(mDeviceIdleController.isCharging());
+
+ // Test changing from charging on to charging off.
+ mDeviceIdleController.updateChargingLocked(false);
+ assertFalse(mDeviceIdleController.isCharging());
+ }
+
+ @Test
+ public void testStateActiveToStateInactive_ConditionsNotMet() {
+ mDeviceIdleController.becomeActiveLocked("testing", 0);
+ verifyStateConditions(STATE_ACTIVE);
+
+ // State should stay ACTIVE with screen on and charging.
+ setChargingOn(true);
+ setScreenOn(true);
+
+ mDeviceIdleController.becomeInactiveIfAppropriateLocked();
+ verifyStateConditions(STATE_ACTIVE);
+
+ // State should stay ACTIVE with charging on.
+ setChargingOn(true);
+ setScreenOn(false);
+
+ mDeviceIdleController.becomeInactiveIfAppropriateLocked();
+ verifyStateConditions(STATE_ACTIVE);
+
+ // State should stay ACTIVE with screen on.
+ // Note the different operation order here makes sure the state doesn't change before test.
+ setScreenOn(true);
+ setChargingOn(false);
+
+ mDeviceIdleController.becomeInactiveIfAppropriateLocked();
+ verifyStateConditions(STATE_ACTIVE);
+ }
+
+ @Test
+ public void testLightStateActiveToLightStateInactive_ConditionsNotMet() {
+ mDeviceIdleController.becomeActiveLocked("testing", 0);
+ verifyLightStateConditions(LIGHT_STATE_ACTIVE);
+
+ // State should stay ACTIVE with screen on and charging.
+ setChargingOn(true);
+ setScreenOn(true);
+
+ mDeviceIdleController.becomeInactiveIfAppropriateLocked();
+ verifyLightStateConditions(LIGHT_STATE_ACTIVE);
+
+ // State should stay ACTIVE with charging on.
+ setChargingOn(true);
+ setScreenOn(false);
+
+ mDeviceIdleController.becomeInactiveIfAppropriateLocked();
+ verifyLightStateConditions(LIGHT_STATE_ACTIVE);
+
+ // State should stay ACTIVE with screen on.
+ // Note the different operation order here makes sure the state doesn't change before test.
+ setScreenOn(true);
+ setChargingOn(false);
+
+ mDeviceIdleController.becomeInactiveIfAppropriateLocked();
+ verifyLightStateConditions(LIGHT_STATE_ACTIVE);
+ }
+
+ @Test
+ public void testStateActiveToStateInactive_ConditionsMet() {
+ mDeviceIdleController.becomeActiveLocked("testing", 0);
+ verifyStateConditions(STATE_ACTIVE);
+
+ setChargingOn(false);
+ setScreenOn(false);
+
+ mDeviceIdleController.becomeInactiveIfAppropriateLocked();
+ verifyStateConditions(STATE_INACTIVE);
+ }
+
+ @Test
+ public void testLightStateActiveToLightStateInactive_ConditionsMet() {
+ mDeviceIdleController.becomeActiveLocked("testing", 0);
+ verifyLightStateConditions(LIGHT_STATE_ACTIVE);
+
+ setChargingOn(false);
+ setScreenOn(false);
+
+ mDeviceIdleController.becomeInactiveIfAppropriateLocked();
+ verifyLightStateConditions(LIGHT_STATE_INACTIVE);
+ }
+
+ @Test
+ public void testStepIdleStateLocked_InvalidStates() {
+ mDeviceIdleController.becomeActiveLocked("testing", 0);
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ // mDeviceIdleController.stepIdleStateLocked doesn't handle the ACTIVE case, so the state
+ // should stay as ACTIVE.
+ verifyStateConditions(STATE_ACTIVE);
+ }
+
+ @Test
+ public void testStepIdleStateLocked_ValidStates_NoLocationManager() {
+ mDeviceIdleController.setLocationManagerForTest(null);
+ // Make sure the controller doesn't think there's a wake-from-idle alarm coming soon.
+ doReturn(Long.MAX_VALUE).when(mAlarmManager).getNextWakeFromIdleTime();
+ // Set state to INACTIVE.
+ mDeviceIdleController.becomeActiveLocked("testing", 0);
+ setChargingOn(false);
+ setScreenOn(false);
+ verifyStateConditions(STATE_INACTIVE);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_IDLE_PENDING);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_SENSING);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ // No location manager, so SENSING should go straight to IDLE.
+ verifyStateConditions(STATE_IDLE);
+
+ // Should just alternate between IDLE and IDLE_MAINTENANCE now.
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_IDLE_MAINTENANCE);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_IDLE);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_IDLE_MAINTENANCE);
+ }
+
+ @Test
+ public void testStepIdleStateLocked_ValidStates_WithLocationManager_NoProviders() {
+ // Make sure the controller doesn't think there's a wake-from-idle alarm coming soon.
+ doReturn(Long.MAX_VALUE).when(mAlarmManager).getNextWakeFromIdleTime();
+ // Set state to INACTIVE.
+ mDeviceIdleController.becomeActiveLocked("testing", 0);
+ setChargingOn(false);
+ setScreenOn(false);
+ verifyStateConditions(STATE_INACTIVE);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_IDLE_PENDING);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_SENSING);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ // Location manager exists but there isn't a network or GPS provider,
+ // so SENSING should go straight to IDLE.
+ verifyStateConditions(STATE_IDLE);
+
+ // Should just alternate between IDLE and IDLE_MAINTENANCE now.
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_IDLE_MAINTENANCE);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_IDLE);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_IDLE_MAINTENANCE);
+ }
+
+ @Test
+ public void testStepIdleStateLocked_ValidStates_WithLocationManager_WithProviders() {
+ doReturn(mock(LocationProvider.class)).when(mLocationManager).getProvider(anyString());
+ // Make sure the controller doesn't think there's a wake-from-idle alarm coming soon.
+ // TODO: add tests for when there's a wake-from-idle alarm coming soon.
+ doReturn(Long.MAX_VALUE).when(mAlarmManager).getNextWakeFromIdleTime();
+ // Set state to INACTIVE.
+ mDeviceIdleController.becomeActiveLocked("testing", 0);
+ setChargingOn(false);
+ setScreenOn(false);
+ verifyStateConditions(STATE_INACTIVE);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_IDLE_PENDING);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_SENSING);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ // Location manager exists with a provider, so SENSING should go to LOCATING.
+ verifyStateConditions(STATE_LOCATING);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_IDLE);
+
+ // Should just alternate between IDLE and IDLE_MAINTENANCE now.
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_IDLE_MAINTENANCE);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_IDLE);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_IDLE_MAINTENANCE);
+ }
+
+ private void setChargingOn(boolean on) {
+ mDeviceIdleController.updateChargingLocked(on);
+ }
+
+ private void setScreenOn(boolean on) {
+ doReturn(on).when(mPowerManager).isInteractive();
+ mDeviceIdleController.updateInteractivityLocked();
+ }
+
+ private void verifyStateConditions(int expectedState) {
+ int curState = mDeviceIdleController.getState();
+ assertEquals(
+ "Expected " + stateToString(expectedState) + " but was " + stateToString(curState),
+ expectedState, curState);
+
+ switch (expectedState) {
+ case STATE_ACTIVE:
+ assertFalse(mDeviceIdleController.mMotionListener.isActive());
+ assertFalse(mAnyMotionDetector.isMonitoring);
+ break;
+ case STATE_INACTIVE:
+ assertFalse(mDeviceIdleController.mMotionListener.isActive());
+ assertFalse(mAnyMotionDetector.isMonitoring);
+ assertFalse(mDeviceIdleController.isCharging());
+ assertFalse(mDeviceIdleController.isScreenOn());
+ break;
+ case STATE_IDLE_PENDING:
+ assertTrue(mDeviceIdleController.mMotionListener.isActive());
+ assertFalse(mAnyMotionDetector.isMonitoring);
+ assertFalse(mDeviceIdleController.isCharging());
+ assertFalse(mDeviceIdleController.isScreenOn());
+ break;
+ case STATE_SENSING:
+ assertTrue(mDeviceIdleController.mMotionListener.isActive());
+ assertTrue(mAnyMotionDetector.isMonitoring);
+ assertFalse(mDeviceIdleController.isCharging());
+ assertFalse(mDeviceIdleController.isScreenOn());
+ break;
+ case STATE_LOCATING:
+ assertTrue(mDeviceIdleController.mMotionListener.isActive());
+ assertTrue(mAnyMotionDetector.isMonitoring);
+ assertFalse(mDeviceIdleController.isCharging());
+ assertFalse(mDeviceIdleController.isScreenOn());
+ break;
+ case STATE_IDLE:
+ assertTrue(mDeviceIdleController.mMotionListener.isActive());
+ assertFalse(mAnyMotionDetector.isMonitoring);
+ assertFalse(mDeviceIdleController.isCharging());
+ assertFalse(mDeviceIdleController.isScreenOn());
+ // Light state should be OVERRIDE at this point.
+ verifyLightStateConditions(LIGHT_STATE_OVERRIDE);
+ break;
+ case STATE_IDLE_MAINTENANCE:
+ assertTrue(mDeviceIdleController.mMotionListener.isActive());
+ assertFalse(mAnyMotionDetector.isMonitoring);
+ assertFalse(mDeviceIdleController.isCharging());
+ assertFalse(mDeviceIdleController.isScreenOn());
+ break;
+ default:
+ fail("Conditions for " + stateToString(expectedState) + " unknown.");
+ }
+ }
+
+ private void verifyLightStateConditions(int expectedLightState) {
+ int curLightState = mDeviceIdleController.getLightState();
+ assertEquals(
+ "Expected " + lightStateToString(expectedLightState)
+ + " but was " + lightStateToString(curLightState),
+ expectedLightState, curLightState);
+
+ switch (expectedLightState) {
+ case LIGHT_STATE_ACTIVE:
+ assertTrue(
+ mDeviceIdleController.isCharging() || mDeviceIdleController.isScreenOn());
+ break;
+ case LIGHT_STATE_INACTIVE:
+ case LIGHT_STATE_PRE_IDLE:
+ case LIGHT_STATE_IDLE:
+ case LIGHT_STATE_WAITING_FOR_NETWORK:
+ case LIGHT_STATE_IDLE_MAINTENANCE:
+ case LIGHT_STATE_OVERRIDE:
+ assertFalse(mDeviceIdleController.isCharging());
+ assertFalse(mDeviceIdleController.isScreenOn());
+ break;
+ default:
+ fail("Conditions for " + lightStateToString(expectedLightState) + " unknown.");
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/PersistentConnectionTest.java b/services/tests/mockingservicestests/src/com/android/server/am/PersistentConnectionTest.java
similarity index 79%
rename from services/tests/servicestests/src/com/android/server/am/PersistentConnectionTest.java
rename to services/tests/mockingservicestests/src/com/android/server/am/PersistentConnectionTest.java
index 39cab8d..26e77eb 100644
--- a/services/tests/servicestests/src/com/android/server/am/PersistentConnectionTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/PersistentConnectionTest.java
@@ -43,6 +43,8 @@
@SmallTest
public class PersistentConnectionTest extends AndroidTestCase {
+ private static final String TAG = "PersistentConnectionTest";
+
private static class MyConnection extends PersistentConnection<IDeviceAdminService> {
public long uptimeMillis = 12345;
@@ -50,9 +52,11 @@
public MyConnection(String tag, Context context, Handler handler, int userId,
ComponentName componentName, long rebindBackoffSeconds,
- double rebindBackoffIncrease, long rebindMaxBackoffSeconds) {
+ double rebindBackoffIncrease, long rebindMaxBackoffSeconds,
+ long resetBackoffDelay) {
super(tag, context, handler, userId, componentName,
- rebindBackoffSeconds, rebindBackoffIncrease, rebindMaxBackoffSeconds);
+ rebindBackoffSeconds, rebindBackoffIncrease, rebindMaxBackoffSeconds,
+ resetBackoffDelay);
}
@Override
@@ -113,10 +117,11 @@
final ComponentName cn = ComponentName.unflattenFromString("a.b.c/def");
final Handler handler = new Handler(Looper.getMainLooper());
- final MyConnection conn = new MyConnection("tag", context, handler, userId, cn,
+ final MyConnection conn = new MyConnection(TAG, context, handler, userId, cn,
/* rebindBackoffSeconds= */ 5,
/* rebindBackoffIncrease= */ 1.5,
- /* rebindMaxBackoffSeconds= */ 11);
+ /* rebindMaxBackoffSeconds= */ 11,
+ /* resetBackoffDelay= */ 999);
assertFalse(conn.isBound());
assertFalse(conn.isConnected());
@@ -315,10 +320,11 @@
final ComponentName cn = ComponentName.unflattenFromString("a.b.c/def");
final Handler handler = new Handler(Looper.getMainLooper());
- final MyConnection conn = new MyConnection("tag", context, handler, userId, cn,
+ final MyConnection conn = new MyConnection(TAG, context, handler, userId, cn,
/* rebindBackoffSeconds= */ 5,
/* rebindBackoffIncrease= */ 1.5,
- /* rebindMaxBackoffSeconds= */ 11);
+ /* rebindMaxBackoffSeconds= */ 11,
+ /* resetBackoffDelay= */ 999);
when(context.bindServiceAsUser(any(Intent.class), any(ServiceConnection.class), anyInt(),
any(Handler.class), any(UserHandle.class)))
@@ -356,4 +362,78 @@
assertFalse(conn.isBound());
assertFalse(conn.shouldBeBoundForTest());
}
+
+ public void testResetBackoff() {
+ final Context context = mock(Context.class);
+ final int userId = 11;
+ final ComponentName cn = ComponentName.unflattenFromString("a.b.c/def");
+ final Handler handler = new Handler(Looper.getMainLooper());
+
+ final MyConnection conn = new MyConnection(TAG, context, handler, userId, cn,
+ /* rebindBackoffSeconds= */ 5,
+ /* rebindBackoffIncrease= */ 1.5,
+ /* rebindMaxBackoffSeconds= */ 11,
+ /* resetBackoffDelay= */ 20);
+
+ when(context.bindServiceAsUser(any(Intent.class), any(ServiceConnection.class), anyInt(),
+ any(Handler.class), any(UserHandle.class)))
+ .thenReturn(true);
+
+ // Bind.
+ conn.bind();
+
+ assertTrue(conn.isBound());
+ assertTrue(conn.shouldBeBoundForTest());
+ assertFalse(conn.isRebindScheduled());
+
+ conn.elapse(1000);
+
+ // Then the binding is "died"...
+ conn.getServiceConnectionForTest().onBindingDied(cn);
+
+ assertFalse(conn.isBound());
+ assertTrue(conn.shouldBeBoundForTest());
+ assertFalse(conn.isConnected());
+ assertNull(conn.getServiceBinder());
+ assertTrue(conn.isRebindScheduled());
+
+ assertEquals(7500, conn.getNextBackoffMsForTest());
+
+ assertEquals(
+ Arrays.asList(Pair.create(conn.getBindForBackoffRunnableForTest(),
+ conn.uptimeMillis + 5000)),
+ conn.scheduledRunnables);
+
+ // 5000 ms later...
+ conn.elapse(5000);
+
+ assertTrue(conn.isBound());
+ assertTrue(conn.shouldBeBoundForTest());
+ assertFalse(conn.isConnected());
+ assertNull(conn.getServiceBinder());
+ assertFalse(conn.isRebindScheduled());
+
+ assertEquals(7500, conn.getNextBackoffMsForTest());
+
+ // Connected.
+ conn.getServiceConnectionForTest().onServiceConnected(cn,
+ new IDeviceAdminService.Stub() {});
+
+ assertTrue(conn.isBound());
+ assertTrue(conn.shouldBeBoundForTest());
+ assertTrue(conn.isConnected());
+ assertNotNull(conn.getServiceBinder());
+ assertFalse(conn.isRebindScheduled());
+
+ assertEquals(7500, conn.getNextBackoffMsForTest());
+
+ assertEquals(
+ Arrays.asList(Pair.create(conn.getStableCheckRunnableForTest(),
+ conn.uptimeMillis + 20000)),
+ conn.scheduledRunnables);
+
+ conn.elapse(20000);
+
+ assertEquals(5000, conn.getNextBackoffMsForTest());
+ }
}
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/Android.mk b/services/tests/servicestests/Android.mk
index 80307ee..2957267 100644
--- a/services/tests/servicestests/Android.mk
+++ b/services/tests/servicestests/Android.mk
@@ -58,6 +58,7 @@
libbacktrace \
libbase \
libbinder \
+ libbinderthreadstate \
libc++ \
libcutils \
liblog \
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256-lineage-2-signers b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256-lineage-2-signers
new file mode 100644
index 0000000..509ea3b
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256-lineage-2-signers
Binary files differ
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256-lineage-3-signers b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256-lineage-3-signers
new file mode 100644
index 0000000..bee71c0
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256-lineage-3-signers
Binary files differ
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256.pk8 b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256.pk8
new file mode 100644
index 0000000..f781c30
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256.pk8
Binary files differ
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256.x509.der b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256.x509.der
new file mode 100644
index 0000000..e611e3d
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256.x509.der
Binary files differ
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256_2.pk8 b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256_2.pk8
new file mode 100644
index 0000000..5e73f27
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256_2.pk8
Binary files differ
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256_2.x509.der b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256_2.x509.der
new file mode 100644
index 0000000..7723bea
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256_2.x509.der
Binary files differ
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256_3.pk8 b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256_3.pk8
new file mode 100644
index 0000000..d7309dd
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256_3.pk8
Binary files differ
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256_3.x509.der b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256_3.x509.der
new file mode 100644
index 0000000..cc82af9
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256_3.x509.der
Binary files differ
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/README b/services/tests/servicestests/assets/PackageSignaturesTest/xml/README
new file mode 100644
index 0000000..43d5bb8
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/README
@@ -0,0 +1,58 @@
+The XML files in this directory are taken from the packages tag of a test APK signed with the
+certificates and keys under the certs/ directory. To recreate the XML files run the following:
+
+1. Build the test APK:
+mmm -j cts/hostsidetests/appsecurity/test-apps/tinyapp/
+
+2. Sign the APK with the first signer:
+apksigner sign --in ${OUT}/data/app/CtsPkgInstallTinyApp/CtsPkgInstallTinyApp.apk --out test.apk \
+ --cert certs/ec-p256.x509.der --key certs/ec-p256.pk8
+
+3. Install the APK on a device:
+adb install test.apk
+
+4. Pull the packages.xml file containing the new entry for the APK from the device:
+adb pull /data/system/packages.xml
+
+5. Search the packages.xml file for the package name 'android.appsecurity.cts.tinyapp'. Following is
+ the full entry when the APK is signed as above:
+
+ <package name="android.appsecurity.cts.tinyapp" codePath="/data/app/android.appsecurity.cts.tiny
+ app-4ix3umoWct_iD26jQ03Z_g==" nativeLibraryPath="/data/app/android.appsecurity.cts.tinyapp-4ix3u
+ moWct_iD26jQ03Z_g==/lib" publicFlags="805879364" privateFlags="0" ft="1663710dd00" it="1663710de
+ 41" ut="1663710de41" version="10" userId="10051">
+ <sigs count="1" schemeVersion="3">
+ <cert index="16" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d
+ 04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433
+ 303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d0201
+ 06082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2
+ b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d
+ 0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b
+ 30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d04030203490030
+ 46022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea48297
+ 99c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" />
+ </sigs>
+ <proper-signing-keyset identifier="480" />
+ </package>
+
+The PackageSignatures#readXml and writeXml methods read and write everything within the sigs tag.
+The tags and attributes within the sigs tag can be modified and used to verify various good and
+error paths for the PackageSignaturesTest.
+
+Step 2 can be modified to sign with multiple signers by running one of the following commands:
+
+- To sign with two signers in the lineage (after the signing key has been rotated once):
+apksigner sign --in ${OUT}/data/app/CtsPkgInstallTinyApp/CtsPkgInstallTinyApp.apk --out test.apk \
+ --cert certs/ec-p256.x509.der --key certs/ec-p256.pk8 --next-signer --cert \
+ certs/ec-p256_2.x509.der --key certs/ec-p256_2.pk8 --lineage certs/ec-p256-lineage-2-signers
+
+- To sign with three signers in the lineage (after the second key rotation):
+apksigner sign --in ${OUT}/data/app/CtsPkgInstallTinyApp/CtsPkgInstallTinyApp.apk --out test.apk \
+ --cert certs/ec-p256.x509.der --key certs/ec-p256.pk8 --next-signer --cert \
+ certs/ec-p256_3.x509.der --key certs/ec-p256_3.pk8 --lineage certs/ec-p256-lineage-3-signers
+
+- To sign with two distinct signers (NOTE: The V3 signature scheme only supports a single signer,
+ so this method can only be used with signature schemes V1 and V2):
+apksigner sign --in ${OUT}/data/app/CtsPkgInstallTinyApp/CtsPkgInstallTinyApp.apk --out test.apk \
+ --cert certs/ec-p256.x509.der --key certs/ec-p256.pk8 --next-signer --cert \
+ certs/ec-p256_3.x509.der --key certs/ec-p256_3.pk8 --v3-signing-enabled false
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-extra-cert-tag.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-extra-cert-tag.xml
new file mode 100644
index 0000000..4d55bad
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-extra-cert-tag.xml
@@ -0,0 +1,5 @@
+ <sigs count="1" schemeVersion="3">
+ <cert index="0" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" />
+ <cert index="0" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" />
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-invalid-cert-index.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-invalid-cert-index.xml
new file mode 100644
index 0000000..f7882b1
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-invalid-cert-index.xml
@@ -0,0 +1,4 @@
+ <sigs count="1" schemeVersion="3">
+ <cert index="x" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" />
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-invalid-cert-key.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-invalid-cert-key.xml
new file mode 100644
index 0000000..af2c293
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-invalid-cert-key.xml
@@ -0,0 +1,4 @@
+ <sigs count="1" schemeVersion="3">
+ <cert index="0" key="082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" />
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-invalid-public-key-cert-key.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-invalid-public-key-cert-key.xml
new file mode 100644
index 0000000..893402d
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-invalid-public-key-cert-key.xml
@@ -0,0 +1,4 @@
+ <sigs count="1" schemeVersion="1">
+ <cert index="0" key="4082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" />
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-invalid-tag.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-invalid-tag.xml
new file mode 100644
index 0000000..1f81dac
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-invalid-tag.xml
@@ -0,0 +1,5 @@
+ <sigs count="1" schemeVersion="3">
+ <cert index="0" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" />
+ <invalid />
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-missing-cert-index.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-missing-cert-index.xml
new file mode 100644
index 0000000..c38e4d9
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-missing-cert-index.xml
@@ -0,0 +1,4 @@
+ <sigs count="1" schemeVersion="3">
+ <cert key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" />
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-missing-cert-key.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-missing-cert-key.xml
new file mode 100644
index 0000000..8e8cbcf
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-missing-cert-key.xml
@@ -0,0 +1,4 @@
+ <sigs count="1" schemeVersion="3">
+ <cert index="0" />
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-missing-cert-tag.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-missing-cert-tag.xml
new file mode 100644
index 0000000..57e96a8
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-missing-cert-tag.xml
@@ -0,0 +1,3 @@
+ <sigs count="1" schemeVersion="3">
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-missing-scheme-version.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-missing-scheme-version.xml
new file mode 100644
index 0000000..d9f7a5f
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-missing-scheme-version.xml
@@ -0,0 +1,4 @@
+ <sigs count="1">
+ <cert index="0" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" />
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-missing-sigs-count.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-missing-sigs-count.xml
new file mode 100644
index 0000000..4eefdd9
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-missing-sigs-count.xml
@@ -0,0 +1,4 @@
+ <sigs schemeVersion="3">
+ <cert index="0" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" />
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-previous-cert.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-previous-cert.xml
new file mode 100644
index 0000000..2aeeb71
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-previous-cert.xml
@@ -0,0 +1,4 @@
+ <sigs count="1" schemeVersion="2">
+ <cert index="0" />
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer.xml
new file mode 100644
index 0000000..14471f8
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer.xml
@@ -0,0 +1,4 @@
+ <sigs count="1" schemeVersion="1">
+ <cert index="0" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" />
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/three-signers-in-lineage-invalid-pastSigs-count.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/three-signers-in-lineage-invalid-pastSigs-count.xml
new file mode 100644
index 0000000..2b2e383
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/three-signers-in-lineage-invalid-pastSigs-count.xml
@@ -0,0 +1,9 @@
+ <sigs count="1" schemeVersion="3">
+ <cert index="0" key="3082016e30820115a0030201020209008394f5cad16a89a7300a06082a8648ce3d04030230143112301006035504030c0965632d703235365f32301e170d3138303731343030303532365a170d3238303731313030303532365a30143112301006035504030c0965632d703235365f333059301306072a8648ce3d020106082a8648ce3d03010703420004f31e62430e9db6fc5928d975fc4e47419bacfcb2e07c89299e6cd7e344dd21adfd308d58cb49a1a2a3fecacceea4862069f30be1643bcc255040d8089dfb3743a350304e301d0603551d0e041604146f8d0828b13efaf577fc86b0e99fa3e54bcbcff0301f0603551d230418301680147991d92b0208fc448bf506d4efc9fff428cb5e5f300c0603551d13040530030101ff300a06082a8648ce3d04030203470030440220256bdaa2784c273e4cc291a595a46779dee9de9044dc9f7ab820309567df9fe902201a4ad8c69891b5a8c47434fe9540ed1f4979b5fad3483f3fa04d5677355a579e" />
+ <pastSigs count="x">
+ <cert index="1" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" flags="3" />
+ <cert index="2" key="3082016d30820113a0030201020209008855bd1dd2b2b225300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3138303731333137343135315a170d3238303731303137343135315a30143112301006035504030c0965632d703235365f323059301306072a8648ce3d020106082a8648ce3d030107034200041d4cca0472ad97ee3cecef0da93d62b450c6788333b36e7553cde9f74ab5df00bbba6ba950e68461d70bbc271b62151dad2de2bf6203cd2076801c7a9d4422e1a350304e301d0603551d0e041604147991d92b0208fc448bf506d4efc9fff428cb5e5f301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d040302034800304502202769abb1b49fc2f53479c4ae92a6631dabfd522c9acb0bba2b43ebeb99c63011022100d260fb1d1f176cf9b7fa60098bfd24319f4905a3e5fda100a6fe1a2ab19ff09e" flags="7" />
+ <cert index="0" flags="23" />
+ </pastSigs>
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/three-signers-in-lineage-missing-pastSigs-cert-tag.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/three-signers-in-lineage-missing-pastSigs-cert-tag.xml
new file mode 100644
index 0000000..f992104
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/three-signers-in-lineage-missing-pastSigs-cert-tag.xml
@@ -0,0 +1,8 @@
+ <sigs count="1" schemeVersion="3">
+ <cert index="0" key="3082016e30820115a0030201020209008394f5cad16a89a7300a06082a8648ce3d04030230143112301006035504030c0965632d703235365f32301e170d3138303731343030303532365a170d3238303731313030303532365a30143112301006035504030c0965632d703235365f333059301306072a8648ce3d020106082a8648ce3d03010703420004f31e62430e9db6fc5928d975fc4e47419bacfcb2e07c89299e6cd7e344dd21adfd308d58cb49a1a2a3fecacceea4862069f30be1643bcc255040d8089dfb3743a350304e301d0603551d0e041604146f8d0828b13efaf577fc86b0e99fa3e54bcbcff0301f0603551d230418301680147991d92b0208fc448bf506d4efc9fff428cb5e5f300c0603551d13040530030101ff300a06082a8648ce3d04030203470030440220256bdaa2784c273e4cc291a595a46779dee9de9044dc9f7ab820309567df9fe902201a4ad8c69891b5a8c47434fe9540ed1f4979b5fad3483f3fa04d5677355a579e" />
+ <pastSigs count="3">
+ <cert index="1" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" flags="3" />
+ <cert index="0" flags="23" />
+ </pastSigs>
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/three-signers-in-lineage-missing-pastSigs-count.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/three-signers-in-lineage-missing-pastSigs-count.xml
new file mode 100644
index 0000000..6ef0fe5
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/three-signers-in-lineage-missing-pastSigs-count.xml
@@ -0,0 +1,9 @@
+ <sigs count="1" schemeVersion="3">
+ <cert index="0" key="3082016e30820115a0030201020209008394f5cad16a89a7300a06082a8648ce3d04030230143112301006035504030c0965632d703235365f32301e170d3138303731343030303532365a170d3238303731313030303532365a30143112301006035504030c0965632d703235365f333059301306072a8648ce3d020106082a8648ce3d03010703420004f31e62430e9db6fc5928d975fc4e47419bacfcb2e07c89299e6cd7e344dd21adfd308d58cb49a1a2a3fecacceea4862069f30be1643bcc255040d8089dfb3743a350304e301d0603551d0e041604146f8d0828b13efaf577fc86b0e99fa3e54bcbcff0301f0603551d230418301680147991d92b0208fc448bf506d4efc9fff428cb5e5f300c0603551d13040530030101ff300a06082a8648ce3d04030203470030440220256bdaa2784c273e4cc291a595a46779dee9de9044dc9f7ab820309567df9fe902201a4ad8c69891b5a8c47434fe9540ed1f4979b5fad3483f3fa04d5677355a579e" />
+ <pastSigs>
+ <cert index="1" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" flags="3" />
+ <cert index="2" key="3082016d30820113a0030201020209008855bd1dd2b2b225300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3138303731333137343135315a170d3238303731303137343135315a30143112301006035504030c0965632d703235365f323059301306072a8648ce3d020106082a8648ce3d030107034200041d4cca0472ad97ee3cecef0da93d62b450c6788333b36e7553cde9f74ab5df00bbba6ba950e68461d70bbc271b62151dad2de2bf6203cd2076801c7a9d4422e1a350304e301d0603551d0e041604147991d92b0208fc448bf506d4efc9fff428cb5e5f301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d040302034800304502202769abb1b49fc2f53479c4ae92a6631dabfd522c9acb0bba2b43ebeb99c63011022100d260fb1d1f176cf9b7fa60098bfd24319f4905a3e5fda100a6fe1a2ab19ff09e" flags="7" />
+ <cert index="0" flags="23" />
+ </pastSigs>
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/three-signers-in-lineage-missing-scheme-version.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/three-signers-in-lineage-missing-scheme-version.xml
new file mode 100644
index 0000000..d98573d
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/three-signers-in-lineage-missing-scheme-version.xml
@@ -0,0 +1,9 @@
+ <sigs count="1">
+ <cert index="0" key="3082016e30820115a0030201020209008394f5cad16a89a7300a06082a8648ce3d04030230143112301006035504030c0965632d703235365f32301e170d3138303731343030303532365a170d3238303731313030303532365a30143112301006035504030c0965632d703235365f333059301306072a8648ce3d020106082a8648ce3d03010703420004f31e62430e9db6fc5928d975fc4e47419bacfcb2e07c89299e6cd7e344dd21adfd308d58cb49a1a2a3fecacceea4862069f30be1643bcc255040d8089dfb3743a350304e301d0603551d0e041604146f8d0828b13efaf577fc86b0e99fa3e54bcbcff0301f0603551d230418301680147991d92b0208fc448bf506d4efc9fff428cb5e5f300c0603551d13040530030101ff300a06082a8648ce3d04030203470030440220256bdaa2784c273e4cc291a595a46779dee9de9044dc9f7ab820309567df9fe902201a4ad8c69891b5a8c47434fe9540ed1f4979b5fad3483f3fa04d5677355a579e" />
+ <pastSigs count="3">
+ <cert index="1" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" flags="3" />
+ <cert index="2" key="3082016d30820113a0030201020209008855bd1dd2b2b225300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3138303731333137343135315a170d3238303731303137343135315a30143112301006035504030c0965632d703235365f323059301306072a8648ce3d020106082a8648ce3d030107034200041d4cca0472ad97ee3cecef0da93d62b450c6788333b36e7553cde9f74ab5df00bbba6ba950e68461d70bbc271b62151dad2de2bf6203cd2076801c7a9d4422e1a350304e301d0603551d0e041604147991d92b0208fc448bf506d4efc9fff428cb5e5f301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d040302034800304502202769abb1b49fc2f53479c4ae92a6631dabfd522c9acb0bba2b43ebeb99c63011022100d260fb1d1f176cf9b7fa60098bfd24319f4905a3e5fda100a6fe1a2ab19ff09e" flags="7" />
+ <cert index="0" flags="23" />
+ </pastSigs>
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/three-signers-in-lineage.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/three-signers-in-lineage.xml
new file mode 100644
index 0000000..2ccf5060
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/three-signers-in-lineage.xml
@@ -0,0 +1,9 @@
+ <sigs count="1" schemeVersion="3">
+ <cert index="0" key="3082016e30820115a0030201020209008394f5cad16a89a7300a06082a8648ce3d04030230143112301006035504030c0965632d703235365f32301e170d3138303731343030303532365a170d3238303731313030303532365a30143112301006035504030c0965632d703235365f333059301306072a8648ce3d020106082a8648ce3d03010703420004f31e62430e9db6fc5928d975fc4e47419bacfcb2e07c89299e6cd7e344dd21adfd308d58cb49a1a2a3fecacceea4862069f30be1643bcc255040d8089dfb3743a350304e301d0603551d0e041604146f8d0828b13efaf577fc86b0e99fa3e54bcbcff0301f0603551d230418301680147991d92b0208fc448bf506d4efc9fff428cb5e5f300c0603551d13040530030101ff300a06082a8648ce3d04030203470030440220256bdaa2784c273e4cc291a595a46779dee9de9044dc9f7ab820309567df9fe902201a4ad8c69891b5a8c47434fe9540ed1f4979b5fad3483f3fa04d5677355a579e" />
+ <pastSigs count="3">
+ <cert index="1" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" flags="3" />
+ <cert index="2" key="3082016d30820113a0030201020209008855bd1dd2b2b225300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3138303731333137343135315a170d3238303731303137343135315a30143112301006035504030c0965632d703235365f323059301306072a8648ce3d020106082a8648ce3d030107034200041d4cca0472ad97ee3cecef0da93d62b450c6788333b36e7553cde9f74ab5df00bbba6ba950e68461d70bbc271b62151dad2de2bf6203cd2076801c7a9d4422e1a350304e301d0603551d0e041604147991d92b0208fc448bf506d4efc9fff428cb5e5f301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d040302034800304502202769abb1b49fc2f53479c4ae92a6631dabfd522c9acb0bba2b43ebeb99c63011022100d260fb1d1f176cf9b7fa60098bfd24319f4905a3e5fda100a6fe1a2ab19ff09e" flags="7" />
+ <cert index="0" flags="23" />
+ </pastSigs>
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-invalid-certs-flags.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-invalid-certs-flags.xml
new file mode 100644
index 0000000..6d567e9
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-invalid-certs-flags.xml
@@ -0,0 +1,8 @@
+ <sigs count="1" schemeVersion="3">
+ <cert index="0" key="3082016d30820113a0030201020209008855bd1dd2b2b225300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3138303731333137343135315a170d3238303731303137343135315a30143112301006035504030c0965632d703235365f323059301306072a8648ce3d020106082a8648ce3d030107034200041d4cca0472ad97ee3cecef0da93d62b450c6788333b36e7553cde9f74ab5df00bbba6ba950e68461d70bbc271b62151dad2de2bf6203cd2076801c7a9d4422e1a350304e301d0603551d0e041604147991d92b0208fc448bf506d4efc9fff428cb5e5f301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d040302034800304502202769abb1b49fc2f53479c4ae92a6631dabfd522c9acb0bba2b43ebeb99c63011022100d260fb1d1f176cf9b7fa60098bfd24319f4905a3e5fda100a6fe1a2ab19ff09e" />
+ <pastSigs count="2">
+ <cert index="1" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" flags="x" />
+ <cert index="0" flags="23" />
+ </pastSigs>
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-invalid-pastSigs-cert-index.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-invalid-pastSigs-cert-index.xml
new file mode 100644
index 0000000..a2146b7
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-invalid-pastSigs-cert-index.xml
@@ -0,0 +1,8 @@
+ <sigs count="1" schemeVersion="3">
+ <cert index="0" key="3082016d30820113a0030201020209008855bd1dd2b2b225300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3138303731333137343135315a170d3238303731303137343135315a30143112301006035504030c0965632d703235365f323059301306072a8648ce3d020106082a8648ce3d030107034200041d4cca0472ad97ee3cecef0da93d62b450c6788333b36e7553cde9f74ab5df00bbba6ba950e68461d70bbc271b62151dad2de2bf6203cd2076801c7a9d4422e1a350304e301d0603551d0e041604147991d92b0208fc448bf506d4efc9fff428cb5e5f301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d040302034800304502202769abb1b49fc2f53479c4ae92a6631dabfd522c9acb0bba2b43ebeb99c63011022100d260fb1d1f176cf9b7fa60098bfd24319f4905a3e5fda100a6fe1a2ab19ff09e" />
+ <pastSigs count="2">
+ <cert index="x" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" flags="7" />
+ <cert index="0" flags="0" />
+ </pastSigs>
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-missing-certs-flags.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-missing-certs-flags.xml
new file mode 100644
index 0000000..90a4a84
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-missing-certs-flags.xml
@@ -0,0 +1,8 @@
+ <sigs count="1" schemeVersion="3">
+ <cert index="0" key="3082016d30820113a0030201020209008855bd1dd2b2b225300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3138303731333137343135315a170d3238303731303137343135315a30143112301006035504030c0965632d703235365f323059301306072a8648ce3d020106082a8648ce3d030107034200041d4cca0472ad97ee3cecef0da93d62b450c6788333b36e7553cde9f74ab5df00bbba6ba950e68461d70bbc271b62151dad2de2bf6203cd2076801c7a9d4422e1a350304e301d0603551d0e041604147991d92b0208fc448bf506d4efc9fff428cb5e5f301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d040302034800304502202769abb1b49fc2f53479c4ae92a6631dabfd522c9acb0bba2b43ebeb99c63011022100d260fb1d1f176cf9b7fa60098bfd24319f4905a3e5fda100a6fe1a2ab19ff09e" />
+ <pastSigs count="2">
+ <cert index="1" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" />
+ <cert index="0" flags="23" />
+ </pastSigs>
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-missing-pastSigs-cert-index.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-missing-pastSigs-cert-index.xml
new file mode 100644
index 0000000..6525e48
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-missing-pastSigs-cert-index.xml
@@ -0,0 +1,8 @@
+ <sigs count="1" schemeVersion="3">
+ <cert index="0" key="3082016d30820113a0030201020209008855bd1dd2b2b225300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3138303731333137343135315a170d3238303731303137343135315a30143112301006035504030c0965632d703235365f323059301306072a8648ce3d020106082a8648ce3d030107034200041d4cca0472ad97ee3cecef0da93d62b450c6788333b36e7553cde9f74ab5df00bbba6ba950e68461d70bbc271b62151dad2de2bf6203cd2076801c7a9d4422e1a350304e301d0603551d0e041604147991d92b0208fc448bf506d4efc9fff428cb5e5f301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d040302034800304502202769abb1b49fc2f53479c4ae92a6631dabfd522c9acb0bba2b43ebeb99c63011022100d260fb1d1f176cf9b7fa60098bfd24319f4905a3e5fda100a6fe1a2ab19ff09e" />
+ <pastSigs count="2">
+ <cert key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" flags="7" />
+ <cert flags="0" />
+ </pastSigs>
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-multiple-pastSigs-tags.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-multiple-pastSigs-tags.xml
new file mode 100644
index 0000000..e06892c
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-multiple-pastSigs-tags.xml
@@ -0,0 +1,12 @@
+ <sigs count="1" schemeVersion="3">
+ <cert index="0" key="3082016d30820113a0030201020209008855bd1dd2b2b225300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3138303731333137343135315a170d3238303731303137343135315a30143112301006035504030c0965632d703235365f323059301306072a8648ce3d020106082a8648ce3d030107034200041d4cca0472ad97ee3cecef0da93d62b450c6788333b36e7553cde9f74ab5df00bbba6ba950e68461d70bbc271b62151dad2de2bf6203cd2076801c7a9d4422e1a350304e301d0603551d0e041604147991d92b0208fc448bf506d4efc9fff428cb5e5f301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d040302034800304502202769abb1b49fc2f53479c4ae92a6631dabfd522c9acb0bba2b43ebeb99c63011022100d260fb1d1f176cf9b7fa60098bfd24319f4905a3e5fda100a6fe1a2ab19ff09e" />
+ <pastSigs count="2">
+ <cert index="1" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" flags="23" />
+ <cert index="0" flags="23" />
+ <pastSigs count="2">
+ <cert index="1" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" flags="23" />
+ <cert index="0" flags="23" />
+ </pastSigs>
+ </pastSigs>
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-no-caps.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-no-caps.xml
new file mode 100644
index 0000000..8081d2e
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-no-caps.xml
@@ -0,0 +1,8 @@
+ <sigs count="1" schemeVersion="3">
+ <cert index="0" key="3082016d30820113a0030201020209008855bd1dd2b2b225300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3138303731333137343135315a170d3238303731303137343135315a30143112301006035504030c0965632d703235365f323059301306072a8648ce3d020106082a8648ce3d030107034200041d4cca0472ad97ee3cecef0da93d62b450c6788333b36e7553cde9f74ab5df00bbba6ba950e68461d70bbc271b62151dad2de2bf6203cd2076801c7a9d4422e1a350304e301d0603551d0e041604147991d92b0208fc448bf506d4efc9fff428cb5e5f301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d040302034800304502202769abb1b49fc2f53479c4ae92a6631dabfd522c9acb0bba2b43ebeb99c63011022100d260fb1d1f176cf9b7fa60098bfd24319f4905a3e5fda100a6fe1a2ab19ff09e" />
+ <pastSigs count="2">
+ <cert index="1" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" flags="0" />
+ <cert index="0" flags="7" />
+ </pastSigs>
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-undefined-pastSigs-index.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-undefined-pastSigs-index.xml
new file mode 100644
index 0000000..127000a
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-undefined-pastSigs-index.xml
@@ -0,0 +1,8 @@
+ <sigs count="2" schemeVersion="3">
+ <cert index="1" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" />
+ <pastSigs count="2">
+ <cert index="1" flags="23" />
+ <cert index="0" flags="23" />
+ </pastSigs>
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage.xml
new file mode 100644
index 0000000..6097ea6
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage.xml
@@ -0,0 +1,8 @@
+ <sigs count="1" schemeVersion="3">
+ <cert index="0" key="3082016d30820113a0030201020209008855bd1dd2b2b225300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3138303731333137343135315a170d3238303731303137343135315a30143112301006035504030c0965632d703235365f323059301306072a8648ce3d020106082a8648ce3d030107034200041d4cca0472ad97ee3cecef0da93d62b450c6788333b36e7553cde9f74ab5df00bbba6ba950e68461d70bbc271b62151dad2de2bf6203cd2076801c7a9d4422e1a350304e301d0603551d0e041604147991d92b0208fc448bf506d4efc9fff428cb5e5f301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d040302034800304502202769abb1b49fc2f53479c4ae92a6631dabfd522c9acb0bba2b43ebeb99c63011022100d260fb1d1f176cf9b7fa60098bfd24319f4905a3e5fda100a6fe1a2ab19ff09e" />
+ <pastSigs count="2">
+ <cert index="1" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" flags="7" />
+ <cert index="0" flags="3" />
+ </pastSigs>
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-v1v2-missing-cert-tag.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-v1v2-missing-cert-tag.xml
new file mode 100644
index 0000000..6ed3be8
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-v1v2-missing-cert-tag.xml
@@ -0,0 +1,4 @@
+ <sigs count="2" schemeVersion="1">
+ <cert index="0" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" />
+ </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-v1v2.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-v1v2.xml
new file mode 100644
index 0000000..ee4c4eb
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-v1v2.xml
@@ -0,0 +1,5 @@
+ <sigs count="2" schemeVersion="2">
+ <cert index="0" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" />
+ <cert index="1" key="3082016d30820113a0030201020209008855bd1dd2b2b225300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3138303731333137343135315a170d3238303731303137343135315a30143112301006035504030c0965632d703235365f323059301306072a8648ce3d020106082a8648ce3d030107034200041d4cca0472ad97ee3cecef0da93d62b450c6788333b36e7553cde9f74ab5df00bbba6ba950e68461d70bbc271b62151dad2de2bf6203cd2076801c7a9d4422e1a350304e301d0603551d0e041604147991d92b0208fc448bf506d4efc9fff428cb5e5f301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d040302034800304502202769abb1b49fc2f53479c4ae92a6631dabfd522c9acb0bba2b43ebeb99c63011022100d260fb1d1f176cf9b7fa60098bfd24319f4905a3e5fda100a6fe1a2ab19ff09e" />
+ </sigs>
+
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/PersisterQueueTests.java b/services/tests/servicestests/src/com/android/server/am/PersisterQueueTests.java
new file mode 100644
index 0000000..d7794b0
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/PersisterQueueTests.java
@@ -0,0 +1,300 @@
+/*
+ * 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 junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertSame;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.os.SystemClock;
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Predicate;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
+
+/**
+ * atest PersisterQueueTests
+ */
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+@Presubmit
+@FlakyTest(detail = "Confirm stable in post-submit before removing")
+public class PersisterQueueTests implements PersisterQueue.Listener {
+ private static final long INTER_WRITE_DELAY_MS = 50;
+ private static final long PRE_TASK_DELAY_MS = 300;
+ // We allow at most 1s more than the expected timeout.
+ private static final long TIMEOUT_ALLOWANCE = 100;
+
+ private static final Predicate<MatchingTestItem> TEST_ITEM_PREDICATE = item -> item.mMatching;
+
+ private AtomicInteger mItemCount;
+ private CountDownLatch mSetUpLatch;
+ private volatile CountDownLatch mLatch;
+ private List<Boolean> mProbablyDoneResults;
+
+ private PersisterQueue mTarget;
+
+ @Before
+ public void setUp() throws Exception {
+ mItemCount = new AtomicInteger(0);
+ mProbablyDoneResults = new ArrayList<>();
+ mSetUpLatch = new CountDownLatch(1);
+
+ mTarget = new PersisterQueue(INTER_WRITE_DELAY_MS, PRE_TASK_DELAY_MS);
+ mTarget.addListener(this);
+ mTarget.startPersisting();
+
+ assertTrue("Target didn't call callback on start up.",
+ mSetUpLatch.await(TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mTarget.stopPersisting();
+ }
+
+ @Test
+ public void testCallCallbackOnStartUp() throws Exception {
+ // The onPreProcessItem() must be called on start up.
+ assertEquals(1, mProbablyDoneResults.size());
+ // The last one must be called with probably done being true.
+ assertTrue("The last probablyDone must be true.", mProbablyDoneResults.get(0));
+ }
+
+ @Test
+ public void testProcessOneItem() throws Exception {
+ mLatch = new CountDownLatch(1);
+
+ final long dispatchTime = SystemClock.uptimeMillis();
+ mTarget.addItem(new TestItem(), false);
+ assertTrue("Target didn't call callback enough times.",
+ mLatch.await(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
+ assertEquals("Target didn't process item.", 1, mItemCount.get());
+ final long processDuration = SystemClock.uptimeMillis() - dispatchTime;
+ assertTrue("Target didn't wait enough time before processing item. duration: "
+ + processDuration + "ms pretask delay: " + PRE_TASK_DELAY_MS + "ms",
+ processDuration >= PRE_TASK_DELAY_MS);
+
+ // Once before processing this item, once after that.
+ assertEquals(2, mProbablyDoneResults.size());
+ // The last one must be called with probably done being true.
+ assertTrue("The last probablyDone must be true.", mProbablyDoneResults.get(1));
+ }
+
+ @Test
+ public void testProcessOneItem_Flush() throws Exception {
+ mLatch = new CountDownLatch(1);
+
+ final long dispatchTime = SystemClock.uptimeMillis();
+ mTarget.addItem(new TestItem(), true);
+ assertTrue("Target didn't call callback enough times.",
+ mLatch.await(TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
+ assertEquals("Target didn't process item.", 1, mItemCount.get());
+ final long processDuration = SystemClock.uptimeMillis() - dispatchTime;
+ assertTrue("Target didn't process item immediately when flushing. duration: "
+ + processDuration + "ms pretask delay: "
+ + PRE_TASK_DELAY_MS + "ms",
+ processDuration < PRE_TASK_DELAY_MS);
+
+ // Once before processing this item, once after that.
+ assertEquals(2, mProbablyDoneResults.size());
+ // The last one must be called with probably done being true.
+ assertTrue("The last probablyDone must be true.", mProbablyDoneResults.get(1));
+ }
+
+ @Test
+ public void testProcessTwoItems() throws Exception {
+ mLatch = new CountDownLatch(2);
+
+ final long dispatchTime = SystemClock.uptimeMillis();
+ mTarget.addItem(new TestItem(), false);
+ mTarget.addItem(new TestItem(), false);
+ assertTrue("Target didn't call callback enough times.",
+ mLatch.await(PRE_TASK_DELAY_MS + INTER_WRITE_DELAY_MS + TIMEOUT_ALLOWANCE,
+ TimeUnit.MILLISECONDS));
+ assertEquals("Target didn't process all items.", 2, mItemCount.get());
+ final long processDuration = SystemClock.uptimeMillis() - dispatchTime;
+ assertTrue("Target didn't wait enough time before processing item. duration: "
+ + processDuration + "ms pretask delay: " + PRE_TASK_DELAY_MS
+ + "ms inter write delay: " + INTER_WRITE_DELAY_MS + "ms",
+ processDuration >= PRE_TASK_DELAY_MS + INTER_WRITE_DELAY_MS);
+
+ // Once before processing this item, once after that.
+ assertEquals(3, mProbablyDoneResults.size());
+ // The first one must be called with probably done being false.
+ assertFalse("The first probablyDone must be false.", mProbablyDoneResults.get(1));
+ // The last one must be called with probably done being true.
+ assertTrue("The last probablyDone must be true.", mProbablyDoneResults.get(2));
+ }
+
+ @Test
+ public void testProcessTwoItems_OneAfterAnother() throws Exception {
+ // First item
+ mLatch = new CountDownLatch(1);
+ long dispatchTime = SystemClock.uptimeMillis();
+ mTarget.addItem(new TestItem(), false);
+ assertTrue("Target didn't call callback enough times.",
+ mLatch.await(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
+ long processDuration = SystemClock.uptimeMillis() - dispatchTime;
+ assertTrue("Target didn't wait enough time before processing item."
+ + processDuration + "ms pretask delay: "
+ + PRE_TASK_DELAY_MS + "ms",
+ processDuration >= PRE_TASK_DELAY_MS);
+ assertEquals("Target didn't process item.", 1, mItemCount.get());
+
+ // Second item
+ mLatch = new CountDownLatch(1);
+ dispatchTime = SystemClock.uptimeMillis();
+ // Synchronize on the instance to make sure we schedule the item after it starts to wait for
+ // task indefinitely.
+ synchronized (mTarget) {
+ mTarget.addItem(new TestItem(), false);
+ }
+ assertTrue("Target didn't call callback enough times.",
+ mLatch.await(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
+ assertEquals("Target didn't process all items.", 2, mItemCount.get());
+ processDuration = SystemClock.uptimeMillis() - dispatchTime;
+ assertTrue("Target didn't wait enough time before processing item."
+ + processDuration + "ms pre task delay: "
+ + PRE_TASK_DELAY_MS + "ms",
+ processDuration >= PRE_TASK_DELAY_MS);
+
+ // Once before processing this item, once after that.
+ assertEquals(3, mProbablyDoneResults.size());
+ // The last one must be called with probably done being true.
+ assertTrue("The last probablyDone must be true.", mProbablyDoneResults.get(2));
+ }
+
+ @Test
+ public void testFindLastItemNotReturnDifferentType() throws Exception {
+ synchronized (mTarget) {
+ mTarget.addItem(new TestItem(), false);
+ assertNull(mTarget.findLastItem(TEST_ITEM_PREDICATE, MatchingTestItem.class));
+ }
+ }
+
+ @Test
+ public void testFindLastItemNotReturnMismatchItem() throws Exception {
+ synchronized (mTarget) {
+ mTarget.addItem(new MatchingTestItem(false), false);
+ assertNull(mTarget.findLastItem(TEST_ITEM_PREDICATE, MatchingTestItem.class));
+ }
+ }
+
+ @Test
+ public void testFindLastItemReturnMatchedItem() throws Exception {
+ synchronized (mTarget) {
+ final MatchingTestItem item = new MatchingTestItem(true);
+ mTarget.addItem(item, false);
+ assertSame(item, mTarget.findLastItem(TEST_ITEM_PREDICATE, MatchingTestItem.class));
+ }
+ }
+
+ @Test
+ public void testRemoveItemsNotRemoveDifferentType() throws Exception {
+ mLatch = new CountDownLatch(1);
+ synchronized (mTarget) {
+ mTarget.addItem(new TestItem(), false);
+ mTarget.removeItems(TEST_ITEM_PREDICATE, MatchingTestItem.class);
+ }
+ assertTrue("Target didn't call callback enough times.",
+ mLatch.await(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
+ assertEquals("Target didn't process item.", 1, mItemCount.get());
+ }
+
+ @Test
+ public void testRemoveItemsNotRemoveMismatchedItem() throws Exception {
+ mLatch = new CountDownLatch(1);
+ synchronized (mTarget) {
+ mTarget.addItem(new MatchingTestItem(false), false);
+ mTarget.removeItems(TEST_ITEM_PREDICATE, MatchingTestItem.class);
+ }
+ assertTrue("Target didn't call callback enough times.",
+ mLatch.await(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
+ assertEquals("Target didn't process item.", 1, mItemCount.get());
+ }
+
+ @Test
+ public void testRemoveItemsRemoveMatchedItem() throws Exception {
+ mLatch = new CountDownLatch(1);
+ synchronized (mTarget) {
+ mTarget.addItem(new TestItem(), false);
+ mTarget.addItem(new MatchingTestItem(true), false);
+ mTarget.removeItems(TEST_ITEM_PREDICATE, MatchingTestItem.class);
+ }
+ assertTrue("Target didn't call callback enough times.",
+ mLatch.await(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
+ assertEquals("Target didn't process item.", 1, mItemCount.get());
+ }
+
+ @Test
+ public void testFlushWaitSynchronously() {
+ final long dispatchTime = SystemClock.uptimeMillis();
+ mTarget.addItem(new TestItem(), false);
+ mTarget.addItem(new TestItem(), false);
+ mTarget.flush();
+ assertEquals("Flush should wait until all items are processed before return.",
+ 2, mItemCount.get());
+ final long processTime = SystemClock.uptimeMillis() - dispatchTime;
+ assertTrue("Flush should trigger immediate flush without delays. processTime: "
+ + processTime, processTime < TIMEOUT_ALLOWANCE);
+ }
+
+ @Override
+ public void onPreProcessItem(boolean queueEmpty) {
+ mProbablyDoneResults.add(queueEmpty);
+
+ final CountDownLatch latch = mLatch;
+ if (latch != null) {
+ latch.countDown();
+ }
+
+ mSetUpLatch.countDown();
+ }
+
+ private class TestItem implements PersisterQueue.WriteQueueItem {
+ @Override
+ public void process() {
+ mItemCount.getAndIncrement();
+ }
+ }
+
+ private class MatchingTestItem extends TestItem {
+ private boolean mMatching;
+
+ private MatchingTestItem(boolean matching) {
+ mMatching = matching;
+ }
+ }
+}
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/backup/utils/AppBackupUtilsTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java
index 9fcdf2d..d52051eec 100644
--- a/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java
@@ -436,7 +436,6 @@
new Signature[] {SIGNATURE_1},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -456,7 +455,6 @@
new Signature[] {SIGNATURE_1},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -537,7 +535,6 @@
new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -560,7 +557,6 @@
new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -583,7 +579,6 @@
new Signature[] {signature1Copy, signature2Copy},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -606,7 +601,6 @@
new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -629,7 +623,6 @@
new Signature[] {SIGNATURE_1},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -654,8 +647,7 @@
new Signature[] {SIGNATURE_2},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- new Signature[] {SIGNATURE_1, SIGNATURE_2},
- new int[] {0, 0}));
+ new Signature[] {SIGNATURE_1, SIGNATURE_2}));
packageInfo.applicationInfo = new ApplicationInfo();
// we know signature1Copy is in history, and we want to assume it has
@@ -682,8 +674,7 @@
new Signature[] {SIGNATURE_2},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- new Signature[] {SIGNATURE_1, SIGNATURE_2},
- new int[] {0, 0}));
+ new Signature[] {SIGNATURE_1, SIGNATURE_2}));
packageInfo.applicationInfo = new ApplicationInfo();
// we know signature1Copy is in history, but we want to assume it does not have
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
index 12f2991..4774985 100644
--- a/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
@@ -377,7 +377,6 @@
new Signature[] {FAKE_SIGNATURE_2},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
PackageManagerStub.sPackageInfo = packageInfo;
@@ -414,7 +413,6 @@
new Signature[] {FAKE_SIGNATURE_1},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
PackageManagerStub.sPackageInfo = packageInfo;
@@ -452,7 +450,6 @@
new Signature[] {FAKE_SIGNATURE_1},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
PackageManagerStub.sPackageInfo = packageInfo;
@@ -493,7 +490,6 @@
new Signature[] {FAKE_SIGNATURE_1},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
packageInfo.versionCode = 2;
PackageManagerStub.sPackageInfo = packageInfo;
@@ -537,7 +533,6 @@
new Signature[] {FAKE_SIGNATURE_1},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
packageInfo.versionCode = 1;
PackageManagerStub.sPackageInfo = packageInfo;
@@ -577,7 +572,6 @@
new Signature[] {FAKE_SIGNATURE_1},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
packageInfo.versionCode = 1;
PackageManagerStub.sPackageInfo = packageInfo;
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index ceee60c..b421280 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -16,6 +16,8 @@
package com.android.server.display;
+import static com.android.server.display.VirtualDisplayAdapter.UNIQUE_ID_PREFIX;
+
import android.content.Context;
import android.hardware.display.BrightnessConfiguration;
import android.hardware.display.Curve;
@@ -25,35 +27,48 @@
import android.hardware.input.InputManagerInternal;
import android.os.Handler;
import android.os.IBinder;
-import android.os.UserHandle;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.view.Display;
+import android.view.DisplayInfo;
import android.view.SurfaceControl;
+import androidx.test.runner.AndroidJUnit4;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.display.DisplayDeviceInfo;
import com.android.server.display.DisplayManagerService.SyncRoot;
-import com.android.server.display.VirtualDisplayAdapter.SurfaceControlDisplayFactory;
import com.android.server.lights.LightsManager;
import com.android.server.wm.WindowManagerInternal;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.Arrays;
import java.util.List;
-import static org.mockito.Matchers.any;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.mock;
@SmallTest
-public class DisplayManagerServiceTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class DisplayManagerServiceTest {
private static final int MSG_REGISTER_DEFAULT_DISPLAY_ADAPTERS = 1;
private static final long SHORT_DEFAULT_DISPLAY_TIMEOUT_MILLIS = 10;
+ private Context mContext;
+
private final DisplayManagerService.Injector mShortMockedInjector =
new DisplayManagerService.Injector() {
@Override
@@ -86,8 +101,8 @@
@Mock VirtualDisplayAdapter mMockVirtualDisplayAdapter;
@Mock IBinder mMockDisplayToken;
- @Override
- protected void setUp() throws Exception {
+ @Before
+ public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
LocalServices.removeServiceForTest(InputManagerInternal.class);
@@ -96,15 +111,12 @@
LocalServices.addService(WindowManagerInternal.class, mMockWindowManagerInternal);
LocalServices.removeServiceForTest(LightsManager.class);
LocalServices.addService(LightsManager.class, mMockLightsManager);
- super.setUp();
+
+ mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
}
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
- }
-
- public void testCreateVirtualDisplay_sentToInputManager() throws Exception {
+ @Test
+ public void testCreateVirtualDisplay_sentToInputManager() {
DisplayManagerService displayManager =
new DisplayManagerService(mContext, mBasicInjector);
registerDefaultDisplays(displayManager);
@@ -115,7 +127,7 @@
DisplayManagerService.BinderService bs = displayManager.new BinderService();
String uniqueId = "uniqueId --- Test";
- String uniqueIdPrefix = "virtual:" + mContext.getPackageName() + ":";
+ String uniqueIdPrefix = UNIQUE_ID_PREFIX + mContext.getPackageName() + ":";
int width = 600;
int height = 800;
int dpi = 320;
@@ -132,19 +144,113 @@
// flush the handler
displayManager.getDisplayHandler().runWithScissors(() -> {}, 0 /* now */);
- ArgumentCaptor<List<DisplayViewport>> virtualViewportCaptor =
- ArgumentCaptor.forClass(List.class);
- verify(mMockInputManagerInternal).setDisplayViewports(
- any(), any(), virtualViewportCaptor.capture());
+ ArgumentCaptor<List<DisplayViewport>> viewportCaptor = ArgumentCaptor.forClass(List.class);
+ verify(mMockInputManagerInternal).setDisplayViewports(viewportCaptor.capture());
+ List<DisplayViewport> viewports = viewportCaptor.getValue();
- assertEquals(1, virtualViewportCaptor.getValue().size());
- DisplayViewport dv = virtualViewportCaptor.getValue().get(0);
- assertEquals(height, dv.deviceHeight);
- assertEquals(width, dv.deviceWidth);
- assertEquals(uniqueIdPrefix + uniqueId, dv.uniqueId);
- assertEquals(displayId, dv.displayId);
+ // Expect to receive 3 viewports: internal, external, and virtual
+ assertEquals(3, viewports.size());
+
+ DisplayViewport virtualViewport = null;
+ DisplayViewport internalViewport = null;
+ DisplayViewport externalViewport = null;
+ for (int i = 0; i < viewports.size(); i++) {
+ DisplayViewport v = viewports.get(i);
+ switch (v.type) {
+ case DisplayViewport.VIEWPORT_INTERNAL: {
+ internalViewport = v;
+ break;
+ }
+ case DisplayViewport.VIEWPORT_EXTERNAL: {
+ externalViewport = v;
+ break;
+ }
+ case DisplayViewport.VIEWPORT_VIRTUAL: {
+ virtualViewport = v;
+ break;
+ }
+ }
+ }
+ // INTERNAL and EXTERNAL viewports get created upon access
+ assertNotNull(internalViewport);
+ assertNotNull(externalViewport);
+ assertNotNull(virtualViewport);
+
+ // INTERNAL and EXTERNAL
+ assertTrue(internalViewport.valid);
+ assertTrue(externalViewport.valid);
+
+ // VIRTUAL
+ assertEquals(height, virtualViewport.deviceHeight);
+ assertEquals(width, virtualViewport.deviceWidth);
+ assertEquals(uniqueIdPrefix + uniqueId, virtualViewport.uniqueId);
+ assertEquals(displayId, virtualViewport.displayId);
}
+ @Test
+ public void testPhysicalViewports() {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mBasicInjector);
+ registerDefaultDisplays(displayManager);
+ displayManager.systemReady(false /* safeMode */, true /* onlyCore */);
+ displayManager.windowManagerAndInputReady();
+
+ // This is effectively the DisplayManager service published to ServiceManager.
+ DisplayManagerService.BinderService bs = displayManager.new BinderService();
+
+ when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
+
+ final int displayIds[] = bs.getDisplayIds();
+ assertEquals(1, displayIds.length);
+ final int displayId = displayIds[0];
+ DisplayInfo info = bs.getDisplayInfo(displayId);
+ assertEquals(info.type, Display.TYPE_BUILT_IN);
+
+ displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
+
+ // flush the handler
+ displayManager.getDisplayHandler().runWithScissors(() -> {}, 0 /* now */);
+
+ ArgumentCaptor<List<DisplayViewport>> viewportCaptor = ArgumentCaptor.forClass(List.class);
+ verify(mMockInputManagerInternal).setDisplayViewports(viewportCaptor.capture());
+ List<DisplayViewport> viewports = viewportCaptor.getValue();
+
+ // Expect to receive 2 viewports: 1 internal, 1 external
+ assertEquals(2, viewports.size());
+
+ DisplayViewport internalViewport = null;
+ DisplayViewport externalViewport = null;
+ for (int i = 0; i < viewports.size(); i++) {
+ DisplayViewport v = viewports.get(i);
+ switch (v.type) {
+ case DisplayViewport.VIEWPORT_INTERNAL: {
+ internalViewport = v;
+ break;
+ }
+ case DisplayViewport.VIEWPORT_EXTERNAL: {
+ externalViewport = v;
+ break;
+ }
+ default: {
+ fail("Unexpected viewport type: " + DisplayViewport.typeToString(v.type));
+ break;
+ }
+ }
+ }
+ // INTERNAL and EXTERNAL viewports get created upon access
+ assertNotNull(internalViewport);
+ assertNotNull(externalViewport);
+ assertTrue(internalViewport.valid);
+ assertEquals(displayId, internalViewport.displayId);
+
+ // To simplify comparison, override the type for external Viewport
+ // TODO (b/116850516) remove this
+ externalViewport.type = internalViewport.type;
+ assertEquals(internalViewport, externalViewport);
+ externalViewport.type = DisplayViewport.VIEWPORT_EXTERNAL; // undo the changes above
+ }
+
+ @Test
public void testCreateVirtualDisplayRotatesWithContent() throws Exception {
DisplayManagerService displayManager =
new DisplayManagerService(mContext, mBasicInjector);
@@ -178,6 +284,7 @@
/**
* Tests that the virtual display is created along-side the default display.
*/
+ @Test
public void testStartVirtualDisplayWithDefaultDisplay_Succeeds() throws Exception {
DisplayManagerService displayManager =
new DisplayManagerService(mContext, mShortMockedInjector);
@@ -188,6 +295,7 @@
/**
* Tests that we get a Runtime exception when we cannot initialize the default display.
*/
+ @Test
public void testStartVirtualDisplayWithDefDisplay_NoDefaultDisplay() throws Exception {
DisplayManagerService displayManager =
new DisplayManagerService(mContext, mShortMockedInjector);
@@ -206,6 +314,7 @@
/**
* Tests that we get a Runtime exception when we cannot initialize the virtual display.
*/
+ @Test
public void testStartVirtualDisplayWithDefDisplay_NoVirtualDisplayAdapter() throws Exception {
DisplayManagerService displayManager = new DisplayManagerService(mContext,
new DisplayManagerService.Injector() {
@@ -232,6 +341,7 @@
/**
* Tests that an exception is raised for too dark a brightness configuration.
*/
+ @Test
public void testTooDarkBrightnessConfigurationThrowException() {
DisplayManagerService displayManager =
new DisplayManagerService(mContext, mShortMockedInjector);
@@ -266,6 +376,7 @@
/**
* Tests that no exception is raised for not too dark a brightness configuration.
*/
+ @Test
public void testBrightEnoughBrightnessConfigurationDoesNotThrowException() {
DisplayManagerService displayManager =
new DisplayManagerService(mContext, mShortMockedInjector);
@@ -279,6 +390,7 @@
/**
* Tests that null brightness configurations are alright.
*/
+ @Test
public void testNullBrightnessConfiguration() {
DisplayManagerService displayManager =
new DisplayManagerService(mContext, mShortMockedInjector);
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index 2de5d87..a3348c2 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -1052,7 +1052,6 @@
genSignatures(signatures),
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
return pi;
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index 318ed3a..9af7b1c 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -504,7 +504,6 @@
new Signature[] { new Signature(new byte[16]) },
2,
new ArraySet<>(),
- null,
null);
pkg.mExtras = new Bundle();
pkg.mRestrictedAccountType = "foo19";
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
new file mode 100644
index 0000000..d3a77d3
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
@@ -0,0 +1,474 @@
+/*
+ * 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.pm;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.content.Context;
+import android.content.pm.PackageParser;
+import android.content.pm.Signature;
+import android.util.Xml;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.util.HexDump;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParser;
+
+import java.io.File;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+@RunWith(AndroidJUnit4.class)
+public class PackageSignaturesTest {
+ private static final String TEST_RESOURCES_FOLDER = "PackageSignaturesTest";
+
+ private Context mContext;
+
+ private PackageSetting mPackageSetting;
+
+ // These signatures are the DER encoding of the ec-p256[_X] X509 certificates in the certs/
+ // directory. The apksigner tool was used to sign a test APK with these certificates and the
+ // corresponding ec-p256{_X].pk8 private key file. For the lineage tests the
+ // ec-p256-lineage-X-signers file was provided as the parameter to the --lineage option when
+ // signing the APK. The APK was then installed on a test device, the packages.xml file was
+ // pulled from the device, and the APK's <sig> tag was used as the basis for these tests.
+ // For more details see the README under the xml/ directory.
+ private static final String FIRST_EXPECTED_SIGNATURE =
+ "3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06"
+ + "035504030c0765632d70323536301e170d3136303333313134353830365a170d34333038313731343538"
+ + "30365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce"
+ + "3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194"
+ + "b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04"
+ + "160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b"
+ + "30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349"
+ + "003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8"
+ + "eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd";
+ private static final String SECOND_EXPECTED_SIGNATURE =
+ "3082016d30820113a0030201020209008855bd1dd2b2b225300a06082a8648ce3d04030230123110300e06"
+ + "035504030c0765632d70323536301e170d3138303731333137343135315a170d32383037313031373431"
+ + "35315a30143112301006035504030c0965632d703235365f323059301306072a8648ce3d020106082a86"
+ + "48ce3d030107034200041d4cca0472ad97ee3cecef0da93d62b450c6788333b36e7553cde9f74ab5df00"
+ + "bbba6ba950e68461d70bbc271b62151dad2de2bf6203cd2076801c7a9d4422e1a350304e301d0603551d"
+ + "0e041604147991d92b0208fc448bf506d4efc9fff428cb5e5f301f0603551d23041830168014d4133568"
+ + "b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d040302"
+ + "034800304502202769abb1b49fc2f53479c4ae92a6631dabfd522c9acb0bba2b43ebeb99c63011022100"
+ + "d260fb1d1f176cf9b7fa60098bfd24319f4905a3e5fda100a6fe1a2ab19ff09e";
+ private static final String THIRD_EXPECTED_SIGNATURE =
+ "3082016e30820115a0030201020209008394f5cad16a89a7300a06082a8648ce3d04030230143112301006"
+ + "035504030c0965632d703235365f32301e170d3138303731343030303532365a170d3238303731313030"
+ + "303532365a30143112301006035504030c0965632d703235365f333059301306072a8648ce3d02010608"
+ + "2a8648ce3d03010703420004f31e62430e9db6fc5928d975fc4e47419bacfcb2e07c89299e6cd7e344dd"
+ + "21adfd308d58cb49a1a2a3fecacceea4862069f30be1643bcc255040d8089dfb3743a350304e301d0603"
+ + "551d0e041604146f8d0828b13efaf577fc86b0e99fa3e54bcbcff0301f0603551d230418301680147991"
+ + "d92b0208fc448bf506d4efc9fff428cb5e5f300c0603551d13040530030101ff300a06082a8648ce3d04"
+ + "030203470030440220256bdaa2784c273e4cc291a595a46779dee9de9044dc9f7ab820309567df9fe902"
+ + "201a4ad8c69891b5a8c47434fe9540ed1f4979b5fad3483f3fa04d5677355a579e";
+
+ // When running tests using the pastSigs tag / lineage the past signers and their capabilities
+ // should be returned in the SigningDetails. The flags attribute of the cert tag under the
+ // pastSigs tag contains these capabilities; for tests that verify the lineage the capabilities
+ // of the signers should be set to the values in this Map.
+ private static final Map<String, Integer> SIGNATURE_TO_CAPABILITY_MAP;
+
+ static {
+ SIGNATURE_TO_CAPABILITY_MAP = new HashMap<>();
+ SIGNATURE_TO_CAPABILITY_MAP.put(FIRST_EXPECTED_SIGNATURE, 3);
+ SIGNATURE_TO_CAPABILITY_MAP.put(SECOND_EXPECTED_SIGNATURE, 7);
+ SIGNATURE_TO_CAPABILITY_MAP.put(THIRD_EXPECTED_SIGNATURE, 23);
+ }
+
+ private static final int[] CAPABILITIES =
+ {PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA,
+ PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID,
+ PackageParser.SigningDetails.CertCapabilities.PERMISSION,
+ PackageParser.SigningDetails.CertCapabilities.ROLLBACK};
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getContext();
+ mPackageSetting = createPackageSetting();
+ }
+
+ @Test
+ public void testReadXmlWithOneSignerCompletesSuccessfully() throws Exception {
+ // Verifies the good path of reading a single sigs tag with one signer returns the
+ // expected signature and scheme version.
+ verifyReadXmlReturnsExpectedSignatures("xml/one-signer.xml", 1, FIRST_EXPECTED_SIGNATURE);
+ }
+
+ @Test
+ public void testReadXmlWithTwoV1V2Signers() throws Exception {
+ // Verifies the good path of reading a single sigs tag with multiple signers returns the
+ // expected signatures and scheme version.
+ verifyReadXmlReturnsExpectedSignatures("xml/two-signers-v1v2.xml", 2,
+ FIRST_EXPECTED_SIGNATURE, SECOND_EXPECTED_SIGNATURE);
+ }
+
+ @Test
+ public void testReadXmlFromTwoSigsTagsWithSameSigner() throws Exception {
+ // Verifies the good path of reading two separate packages tags from the same signer. The
+ // first call to readXml should return the list with the expected signature, then the second
+ // call should reference this signature and complete successfully with no new entries in the
+ // List.
+ XmlPullParser parser = getXMLFromResources("xml/one-signer.xml");
+ ArrayList<Signature> signatures = new ArrayList<>();
+ mPackageSetting.signatures.readXml(parser, signatures);
+ Set<String> expectedSignatures = createSetOfSignatures(FIRST_EXPECTED_SIGNATURE);
+ verifySignaturesContainExpectedValues(signatures, expectedSignatures);
+ parser = getXMLFromResources("xml/one-signer-previous-cert.xml");
+ mPackageSetting.signatures.readXml(parser, signatures);
+ expectedSignatures = createSetOfSignatures(FIRST_EXPECTED_SIGNATURE);
+ verifySignaturesContainExpectedValues(signatures, expectedSignatures);
+ }
+
+ @Test
+ public void testReadXmlWithSigningLineage() throws Exception {
+ // Verifies the good path of reading a single sigs tag including pastSigs with the
+ // signing lineage returns the expected signatures and lineage for two and three signers
+ // in the lineage.
+ verifyReadXmlReturnsExpectedSignaturesAndLineage("xml/two-signers-in-lineage.xml", 3,
+ FIRST_EXPECTED_SIGNATURE, SECOND_EXPECTED_SIGNATURE);
+ verifyReadXmlReturnsExpectedSignaturesAndLineage("xml/three-signers-in-lineage.xml", 3,
+ FIRST_EXPECTED_SIGNATURE, SECOND_EXPECTED_SIGNATURE, THIRD_EXPECTED_SIGNATURE);
+ }
+
+ @Test
+ public void testReadXmlWithInvalidPublicKeyInCertKey() throws Exception {
+ // If the cert tag key attribute does not contain a valid public key then a
+ // CertificateException should be thrown when attempting to build the SigningDetails; in
+ // this case the signing details should be set to UNKNOWN.
+ XmlPullParser parser = getXMLFromResources(
+ "xml/one-signer-invalid-public-key-cert-key.xml");
+ ArrayList<Signature> signatures = new ArrayList<>();
+ mPackageSetting.signatures.readXml(parser, signatures);
+ assertEquals(
+ "The signing details was not UNKNOWN after parsing an invalid public key cert key"
+ + " attribute",
+ PackageParser.SigningDetails.UNKNOWN, mPackageSetting.signatures.mSigningDetails);
+ }
+
+ @Test
+ public void testReadXmlWithMissingSigsCount() throws Exception {
+ // Verifies if the sigs count attribute is missing then the signature cannot be read but the
+ // method does not throw an exception.
+ verifyReadXmlReturnsExpectedSignatures("xml/one-signer-missing-sigs-count.xml",
+ PackageParser.SigningDetails.SignatureSchemeVersion.UNKNOWN);
+ }
+
+ @Test
+ public void testReadXmlWithMissingSchemeVersion() throws Exception {
+ // Verifies if the schemeVersion is an invalid value the signature can still be obtained.
+ verifyReadXmlReturnsExpectedSignatures("xml/one-signer-missing-scheme-version.xml",
+ PackageParser.SigningDetails.SignatureSchemeVersion.UNKNOWN,
+ FIRST_EXPECTED_SIGNATURE);
+ }
+
+ @Test
+ public void testReadXmlWithSigningLineageWithMissingSchemeVersion() throws Exception {
+ // Verifies if the scheme version cannot be read the signers in the lineage can still be
+ // obtained.
+ verifyReadXmlReturnsExpectedSignaturesAndLineage(
+ "xml/three-signers-in-lineage-missing-scheme-version.xml",
+ PackageParser.SigningDetails.SignatureSchemeVersion.UNKNOWN,
+ FIRST_EXPECTED_SIGNATURE, SECOND_EXPECTED_SIGNATURE, THIRD_EXPECTED_SIGNATURE);
+ }
+
+ @Test
+ public void testReadXmlWithInvalidCertIndex() throws Exception {
+ // If the cert index attribute is invalid the signature will not be read but the call
+ // should exit gracefully.
+ verifyReadXmlReturnsExpectedSignatures("xml/one-signer-invalid-cert-index.xml", 3);
+ }
+
+ @Test
+ public void testReadXmlWithMissingCertIndex() throws Exception {
+ // If the cert index attribute is missing the signature will not be read but the call should
+ // exit gracefully.
+ verifyReadXmlReturnsExpectedSignatures("xml/one-signer-missing-cert-index.xml", 3);
+ }
+
+ @Test
+ public void testReadXmlWithInvalidCertKey() throws Exception {
+ // If the cert key value is invalid the signature cannot be read but the call should exit
+ // gracefully.
+ verifyReadXmlReturnsExpectedSignatures("xml/one-signer-invalid-cert-key.xml", 3);
+ }
+
+ @Test
+ public void testReadXmlWithMissingCertKey() throws Exception {
+ // If the cert key is missing the signature cannot be read but the call should exit
+ // gracefully.
+ verifyReadXmlReturnsExpectedSignatures("xml/one-signer-missing-cert-key.xml", 3);
+ }
+
+ @Test
+ public void testReadXmlWithMissingCertTag() throws Exception {
+ // If the cert tag is missing there is no signature to read but the call should exit
+ // gracefully.
+ verifyReadXmlReturnsExpectedSignatures("xml/one-signer-missing-cert-tag.xml", 3);
+ }
+
+ @Test
+ public void testReadXmlWithTooFewCertTags() throws Exception {
+ // If the number of cert tags is less than that specified in the count attribute then the
+ // signatures that could be read are copied to a smaller array to be used when building
+ // the SigningDetails object. This test verifies if there are too few cert tags the
+ // available signatures can still be obtained.
+ verifyReadXmlReturnsExpectedSignatures("xml/two-signers-v1v2-missing-cert-tag.xml", 1,
+ FIRST_EXPECTED_SIGNATURE);
+ }
+
+ @Test
+ public void testReadXmlWithExtraCertTag() throws Exception {
+ // Verifies if there are more cert tags than specified by the count attribute the extra cert
+ // tag is ignored and the expected signature from the first cert tag is returned.
+ verifyReadXmlReturnsExpectedSignatures("xml/one-signer-extra-cert-tag.xml", 3,
+ FIRST_EXPECTED_SIGNATURE);
+ }
+
+ @Test
+ public void testReadXmlWithInvalidTag() throws Exception {
+ // Verifies an invalid tag under sigs is ignored and the expected signature is returned.
+ verifyReadXmlReturnsExpectedSignatures("xml/one-signer-invalid-tag.xml", 3,
+ FIRST_EXPECTED_SIGNATURE);
+ }
+
+ @Test
+ public void testReadXmlWithInvalidPastSigsCount() throws Exception {
+ // Verifies if the pastSigs tag contains an invalid count attribute the current signature
+ // is still returned; in this case the third expected signature is the most recent signer.
+ verifyReadXmlReturnsExpectedSignatures(
+ "xml/three-signers-in-lineage-invalid-pastSigs-count.xml", 3,
+ THIRD_EXPECTED_SIGNATURE);
+ }
+
+ @Test
+ public void testReadXmlWithMissingPastSigsCount() throws Exception {
+ // Verifies if the pastSigs tag is missing the count attribute the current signature is
+ // still returned; in this case the third expected signature is the most recent signer.
+ verifyReadXmlReturnsExpectedSignaturesAndLineage(
+ "xml/three-signers-in-lineage-missing-pastSigs-count.xml", 3,
+ THIRD_EXPECTED_SIGNATURE);
+ }
+
+ @Test
+ public void testReadXmlWithInvalidCertFlags() throws Exception {
+ // Verifies if the cert tag contains an invalid flags attribute the expected signatures
+ // are still returned, although since the flags could not be read these signatures will not
+ // include the capabilities of the previous signers in the lineage.
+ verifyReadXmlReturnsExpectedSignatures("xml/two-signers-in-lineage-invalid-certs-flags.xml",
+ 3, FIRST_EXPECTED_SIGNATURE, SECOND_EXPECTED_SIGNATURE);
+ }
+
+ @Test
+ public void testReadXmlWithMissingCertFlags() throws Exception {
+ // Verifies if the cert tag does not contain a flags attribute the expected signatures are
+ // still returned, although since there are no flags to read these signatures will not
+ // include the capabilities of the previous signers in the lineage.
+ verifyReadXmlReturnsExpectedSignatures("xml/two-signers-in-lineage-missing-certs-flags.xml",
+ 3, FIRST_EXPECTED_SIGNATURE, SECOND_EXPECTED_SIGNATURE);
+ }
+
+ @Test
+ public void testReadXmlWithMultiplePastSigsTags() throws Exception {
+ // Verifies if multiple pastSigs tags are found under the sigs tag the additional pastSigs
+ // tag is ignored and the expected signatures are returned along with the previous signer in
+ // the lineage.
+ verifyReadXmlReturnsExpectedSignaturesAndLineage(
+ "xml/two-signers-in-lineage-multiple-pastSigs-tags.xml", 3,
+ FIRST_EXPECTED_SIGNATURE, SECOND_EXPECTED_SIGNATURE);
+ }
+
+ @Test
+ public void testReadXmlWithInvalidPastSigsCertIndex() throws Exception {
+ // If the pastSigs cert tag contains an invalid index attribute that signature cannot be
+ // read but the current signature should still be returned.
+ verifyReadXmlReturnsExpectedSignaturesAndLineage(
+ "xml/two-signers-in-lineage-invalid-pastSigs-cert-index.xml", 3,
+ SECOND_EXPECTED_SIGNATURE);
+ }
+
+ @Test
+ public void testReadXmlWithMissingPastSigsCertIndex() throws Exception {
+ // If the pastSigs cert tag does not contain an index attribute that signature cannot be
+ // read but the current signature should still be returned.
+ verifyReadXmlReturnsExpectedSignaturesAndLineage(
+ "xml/two-signers-in-lineage-missing-pastSigs-cert-index.xml", 3,
+ SECOND_EXPECTED_SIGNATURE);
+ }
+
+ @Test
+ public void testReadXmlWithUndefinedPastSigsIndex() throws Exception {
+ // If a cert tag does not contain a key attribute it is assumed that the index attribute
+ // refers to a previously seen signature. If a signature does not yet exist at this index
+ // then the current signature cannot be read but any other signatures should still be
+ // returned.
+ verifyReadXmlReturnsExpectedSignatures(
+ "xml/two-signers-in-lineage-undefined-pastSigs-index.xml", 3,
+ FIRST_EXPECTED_SIGNATURE, null);
+ }
+
+ @Test
+ public void testReadXmlWithTooFewPastSigsCertTags() throws Exception {
+ // If the number of cert tags is less than that specified in the count attribute of the
+ // pastSigs tag then the signatures that could be read are copied to a smaller array to be
+ // used when building the SigningDetails object. This test verifies if there are too few
+ // cert tags the available signatures and lineage can still be obtained.
+ verifyReadXmlReturnsExpectedSignaturesAndLineage(
+ "xml/three-signers-in-lineage-missing-pastSigs-cert-tag.xml", 3,
+ FIRST_EXPECTED_SIGNATURE, THIRD_EXPECTED_SIGNATURE);
+ }
+
+ @Test
+ public void testReadXmlWithPastSignerWithNoCapabilities() throws Exception {
+ // When rotating the signing key a developer is able to specify the capabilities granted to
+ // the apps signed with the previous key. This test verifies a previous signing certificate
+ // with the flags set to 0 does not have any capabilities.
+ XmlPullParser parser = getXMLFromResources("xml/two-signers-in-lineage-no-caps.xml");
+ ArrayList<Signature> signatures = new ArrayList<>();
+ mPackageSetting.signatures.readXml(parser, signatures);
+ // obtain the Signature in the list matching the previous signing certificate
+ Signature previousSignature = null;
+ for (Signature signature : signatures) {
+ String signatureValue = HexDump.toHexString(signature.toByteArray(), false);
+ if (signatureValue.equals(FIRST_EXPECTED_SIGNATURE)) {
+ previousSignature = signature;
+ break;
+ }
+ }
+ assertNotNull("Unable to find the expected previous signer", previousSignature);
+ for (int capability : CAPABILITIES) {
+ assertFalse("The previous signer should not have the " + capability + " capability",
+ mPackageSetting.signatures.mSigningDetails.hasCertificate(previousSignature,
+ capability));
+ }
+ }
+
+ /**
+ * Verifies reading the sigs tag of the provided XML file returns the specified signature scheme
+ * version and the provided signatures.
+ */
+ private void verifyReadXmlReturnsExpectedSignatures(String xmlFile, int expectedSchemeVersion,
+ String... expectedSignatureValues) throws Exception {
+ XmlPullParser parser = getXMLFromResources(xmlFile);
+ ArrayList<Signature> signatures = new ArrayList<>();
+ mPackageSetting.signatures.readXml(parser, signatures);
+ Set<String> expectedSignatures = createSetOfSignatures(expectedSignatureValues);
+ verifySignaturesContainExpectedValues(signatures, expectedSignatures);
+ assertEquals("The returned signature scheme is not the expected value",
+ expectedSchemeVersion,
+ mPackageSetting.signatures.mSigningDetails.signatureSchemeVersion);
+ }
+
+ /**
+ * Verifies reading the sigs tag of the provided XML file returns the specified signature scheme
+ * version, the provided signatures, and that the previous signers have the expected
+ * capabilities.
+ */
+ private void verifyReadXmlReturnsExpectedSignaturesAndLineage(String xmlFile,
+ int schemeVersion, String... expectedSignatureValues) throws Exception {
+ XmlPullParser parser = getXMLFromResources(xmlFile);
+ ArrayList<Signature> signatures = new ArrayList<>();
+ mPackageSetting.signatures.readXml(parser, signatures);
+ Set<String> expectedSignatures = createSetOfSignatures(expectedSignatureValues);
+ verifySignaturesContainExpectedValues(signatures, expectedSignatures);
+ assertEquals("The returned signature scheme is not the expected value", schemeVersion,
+ mPackageSetting.signatures.mSigningDetails.signatureSchemeVersion);
+ for (Signature signature : signatures) {
+ String signatureValue = HexDump.toHexString(signature.toByteArray(), false);
+ int expectedCapabilities = SIGNATURE_TO_CAPABILITY_MAP.get(signatureValue);
+ assertTrue("The signature " + signatureValue
+ + " was not found with the expected capabilities of " +
+ expectedCapabilities
+ + " in the signing details",
+ mPackageSetting.signatures.mSigningDetails.hasCertificate(signature,
+ expectedCapabilities));
+ }
+ }
+
+ /**
+ * Verifies the provided {@code List} contains Signatures that match the provided hex encoded
+ * signature values.
+ *
+ * The provided {@code Set} will be modified by this method as elements will be removed to
+ * ensure duplicate expected Signatures are not in the {@code List}.
+ */
+ private static void verifySignaturesContainExpectedValues(ArrayList<Signature> signatures,
+ Set<String> expectedSignatures) {
+ assertEquals("The number of signatures does not equal the expected number of signatures",
+ expectedSignatures.size(), signatures.size());
+ for (Signature signature : signatures) {
+ String signatureString = null;
+ if (signature != null) {
+ signatureString = HexDump.toHexString(signature.toByteArray(), false);
+ }
+ // If the signature is in the expected set then remove it so that duplicate matching
+ // signatures are reported.
+ if (expectedSignatures.contains(signatureString)) {
+ expectedSignatures.remove(signatureString);
+ } else {
+ fail("The following unexpected signature was returned: " + signatureString);
+ }
+ }
+ }
+
+ private static Set<String> createSetOfSignatures(String... signatures) {
+ Set<String> result = new HashSet<String>();
+ for (String signature : signatures) {
+ result.add(signature);
+ }
+ return result;
+ }
+
+ private XmlPullParser getXMLFromResources(String xmlFile) throws Exception {
+ InputStream xmlStream = mContext.getResources().getAssets().open(
+ TEST_RESOURCES_FOLDER + "/" + xmlFile);
+ XmlPullParser result = Xml.newPullParser();
+ result.setInput(xmlStream, StandardCharsets.UTF_8.name());
+ int type;
+ // advance the parser to the first tag
+ while ((type = result.next()) != XmlPullParser.START_TAG
+ && type != XmlPullParser.END_DOCUMENT) {
+ ;
+ }
+ return result;
+ }
+
+ private static PackageSetting createPackageSetting() {
+ // Generic PackageSetting object with values from a test app installed on a device to be
+ // used to test the methods under the PackageSignatures signatures data member.
+ File appPath = new File("/data/app/app");
+ PackageSetting result = new PackageSetting("test.app", null, appPath, appPath,
+ "/data/app/app", null, null, null,
+ 1, 940097092, 0, null,
+ null, 0 /*userId*/, null, null);
+ return result;
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java
index 13612a1..182760b 100644
--- a/services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java
@@ -99,7 +99,6 @@
new Signature[] {SIGNATURE_1},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -119,7 +118,6 @@
new Signature[] {SIGNATURE_1},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -203,7 +201,6 @@
new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -226,7 +223,6 @@
new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -248,7 +244,6 @@
new Signature[] {SIGNATURE_1, SIGNATURE_2},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -271,7 +266,6 @@
new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -295,7 +289,6 @@
new Signature[] {SIGNATURE_1},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -320,7 +313,6 @@
new Signature[] {SIGNATURE_1, SIGNATURE_2},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -348,7 +340,6 @@
new Signature[] {SIGNATURE_1, SIGNATURE_2},
PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
- null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
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/power/BatterySaverPolicyTest.java b/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java
index 54ac6fc..bd4a356 100644
--- a/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java
@@ -84,9 +84,6 @@
}
@Mock
- Handler mHandler;
-
- @Mock
MetricsLogger mMetricsLogger = mock(MetricsLogger.class);
private BatterySaverPolicyForTest mBatterySaverPolicy;
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/services/tests/uiservicestests/Android.mk b/services/tests/uiservicestests/Android.mk
index 8405179..f3f4355 100644
--- a/services/tests/uiservicestests/Android.mk
+++ b/services/tests/uiservicestests/Android.mk
@@ -45,6 +45,7 @@
libbacktrace \
libbase \
libbinder \
+ libbinderthreadstate \
libc++ \
libcutils \
liblog \
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 58aae2b..4e007c2d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -16,6 +16,8 @@
package com.android.server.notification;
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE;
import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
import static android.app.NotificationManager.EXTRA_BLOCKED_STATE;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
@@ -74,6 +76,7 @@
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
+import android.app.ITransientNotification;
import android.app.IUriGrantsManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.usage.UsageStatsManagerInternal;
@@ -118,12 +121,14 @@
import com.android.internal.R;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.server.LocalServices;
+import com.android.server.SystemService;
import com.android.server.UiServiceTestCase;
import com.android.server.lights.Light;
import com.android.server.lights.LightsManager;
import com.android.server.notification.NotificationManagerService.NotificationAssistants;
import com.android.server.notification.NotificationManagerService.NotificationListeners;
import com.android.server.uri.UriGrantsManagerInternal;
+import com.android.server.wm.WindowManagerInternal;
import org.junit.After;
import org.junit.Before;
@@ -160,6 +165,8 @@
private IPackageManager mPackageManager;
@Mock
private PackageManager mPackageManagerClient;
+ @Mock
+ private WindowManagerInternal mWindowManagerInternal;
private TestableContext mContext = spy(getContext());
private final String PKG = mContext.getPackageName();
private TestableLooper mTestableLooper;
@@ -238,6 +245,16 @@
}
}
+ private class TestableToastCallback extends ITransientNotification.Stub {
+ @Override
+ public void show(IBinder windowToken) {
+ }
+
+ @Override
+ public void hide() {
+ }
+ }
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -249,6 +266,8 @@
LocalServices.removeServiceForTest(UriGrantsManagerInternal.class);
LocalServices.addService(UriGrantsManagerInternal.class, mUgmInternal);
+ LocalServices.removeServiceForTest(WindowManagerInternal.class);
+ LocalServices.addService(WindowManagerInternal.class, mWindowManagerInternal);
mService = new TestableNotificationManagerService(mContext);
@@ -302,6 +321,7 @@
mGroupHelper, mAm, mAppUsageStats,
mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal,
mAppOpsManager);
+ mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
} catch (SecurityException e) {
if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
throw e;
@@ -3548,4 +3568,93 @@
assertEquals(0, captor.getValue().getNotification().flags);
}
+
+ @Test
+ public void testAllowForegroundToasts() throws Exception {
+ final String testPackage = "testPackageName";
+ assertEquals(0, mService.mToastQueue.size());
+ mService.isSystemUid = false;
+
+ // package is not suspended
+ when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
+ .thenReturn(false);
+
+ // notifications from this package are blocked by the user
+ mService.setPreferencesHelper(mPreferencesHelper);
+ when(mPreferencesHelper.getImportance(testPackage, mUid)).thenReturn(IMPORTANCE_NONE);
+
+ // this app is in the foreground
+ when(mActivityManager.getUidImportance(mUid)).thenReturn(IMPORTANCE_FOREGROUND);
+
+ // enqueue toast -> toast should still enqueue
+ ((INotificationManager)mService.mService).enqueueToast(testPackage,
+ new TestableToastCallback(), 2000, 0);
+ assertEquals(1, mService.mToastQueue.size());
+ }
+
+ @Test
+ public void testDisallowToastsFromSuspendedPackages() throws Exception {
+ final String testPackage = "testPackageName";
+ assertEquals(0, mService.mToastQueue.size());
+ mService.isSystemUid = false;
+
+ // package is suspended
+ when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
+ .thenReturn(true);
+
+ // notifications from this package are NOT blocked by the user
+ mService.setPreferencesHelper(mPreferencesHelper);
+ when(mPreferencesHelper.getImportance(testPackage, mUid)).thenReturn(IMPORTANCE_LOW);
+
+ // enqueue toast -> no toasts enqueued
+ ((INotificationManager)mService.mService).enqueueToast(testPackage,
+ new TestableToastCallback(), 2000, 0);
+ assertEquals(0, mService.mToastQueue.size());
+ }
+
+ @Test
+ public void testDisallowToastsFromBlockedApps() throws Exception {
+ final String testPackage = "testPackageName";
+ assertEquals(0, mService.mToastQueue.size());
+ mService.isSystemUid = false;
+
+ // package is not suspended
+ when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
+ .thenReturn(false);
+
+ // notifications from this package are blocked by the user
+ mService.setPreferencesHelper(mPreferencesHelper);
+ when(mPreferencesHelper.getImportance(testPackage, mUid)).thenReturn(IMPORTANCE_NONE);
+
+ // this app is NOT in the foreground
+ when(mActivityManager.getUidImportance(mUid)).thenReturn(IMPORTANCE_GONE);
+
+ // enqueue toast -> no toasts enqueued
+ ((INotificationManager)mService.mService).enqueueToast(testPackage,
+ new TestableToastCallback(), 2000, 0);
+ assertEquals(0, mService.mToastQueue.size());
+ }
+
+ @Test
+ public void testAlwaysAllowSystemToasts() throws Exception {
+ final String testPackage = "testPackageName";
+ assertEquals(0, mService.mToastQueue.size());
+ mService.isSystemUid = true;
+
+ // package is suspended
+ when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
+ .thenReturn(true);
+
+ // notifications from this package ARE blocked by the user
+ mService.setPreferencesHelper(mPreferencesHelper);
+ when(mPreferencesHelper.getImportance(testPackage, mUid)).thenReturn(IMPORTANCE_NONE);
+
+ // this app is NOT in the foreground
+ when(mActivityManager.getUidImportance(mUid)).thenReturn(IMPORTANCE_GONE);
+
+ // enqueue toast -> system toast can still be enqueued
+ ((INotificationManager)mService.mService).enqueueToast(testPackage,
+ new TestableToastCallback(), 2000, 0);
+ assertEquals(1, mService.mToastQueue.size());
+ }
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
index c616685..8946d25 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
@@ -79,7 +79,7 @@
* and downgrades have taken place. The original file version denotes how to parse the file.
*/
public class UsageStatsDatabase {
- private static final int DEFAULT_CURRENT_VERSION = 4;
+ private static final int DEFAULT_CURRENT_VERSION = 3;
// Current version of the backup schema
static final int BACKUP_VERSION = 1;
@@ -856,6 +856,9 @@
Slog.e(TAG, "UsageStatsDatabase", e);
throw e;
}
+ // If old version, don't bother sanity checking
+ if (version < 4) return;
+
// STOPSHIP: b/111422946, b/115429334
// Everything below this comment is sanity check against the new database version.
// After the new version has soaked for some time the following should removed.
diff --git a/startop/iorap/Android.bp b/startop/iorap/Android.bp
new file mode 100644
index 0000000..b3b0900
--- /dev/null
+++ b/startop/iorap/Android.bp
@@ -0,0 +1,28 @@
+// 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.
+
+java_library_static {
+ name: "libiorap-java",
+
+ aidl: {
+ include_dirs: [
+ "system/iorap/binder",
+ ],
+ },
+
+ srcs: [
+ ":iorap-aidl",
+ "**/*.java",
+ ],
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/ActivityHintEvent.java b/startop/iorap/src/com/google/android/startop/iorap/ActivityHintEvent.java
new file mode 100644
index 0000000..1d38f4c
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/ActivityHintEvent.java
@@ -0,0 +1,139 @@
+/*
+ * 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.os.Parcelable;
+import android.os.Parcel;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Provide a hint to iorapd that an activity has transitioned state.<br /><br />
+ *
+ * Knowledge of when an activity starts/stops can be used by iorapd to increase system
+ * performance (e.g. by launching perfetto tracing to record an io profile, or by
+ * playing back an ioprofile via readahead) over the long run.<br /><br />
+ *
+ * /@see com.google.android.startop.iorap.IIorap#onActivityHintEvent<br /><br />
+ *
+ * Once an activity hint is in {@link #TYPE_STARTED} it must transition to another type.
+ * All other states could be terminal, see below: <br /><br />
+ *
+ * <pre>
+ *
+ * ┌──────────────────────────────────────┐
+ * │ ▼
+ * ┌─────────┐ ╔════════════════╗ ╔═══════════╗
+ * ──▶ │ STARTED │ ──▶ ║ COMPLETED ║ ──▶ ║ CANCELLED ║
+ * └─────────┘ ╚════════════════╝ ╚═══════════╝
+ * │
+ * │
+ * ▼
+ * ╔════════════════╗
+ * ║ POST_COMPLETED ║
+ * ╚════════════════╝
+ *
+ * </pre> <!-- system/iorap/docs/binder/ActivityHint.dot -->
+ *
+ * @hide
+ */
+public class ActivityHintEvent implements Parcelable {
+
+ public static final int TYPE_STARTED = 0;
+ public static final int TYPE_CANCELLED = 1;
+ public static final int TYPE_COMPLETED = 2;
+ public static final int TYPE_POST_COMPLETED = 3;
+ private static final int TYPE_MAX = TYPE_POST_COMPLETED;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "TYPE_" }, value = {
+ TYPE_STARTED,
+ TYPE_CANCELLED,
+ TYPE_COMPLETED,
+ TYPE_POST_COMPLETED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Type {}
+
+ @Type public final int type;
+ public final ActivityInfo activityInfo;
+
+ public ActivityHintEvent(@Type int type, ActivityInfo activityInfo) {
+ this.type = type;
+ this.activityInfo = activityInfo;
+ checkConstructorArguments();
+ }
+
+ private void checkConstructorArguments() {
+ CheckHelpers.checkTypeInRange(type, TYPE_MAX);
+ Objects.requireNonNull(activityInfo, "activityInfo");
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{type: %d, activityInfo: %s}", type, activityInfo);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ } else if (other instanceof ActivityHintEvent) {
+ return equals((ActivityHintEvent) other);
+ }
+ return false;
+ }
+
+ private boolean equals(ActivityHintEvent other) {
+ return type == other.type &&
+ Objects.equals(activityInfo, other.activityInfo);
+ }
+
+ //<editor-fold desc="Binder boilerplate">
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(type);
+ activityInfo.writeToParcel(out, flags);
+ }
+
+ private ActivityHintEvent(Parcel in) {
+ this.type = in.readInt();
+ this.activityInfo = ActivityInfo.CREATOR.createFromParcel(in);
+ checkConstructorArguments();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<ActivityHintEvent> CREATOR
+ = new Parcelable.Creator<ActivityHintEvent>() {
+ public ActivityHintEvent createFromParcel(Parcel in) {
+ return new ActivityHintEvent(in);
+ }
+
+ public ActivityHintEvent[] newArray(int size) {
+ return new ActivityHintEvent[size];
+ }
+ };
+ //</editor-fold>
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/ActivityInfo.java b/startop/iorap/src/com/google/android/startop/iorap/ActivityInfo.java
new file mode 100644
index 0000000..f47a42c
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/ActivityInfo.java
@@ -0,0 +1,104 @@
+/*
+ * 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 java.util.Objects;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+/**
+ * Provide minimal information for launched activities to iorap.<br /><br />
+ *
+ * This uniquely identifies a system-wide activity by providing the {@link #packageName} and
+ * {@link #activityName}.
+ *
+ * @see ActivityHintEvent
+ * @see AppIntentEvent
+ *
+ * @hide
+ */
+public class ActivityInfo implements Parcelable {
+
+ /** The name of the package, for example {@code com.android.calculator}. */
+ public final String packageName;
+ /** The name of the activity, for example {@code .activities.activity.MainActivity} */
+ public final String activityName;
+
+ public ActivityInfo(String packageName, String activityName) {
+ this.packageName = packageName;
+ this.activityName = activityName;
+
+ checkConstructorArguments();
+ }
+
+ private void checkConstructorArguments() {
+ Objects.requireNonNull(packageName, "packageName");
+ Objects.requireNonNull(activityName, "activityName");
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{packageName: %s, activityName: %s}", packageName, activityName);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ } else if (other instanceof ActivityInfo) {
+ return equals((ActivityInfo) other);
+ }
+ return false;
+ }
+
+ private boolean equals(ActivityInfo other) {
+ return Objects.equals(packageName, other.packageName) &&
+ Objects.equals(activityName, other.activityName);
+ }
+
+ //<editor-fold desc="Binder boilerplate">
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeString(packageName);
+ out.writeString(activityName);
+ }
+
+ private ActivityInfo(Parcel in) {
+ packageName = in.readString();
+ activityName = in.readString();
+
+ checkConstructorArguments();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<ActivityInfo> CREATOR
+ = new Parcelable.Creator<ActivityInfo>() {
+ public ActivityInfo createFromParcel(Parcel in) {
+ return new ActivityInfo(in);
+ }
+
+ public ActivityInfo[] newArray(int size) {
+ return new ActivityInfo[size];
+ }
+ };
+ //</editor-fold>
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/AppIntentEvent.java b/startop/iorap/src/com/google/android/startop/iorap/AppIntentEvent.java
new file mode 100644
index 0000000..1cd37b5
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/AppIntentEvent.java
@@ -0,0 +1,138 @@
+/*
+ * 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.os.Parcelable;
+import android.os.Parcel;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Notifications for iorapd specifying when a system-wide intent defaults change.<br /><br />
+ *
+ * Intent defaults provide a mechanism for an app to register itself as an automatic handler.
+ * For example the camera app might be registered as the default handler for
+ * {@link android.provider.MediaStore#INTENT_ACTION_STILL_IMAGE_CAMERA} intent. Subsequently,
+ * if an arbitrary other app requests for a still image camera photo to be taken, the system
+ * will launch the respective default camera app to be launched to handle that request.<br /><br />
+ *
+ * In some cases iorapd might need to know default intents, e.g. for boot-time pinning of
+ * applications that resolve from the default intent. If the application would now be resolved
+ * differently, iorapd would unpin the old application and pin the new application.<br /><br />
+ *
+ * @hide
+ */
+public class AppIntentEvent implements Parcelable {
+
+ /** @see android.content.Intent#CATEGORY_DEFAULT */
+ public static final int TYPE_DEFAULT_INTENT_CHANGED = 0;
+ private static final int TYPE_MAX = 0;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "TYPE_" }, value = {
+ TYPE_DEFAULT_INTENT_CHANGED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Type {}
+
+ @Type public final int type;
+
+ public final ActivityInfo oldActivityInfo;
+ public final ActivityInfo newActivityInfo;
+
+ // TODO: Probably need the corresponding action here as well.
+
+ public static AppIntentEvent createDefaultIntentChanged(ActivityInfo oldActivityInfo,
+ ActivityInfo newActivityInfo) {
+ return new AppIntentEvent(TYPE_DEFAULT_INTENT_CHANGED, oldActivityInfo,
+ newActivityInfo);
+ }
+
+ private AppIntentEvent(@Type int type, ActivityInfo oldActivityInfo,
+ ActivityInfo newActivityInfo) {
+ this.type = type;
+ this.oldActivityInfo = oldActivityInfo;
+ this.newActivityInfo = newActivityInfo;
+
+ checkConstructorArguments();
+ }
+
+ private void checkConstructorArguments() {
+ CheckHelpers.checkTypeInRange(type, TYPE_MAX);
+ Objects.requireNonNull(oldActivityInfo, "oldActivityInfo");
+ Objects.requireNonNull(oldActivityInfo, "newActivityInfo");
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{oldActivityInfo: %s, newActivityInfo: %s}", oldActivityInfo,
+ newActivityInfo);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ } else if (other instanceof AppIntentEvent) {
+ return equals((AppIntentEvent) other);
+ }
+ return false;
+ }
+
+ private boolean equals(AppIntentEvent other) {
+ return type == other.type &&
+ Objects.equals(oldActivityInfo, other.oldActivityInfo) &&
+ Objects.equals(newActivityInfo, other.newActivityInfo);
+ }
+
+ //<editor-fold desc="Binder boilerplate">
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(type);
+ oldActivityInfo.writeToParcel(out, flags);
+ newActivityInfo.writeToParcel(out, flags);
+ }
+
+ private AppIntentEvent(Parcel in) {
+ this.type = in.readInt();
+ this.oldActivityInfo = ActivityInfo.CREATOR.createFromParcel(in);
+ this.newActivityInfo = ActivityInfo.CREATOR.createFromParcel(in);
+
+ checkConstructorArguments();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<AppIntentEvent> CREATOR
+ = new Parcelable.Creator<AppIntentEvent>() {
+ public AppIntentEvent createFromParcel(Parcel in) {
+ return new AppIntentEvent(in);
+ }
+
+ public AppIntentEvent[] newArray(int size) {
+ return new AppIntentEvent[size];
+ }
+ };
+ //</editor-fold>
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/CheckHelpers.java b/startop/iorap/src/com/google/android/startop/iorap/CheckHelpers.java
new file mode 100644
index 0000000..34aedd7
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/CheckHelpers.java
@@ -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.
+ */
+package com.google.android.startop.iorap;
+
+/**
+ * Convenience short-hand to throw {@link IllegalAccessException} when the arguments
+ * are out-of-range.
+ */
+public class CheckHelpers {
+ /** @throws IllegalAccessException if {@param type} is not in {@code [0..maxValue]} */
+ public static void checkTypeInRange(int type, int maxValue) {
+ if (type < 0) {
+ throw new IllegalArgumentException(
+ String.format("type must be non-negative (value=%d)", type));
+ }
+ if (type > maxValue) {
+ throw new IllegalArgumentException(
+ String.format("type out of range (value=%d, max=%d)", type, maxValue));
+ }
+ }
+
+ /** @throws IllegalAccessException if {@param state} is not in {@code [0..maxValue]} */
+ public static void checkStateInRange(int state, int maxValue) {
+ if (state < 0) {
+ throw new IllegalArgumentException(
+ String.format("state must be non-negative (value=%d)", state));
+ }
+ if (state > maxValue) {
+ throw new IllegalArgumentException(
+ String.format("state out of range (value=%d, max=%d)", state, maxValue));
+ }
+ }
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/PackageEvent.java b/startop/iorap/src/com/google/android/startop/iorap/PackageEvent.java
new file mode 100644
index 0000000..aa4eea7
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/PackageEvent.java
@@ -0,0 +1,131 @@
+/*
+ * 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.annotation.NonNull;
+import android.os.Parcelable;
+import android.os.Parcel;
+import android.net.Uri;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Forward package manager events to iorapd. <br /><br />
+ *
+ * Knowing when packages are modified by the system are a useful tidbit to help with performance:
+ * for example when a package is replaced, it could be a hint used to invalidate any collected
+ * io profiles used for prefetching or pinning.
+ *
+ * @hide
+ */
+public class PackageEvent implements Parcelable {
+
+ /** @see android.content.Intent#ACTION_PACKAGE_REPLACED */
+ public static final int TYPE_REPLACED = 0;
+ private static final int TYPE_MAX = 0;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "TYPE_" }, value = {
+ TYPE_REPLACED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Type {}
+
+ @Type public final int type;
+
+ /** The path that a package is installed in, for example {@code /data/app/.../base.apk}. */
+ public final Uri packageUri;
+ /** The name of the package, for example {@code com.android.calculator}. */
+ public final String packageName;
+
+ @NonNull
+ public static PackageEvent createReplaced(Uri packageUri, String packageName) {
+ return new PackageEvent(TYPE_REPLACED, packageUri, packageName);
+ }
+
+ private PackageEvent(@Type int type, Uri packageUri, String packageName) {
+ this.type = type;
+ this.packageUri = packageUri;
+ this.packageName = packageName;
+
+ checkConstructorArguments();
+ }
+
+ private void checkConstructorArguments() {
+ CheckHelpers.checkTypeInRange(type, TYPE_MAX);
+ Objects.requireNonNull(packageUri, "packageUri");
+ Objects.requireNonNull(packageName, "packageName");
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ } else if (other instanceof PackageEvent) {
+ return equals((PackageEvent) other);
+ }
+ return false;
+ }
+
+ private boolean equals(PackageEvent other) {
+ return type == other.type &&
+ Objects.equals(packageUri, other.packageUri) &&
+ Objects.equals(packageName, other.packageName);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{packageUri: %s, packageName: %s}", packageUri, packageName);
+ }
+
+ //<editor-fold desc="Binder boilerplate">
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(type);
+ packageUri.writeToParcel(out, flags);
+ out.writeString(packageName);
+ }
+
+ private PackageEvent(Parcel in) {
+ this.type = in.readInt();
+ this.packageUri = Uri.CREATOR.createFromParcel(in);
+ this.packageName = in.readString();
+
+ checkConstructorArguments();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<PackageEvent> CREATOR
+ = new Parcelable.Creator<PackageEvent>() {
+ public PackageEvent createFromParcel(Parcel in) {
+ return new PackageEvent(in);
+ }
+
+ public PackageEvent[] newArray(int size) {
+ return new PackageEvent[size];
+ }
+ };
+ //</editor-fold>
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/RequestId.java b/startop/iorap/src/com/google/android/startop/iorap/RequestId.java
new file mode 100644
index 0000000..2c79319
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/RequestId.java
@@ -0,0 +1,120 @@
+/*
+ * 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.os.Parcelable;
+import android.os.Parcel;
+
+import android.annotation.NonNull;
+
+/**
+ * Uniquely identify an {@link com.google.android.startop.iorap.IIorap} method invocation,
+ * used for asynchronous callbacks by the server. <br /><br />
+ *
+ * As all system server binder calls must be {@code oneway}, this means all invocations
+ * into {@link com.google.android.startop.iorap.IIorap} are non-blocking. The request ID
+ * exists to associate all calls with their respective callbacks in
+ * {@link com.google.android.startop.iorap.ITaskListener}.
+ *
+ * @see com.google.android.startop.iorap.IIorap
+ *
+ * @hide
+ */
+public class RequestId implements Parcelable {
+
+ public final long requestId;
+
+ private static Object mLock = new Object();
+ private static long mNextRequestId = 0;
+
+ /**
+ * Create a monotonically increasing request ID.<br /><br />
+ *
+ * It is invalid to re-use the same request ID for multiple method calls on
+ * {@link com.google.android.startop.iorap.IIorap}; a new request ID must be created
+ * each time.
+ */
+ @NonNull public static RequestId nextValueForSequence() {
+ long currentRequestId;
+ synchronized (mLock) {
+ currentRequestId = mNextRequestId;
+ ++mNextRequestId;
+ }
+ return new RequestId(currentRequestId);
+ }
+
+ private RequestId(long requestId) {
+ this.requestId = requestId;
+
+ checkConstructorArguments();
+ }
+
+ private void checkConstructorArguments() {
+ if (requestId < 0) {
+ throw new IllegalArgumentException("request id must be non-negative");
+ }
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{requestId: %ld}", requestId);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ } else if (other instanceof RequestId) {
+ return equals((RequestId) other);
+ }
+ return false;
+ }
+
+ private boolean equals(RequestId other) {
+ return requestId == other.requestId;
+ }
+
+
+ //<editor-fold desc="Binder boilerplate">
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeLong(requestId);
+ }
+
+ private RequestId(Parcel in) {
+ requestId = in.readLong();
+
+ checkConstructorArguments();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<RequestId> CREATOR
+ = new Parcelable.Creator<RequestId>() {
+ public RequestId createFromParcel(Parcel in) {
+ return new RequestId(in);
+ }
+
+ public RequestId[] newArray(int size) {
+ return new RequestId[size];
+ }
+ };
+ //</editor-fold>
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/SystemServiceEvent.java b/startop/iorap/src/com/google/android/startop/iorap/SystemServiceEvent.java
new file mode 100644
index 0000000..75d47f9
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/SystemServiceEvent.java
@@ -0,0 +1,109 @@
+/*
+ * 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.os.Parcelable;
+import android.os.Parcel;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Forward system service events to iorapd.
+ *
+ * @see com.android.server.SystemService
+ *
+ * @hide
+ */
+public class SystemServiceEvent implements Parcelable {
+
+ /** @see com.android.server.SystemService#onBootPhase */
+ public static final int TYPE_BOOT_PHASE = 0;
+ /** @see com.android.server.SystemService#onStart */
+ public static final int TYPE_START = 1;
+ private static final int TYPE_MAX = TYPE_START;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "TYPE_" }, value = {
+ TYPE_BOOT_PHASE,
+ TYPE_START,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Type {}
+
+ @Type public final int type;
+
+ // TODO: do we want to pass the exact build phase enum?
+
+ public SystemServiceEvent(@Type int type) {
+ this.type = type;
+ checkConstructorArguments();
+ }
+
+ private void checkConstructorArguments() {
+ CheckHelpers.checkTypeInRange(type, TYPE_MAX);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{type: %d}", type);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ } else if (other instanceof SystemServiceEvent) {
+ return equals((SystemServiceEvent) other);
+ }
+ return false;
+ }
+
+ private boolean equals(SystemServiceEvent other) {
+ return type == other.type;
+ }
+
+ //<editor-fold desc="Binder boilerplate">
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(type);
+ }
+
+ private SystemServiceEvent(Parcel in) {
+ this.type = in.readInt();
+ checkConstructorArguments();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<SystemServiceEvent> CREATOR
+ = new Parcelable.Creator<SystemServiceEvent>() {
+ public SystemServiceEvent createFromParcel(Parcel in) {
+ return new SystemServiceEvent(in);
+ }
+
+ public SystemServiceEvent[] newArray(int size) {
+ return new SystemServiceEvent[size];
+ }
+ };
+ //</editor-fold>
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/SystemServiceUserEvent.java b/startop/iorap/src/com/google/android/startop/iorap/SystemServiceUserEvent.java
new file mode 100644
index 0000000..b77c03c
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/SystemServiceUserEvent.java
@@ -0,0 +1,127 @@
+/*
+ * 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.os.Parcelable;
+import android.os.Parcel;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Forward user events to iorapd.<br /><br />
+ *
+ * Knowledge of the logged-in user is reserved to be used to set-up appropriate policies
+ * by iorapd (e.g. to handle user default pinned applications changing).
+ *
+ * @see com.android.server.SystemService
+ *
+ * @hide
+ */
+public class SystemServiceUserEvent implements Parcelable {
+
+ /** @see com.android.server.SystemService#onStartUser */
+ public static final int TYPE_START_USER = 0;
+ /** @see com.android.server.SystemService#onUnlockUser */
+ public static final int TYPE_UNLOCK_USER = 1;
+ /** @see com.android.server.SystemService#onSwitchUser*/
+ public static final int TYPE_SWITCH_USER = 2;
+ /** @see com.android.server.SystemService#onStopUser */
+ public static final int TYPE_STOP_USER = 3;
+ /** @see com.android.server.SystemService#onCleanupUser */
+ public static final int TYPE_CLEANUP_USER = 4;
+ private static final int TYPE_MAX = TYPE_CLEANUP_USER;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "TYPE_" }, value = {
+ TYPE_START_USER,
+ TYPE_UNLOCK_USER,
+ TYPE_SWITCH_USER,
+ TYPE_STOP_USER,
+ TYPE_CLEANUP_USER,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Type {}
+
+ @Type public final int type;
+ public final int userHandle;
+
+ public SystemServiceUserEvent(@Type int type, int userHandle) {
+ this.type = type;
+ this.userHandle = userHandle;
+ checkConstructorArguments();
+ }
+
+ private void checkConstructorArguments() {
+ CheckHelpers.checkTypeInRange(type, TYPE_MAX);
+ if (userHandle < 0) {
+ throw new IllegalArgumentException("userHandle must be non-negative");
+ }
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{type: %d, userHandle: %d}", type, userHandle);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ } else if (other instanceof SystemServiceUserEvent) {
+ return equals((SystemServiceUserEvent) other);
+ }
+ return false;
+ }
+
+ private boolean equals(SystemServiceUserEvent other) {
+ return type == other.type &&
+ userHandle == other.userHandle;
+ }
+
+ //<editor-fold desc="Binder boilerplate">
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(type);
+ out.writeInt(userHandle);
+ }
+
+ private SystemServiceUserEvent(Parcel in) {
+ this.type = in.readInt();
+ this.userHandle = in.readInt();
+ checkConstructorArguments();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<SystemServiceUserEvent> CREATOR
+ = new Parcelable.Creator<SystemServiceUserEvent>() {
+ public SystemServiceUserEvent createFromParcel(Parcel in) {
+ return new SystemServiceUserEvent(in);
+ }
+
+ public SystemServiceUserEvent[] newArray(int size) {
+ return new SystemServiceUserEvent[size];
+ }
+ };
+ //</editor-fold>
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/TaskResult.java b/startop/iorap/src/com/google/android/startop/iorap/TaskResult.java
new file mode 100644
index 0000000..b5fd6d8
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/TaskResult.java
@@ -0,0 +1,130 @@
+/*
+ * 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.os.Parcelable;
+import android.os.Parcel;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Result data accompanying a request for {@link com.google.android.startop.iorap.ITaskListener}
+ * callbacks.<br /><br />
+ *
+ * Following {@link com.google.android.startop.iorap.IIorap} method invocation,
+ * iorapd will issue in-order callbacks for that corresponding {@link RequestId}.<br /><br />
+ *
+ * State transitions are as follows: <br /><br />
+ *
+ * <pre>
+ * ┌─────────────────────────────┐
+ * │ ▼
+ * ┌───────┐ ┌─────────┐ ╔═══════════╗
+ * ──▶ │ BEGAN │ ──▶ │ ONGOING │ ──▶ ║ COMPLETED ║
+ * └───────┘ └─────────┘ ╚═══════════╝
+ * │ │
+ * │ │
+ * ▼ │
+ * ╔═══════╗ │
+ * ──▶ ║ ERROR ║ ◀─────┘
+ * ╚═══════╝
+ *
+ * </pre> <!-- system/iorap/docs/binder/TaskResult.dot -->
+ *
+ * @hide
+ */
+public class TaskResult implements Parcelable {
+
+ public static final int STATE_BEGAN = 0;
+ public static final int STATE_ONGOING = 1;
+ public static final int STATE_COMPLETED = 2;
+ public static final int STATE_ERROR = 3;
+ private static final int STATE_MAX = STATE_ERROR;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "STATE_" }, value = {
+ STATE_BEGAN,
+ STATE_ONGOING,
+ STATE_COMPLETED,
+ STATE_ERROR,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface State {}
+
+ @State public final int state;
+
+ @Override
+ public String toString() {
+ return String.format("{state: %d}", state);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ } else if (other instanceof TaskResult) {
+ return equals((TaskResult) other);
+ }
+ return false;
+ }
+
+ private boolean equals(TaskResult other) {
+ return state == other.state;
+ }
+
+ public TaskResult(@State int state) {
+ this.state = state;
+
+ checkConstructorArguments();
+ }
+
+ private void checkConstructorArguments() {
+ CheckHelpers.checkStateInRange(state, STATE_MAX);
+ }
+
+ //<editor-fold desc="Binder boilerplate">
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(state);
+ }
+
+ private TaskResult(Parcel in) {
+ state = in.readInt();
+
+ checkConstructorArguments();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<TaskResult> CREATOR
+ = new Parcelable.Creator<TaskResult>() {
+ public TaskResult createFromParcel(Parcel in) {
+ return new TaskResult(in);
+ }
+
+ public TaskResult[] newArray(int size) {
+ return new TaskResult[size];
+ }
+ };
+ //</editor-fold>
+}
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/tests/AndroidManifest.xml b/startop/iorap/tests/AndroidManifest.xml
new file mode 100644
index 0000000..99f4add
--- /dev/null
+++ b/startop/iorap/tests/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<!--suppress AndroidUnknownAttribute -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.google.android.startop.iorap.tests"
+ android:sharedUserId="com.google.android.startop.iorap.tests"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <!--suppress AndroidDomInspection -->
+ <instrumentation
+ android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.google.android.startop.iorap.tests" />
+
+ <!--
+ '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/startop/iorap/tests/src/com/google/android/startop/iorap/ParcelablesTest.kt b/startop/iorap/tests/src/com/google/android/startop/iorap/ParcelablesTest.kt
new file mode 100644
index 0000000..4abbb3e
--- /dev/null
+++ b/startop/iorap/tests/src/com/google/android/startop/iorap/ParcelablesTest.kt
@@ -0,0 +1,140 @@
+/*
+ * 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.Parcel
+import android.os.Parcelable
+import android.support.test.filters.SmallTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import com.google.common.truth.Truth.assertThat
+import org.junit.runners.Parameterized
+
+/**
+ * Basic unit tests to ensure that all of the [Parcelable]s in [com.google.android.startop.iorap]
+ * have a valid-conforming interface implementation.
+ */
+@SmallTest
+@RunWith(Parameterized::class)
+class ParcelablesTest<T : Parcelable>(private val inputData : InputData<T>) {
+ companion object {
+ private val initialRequestId = RequestId.nextValueForSequence()!!
+
+ @JvmStatic
+ @Parameterized.Parameters
+ fun data() = listOf(
+ InputData(
+ newActivityInfo(),
+ newActivityInfo(),
+ ActivityInfo("some package", "some other activity")),
+ InputData(
+ ActivityHintEvent(ActivityHintEvent.TYPE_COMPLETED, newActivityInfo()),
+ ActivityHintEvent(ActivityHintEvent.TYPE_COMPLETED, newActivityInfo()),
+ ActivityHintEvent(ActivityHintEvent.TYPE_POST_COMPLETED,
+ newActivityInfo())),
+ InputData(
+ AppIntentEvent.createDefaultIntentChanged(newActivityInfo(),
+ newActivityInfoOther()),
+ AppIntentEvent.createDefaultIntentChanged(newActivityInfo(),
+ newActivityInfoOther()),
+ AppIntentEvent.createDefaultIntentChanged(newActivityInfoOther(),
+ newActivityInfo())),
+ InputData(
+ PackageEvent.createReplaced(newUri(), "some package"),
+ PackageEvent.createReplaced(newUri(), "some package"),
+ PackageEvent.createReplaced(newUri(), "some other package")
+ ),
+ InputData(initialRequestId, cloneRequestId(initialRequestId),
+ RequestId.nextValueForSequence()),
+ InputData(
+ SystemServiceEvent(SystemServiceEvent.TYPE_BOOT_PHASE),
+ SystemServiceEvent(SystemServiceEvent.TYPE_BOOT_PHASE),
+ SystemServiceEvent(SystemServiceEvent.TYPE_START)),
+ InputData(
+ SystemServiceUserEvent(SystemServiceUserEvent.TYPE_START_USER, 12345),
+ SystemServiceUserEvent(SystemServiceUserEvent.TYPE_START_USER, 12345),
+ SystemServiceUserEvent(SystemServiceUserEvent.TYPE_CLEANUP_USER, 12345)),
+ InputData(
+ TaskResult(TaskResult.STATE_COMPLETED),
+ TaskResult(TaskResult.STATE_COMPLETED),
+ TaskResult(TaskResult.STATE_ONGOING))
+ )
+
+ private fun newActivityInfo() : ActivityInfo {
+ return ActivityInfo("some package", "some activity")
+ }
+
+ private fun newActivityInfoOther() : ActivityInfo {
+ return ActivityInfo("some package 2", "some activity 2")
+ }
+
+ private fun newUri() : Uri {
+ return Uri.parse("https://www.google.com")
+ }
+
+ private fun cloneRequestId(requestId: RequestId) : RequestId {
+ val constructor = requestId::class.java.declaredConstructors[0]
+ constructor.isAccessible = true
+ return constructor.newInstance(requestId.requestId) as RequestId
+ }
+ }
+
+ /**
+ * Test for [Object.equals] implementation.
+ */
+ @Test
+ fun testEquality() {
+ assertThat(inputData.valid).isEqualTo(inputData.valid)
+ assertThat(inputData.valid).isEqualTo(inputData.validCopy)
+ assertThat(inputData.valid).isNotEqualTo(inputData.validOther)
+ }
+
+ /**
+ * Test for [Parcelable] implementation.
+ */
+ @Test
+ fun testParcelRoundTrip() {
+ // calling writeToParcel and then T::CREATOR.createFromParcel would return the same data.
+ val assertParcels = { it : T, data : InputData<T> ->
+ val parcel = Parcel.obtain()
+ it.writeToParcel(parcel, 0)
+ parcel.setDataPosition(0) // future reads will see all previous writes.
+ assertThat(it).isEqualTo(data.createFromParcel(parcel))
+ parcel.recycle()
+ }
+
+ assertParcels(inputData.valid, inputData)
+ assertParcels(inputData.validCopy, inputData)
+ assertParcels(inputData.validOther, inputData)
+ }
+
+ data class InputData<T : Parcelable>(val valid : T, val validCopy : T, val validOther : T) {
+ val kls = valid.javaClass
+ init {
+ assertThat(valid).isNotSameAs(validCopy)
+ // Don't use isInstanceOf because of phantom warnings in intellij about Class!
+ assertThat(validCopy.javaClass).isEqualTo(valid.javaClass)
+ assertThat(validOther.javaClass).isEqualTo(valid.javaClass)
+ }
+
+ fun createFromParcel(parcel : Parcel) : T {
+ val field = kls.getDeclaredField("CREATOR")
+ val creator = field.get(null) as Parcelable.Creator<T>
+
+ return creator.createFromParcel(parcel)
+ }
+ }
+}
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 8c37a21..d33a537 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1905,6 +1905,22 @@
return false;
}
+ /**
+ * Handles {@link Intent#ACTION_CALL} intents trampolined from UserCallActivity.
+ * @param intent The {@link Intent#ACTION_CALL} intent to handle.
+ * @hide
+ */
+ public void handleCallIntent(Intent intent) {
+ try {
+ if (isServiceConnected()) {
+ getTelecomService().handleCallIntent(intent);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException handleCallIntent: " + e);
+ }
+
+ }
+
private ITelecomService getTelecomService() {
if (mTelecomServiceOverride != null) {
return mTelecomServiceOverride;
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 38247bc..df7d683 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -284,4 +284,9 @@
* @see TelecomServiceImpl#isInEmergencyCall
*/
boolean isInEmergencyCall();
+
+ /**
+ * @see TelecomServiceImpl#handleCallIntent
+ */
+ void handleCallIntent(in Intent intent);
}
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/NeighboringCellInfo.java b/telephony/java/android/telephony/NeighboringCellInfo.java
index 5e4518f..79298fd 100644
--- a/telephony/java/android/telephony/NeighboringCellInfo.java
+++ b/telephony/java/android/telephony/NeighboringCellInfo.java
@@ -33,8 +33,9 @@
* Represents the neighboring cell information, including
* Received Signal Strength and Cell ID location.
*
- * @deprecated This class should not be used by anyone targeting SDK level 29 (Q) or higher.
- * Instead callers should use {@Link android.telephony.CellInfo}.
+ * @deprecated This class should not be used by any app targeting
+ * {@link Build.VERSION_CODES.Q Android Q} or higher. Instead callers should use
+ * {@Link android.telephony.CellInfo CellInfo}.
*/
@Deprecated
public class NeighboringCellInfo implements Parcelable
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index f2b73dc..7469186 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -24,6 +24,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.telephony.AccessNetworkConstants.AccessNetworkType;
+import android.telephony.NetworkRegistrationState.Domain;
import android.text.TextUtils;
import java.lang.annotation.Retention;
@@ -1595,7 +1596,7 @@
/**
* Get all of the available network registration states.
*
- * @return List of registration states
+ * @return List of {@link NetworkRegistrationState}
* @hide
*/
@SystemApi
@@ -1606,14 +1607,30 @@
}
/**
- * Get the network registration states with given transport type.
+ * Get the network registration states for the transport type.
*
- * @param transportType The transport type. See {@link AccessNetworkConstants.TransportType}
- * @return List of registration states.
+ * @param transportType The {@link AccessNetworkConstants.TransportType transport type}
+ * @return List of {@link NetworkRegistrationState}
+ * @hide
+ *
+ * @deprecated Use {@link #getNetworkRegistrationStatesFromTransportType(int)}
+ */
+ @Deprecated
+ @SystemApi
+ public List<NetworkRegistrationState> getNetworkRegistrationStates(int transportType) {
+ return getNetworkRegistrationStatesForTransportType(transportType);
+ }
+
+ /**
+ * Get the network registration states for the transport type.
+ *
+ * @param transportType The {@link AccessNetworkConstants.TransportType transport type}
+ * @return List of {@link NetworkRegistrationState}
* @hide
*/
@SystemApi
- public List<NetworkRegistrationState> getNetworkRegistrationStates(int transportType) {
+ public List<NetworkRegistrationState> getNetworkRegistrationStatesForTransportType(
+ int transportType) {
List<NetworkRegistrationState> list = new ArrayList<>();
synchronized (mNetworkRegistrationStates) {
@@ -1628,16 +1645,57 @@
}
/**
- * Get the network registration states with given transport type and domain.
+ * Get the network registration states for the network domain.
*
- * @param domain The network domain. Must be {@link NetworkRegistrationState#DOMAIN_CS} or
- * {@link NetworkRegistrationState#DOMAIN_PS}.
- * @param transportType The transport type. See {@link AccessNetworkConstants.TransportType}
- * @return The matching NetworkRegistrationState.
+ * @param domain The network {@link NetworkRegistrationState.Domain domain}
+ * @return List of {@link NetworkRegistrationState}
* @hide
*/
@SystemApi
- public NetworkRegistrationState getNetworkRegistrationStates(int domain, int transportType) {
+ public List<NetworkRegistrationState> getNetworkRegistrationStatesForDomain(
+ @Domain int domain) {
+ List<NetworkRegistrationState> list = new ArrayList<>();
+
+ synchronized (mNetworkRegistrationStates) {
+ for (NetworkRegistrationState networkRegistrationState : mNetworkRegistrationStates) {
+ if (networkRegistrationState.getDomain() == domain) {
+ list.add(networkRegistrationState);
+ }
+ }
+ }
+
+ return list;
+ }
+
+ /**
+ * Get the network registration state for the transport type and network domain.
+ *
+ * @param domain The network {@link NetworkRegistrationState.Domain domain}
+ * @param transportType The {@link AccessNetworkConstants.TransportType transport type}
+ * @return The matching {@link NetworkRegistrationState}
+ * @hide
+ *
+ * @deprecated Use {@link #getNetworkRegistrationState(int, int)}
+ */
+ @Deprecated
+ @SystemApi
+ public NetworkRegistrationState getNetworkRegistrationStates(@Domain int domain,
+ int transportType) {
+ return getNetworkRegistrationState(domain, transportType);
+ }
+
+ /**
+ * Get the network registration state for the transport type and network domain.
+ *
+ * @param domain The network {@link NetworkRegistrationState.Domain domain}
+ * @param transportType The {@link AccessNetworkConstants.TransportType transport type}
+ * @return The matching {@link NetworkRegistrationState}
+ * @hide
+ *
+ */
+ @SystemApi
+ public NetworkRegistrationState getNetworkRegistrationState(@Domain int domain,
+ int transportType) {
synchronized (mNetworkRegistrationStates) {
for (NetworkRegistrationState networkRegistrationState : mNetworkRegistrationStates) {
if (networkRegistrationState.getTransportType() == transportType
@@ -1669,5 +1727,4 @@
mNetworkRegistrationStates.add(regState);
}
}
-
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index fdf8231..52c8f5a 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -167,7 +167,6 @@
/** @hide */
static public final int OTASP_SIM_UNPROVISIONED = 5;
-
/** @hide */
static public final int KEY_TYPE_EPDG = 1;
@@ -2924,7 +2923,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}
@@ -5929,7 +5928,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
@@ -5954,7 +5953,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.
*
@@ -6033,7 +6032,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
@@ -6058,7 +6057,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
@@ -6091,7 +6090,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.
*
@@ -6981,7 +6980,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);
@@ -6993,7 +6993,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}.
@@ -7018,7 +7018,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},
@@ -7087,7 +7087,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);
@@ -7880,7 +7881,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}).
@@ -8349,7 +8350,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
@@ -8380,6 +8381,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/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
index 3138180..cecf2e2 100644
--- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -21,12 +21,11 @@
import android.net.Uri;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
+import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.aidl.IImsRegistration;
import android.telephony.ims.aidl.IImsRegistrationCallback;
import android.util.Log;
-import android.telephony.ims.ImsReasonInfo;
-
import com.android.internal.annotations.VisibleForTesting;
import java.lang.annotation.Retention;
@@ -81,13 +80,14 @@
* Callback class for receiving Registration callback events.
* @hide
*/
- public static class Callback {
+ public static class Callback extends IImsRegistrationCallback.Stub {
/**
* Notifies the framework when the IMS Provider is connected to the IMS network.
*
* @param imsRadioTech the radio access technology. Valid values are defined in
* {@link ImsRegistrationTech}.
*/
+ @Override
public void onRegistered(@ImsRegistrationTech int imsRadioTech) {
}
@@ -97,6 +97,7 @@
* @param imsRadioTech the radio access technology. Valid values are defined in
* {@link ImsRegistrationTech}.
*/
+ @Override
public void onRegistering(@ImsRegistrationTech int imsRadioTech) {
}
@@ -105,6 +106,7 @@
*
* @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
*/
+ @Override
public void onDeregistered(ImsReasonInfo info) {
}
@@ -115,6 +117,7 @@
* @param imsRadioTech The {@link ImsRegistrationTech} type that has failed
* @param info A {@link ImsReasonInfo} that identifies the reason for failure.
*/
+ @Override
public void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech,
ImsReasonInfo info) {
}
@@ -125,6 +128,7 @@
* @param uris new array of subscriber {@link Uri}s that are associated with this IMS
* subscription.
*/
+ @Override
public void onSubscriberAssociatedUriChanged(Uri[] uris) {
}
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/NetworkSecurityConfigTest/Android.mk b/tests/NetworkSecurityConfigTest/Android.mk
index fe65ecc..c225e17 100644
--- a/tests/NetworkSecurityConfigTest/Android.mk
+++ b/tests/NetworkSecurityConfigTest/Android.mk
@@ -7,7 +7,6 @@
LOCAL_JAVA_LIBRARIES := \
android.test.runner \
- bouncycastle \
conscrypt \
android.test.base \
diff --git a/tests/net/Android.mk b/tests/net/Android.mk
index e529b93..750e2fb 100644
--- a/tests/net/Android.mk
+++ b/tests/net/Android.mk
@@ -38,6 +38,7 @@
libbacktrace \
libbase \
libbinder \
+ libbinderthreadstate \
libc++ \
libcrypto \
libcutils \
diff --git a/tests/net/java/android/net/dhcp/DhcpPacketTest.java b/tests/net/java/android/net/dhcp/DhcpPacketTest.java
index 312b3d1..a592809 100644
--- a/tests/net/java/android/net/dhcp/DhcpPacketTest.java
+++ b/tests/net/java/android/net/dhcp/DhcpPacketTest.java
@@ -25,6 +25,7 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.annotation.Nullable;
import android.net.DhcpResults;
import android.net.LinkAddress;
import android.net.NetworkUtils;
@@ -37,6 +38,7 @@
import java.io.ByteArrayOutputStream;
import java.net.Inet4Address;
import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -56,6 +58,8 @@
private static final Inet4Address NETMASK = getPrefixMaskAsInet4Address(PREFIX_LENGTH);
private static final Inet4Address BROADCAST_ADDR = getBroadcastAddress(
SERVER_ADDR, PREFIX_LENGTH);
+ private static final String HOSTNAME = "testhostname";
+ private static final short MTU = 1500;
// Use our own empty address instead of Inet4Address.ANY or INADDR_ANY to ensure that the code
// doesn't use == instead of equals when comparing addresses.
private static final Inet4Address ANY = (Inet4Address) v4Address("0.0.0.0");
@@ -960,7 +964,8 @@
assertTrue(msg, Arrays.equals(expected, actual));
}
- public void checkBuildOfferPacket(int leaseTimeSecs) throws Exception {
+ public void checkBuildOfferPacket(int leaseTimeSecs, @Nullable String hostname)
+ throws Exception {
final int renewalTime = (int) (Integer.toUnsignedLong(leaseTimeSecs) / 2);
final int rebindingTime = (int) (Integer.toUnsignedLong(leaseTimeSecs) * 875 / 1000);
final int transactionId = 0xdeadbeef;
@@ -971,7 +976,8 @@
CLIENT_MAC, leaseTimeSecs, NETMASK /* netMask */,
BROADCAST_ADDR /* bcAddr */, Collections.singletonList(SERVER_ADDR) /* gateways */,
Collections.singletonList(SERVER_ADDR) /* dnsServers */,
- SERVER_ADDR /* dhcpServerIdentifier */, null /* domainName */, false /* metered */);
+ SERVER_ADDR /* dhcpServerIdentifier */, null /* domainName */, hostname,
+ false /* metered */, MTU);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
// BOOTP headers
@@ -1027,12 +1033,22 @@
// Nameserver
bos.write(new byte[] { (byte) 0x06, (byte) 0x04 });
bos.write(SERVER_ADDR.getAddress());
+ // Hostname
+ if (hostname != null) {
+ bos.write(new byte[]{(byte) 0x0c, (byte) hostname.length()});
+ bos.write(hostname.getBytes(Charset.forName("US-ASCII")));
+ }
+ // MTU
+ bos.write(new byte[] { (byte) 0x1a, (byte) 0x02 });
+ bos.write(shortToByteArray(MTU));
// End options.
bos.write(0xff);
- final byte[] expected = bos.toByteArray();
- assertTrue((expected.length & 1) == 0);
+ if ((bos.size() & 1) != 0) {
+ bos.write(0x00);
+ }
+ final byte[] expected = bos.toByteArray();
final byte[] actual = new byte[packet.limit()];
packet.get(actual);
final String msg = "Expected:\n " + HexDump.dumpHexString(expected) +
@@ -1042,13 +1058,18 @@
@Test
public void testOfferPacket() throws Exception {
- checkBuildOfferPacket(3600);
- checkBuildOfferPacket(Integer.MAX_VALUE);
- checkBuildOfferPacket(0x80000000);
- checkBuildOfferPacket(INFINITE_LEASE);
+ checkBuildOfferPacket(3600, HOSTNAME);
+ checkBuildOfferPacket(Integer.MAX_VALUE, HOSTNAME);
+ checkBuildOfferPacket(0x80000000, HOSTNAME);
+ checkBuildOfferPacket(INFINITE_LEASE, HOSTNAME);
+ checkBuildOfferPacket(3600, null);
}
private static byte[] intToByteArray(int val) {
return ByteBuffer.allocate(4).putInt(val).array();
}
+
+ private static byte[] shortToByteArray(short val) {
+ return ByteBuffer.allocate(2).putShort(val).array();
+ }
}
diff --git a/tests/net/java/android/net/dhcp/DhcpServerTest.java b/tests/net/java/android/net/dhcp/DhcpServerTest.java
index 45a50d9..df34c73 100644
--- a/tests/net/java/android/net/dhcp/DhcpServerTest.java
+++ b/tests/net/java/android/net/dhcp/DhcpServerTest.java
@@ -17,6 +17,7 @@
package android.net.dhcp;
import static android.net.dhcp.DhcpPacket.DHCP_CLIENT;
+import static android.net.dhcp.DhcpPacket.DHCP_HOST_NAME;
import static android.net.dhcp.DhcpPacket.ENCAP_BOOTP;
import static android.net.dhcp.DhcpPacket.INADDR_ANY;
import static android.net.dhcp.DhcpPacket.INADDR_BROADCAST;
@@ -87,6 +88,7 @@
Arrays.asList(parseAddr("192.168.0.200"), parseAddr("192.168.0.201")));
private static final long TEST_LEASE_TIME_SECS = 3600L;
private static final int TEST_MTU = 1500;
+ private static final String TEST_HOSTNAME = "testhostname";
private static final int TEST_TRANSACTION_ID = 123;
private static final byte[] TEST_CLIENT_MAC_BYTES = new byte [] { 1, 2, 3, 4, 5, 6 };
@@ -96,7 +98,10 @@
private static final long TEST_CLOCK_TIME = 1234L;
private static final int TEST_LEASE_EXPTIME_SECS = 3600;
private static final DhcpLease TEST_LEASE = new DhcpLease(null, TEST_CLIENT_MAC,
- TEST_CLIENT_ADDR, TEST_LEASE_EXPTIME_SECS*1000L + TEST_CLOCK_TIME, null /* hostname */);
+ TEST_CLIENT_ADDR, TEST_LEASE_EXPTIME_SECS * 1000L + TEST_CLOCK_TIME,
+ null /* hostname */);
+ private static final DhcpLease TEST_LEASE_WITH_HOSTNAME = new DhcpLease(null, TEST_CLIENT_MAC,
+ TEST_CLIENT_ADDR, TEST_LEASE_EXPTIME_SECS * 1000L + TEST_CLOCK_TIME, TEST_HOSTNAME);
@NonNull @Mock
private Dependencies mDeps;
@@ -217,15 +222,17 @@
public void testRequest_Selecting_Ack() throws Exception {
when(mRepository.requestLease(isNull() /* clientId */, eq(TEST_CLIENT_MAC),
eq(INADDR_ANY) /* clientAddr */, eq(INADDR_ANY) /* relayAddr */,
- eq(TEST_CLIENT_ADDR) /* reqAddr */, eq(true) /* sidSet */, isNull() /* hostname */))
- .thenReturn(TEST_LEASE);
+ eq(TEST_CLIENT_ADDR) /* reqAddr */, eq(true) /* sidSet */, eq(TEST_HOSTNAME)))
+ .thenReturn(TEST_LEASE_WITH_HOSTNAME);
final DhcpRequestPacket request = makeRequestSelectingPacket();
+ request.mHostName = TEST_HOSTNAME;
+ request.mRequestedParams = new byte[] { DHCP_HOST_NAME };
mServer.processPacket(request, DHCP_CLIENT);
assertResponseSentTo(TEST_CLIENT_ADDR);
final DhcpAckPacket packet = assertAck(getPacket());
- assertMatchesTestLease(packet);
+ assertMatchesTestLease(packet, TEST_HOSTNAME);
}
@Test
@@ -270,14 +277,18 @@
* - other request states (init-reboot/renewing/rebinding)
*/
- private void assertMatchesTestLease(@NonNull DhcpPacket packet) {
+ private void assertMatchesTestLease(@NonNull DhcpPacket packet, @Nullable String hostname) {
assertMatchesClient(packet);
assertFalse(packet.hasExplicitClientId());
assertEquals(TEST_SERVER_ADDR, packet.mServerIdentifier);
assertEquals(TEST_CLIENT_ADDR, packet.mYourIp);
assertNotNull(packet.mLeaseTime);
assertEquals(TEST_LEASE_EXPTIME_SECS, (int) packet.mLeaseTime);
- assertNull(packet.mHostName);
+ assertEquals(hostname, packet.mHostName);
+ }
+
+ private void assertMatchesTestLease(@NonNull DhcpPacket packet) {
+ assertMatchesTestLease(packet, null);
}
private void assertMatchesClient(@NonNull DhcpPacket packet) {
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/tests/testables/src/android/testing/TestableContext.java b/tests/testables/src/android/testing/TestableContext.java
index cf84c79..fff9635 100644
--- a/tests/testables/src/android/testing/TestableContext.java
+++ b/tests/testables/src/android/testing/TestableContext.java
@@ -53,7 +53,7 @@
* Like the following:</p>
* <pre class="prettyprint">
* @Rule
- * private final TestableContext mContext = new TestableContext(InstrumentationRegister.getContext());
+ * public final TestableContext mContext = new TestableContext(InstrumentationRegister.getContext());
* </pre>
*/
public class TestableContext extends ContextWrapper implements TestRule {
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index c48765b..de00fca 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -38,6 +38,8 @@
namespace aapt {
namespace ResourceUtils {
+constexpr int32_t kNonBreakingSpace = 0xa0;
+
Maybe<ResourceName> ToResourceName(
const android::ResTable::resource_name& name_in) {
ResourceName name_out;
@@ -810,7 +812,7 @@
Utf8Iterator iter(text);
while (iter.HasNext()) {
char32_t codepoint = iter.Next();
- if (!preserve_spaces && !quote_ && iswspace(codepoint)) {
+ if (!preserve_spaces && !quote_ && codepoint != kNonBreakingSpace && iswspace(codepoint)) {
if (!last_codepoint_was_space_) {
// Emit a space if it's the first.
xml_string_.text += ' ';
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index 62c19fb..8060a8de 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -73,9 +73,9 @@
};
// Resource file paths are expected to look like: [--/res/]type[-config]/name
-static Maybe<ResourcePathData> ExtractResourcePathData(const std::string& path,
+static Maybe<ResourcePathData> ExtractResourcePathData(const std::string& path, const char dir_sep,
std::string* out_error) {
- std::vector<std::string> parts = util::Split(path, file::sDirSep);
+ std::vector<std::string> parts = util::Split(path, dir_sep);
if (parts.size() < 2) {
if (out_error) *out_error = "bad resource path";
return {};
@@ -656,7 +656,7 @@
// Extract resource type information from the full path
std::string err_str;
ResourcePathData path_data;
- if (auto maybe_path_data = ExtractResourcePathData(path, &err_str)) {
+ if (auto maybe_path_data = ExtractResourcePathData(path, inputs->GetDirSeparator(), &err_str)) {
path_data = maybe_path_data.value();
} else {
context->GetDiagnostics()->Error(DiagMessage(file->GetSource()) << err_str);
@@ -731,7 +731,7 @@
// Collect the resources files to compile
if (options_.res_dir && options_.res_zip) {
context.GetDiagnostics()->Error(DiagMessage()
- << "only one of --dir and --zip can be specified");
+ << "only one of --dir and --zip can be specified");
return 1;
} else if (options_.res_dir) {
if (!args.empty()) {
diff --git a/tools/aapt2/cmd/Compile_test.cpp b/tools/aapt2/cmd/Compile_test.cpp
index dd5198c..c0c05cd 100644
--- a/tools/aapt2/cmd/Compile_test.cpp
+++ b/tools/aapt2/cmd/Compile_test.cpp
@@ -17,6 +17,9 @@
#include "Compile.h"
#include "android-base/file.h"
+#include "android-base/stringprintf.h"
+#include "android-base/utf8.h"
+
#include "io/StringStream.h"
#include "io/ZipArchive.h"
#include "java/AnnotationProcessor.h"
@@ -24,8 +27,20 @@
namespace aapt {
+std::string BuildPath(std::vector<std::string> args) {
+ std::string out;
+ if (args.empty()) {
+ return out;
+ }
+ out = args[0];
+ for (int i = 1; i < args.size(); i++) {
+ file::AppendPath(&out, args[i]);
+ }
+ return out;
+}
+
int TestCompile(const std::string& path, const std::string& outDir, bool legacy,
- StdErrDiagnostics& diag) {
+ StdErrDiagnostics& diag) {
std::vector<android::StringPiece> args;
args.push_back(path);
args.push_back("-o");
@@ -39,95 +54,101 @@
TEST(CompilerTest, MultiplePeriods) {
StdErrDiagnostics diag;
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
- const std::string kResDir = android::base::Dirname(android::base::GetExecutablePath())
- + "/integration-tests/CompileTest/res";
+ const std::string kResDir = BuildPath({android::base::Dirname(android::base::GetExecutablePath()),
+ "integration-tests", "CompileTest", "res"});
// Resource files without periods in the file name should not throw errors
- const std::string path0 = kResDir + "/values/values.xml";
- const std::string path0_out = kResDir + "/values_values.arsc.flat";
-
- remove(path0_out.c_str());
+ const std::string path0 = BuildPath({kResDir, "values", "values.xml"});
+ const std::string path0_out = BuildPath({kResDir, "values_values.arsc.flat"});
+ ::android::base::utf8::unlink(path0_out.c_str());
ASSERT_EQ(TestCompile(path0, kResDir, /** legacy */ false, diag), 0);
- ASSERT_EQ(remove(path0_out.c_str()), 0);
+ ASSERT_EQ(::android::base::utf8::unlink(path0_out.c_str()), 0);
ASSERT_EQ(TestCompile(path0, kResDir, /** legacy */ true, diag), 0);
- ASSERT_EQ(remove(path0_out.c_str()), 0);
+ ASSERT_EQ(::android::base::utf8::unlink(path0_out.c_str()), 0);
- const std::string path1 = kResDir + "/drawable/image.png";
- const std::string path1_out = kResDir + "/drawable_image.png.flat";
- remove(path1_out.c_str());
+ const std::string path1 = BuildPath({kResDir, "drawable", "image.png"});
+ const std::string path1_out = BuildPath({kResDir, "drawable_image.png.flat"});
+ ::android::base::utf8::unlink(path1_out.c_str());
ASSERT_EQ(TestCompile(path1, kResDir, /** legacy */ false, diag), 0);
- ASSERT_EQ(remove(path1_out.c_str()), 0);
+ ASSERT_EQ(::android::base::utf8::unlink(path1_out.c_str()), 0);
ASSERT_EQ(TestCompile(path1, kResDir, /** legacy */ true, diag), 0);
- ASSERT_EQ(remove(path1_out.c_str()), 0);
+ ASSERT_EQ(::android::base::utf8::unlink(path1_out.c_str()), 0);
- const std::string path2 = kResDir + "/drawable/image.9.png";
- const std::string path2_out = kResDir + "/drawable_image.9.png.flat";
- remove(path2_out.c_str());
+ const std::string path2 = BuildPath({kResDir, "drawable", "image.9.png"});
+ const std::string path2_out = BuildPath({kResDir, "drawable_image.9.png.flat"});
+ ::android::base::utf8::unlink(path2_out.c_str());
ASSERT_EQ(TestCompile(path2, kResDir, /** legacy */ false, diag), 0);
- ASSERT_EQ(remove(path2_out.c_str()), 0);
+ ASSERT_EQ(::android::base::utf8::unlink(path2_out.c_str()), 0);
ASSERT_EQ(TestCompile(path2, kResDir, /** legacy */ true, diag), 0);
- ASSERT_EQ(remove(path2_out.c_str()), 0);
+ ASSERT_EQ(::android::base::utf8::unlink(path2_out.c_str()), 0);
// Resource files with periods in the file name should fail on non-legacy compilations
- const std::string path3 = kResDir + "/values/values.all.xml";
- const std::string path3_out = kResDir + "/values_values.all.arsc.flat";
- remove(path3_out.c_str());
+ const std::string path3 = BuildPath({kResDir, "values", "values.all.xml"});
+ const std::string path3_out = BuildPath({kResDir, "values_values.all.arsc.flat"});
+ ::android::base::utf8::unlink(path3_out.c_str());
ASSERT_NE(TestCompile(path3, kResDir, /** legacy */ false, diag), 0);
- ASSERT_NE(remove(path3_out.c_str()), 0);
+ ASSERT_NE(::android::base::utf8::unlink(path3_out.c_str()), 0);
ASSERT_EQ(TestCompile(path3, kResDir, /** legacy */ true, diag), 0);
- ASSERT_EQ(remove(path3_out.c_str()), 0);
+ ASSERT_EQ(::android::base::utf8::unlink(path3_out.c_str()), 0);
- const std::string path4 = kResDir + "/drawable/image.small.png";
- const std::string path4_out = (kResDir + std::string("/drawable_image.small.png.flat")).c_str();
- remove(path4_out.c_str());
+ const std::string path4 = BuildPath({kResDir, "drawable", "image.small.png"});
+ const std::string path4_out = BuildPath({kResDir, "drawable_image.small.png.flat"});
+ ::android::base::utf8::unlink(path4_out.c_str());
ASSERT_NE(TestCompile(path4, kResDir, /** legacy */ false, diag), 0);
- ASSERT_NE(remove(path4_out.c_str()), 0);
+ ASSERT_NE(::android::base::utf8::unlink(path4_out.c_str()), 0);
ASSERT_EQ(TestCompile(path4, kResDir, /** legacy */ true, diag), 0);
- ASSERT_EQ(remove(path4_out.c_str()), 0);
+ ASSERT_EQ(::android::base::utf8::unlink(path4_out.c_str()), 0);
- const std::string path5 = kResDir + "/drawable/image.small.9.png";
- const std::string path5_out = (kResDir + std::string("/drawable_image.small.9.png.flat")).c_str();
- remove(path5_out.c_str());
+ const std::string path5 = BuildPath({kResDir, "drawable", "image.small.9.png"});
+ const std::string path5_out = BuildPath({kResDir, "drawable_image.small.9.png.flat"});
+ ::android::base::utf8::unlink(path5_out.c_str());
ASSERT_NE(TestCompile(path5, kResDir, /** legacy */ false, diag), 0);
- ASSERT_NE(remove(path5_out.c_str()), 0);
+ ASSERT_NE(::android::base::utf8::unlink(path5_out.c_str()), 0);
ASSERT_EQ(TestCompile(path5, kResDir, /** legacy */ true, diag), 0);
- ASSERT_EQ(remove(path5_out.c_str()), 0);
+ ASSERT_EQ(::android::base::utf8::unlink(path5_out.c_str()), 0);
}
TEST(CompilerTest, DirInput) {
StdErrDiagnostics diag;
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
- const std::string kResDir = android::base::Dirname(android::base::GetExecutablePath())
- + "/integration-tests/CompileTest/DirInput/res";
- const std::string kOutputFlata = android::base::Dirname(android::base::GetExecutablePath())
- + "/integration-tests/CompileTest/DirInput/compiled.flata";
- remove(kOutputFlata.c_str());
+ const std::string kResDir = BuildPath({android::base::Dirname(android::base::GetExecutablePath()),
+ "integration-tests", "CompileTest", "DirInput", "res"});
+ const std::string kOutputFlata =
+ BuildPath({android::base::Dirname(android::base::GetExecutablePath()), "integration-tests",
+ "CompileTest", "DirInput", "compiled.flata"});
+ ::android::base::utf8::unlink(kOutputFlata.c_str());
std::vector<android::StringPiece> args;
args.push_back("--dir");
args.push_back(kResDir);
args.push_back("-o");
args.push_back(kOutputFlata);
+ args.push_back("-v");
ASSERT_EQ(CompileCommand(&diag).Execute(args, &std::cerr), 0);
- // Check for the presence of the compiled files
- std::string err;
- std::unique_ptr<io::ZipFileCollection> zip = io::ZipFileCollection::Create(kOutputFlata, &err);
- ASSERT_NE(zip, nullptr) << err;
- ASSERT_NE(zip->FindFile("drawable_image.png.flat"), nullptr);
- ASSERT_NE(zip->FindFile("layout_layout.xml.flat"), nullptr);
- ASSERT_NE(zip->FindFile("values_values.arsc.flat"), nullptr);
- ASSERT_EQ(remove(kOutputFlata.c_str()), 0);
+ {
+ // Check for the presence of the compiled files
+ std::string err;
+ std::unique_ptr<io::ZipFileCollection> zip = io::ZipFileCollection::Create(kOutputFlata, &err);
+ ASSERT_NE(zip, nullptr) << err;
+ ASSERT_NE(zip->FindFile("drawable_image.png.flat"), nullptr);
+ ASSERT_NE(zip->FindFile("layout_layout.xml.flat"), nullptr);
+ ASSERT_NE(zip->FindFile("values_values.arsc.flat"), nullptr);
+ }
+ ASSERT_EQ(::android::base::utf8::unlink(kOutputFlata.c_str()), 0);
}
TEST(CompilerTest, ZipInput) {
StdErrDiagnostics diag;
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
- const std::string kResZip = android::base::Dirname(android::base::GetExecutablePath())
- + "/integration-tests/CompileTest/ZipInput/res.zip";
- const std::string kOutputFlata = android::base::Dirname(android::base::GetExecutablePath())
- + "/integration-tests/CompileTest/ZipInput/compiled.flata";
- remove(kOutputFlata.c_str());
+ const std::string kResZip =
+ BuildPath({android::base::Dirname(android::base::GetExecutablePath()), "integration-tests",
+ "CompileTest", "ZipInput", "res.zip"});
+ const std::string kOutputFlata =
+ BuildPath({android::base::Dirname(android::base::GetExecutablePath()), "integration-tests",
+ "CompileTest", "ZipInput", "compiled.flata"});
+
+ ::android::base::utf8::unlink(kOutputFlata.c_str());
std::vector<android::StringPiece> args;
args.push_back("--zip");
@@ -136,14 +157,16 @@
args.push_back(kOutputFlata);
ASSERT_EQ(CompileCommand(&diag).Execute(args, &std::cerr), 0);
- // Check for the presence of the compiled files
- std::string err;
- std::unique_ptr<io::ZipFileCollection> zip = io::ZipFileCollection::Create(kOutputFlata, &err);
- ASSERT_NE(zip, nullptr) << err;
- ASSERT_NE(zip->FindFile("drawable_image.png.flat"), nullptr);
- ASSERT_NE(zip->FindFile("layout_layout.xml.flat"), nullptr);
- ASSERT_NE(zip->FindFile("values_values.arsc.flat"), nullptr);
- ASSERT_EQ(remove(kOutputFlata.c_str()), 0);
+ {
+ // Check for the presence of the compiled files
+ std::string err;
+ std::unique_ptr<io::ZipFileCollection> zip = io::ZipFileCollection::Create(kOutputFlata, &err);
+ ASSERT_NE(zip, nullptr) << err;
+ ASSERT_NE(zip->FindFile("drawable_image.png.flat"), nullptr);
+ ASSERT_NE(zip->FindFile("layout_layout.xml.flat"), nullptr);
+ ASSERT_NE(zip->FindFile("values_values.arsc.flat"), nullptr);
+ }
+ ASSERT_EQ(::android::base::utf8::unlink(kOutputFlata.c_str()), 0);
}
-} // namespace aapt
\ No newline at end of file
+} // namespace aapt
\ No newline at end of file
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 13c1047..20ea3cb6 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -1260,7 +1260,7 @@
return false;
}
- proguard::WriteKeepSet(keep_set, &fout);
+ proguard::WriteKeepSet(keep_set, &fout, options_.generate_minimal_proguard_rules);
fout.Flush();
if (fout.HadError()) {
diff --git a/tools/aapt2/cmd/Link.h b/tools/aapt2/cmd/Link.h
index e58a93e..a42c0f2 100644
--- a/tools/aapt2/cmd/Link.h
+++ b/tools/aapt2/cmd/Link.h
@@ -49,6 +49,7 @@
Maybe<std::string> generate_proguard_rules_path;
Maybe<std::string> generate_main_dex_proguard_rules_path;
bool generate_conditional_proguard_rules = false;
+ bool generate_minimal_proguard_rules = false;
bool generate_non_final_ids = false;
std::vector<std::string> javadoc_annotations;
Maybe<std::string> private_symbols;
@@ -119,6 +120,9 @@
AddOptionalSwitch("--proguard-conditional-keep-rules",
"Generate conditional Proguard keep rules.",
&options_.generate_conditional_proguard_rules);
+ AddOptionalSwitch("--proguard-minimal-keep-rules",
+ "Generate a minimal set of Proguard keep rules.",
+ &options_.generate_minimal_proguard_rules);
AddOptionalSwitch("--no-auto-version", "Disables automatic style and layout SDK versioning.",
&options_.no_auto_version);
AddOptionalSwitch("--no-version-vectors",
diff --git a/tools/aapt2/cmd/Util_test.cpp b/tools/aapt2/cmd/Util_test.cpp
index 158ef29..6aeff08 100644
--- a/tools/aapt2/cmd/Util_test.cpp
+++ b/tools/aapt2/cmd/Util_test.cpp
@@ -16,12 +16,22 @@
#include "Util.h"
+#include "android-base/stringprintf.h"
+
#include "AppInfo.h"
#include "split/TableSplitter.h"
#include "test/Builders.h"
#include "test/Test.h"
+#include "util/Files.h"
namespace aapt {
+
+#ifdef _WIN32
+#define CREATE_PATH(path) android::base::StringPrintf(";%s", path)
+#else
+#define CREATE_PATH(path) android::base::StringPrintf(":%s", path)
+#endif
+
#define EXPECT_CONFIG_EQ(constraints, config) \
EXPECT_EQ(constraints.configs.size(), 1); \
EXPECT_EQ(*constraints.configs.begin(), config); \
@@ -89,7 +99,7 @@
}
-TEST (UtilTest, ParseSplitParameter) {
+TEST (UtilTest, ParseSplitParameters) {
IDiagnostics* diagnostics = test::ContextBuilder().Build().get()->GetDiagnostics();
std::string path;
SplitConstraints constraints;
@@ -98,14 +108,14 @@
// ========== Test IMSI ==========
// mcc: 'mcc[0-9]{3}'
// mnc: 'mnc[0-9]{1,3}'
- ASSERT_TRUE(ParseSplitParameter(":mcc310",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("mcc310"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setMcc(0x0136)
.Build();
EXPECT_CONFIG_EQ(constraints, expected_configuration);
- ASSERT_TRUE(ParseSplitParameter(":mcc310-mnc004",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("mcc310-mnc004"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setMcc(0x0136)
@@ -113,7 +123,7 @@
.Build();
EXPECT_CONFIG_EQ(constraints, expected_configuration);
- ASSERT_TRUE(ParseSplitParameter(":mcc310-mnc000",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("mcc310-mnc000"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setMcc(0x0136)
@@ -124,14 +134,14 @@
// ========== Test LOCALE ==========
// locale: '[a-z]{2,3}(-r[a-z]{2})?'
// locale: 'b+[a-z]{2,3}(+[a-z[0-9]]{2})?'
- ASSERT_TRUE(ParseSplitParameter(":es",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("es"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setLanguage(0x6573)
.Build();
EXPECT_CONFIG_EQ(constraints, expected_configuration);
- ASSERT_TRUE(ParseSplitParameter(":fr-rCA",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("fr-rCA"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setLanguage(0x6672)
@@ -139,7 +149,7 @@
.Build();
EXPECT_CONFIG_EQ(constraints, expected_configuration);
- ASSERT_TRUE(ParseSplitParameter(":b+es+419",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("b+es+419"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setLanguage(0x6573)
@@ -151,21 +161,21 @@
// orientation: '(port|land|square)'
// touchscreen: '(notouch|stylus|finger)'
// density" '(anydpi|nodpi|ldpi|mdpi|tvdpi|hdpi|xhdpi|xxhdpi|xxxhdpi|[0-9]*dpi)'
- ASSERT_TRUE(ParseSplitParameter(":square",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("square"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setOrientation(0x03)
.Build();
EXPECT_CONFIG_EQ(constraints, expected_configuration);
- ASSERT_TRUE(ParseSplitParameter(":stylus",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("stylus"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setTouchscreen(0x02)
.Build();
EXPECT_CONFIG_EQ(constraints, expected_configuration);
- ASSERT_TRUE(ParseSplitParameter(":xxxhdpi",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("xxxhdpi"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setDensity(0x0280)
@@ -173,7 +183,7 @@
.Build();
EXPECT_CONFIG_EQ(constraints, expected_configuration);
- ASSERT_TRUE(ParseSplitParameter(":land-xhdpi-finger",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("land-xhdpi-finger"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setOrientation(0x02)
@@ -188,28 +198,28 @@
// navigation: '(nonav|dpad|trackball|wheel)'
// inputFlags: '(keysexposed|keyshidden|keyssoft)'
// inputFlags: '(navexposed|navhidden)'
- ASSERT_TRUE(ParseSplitParameter(":qwerty",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("qwerty"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setKeyboard(0x02)
.Build();
EXPECT_CONFIG_EQ(constraints, expected_configuration);
- ASSERT_TRUE(ParseSplitParameter(":dpad",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("dpad"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setNavigation(0x02)
.Build();
EXPECT_CONFIG_EQ(constraints, expected_configuration);
- ASSERT_TRUE(ParseSplitParameter(":keyssoft-navhidden",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("keyssoft-navhidden"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setInputFlags(0x0B)
.Build();
EXPECT_CONFIG_EQ(constraints, expected_configuration);
- ASSERT_TRUE(ParseSplitParameter(":keyshidden-nokeys-navexposed-trackball",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("keyshidden-nokeys-navexposed-trackball"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setKeyboard(0x01)
@@ -220,7 +230,7 @@
// ========== Test SCREEN_SIZE ==========
// screenWidth/screenHeight: '[0-9]+x[0-9]+'
- ASSERT_TRUE(ParseSplitParameter(":1920x1080",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("1920x1080"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setScreenWidth(0x0780)
@@ -238,14 +248,14 @@
// uiMode [type]: '(desk|car|television|appliance|watch|vrheadset)'
// uiMode [night]: '(night|notnight)'
// smallestScreenWidthDp: 'sw[0-9]dp'
- ASSERT_TRUE(ParseSplitParameter(":ldrtl",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("ldrtl"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setScreenLayout(0x80)
.Build();
EXPECT_CONFIG_EQ(constraints, expected_configuration);
- ASSERT_TRUE(ParseSplitParameter(":small",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("small"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setScreenLayout(0x01)
@@ -253,7 +263,7 @@
.Build();
EXPECT_CONFIG_EQ(constraints, expected_configuration);
- ASSERT_TRUE(ParseSplitParameter(":notlong",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("notlong"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setScreenLayout(0x10)
@@ -261,15 +271,15 @@
.Build();
EXPECT_CONFIG_EQ(constraints, expected_configuration);
- ASSERT_TRUE(ParseSplitParameter(":ldltr-normal-long",
- diagnostics, &path, &constraints));
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("ldltr-normal-long"),
+ diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setScreenLayout(0x62)
.setSdkVersion(0x0004) // screenLayout (size|long) requires donut
.Build();
EXPECT_CONFIG_EQ(constraints, expected_configuration);
- ASSERT_TRUE(ParseSplitParameter(":car",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("car"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setUiMode(0x03)
@@ -277,7 +287,7 @@
.Build();
EXPECT_CONFIG_EQ(constraints, expected_configuration);
- ASSERT_TRUE(ParseSplitParameter(":vrheadset",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("vrheadset"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setUiMode(0x07)
@@ -285,7 +295,7 @@
.Build();
EXPECT_CONFIG_EQ(constraints, expected_configuration);
- ASSERT_TRUE(ParseSplitParameter(":television-night",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("television-night"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setUiMode(0x24)
@@ -293,7 +303,7 @@
.Build();
EXPECT_CONFIG_EQ(constraints, expected_configuration);
- ASSERT_TRUE(ParseSplitParameter(":sw1920dp",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("sw1920dp"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setSmallestScreenWidthDp(0x0780)
@@ -304,7 +314,7 @@
// ========== Test SCREEN_SIZE_DP ==========
// screenWidthDp: 'w[0-9]dp'
// screenHeightDp: 'h[0-9]dp'
- ASSERT_TRUE(ParseSplitParameter(":w1920dp",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("w1920dp"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setScreenWidthDp(0x0780)
@@ -312,7 +322,7 @@
.Build();
EXPECT_CONFIG_EQ(constraints, expected_configuration);
- ASSERT_TRUE(ParseSplitParameter(":h1080dp",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("h1080dp"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setScreenHeightDp(0x0438)
@@ -324,7 +334,7 @@
// screenLayout2: '(round|notround)'
// colorMode: '(widecg|nowidecg)'
// colorMode: '(highhdr|lowdr)'
- ASSERT_TRUE(ParseSplitParameter(":round",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("round"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setScreenLayout2(0x02)
@@ -332,7 +342,7 @@
.Build();
EXPECT_CONFIG_EQ(constraints, expected_configuration);
- ASSERT_TRUE(ParseSplitParameter(":widecg-highdr",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("widecg-highdr"),
diagnostics, &path, &constraints));
expected_configuration = test::ConfigDescriptionBuilder()
.setColorMode(0x0A)
@@ -349,10 +359,10 @@
std::string path;
test_constraints.push_back({});
- ASSERT_TRUE(ParseSplitParameter(":v7",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("v7"),
diagnostics, &path, &test_constraints.back()));
test_constraints.push_back({});
- ASSERT_TRUE(ParseSplitParameter(":xhdpi",
+ ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("xhdpi"),
diagnostics, &path, &test_constraints.back()));
EXPECT_EQ(test_constraints.size(), 2);
EXPECT_EQ(test_constraints[0].name, "v7");
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/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
index 21fdbd8..74295ab 100644
--- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -33,6 +33,7 @@
public:
MOCK_METHOD1(FindFile, io::IFile*(const StringPiece& path));
MOCK_METHOD0(Iterator, std::unique_ptr<io::IFileCollectionIterator>());
+ MOCK_METHOD0(GetDirSeparator, char());
};
TEST(ProtoSerializeTest, SerializeSinglePackage) {
diff --git a/tools/aapt2/io/File.h b/tools/aapt2/io/File.h
index f06e28c..565aad6 100644
--- a/tools/aapt2/io/File.h
+++ b/tools/aapt2/io/File.h
@@ -25,6 +25,7 @@
#include "Source.h"
#include "io/Data.h"
+#include "util/Files.h"
#include "util/Util.h"
namespace aapt {
@@ -103,6 +104,7 @@
virtual IFile* FindFile(const android::StringPiece& path) = 0;
virtual std::unique_ptr<IFileCollectionIterator> Iterator() = 0;
+ virtual char GetDirSeparator() = 0;
};
} // namespace io
diff --git a/tools/aapt2/io/FileSystem.cpp b/tools/aapt2/io/FileSystem.cpp
index 16a20f4c..51cc903 100644
--- a/tools/aapt2/io/FileSystem.cpp
+++ b/tools/aapt2/io/FileSystem.cpp
@@ -128,5 +128,9 @@
return util::make_unique<FileCollectionIterator>(this);
}
+char FileCollection::GetDirSeparator() {
+ return file::sDirSep;
+}
+
} // namespace io
} // namespace aapt
diff --git a/tools/aapt2/io/FileSystem.h b/tools/aapt2/io/FileSystem.h
index fb6bf6e..04c6fa1 100644
--- a/tools/aapt2/io/FileSystem.h
+++ b/tools/aapt2/io/FileSystem.h
@@ -67,6 +67,7 @@
IFile* InsertFile(const android::StringPiece& path);
IFile* FindFile(const android::StringPiece& path) override;
std::unique_ptr<IFileCollectionIterator> Iterator() override;
+ char GetDirSeparator() override;
private:
DISALLOW_COPY_AND_ASSIGN(FileCollection);
diff --git a/tools/aapt2/io/ZipArchive.cpp b/tools/aapt2/io/ZipArchive.cpp
index 866e96d..427dc92 100644
--- a/tools/aapt2/io/ZipArchive.cpp
+++ b/tools/aapt2/io/ZipArchive.cpp
@@ -159,6 +159,13 @@
return util::make_unique<ZipFileCollectionIterator>(this);
}
+char ZipFileCollection::GetDirSeparator() {
+ // According to the zip file specification, section 4.4.17.1:
+ // "All slashes MUST be forward slashes '/' as opposed to backwards slashes '\' for compatibility
+ // with Amiga and UNIX file systems etc."
+ return '/';
+}
+
ZipFileCollection::~ZipFileCollection() {
if (handle_) {
CloseArchive(handle_);
diff --git a/tools/aapt2/io/ZipArchive.h b/tools/aapt2/io/ZipArchive.h
index 8381259..b283e57 100644
--- a/tools/aapt2/io/ZipArchive.h
+++ b/tools/aapt2/io/ZipArchive.h
@@ -66,6 +66,7 @@
io::IFile* FindFile(const android::StringPiece& path) override;
std::unique_ptr<IFileCollectionIterator> Iterator() override;
+ char GetDirSeparator() override;
~ZipFileCollection() override;
diff --git a/tools/aapt2/java/ProguardRules.cpp b/tools/aapt2/java/ProguardRules.cpp
index d40795a..52e168e 100644
--- a/tools/aapt2/java/ProguardRules.cpp
+++ b/tools/aapt2/java/ProguardRules.cpp
@@ -384,7 +384,7 @@
return true;
}
-void WriteKeepSet(const KeepSet& keep_set, OutputStream* out) {
+void WriteKeepSet(const KeepSet& keep_set, OutputStream* out, bool minimal_keep) {
Printer printer(out);
for (const auto& entry : keep_set.manifest_class_set_) {
for (const UsageLocation& location : entry.second) {
@@ -406,15 +406,19 @@
printer.Print("-if class **.R$layout { int ")
.Print(JavaClassGenerator::TransformToFieldName(location.name.entry))
.Println("; }");
- printer.Print("-keep class ").Print(entry.first.name).Print(" { <init>(")
- .Print(entry.first.signature).Println("); }");
+
+ printer.Print("-keep class ").Print(entry.first.name).Print(" { <init>(");
+ printer.Print((minimal_keep) ? entry.first.signature : "...");
+ printer.Println("); }");
}
} else {
for (const UsageLocation& location : entry.second) {
printer.Print("# Referenced at ").Println(location.source.to_string());
}
- printer.Print("-keep class ").Print(entry.first.name).Print(" { <init>(")
- .Print(entry.first.signature).Println("); }");
+
+ printer.Print("-keep class ").Print(entry.first.name).Print(" { <init>(");
+ printer.Print((minimal_keep) ? entry.first.signature : "...");
+ printer.Println("); }");
}
printer.Println();
}
diff --git a/tools/aapt2/java/ProguardRules.h b/tools/aapt2/java/ProguardRules.h
index 01dad0b..38b4860 100644
--- a/tools/aapt2/java/ProguardRules.h
+++ b/tools/aapt2/java/ProguardRules.h
@@ -70,7 +70,7 @@
}
private:
- friend void WriteKeepSet(const KeepSet& keep_set, io::OutputStream* out);
+ friend void WriteKeepSet(const KeepSet& keep_set, io::OutputStream* out, bool minimal_keep);
friend bool CollectLocations(const UsageLocation& location, const KeepSet& keep_set,
std::set<UsageLocation>* locations);
@@ -89,7 +89,7 @@
bool CollectResourceReferences(IAaptContext* context, ResourceTable* table, KeepSet* keep_set);
-void WriteKeepSet(const KeepSet& keep_set, io::OutputStream* out);
+void WriteKeepSet(const KeepSet& keep_set, io::OutputStream* out, bool minimal_keep);
bool CollectLocations(const UsageLocation& location, const KeepSet& keep_set,
std::set<UsageLocation>* locations);
diff --git a/tools/aapt2/java/ProguardRules_test.cpp b/tools/aapt2/java/ProguardRules_test.cpp
index 83c72d8..3d93cb1 100644
--- a/tools/aapt2/java/ProguardRules_test.cpp
+++ b/tools/aapt2/java/ProguardRules_test.cpp
@@ -26,10 +26,10 @@
namespace aapt {
-std::string GetKeepSetString(const proguard::KeepSet& set) {
+std::string GetKeepSetString(const proguard::KeepSet& set, bool minimal_rules) {
std::string out;
StringOutputStream sout(&out);
- proguard::WriteKeepSet(set, &sout);
+ proguard::WriteKeepSet(set, &sout, minimal_rules);
sout.Flush();
return out;
}
@@ -53,8 +53,17 @@
proguard::KeepSet set;
ASSERT_TRUE(proguard::CollectProguardRulesForManifest(manifest.get(), &set, false));
- std::string actual = GetKeepSetString(set);
+ std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarAppComponentFactory { <init>(); }"));
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarBackupAgent { <init>(); }"));
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarApplication { <init>(); }"));
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarActivity { <init>(); }"));
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarService { <init>(); }"));
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarReceiver { <init>(); }"));
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarProvider { <init>(); }"));
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarInstrumentation { <init>(); }"));
+ actual = GetKeepSetString(set, /** minimal_rules */ true);
EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarAppComponentFactory { <init>(); }"));
EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarBackupAgent { <init>(); }"));
EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarApplication { <init>(); }"));
@@ -75,8 +84,10 @@
proguard::KeepSet set;
ASSERT_TRUE(proguard::CollectProguardRules(context.get(), layout.get(), &set));
- std::string actual = GetKeepSetString(set);
+ std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
+ actual = GetKeepSetString(set, /** minimal_rules */ true);
EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(); }"));
}
@@ -89,8 +100,10 @@
proguard::KeepSet set;
ASSERT_TRUE(proguard::CollectProguardRules(context.get(), layout.get(), &set));
- std::string actual = GetKeepSetString(set);
+ std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
+ actual = GetKeepSetString(set, /** minimal_rules */ true);
EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(); }"));
}
@@ -105,8 +118,11 @@
proguard::KeepSet set;
ASSERT_TRUE(proguard::CollectProguardRules(context.get(), layout.get(), &set));
- std::string actual = GetKeepSetString(set);
+ std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Baz { <init>(...); }"));
+ actual = GetKeepSetString(set, /** minimal_rules */ true);
EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(); }"));
EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Baz { <init>(); }"));
}
@@ -133,7 +149,12 @@
proguard::KeepSet set;
ASSERT_TRUE(proguard::CollectProguardRules(context.get(), navigation.get(), &set));
- std::string actual = GetKeepSetString(set);
+ std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
+ EXPECT_THAT(actual, HasSubstr("-keep class com.package.Foo { <init>(...); }"));
+ EXPECT_THAT(actual, HasSubstr("-keep class com.package.Bar { <init>(...); }"));
+ EXPECT_THAT(actual, HasSubstr("-keep class com.base.Nested { <init>(...); }"));
+
+ actual = GetKeepSetString(set, /** minimal_rules */ true);
EXPECT_THAT(actual, HasSubstr("-keep class com.package.Foo { <init>(...); }"));
EXPECT_THAT(actual, HasSubstr("-keep class com.package.Bar { <init>(...); }"));
EXPECT_THAT(actual, HasSubstr("-keep class com.base.Nested { <init>(...); }"));
@@ -150,8 +171,10 @@
proguard::KeepSet set;
ASSERT_TRUE(proguard::CollectProguardRules(context.get(), layout.get(), &set));
- std::string actual = GetKeepSetString(set);
+ std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
+ actual = GetKeepSetString(set, /** minimal_rules */ true);
EXPECT_THAT(actual, HasSubstr(
"-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
}
@@ -188,11 +211,16 @@
ASSERT_TRUE(proguard::CollectProguardRules(context.get(), bar_layout.get(), &set));
ASSERT_TRUE(proguard::CollectProguardRules(context.get(), foo_layout.get(), &set));
- std::string actual = GetKeepSetString(set);
+ std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
+ EXPECT_THAT(actual, HasSubstr("-if class **.R$layout"));
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
+ EXPECT_THAT(actual, HasSubstr("int foo"));
+ EXPECT_THAT(actual, HasSubstr("int bar"));
+ actual = GetKeepSetString(set, /** minimal_rules */ true);
EXPECT_THAT(actual, HasSubstr("-if class **.R$layout"));
EXPECT_THAT(actual, HasSubstr(
- "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
+ "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
EXPECT_THAT(actual, HasSubstr("int foo"));
EXPECT_THAT(actual, HasSubstr("int bar"));
}
@@ -209,10 +237,16 @@
set.AddReference({test::ParseNameOrDie("layout/bar"), {}}, layout->file.name);
ASSERT_TRUE(proguard::CollectProguardRules(context.get(), layout.get(), &set));
- std::string actual = GetKeepSetString(set);
-
+ std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
EXPECT_THAT(actual, HasSubstr(
- "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
+ "-keep class com.foo.Bar { <init>(...); }"));
+ EXPECT_THAT(actual, HasSubstr("-if class **.R$layout"));
+ EXPECT_THAT(actual, HasSubstr("int foo"));
+ EXPECT_THAT(actual, HasSubstr("int bar"));
+
+ actual = GetKeepSetString(set, /** minimal_rules */ true);
+ EXPECT_THAT(actual, HasSubstr(
+ "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
EXPECT_THAT(actual, HasSubstr("-if class **.R$layout"));
EXPECT_THAT(actual, HasSubstr("int foo"));
EXPECT_THAT(actual, HasSubstr("int bar"));
@@ -230,11 +264,14 @@
set.AddReference({test::ParseNameOrDie("style/MyStyle"), {}}, layout->file.name);
ASSERT_TRUE(proguard::CollectProguardRules(context.get(), layout.get(), &set));
- std::string actual = GetKeepSetString(set);
+ std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
+ EXPECT_THAT(actual, Not(HasSubstr("-if")));
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
+ actual = GetKeepSetString(set, /** minimal_rules */ true);
EXPECT_THAT(actual, Not(HasSubstr("-if")));
EXPECT_THAT(actual, HasSubstr(
- "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
+ "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
}
TEST(ProguardRulesTest, ViewOnClickRuleIsEmitted) {
@@ -247,10 +284,13 @@
proguard::KeepSet set;
ASSERT_TRUE(proguard::CollectProguardRules(context.get(), layout.get(), &set));
- std::string actual = GetKeepSetString(set);
-
+ std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
EXPECT_THAT(actual, HasSubstr(
"-keepclassmembers class * { *** bar_method(android.view.View); }"));
+
+ actual = GetKeepSetString(set, /** minimal_rules */ true);
+ EXPECT_THAT(actual, HasSubstr(
+ "-keepclassmembers class * { *** bar_method(android.view.View); }"));
}
TEST(ProguardRulesTest, MenuRulesAreEmitted) {
@@ -267,10 +307,16 @@
proguard::KeepSet set;
ASSERT_TRUE(proguard::CollectProguardRules(context.get(), menu.get(), &set));
- std::string actual = GetKeepSetString(set);
-
+ std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
EXPECT_THAT(actual, HasSubstr(
- "-keepclassmembers class * { *** on_click(android.view.MenuItem); }"));
+ "-keepclassmembers class * { *** on_click(android.view.MenuItem); }"));
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Baz { <init>(...); }"));
+ EXPECT_THAT(actual, Not(HasSubstr("com.foo.Bat")));
+
+ actual = GetKeepSetString(set, /** minimal_rules */ true);
+ EXPECT_THAT(actual, HasSubstr(
+ "-keepclassmembers class * { *** on_click(android.view.MenuItem); }"));
EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(android.content.Context); }"));
EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Baz { <init>(android.content.Context); }"));
EXPECT_THAT(actual, Not(HasSubstr("com.foo.Bat")));
@@ -287,10 +333,12 @@
proguard::KeepSet set;
ASSERT_TRUE(proguard::CollectProguardRules(context.get(), transition.get(), &set));
- std::string actual = GetKeepSetString(set);
+ std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
+ actual = GetKeepSetString(set, /** minimal_rules */ true);
EXPECT_THAT(actual, HasSubstr(
- "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
+ "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
}
TEST(ProguardRulesTest, TransitionRulesAreEmitted) {
@@ -304,10 +352,12 @@
proguard::KeepSet set;
ASSERT_TRUE(proguard::CollectProguardRules(context.get(), transitionSet.get(), &set));
- std::string actual = GetKeepSetString(set);
+ std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
+ actual = GetKeepSetString(set, /** minimal_rules */ true);
EXPECT_THAT(actual, HasSubstr(
- "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
+ "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
}
} // namespace aapt
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/tools/aapt2/util/Files_test.cpp b/tools/aapt2/util/Files_test.cpp
index 219c183..202cc26 100644
--- a/tools/aapt2/util/Files_test.cpp
+++ b/tools/aapt2/util/Files_test.cpp
@@ -18,11 +18,21 @@
#include <sstream>
+#include "android-base/stringprintf.h"
+
#include "test/Test.h"
+using ::android::base::StringPrintf;
+
namespace aapt {
namespace file {
+#ifdef _WIN32
+constexpr const char sTestDirSep = '\\';
+#else
+constexpr const char sTestDirSep = '/';
+#endif
+
class FilesTest : public ::testing::Test {
public:
void SetUp() override {
@@ -42,16 +52,16 @@
}
TEST_F(FilesTest, AppendPathWithLeadingOrTrailingSeparators) {
- std::string base = "hello/";
+ std::string base = StringPrintf("hello%c", sTestDirSep);
AppendPath(&base, "there");
EXPECT_EQ(expected_path_, base);
base = "hello";
- AppendPath(&base, "/there");
+ AppendPath(&base, StringPrintf("%cthere", sTestDirSep));
EXPECT_EQ(expected_path_, base);
- base = "hello/";
- AppendPath(&base, "/there");
+ base = StringPrintf("hello%c", sTestDirSep);
+ AppendPath(&base, StringPrintf("%cthere", sTestDirSep));
EXPECT_EQ(expected_path_, base);
}
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;