diff options
329 files changed, 9815 insertions, 2965 deletions
diff --git a/Android.bp b/Android.bp index faad6f32dec4..9f93d399f074 100644 --- a/Android.bp +++ b/Android.bp @@ -152,9 +152,10 @@ java_defaults { ":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", diff --git a/api/current.txt b/api/current.txt index e6a31afafa1d..4c67bae139ff 100755 --- a/api/current.txt +++ b/api/current.txt @@ -23,6 +23,7 @@ package android { 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 @@ package android { 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 @@ package android { 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 @@ package android { 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 @@ package android { 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 @@ package android { 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 @@ package android { 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 @@ package android.app.admin { 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 @@ package android.graphics { 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 @@ package android.graphics { 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 { 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 @@ package android.provider { 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"; @@ -46641,7 +46658,12 @@ package android.view { } 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 5785e4aa3969..e134554c5257 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -128,6 +128,7 @@ package android { field public static final java.lang.String PEERS_MAC_ADDRESS = "android.permission.PEERS_MAC_ADDRESS"; field public static final java.lang.String PERFORM_CDMA_PROVISIONING = "android.permission.PERFORM_CDMA_PROVISIONING"; field public static final java.lang.String PERFORM_SIM_ACTIVATION = "android.permission.PERFORM_SIM_ACTIVATION"; + field public static final java.lang.String POWER_SAVER = "android.permission.POWER_SAVER"; field public static final java.lang.String PROVIDE_RESOLVER_RANKER_SERVICE = "android.permission.PROVIDE_RESOLVER_RANKER_SERVICE"; field public static final java.lang.String PROVIDE_TRUST_AGENT = "android.permission.PROVIDE_TRUST_AGENT"; field public static final java.lang.String QUERY_TIME_ZONE_RULES = "android.permission.QUERY_TIME_ZONE_RULES"; @@ -3995,6 +3996,7 @@ package android.os { } public final class PowerManager { + method public boolean setPowerSaveMode(boolean); method public void userActivity(long, int, int); field public static final int USER_ACTIVITY_EVENT_ACCESSIBILITY = 3; // 0x3 field public static final int USER_ACTIVITY_EVENT_BUTTON = 1; // 0x1 @@ -4320,13 +4322,6 @@ package android.provider { field public static final java.lang.String STATE = "state"; } - public static final class ContactsContract.RawContacts implements android.provider.BaseColumns android.provider.ContactsContract.ContactNameColumns android.provider.ContactsContract.ContactOptionsColumns android.provider.ContactsContract.RawContactsColumns android.provider.ContactsContract.SyncColumns { - field public static final android.net.Uri RAW_CONTACTS_NOTIFICATION_DELETE_URI; - field public static final android.net.Uri RAW_CONTACTS_NOTIFICATION_INSERT_URI; - field public static final android.net.Uri RAW_CONTACTS_NOTIFICATION_UPDATE_URI; - field public static final android.net.Uri RAW_CONTACTS_NOTIFICATION_URI; - } - public abstract class SearchIndexableData { ctor public SearchIndexableData(); ctor public SearchIndexableData(android.content.Context); @@ -5335,9 +5330,12 @@ package android.telephony { } 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 { @@ -5854,7 +5852,7 @@ package android.telephony.ims { 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/api/test-current.txt b/api/test-current.txt index 956761615254..dd02504e37b1 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -708,6 +708,10 @@ package android.os { method public void removeSyncBarrier(int); } + public final class PowerManager { + method public boolean setPowerSaveMode(boolean); + } + public class Process { method public static final int getThreadScheduler(int) throws java.lang.IllegalArgumentException; } @@ -932,13 +936,6 @@ package android.provider { field public static final android.net.Uri ENTERPRISE_CONTENT_URI; } - public static final class ContactsContract.RawContacts implements android.provider.BaseColumns android.provider.ContactsContract.ContactNameColumns android.provider.ContactsContract.ContactOptionsColumns android.provider.ContactsContract.RawContactsColumns android.provider.ContactsContract.SyncColumns { - field public static final android.net.Uri RAW_CONTACTS_NOTIFICATION_DELETE_URI; - field public static final android.net.Uri RAW_CONTACTS_NOTIFICATION_INSERT_URI; - field public static final android.net.Uri RAW_CONTACTS_NOTIFICATION_UPDATE_URI; - field public static final android.net.Uri RAW_CONTACTS_NOTIFICATION_URI; - } - public static final class ContactsContract.RawContactsEntity implements android.provider.BaseColumns android.provider.ContactsContract.DataColumns android.provider.ContactsContract.RawContactsColumns { field public static final android.net.Uri CORP_CONTENT_URI; } diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 988ffc4d95b7..8ab67e32cd59 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -31,6 +31,7 @@ import "frameworks/base/core/proto/android/telephony/enums.proto"; 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,11 @@ message Atom { 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; } // DO NOT USE field numbers above 100,000 in AOSP. @@ -2665,3 +2667,11 @@ message NumFingerprints { 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 6d7bba028974..3eb05a90e3b4 100644 --- a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp +++ b/cmds/statsd/src/external/StatsCompanionServicePuller.cpp @@ -54,7 +54,7 @@ bool StatsCompanionServicePuller::PullInternal(vector<shared_ptr<LogEvent> >* da 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 fd8671406051..cd215b4bd705 100644 --- a/cmds/statsd/src/external/StatsPullerManager.cpp +++ b/cmds/statsd/src/external/StatsPullerManager.cpp @@ -217,6 +217,9 @@ const std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = { {}, 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/tests/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp index d4907017dc6d..ced65f292d49 100644 --- a/cmds/statsd/tests/LogEvent_test.cpp +++ b/cmds/statsd/tests/LogEvent_test.cpp @@ -327,7 +327,7 @@ TEST(LogEventTest, TestKeyValuePairsEvent) { 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 @@ TEST(LogEventTest, TestKeyValuePairsEvent) { 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 @@ TEST(LogEventTest, TestKeyValuePairsEvent) { 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 @@ TEST(LogEventTest, TestKeyValuePairsEvent) { 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 @@ TEST(LogEventTest, TestKeyValuePairsEvent) { 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 @@ TEST(LogEventTest, TestKeyValuePairsEvent) { 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/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt index ac16fd311c32..6af34f9151b9 100644 --- a/config/hiddenapi-light-greylist.txt +++ b/config/hiddenapi-light-greylist.txt @@ -867,7 +867,6 @@ Landroid/os/PowerManager;->GO_TO_SLEEP_REASON_TIMEOUT:I Landroid/os/PowerManager;->isLightDeviceIdleMode()Z Landroid/os/PowerManager;->mHandler:Landroid/os/Handler; Landroid/os/PowerManager;->mService:Landroid/os/IPowerManager; -Landroid/os/PowerManager;->setPowerSaveMode(Z)Z Landroid/os/PowerManager;->validateWakeLockParameters(ILjava/lang/String;)V Landroid/os/PowerManager;->wakeUp(JLjava/lang/String;)V Landroid/os/Process;->BLUETOOTH_UID:I diff --git a/core/java/android/accounts/AbstractAccountAuthenticator.java b/core/java/android/accounts/AbstractAccountAuthenticator.java index a3b3a9f21954..25cd342cb5e9 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 @@ import java.util.Arrays; /** * 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 @@ public abstract class AbstractAccountAuthenticator { * * @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 294a3ec73efd..76b90f5354b3 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -245,4 +245,6 @@ public abstract class ActivityManagerInternal { 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 34c22822403e..1144e26717fe 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -1760,7 +1760,7 @@ public class Instrumentation { /** * 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 07a8504e3b0b..3f07024d1e5a 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -48,6 +48,7 @@ import android.util.proto.ProtoOutputStream; 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 class NotificationManager { 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 67acfe906915..16f6bdaa4313 100644 --- a/core/java/android/app/Service.java +++ b/core/java/android/app/Service.java @@ -21,13 +21,13 @@ import android.annotation.Nullable; 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 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac * 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 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac * 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 3f2b025016df..3829d7103b07 100644 --- a/core/java/android/app/SmsAppService.java +++ b/core/java/android/app/SmsAppService.java @@ -24,21 +24,42 @@ import android.os.IBinder; * 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 1839263af5bf..09ab67186266 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -664,8 +664,8 @@ public class DevicePolicyManager { /** * 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 @@ public class DevicePolicyManager { * <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 @@ public class DevicePolicyManager { * <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 654bfaf293b8..8e6a3856d51b 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -2973,7 +2973,7 @@ public final class BluetoothAdapter { * 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 @@ public final class BluetoothAdapter { * <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 73e98cd99f8f..30d5fbc7fe99 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -1596,7 +1596,7 @@ public final class BluetoothDevice implements Parcelable { * 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 @@ public final class BluetoothDevice implements Parcelable { * 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 @@ public final class BluetoothDevice implements Parcelable { * 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 @@ public final class BluetoothDevice implements Parcelable { * 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 5fc344a14f99..758c68db1c5f 100644 --- a/core/java/android/bluetooth/BluetoothServerSocket.java +++ b/core/java/android/bluetooth/BluetoothServerSocket.java @@ -203,7 +203,7 @@ public final class BluetoothServerSocket implements Closeable { /** * 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 a64eead04c6f..a13a438cd7f7 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -2104,7 +2104,11 @@ public abstract class ContentProvider implements ComponentCallbacks2 { // 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 f5431caaf319..24675d301f4a 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -5785,52 +5785,32 @@ public class PackageParser { 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 @@ public class PackageParser { 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 @@ public class PackageParser { if (Signature.areEffectiveMatch( oldDetails.signatures[0], pastSigningCertificates[i]) - && pastSigningCertificatesFlags[i] == flags) { + && pastSigningCertificates[i].getFlags() == flags) { return true; } } @@ -6006,7 +5983,7 @@ public class PackageParser { 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 @@ public class PackageParser { 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 @@ public class PackageParser { 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 @@ public class PackageParser { 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 @@ public class PackageParser { if (!Arrays.equals(pastSigningCertificates, that.pastSigningCertificates)) { return false; } - if (!Arrays.equals(pastSigningCertificatesFlags, that.pastSigningCertificatesFlags)) { - return false; - } return true; } @@ -6188,7 +6160,6 @@ public class PackageParser { 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 @@ public class PackageParser { private Signature[] mSignatures; private int mSignatureSchemeVersion = SignatureSchemeVersion.UNKNOWN; private Signature[] mPastSigningCertificates; - private int[] mPastSigningCertificatesFlags; @UnsupportedAppUsage public Builder() { @@ -6226,34 +6196,12 @@ public class PackageParser { 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 @@ public class PackageParser { 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 e58ca609f1a8..349bb693bd9c 100644 --- a/core/java/android/content/pm/Signature.java +++ b/core/java/android/content/pm/Signature.java @@ -45,6 +45,20 @@ public class Signature implements Parcelable { 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 @@ public class Signature implements Parcelable { } /** + * 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 @@ public class Signature implements Parcelable { return sPrime; } -} +}
\ No newline at end of file diff --git a/core/java/android/hardware/biometrics/BiometricConstants.java b/core/java/android/hardware/biometrics/BiometricConstants.java index 6150be361ef2..2a64c2e1e074 100644 --- a/core/java/android/hardware/biometrics/BiometricConstants.java +++ b/core/java/android/hardware/biometrics/BiometricConstants.java @@ -34,6 +34,13 @@ public interface BiometricConstants { // /** + * 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 eea5f9ba9835..0faecb03d4fc 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 @@ public class BiometricManager { } /** - * 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 000000000000..d22e7e295b77 --- /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 67c9346da265..27d25b86b859 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 fd9d5725f921..51e4ecbf56b0 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 @@ interface IBiometricService { // 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 71abdd246097..a6e3696eeb10 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 e8fb28779563..09113e5d175d 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -297,6 +297,15 @@ public final class DisplayManager { */ 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 496f34c0348a..df0d46be1354 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 final class DisplayViewport { 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 @@ public final class DisplayViewport { // 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 @@ public final class DisplayViewport { deviceWidth = viewport.deviceWidth; deviceHeight = viewport.deviceHeight; uniqueId = viewport.uniqueId; + type = viewport.type; } /** @@ -100,7 +112,8 @@ public final class DisplayViewport { && 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 @@ public final class DisplayViewport { 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 @@ public final class DisplayViewport { + ", 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/IFaceServiceReceiver.aidl b/core/java/android/hardware/face/IFaceServiceReceiver.aidl index 16fb69021467..b88574b74c36 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/IFingerprintServiceReceiver.aidl b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl index 370383f4a909..cf1c94ea5346 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 c4d7e400795c..d8da548d9bd3 100644 --- a/core/java/android/hardware/input/InputManagerInternal.java +++ b/core/java/android/hardware/input/InputManagerInternal.java @@ -40,8 +40,7 @@ public abstract class InputManagerInternal { * 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 f7f627ebedc2..097a3e3a270a 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -2095,7 +2095,7 @@ public class InputMethodService extends AbstractInputMethodService { * 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 @@ public class InputMethodService extends AbstractInputMethodService { } /** - * @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 b4b88871d251..0513feef801f 100644 --- a/core/java/android/inputmethodservice/SoftInputWindow.java +++ b/core/java/android/inputmethodservice/SoftInputWindow.java @@ -162,9 +162,9 @@ public class SoftInputWindow extends Dialog { /** * 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 69f50a272af0..a2da6eaa4922 100644 --- a/core/java/android/net/NetworkMisc.java +++ b/core/java/android/net/NetworkMisc.java @@ -46,7 +46,7 @@ public class NetworkMisc implements Parcelable { /** * 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 d2073b4dfd3a..5b674067192e 100644 --- a/core/java/android/net/UrlQuerySanitizer.java +++ b/core/java/android/net/UrlQuerySanitizer.java @@ -287,7 +287,7 @@ public class UrlQuerySanitizer { /** * 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 @@ public class UrlQuerySanitizer { } /** - * 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 @@ public class UrlQuerySanitizer { } /** - * 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 @@ public class UrlQuerySanitizer { /** * 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 @@ public class UrlQuerySanitizer { * 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 @@ public class UrlQuerySanitizer { /** * 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 3c43fd189337..483b7644f4d6 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -219,12 +219,11 @@ public class Environment { * 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 @@ public class Environment { 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/PowerManager.java b/core/java/android/os/PowerManager.java index 89a5defd221d..8ea061e4a8d0 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -1154,10 +1154,15 @@ public final class PowerManager { * * @return True if the set was allowed. * - * @see #isPowerSaveMode() - * * @hide + * @see #isPowerSaveMode() */ + @SystemApi + @TestApi + @RequiresPermission(anyOf = { + android.Manifest.permission.DEVICE_POWER, + android.Manifest.permission.POWER_SAVER + }) public boolean setPowerSaveMode(boolean mode) { try { return mService.setPowerSaveMode(mode); diff --git a/core/java/android/os/StatsLogEventWrapper.java b/core/java/android/os/StatsLogEventWrapper.java index 051ab75c857c..72e1ab972846 100644 --- a/core/java/android/os/StatsLogEventWrapper.java +++ b/core/java/android/os/StatsLogEventWrapper.java @@ -70,11 +70,17 @@ public final class StatsLogEventWrapper implements Parcelable { } }; + /** + * 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 @@ public final class StatsLogEventWrapper implements Parcelable { 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 @@ public final class StatsLogEventWrapper implements Parcelable { 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 @@ public final class StatsLogEventWrapper implements Parcelable { 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 fb34a524f625..bdc776dd3702 100644 --- a/core/java/android/os/SystemProperties.java +++ b/core/java/android/os/SystemProperties.java @@ -208,13 +208,18 @@ public class SystemProperties { 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/ContactsContract.java b/core/java/android/provider/ContactsContract.java index f126c49227fb..112329e48703 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -2783,48 +2783,7 @@ public final class ContactsContract { * The content:// style URI for this table, which requests a directory of * raw contact rows matching the selection criteria. */ - public static final Uri CONTENT_URI = - Uri.withAppendedPath(AUTHORITY_URI, "raw_contacts"); - - /** - * The URI to register for all raw contacts change notification. - * - * @hide - */ - @SystemApi - @TestApi - public static final Uri RAW_CONTACTS_NOTIFICATION_URI = - Uri.parse("content://com.android.contacts.raw_contacts"); - - /** - * The URI to register for raw contacts insert notification. - * - * @hide - */ - @SystemApi - @TestApi - public static final Uri RAW_CONTACTS_NOTIFICATION_INSERT_URI = - Uri.withAppendedPath(RAW_CONTACTS_NOTIFICATION_URI, "insert"); - - /** - * The URI to register for raw contacts update notification. - * - * @hide - */ - @SystemApi - @TestApi - public static final Uri RAW_CONTACTS_NOTIFICATION_UPDATE_URI = - Uri.withAppendedPath(RAW_CONTACTS_NOTIFICATION_URI, "update"); - - /** - * The URI to register for raw contacts delete notification. - * - * @hide - */ - @SystemApi - @TestApi - public static final Uri RAW_CONTACTS_NOTIFICATION_DELETE_URI = - Uri.withAppendedPath(RAW_CONTACTS_NOTIFICATION_URI, "delete"); + public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "raw_contacts"); /** * The MIME type of the results from {@link #CONTENT_URI} when a specific diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index e1c0fe52de37..3f264b717b6c 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -497,6 +497,16 @@ public final class Settings { "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 @@ -9253,6 +9263,19 @@ public final class Settings { public static final String MOBILE_DATA_ALWAYS_ON = "mobile_data_always_on"; /** + * Whether the wifi data connection should remain active even when higher + * priority networks like Ethernet are active, to keep both networks. + * In the case where higher priority networks are connected, wifi will be + * unused unless an application explicitly requests to use it. + * + * See ConnectivityService for more info. + * + * (0 = disabled, 1 = enabled) + * @hide + */ + public static final String WIFI_ALWAYS_REQUESTED = "wifi_always_requested"; + + /** * Size of the event buffer for IP connectivity metrics. * @hide */ diff --git a/core/java/android/util/JsonReader.java b/core/java/android/util/JsonReader.java index 7d1c6c4918db..50f63f8db304 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 @@ public final class JsonReader implements Closeable { /** * 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 a299b11c2b25..ac4ea75b38e3 100644 --- a/core/java/android/util/apk/ApkSignatureVerifier.java +++ b/core/java/android/util/apk/ApkSignatureVerifier.java @@ -81,19 +81,17 @@ public class ApkSignatureVerifier { 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 @@ public class ApkSignatureVerifier { 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 4d96fc3175cd..719a401ce0cf 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -221,6 +221,18 @@ public final class Display { 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 @@ public final class Display { } /** + * 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 5f80d31651a8..f428c798d7f5 100644 --- a/core/java/android/view/DisplayCutout.java +++ b/core/java/android/view/DisplayCutout.java @@ -18,12 +18,19 @@ package android.view; 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.R; 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 @@ public final class DisplayCutout { "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 @@ public final class DisplayCutout { 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,35 +258,76 @@ public final class DisplayCutout { * {@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; + } } /** - * Returns true if the safe insets are empty (and therefore the current view does not - * overlap with the cutout or cutout area). + * Find the position of the bounding rect, and create an array of Rect whose index represents + * the position (= BoundsPosition). * * @hide */ - public boolean isEmpty() { - return mSafeInsets.equals(ZERO_RECT); + 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; } /** @@ -143,6 +339,16 @@ public final class DisplayCutout { return mBounds.isEmpty(); } + /** + * Returns true if the safe insets are empty (and therefore the current view does not + * overlap with the cutout or cutout area). + * + * @hide + */ + public boolean isEmpty() { + return mSafeInsets.equals(ZERO_RECT); + } + /** Returns the inset from the top which avoids the display cutout in pixels. */ public int getSafeInsetTop() { return mSafeInsets.top; @@ -174,69 +380,89 @@ public final class DisplayCutout { } /** - * 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. + * Returns a list of {@code Rect}s, each of which is the bounding rectangle for a non-functional + * area on the display. * - * 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> + * There will be at most one non-functional area per short edge of the device, and none on + * the long edges. * - * @return the bounding region of the cutout. Coordinates are relative - * to the top-left corner of the content view and in pixel units. - * @hide + * @return a list of bounding {@code Rect}s, one for each display cutout area. No empty Rect is + * returned. */ - public Region getBounds() { - return Region.obtain(mBounds); + public List<Rect> getBoundingRects() { + List<Rect> result = new ArrayList<>(); + for (Rect bound : getBoundingRectsAll()) { + if (!bound.isEmpty()) { + result.add(new Rect(bound)); + } + } + return result; } /** - * Returns a list of {@code Rect}s, each of which is the bounding rectangle for a non-functional - * area on the display. + * 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 a list of bounding {@code Rect}s, one for each display cutout area. + * @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 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()); - } - // 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; + 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 @@ public final class DisplayCutout { } 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 @@ public final class DisplayCutout { @Override public String toString() { return "DisplayCutout{insets=" + mSafeInsets - + " boundingRect=" + mBounds.getBounds() + + " boundingRect={" + mBounds + "}" + "}"; } @@ -265,7 +490,10 @@ public final class DisplayCutout { 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 @@ public final class DisplayCutout { * @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 @@ public final class DisplayCutout { 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 @@ public final class DisplayCutout { * @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 @@ public final class DisplayCutout { * @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 @@ public final class DisplayCutout { * * @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 @@ public final class DisplayCutout { 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 @@ public final class DisplayCutout { 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 @@ public final class DisplayCutout { 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 @@ public final class DisplayCutout { } else { out.writeInt(1); out.writeTypedObject(cutout.mSafeInsets, flags); - out.writeTypedObject(cutout.mBounds, flags); + out.writeTypedArray(cutout.mBounds.getRects(), flags); } } @@ -561,7 +790,8 @@ public final class DisplayCutout { } 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 3bab87aea644..3e559d9e106f 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -87,7 +87,6 @@ interface IWindowManager 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 c38fcf36a65c..0a3403bba351 100644 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -896,8 +896,8 @@ public class KeyEvent extends InputEvent implements Parcelable { * @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 b2944d6a9923..cddd83c45c75 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -14639,9 +14639,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * 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 7abe19e795f4..d4c7069cdbf4 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -803,10 +803,6 @@ public final class AutofillManager { return true; } } - if (sVerbose) { - Log.v(TAG, "not ignoring notifyViewEntered(flags=" + flags + ", view=" + id - + ", state " + getStateAsStringLocked() + ", enteredIds=" + mEnteredIds); - } return false; } @@ -845,6 +841,9 @@ public final class AutofillManager { ensureServiceClientAddedIfNeededLocked(); if (!mEnabled) { + if (sVerbose) { + Log.v(TAG, "ignoring notifyViewEntered(" + id + "): disabled"); + } if (mCallback != null) { callback = mCallback; } @@ -995,6 +994,9 @@ public final class AutofillManager { 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 9d74c98a04ba..8027dd7cdb10 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -101,6 +101,7 @@ import android.view.View.OnClickListener; 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 @@ public class Editor { 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 @@ public class Editor { 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/android/widget/TextView.java b/core/java/android/widget/TextView.java index f95b3ce98264..9d06680c4c07 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -882,9 +882,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private boolean mTextSetFromXmlOrResourceId = false; // Resource id used to set the text. private @StringRes int mTextId = ResourceId.ID_NULL; - // Last value used on AFM.notifyValueChanged(), used to optimize autofill workflow by avoiding - // calls when the value did not change - private CharSequence mLastValueSentToAutofillManager; // // End of autofill-related attributes @@ -5884,7 +5881,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (needEditableForNotification) { sendAfterTextChanged((Editable) text); } else { - notifyAutoFillManagerAfterTextChangedIfNeeded(); + notifyAutoFillManagerAfterTextChanged(); } // SelectionModifierCursorController depends on textCanBeSelected, which depends on text @@ -9933,33 +9930,23 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } // Always notify AutoFillManager - it will return right away if autofill is disabled. - notifyAutoFillManagerAfterTextChangedIfNeeded(); + notifyAutoFillManagerAfterTextChanged(); hideErrorIfUnchanged(); } - private void notifyAutoFillManagerAfterTextChangedIfNeeded() { + private void notifyAutoFillManagerAfterTextChanged() { // It is important to not check whether the view is important for autofill // since the user can trigger autofill manually on not important views. if (!isAutofillable()) { return; } final AutofillManager afm = mContext.getSystemService(AutofillManager.class); - if (afm == null) { - return; - } - - if (mLastValueSentToAutofillManager == null - || !mLastValueSentToAutofillManager.equals(mText)) { + if (afm != null) { if (android.view.autofill.Helper.sVerbose) { - Log.v(LOG_TAG, "notifying AFM after text changed"); + Log.v(LOG_TAG, "notifyAutoFillManagerAfterTextChanged"); } afm.notifyValueChanged(TextView.this); - mLastValueSentToAutofillManager = mText; - } else { - if (android.view.autofill.Helper.sVerbose) { - Log.v(LOG_TAG, "not notifying AFM on unchanged text"); - } } } diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java index c0c358d9b44b..856712f48297 100644 --- a/core/java/com/android/internal/os/BinderCallsStats.java +++ b/core/java/com/android/internal/os/BinderCallsStats.java @@ -436,6 +436,12 @@ public class BinderCallsStats implements BinderInternal.Observer { } 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 997b722681ba..8338d78af3a1 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.annotation.UnsupportedAppUsage; 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 class PowerProfile { 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 762b43079c31..8e24d10a206f 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -209,6 +209,8 @@ cc_library_shared { "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 897f6fa53774..644a9746cf2a 100755 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -9,7 +9,6 @@ #include "SkColor.h" #include "SkColorPriv.h" #include "SkColorSpace.h" -#include "SkColorSpaceXform.h" #include "SkHalf.h" #include "SkMatrix44.h" #include "SkPM4f.h" @@ -559,13 +558,10 @@ static jboolean Bitmap_compress(JNIEnv* env, jobject clazz, jlong bitmapHandle, 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 343aef291720..dca2da369540 100644 --- a/core/jni/android_graphics_Canvas.cpp +++ b/core/jni/android_graphics_Canvas.cpp @@ -273,6 +273,32 @@ static void drawRect(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jflo 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 @@ static const JNINativeMethod gDrawMethods[] = { {"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 ab8e685d5441..05f6556bfb35 100644 --- a/core/jni/android_hardware_display_DisplayViewport.cpp +++ b/core/jni/android_hardware_display_DisplayViewport.cpp @@ -40,6 +40,7 @@ static struct { jfieldID deviceWidth; jfieldID deviceHeight; jfieldID uniqueId; + jfieldID type; } gDisplayViewportClassInfo; static struct { @@ -64,6 +65,9 @@ status_t android_hardware_display_DisplayViewport_toNative(JNIEnv* env, jobject 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 @@ int register_android_hardware_display_DisplayViewport(JNIEnv* env) { 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 index 514b6e1c2f70..8ace8da77b2f 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 index c9af711a7586..711561150e51 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 index c13aa38dc133..f4829ad26aef 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 index 44d4620c8b0c..2be267e958ec 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 0a90b97d55ef..2f179078aed8 100644 --- a/core/jni/android_view_InputChannel.cpp +++ b/core/jni/android_view_InputChannel.cpp @@ -202,17 +202,9 @@ static void android_view_InputChannel_nativeReadFromParcel(JNIEnv* env, jobject 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(name.string(), dupFd); + InputChannel* inputChannel = new InputChannel(); + inputChannel->read(*parcel); + NativeInputChannel* nativeInputChannel = new NativeInputChannel(inputChannel); android_view_InputChannel_setNativeInputChannel(env, obj, nativeInputChannel); @@ -230,8 +222,7 @@ static void android_view_InputChannel_nativeWriteToParcel(JNIEnv* env, jobject o 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 000000000000..9dd5e7ee556c --- /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 d33ea0ce503d..c1c86f088873 100644 --- a/core/proto/android/server/windowmanagerservice.proto +++ b/core/proto/android/server/windowmanagerservice.proto @@ -158,6 +158,7 @@ message DisplayContentProto { 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 f4744daa4285..0a3310148145 100644 --- a/core/proto/android/view/displaycutout.proto +++ b/core/proto/android/view/displaycutout.proto @@ -26,5 +26,9 @@ message DisplayCutoutProto { 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 ea92c60015ef..f3f012de53be 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 @@ -3406,6 +3413,12 @@ <permission android:name="android.permission.DEVICE_POWER" android:protectionLevel="signature" /> + <!-- Allows toggling battery saver on the system. + Superseded by DEVICE_POWER permission. @hide @SystemApi + --> + <permission android:name="android.permission.POWER_SAVER" + android:protectionLevel="signature|privileged" /> + <!-- Allows access to the PowerManager.userActivity function. <p>Not for use by third-party applications. @hide @SystemApi --> <permission android:name="android.permission.USER_ACTIVITY" diff --git a/core/res/res/values-night/values.xml b/core/res/res/values-night/values.xml index 45cf0f0fedb3..a2ad3b90e464 100644 --- a/core/res/res/values-night/values.xml +++ b/core/res/res/values-night/values.xml @@ -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 e0db9463a091..cdb65edd81e7 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 8ff29ba4614c..99af0de749d6 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 0daf5a76562d..8b73b6768479 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 fadefff6a030..2e42e4ac27f3 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 72ae0d61654a..a7b6dde12628 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/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index d2420ee1ef10..4ecdc42e3372 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -463,6 +463,7 @@ public class SettingsBackupTest { Settings.Global.WFC_IMS_MODE, Settings.Global.WFC_IMS_ROAMING_ENABLED, Settings.Global.WFC_IMS_ROAMING_MODE, + Settings.Global.WIFI_ALWAYS_REQUESTED, Settings.Global.WIFI_BADGING_THRESHOLDS, Settings.Global.WIFI_BOUNCE_DELAY_OVERRIDE_MS, Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED, diff --git a/core/tests/coretests/src/android/view/DisplayCutoutTest.java b/core/tests/coretests/src/android/view/DisplayCutoutTest.java index fe45fe7d3aaf..c8d994c4f6c1 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.assertNotEquals; 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.Test; 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 hasCutout() throws Exception { - assertTrue(NO_CUTOUT.isEmpty()); - assertFalse(mCutoutTop.isEmpty()); + 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 getSafeInsets() throws Exception { - assertEquals(1, mCutoutNumbers.getSafeInsetLeft()); - assertEquals(2, mCutoutNumbers.getSafeInsetTop()); - assertEquals(3, mCutoutNumbers.getSafeInsetRight()); - assertEquals(4, mCutoutNumbers.getSafeInsetBottom()); + 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})); + } + - assertEquals(new Rect(1, 2, 3, 4), mCutoutNumbers.getSafeInsets()); + @Test + public void hasCutout() throws Exception { + assertTrue(NO_CUTOUT.isEmpty()); + assertFalse(mCutoutTop.isEmpty()); } @Test - public void getBoundingRect() throws Exception { - assertEquals(new Rect(50, 0, 75, 100), mCutoutTop.getBounds().getBounds()); + public void testGetSafeInsets() throws Exception { + assertEquals(5, mCutoutNumbers.getSafeInsetLeft()); + assertEquals(6, mCutoutNumbers.getSafeInsetTop()); + assertEquals(7, mCutoutNumbers.getSafeInsetRight()); + assertEquals(8, mCutoutNumbers.getSafeInsetBottom()); + + assertEquals(new Rect(5, 6, 7, 8), mCutoutNumbers.getSafeInsets()); } @Test @@ -102,30 +149,30 @@ public class DisplayCutoutTest { 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 @@ public class DisplayCutoutTest { @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 @@ public class DisplayCutoutTest { 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 @@ public class DisplayCutoutTest { } 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/com/android/internal/widget/ActionBarOverlayLayoutTest.java b/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java index cac4e88c8edc..218566e8cdf0 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.assertNotNull; 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.Test; 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 @@ public class ActionBarOverlayLayoutTest { 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 @@ public class ActionBarOverlayLayoutTest { 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 @@ public class ActionBarOverlayLayoutTest { 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 fa37bedb06c4..0885a05c74f3 100644 --- a/graphics/java/android/graphics/BaseCanvas.java +++ b/graphics/java/android/graphics/BaseCanvas.java @@ -376,6 +376,53 @@ public abstract class BaseCanvas { 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 @@ public abstract class BaseCanvas { 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 6e936910ef40..fb30ca2d4090 100644 --- a/graphics/java/android/graphics/BaseRecordingCanvas.java +++ b/graphics/java/android/graphics/BaseRecordingCanvas.java @@ -377,6 +377,24 @@ public class BaseRecordingCanvas extends Canvas { } @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 @@ public class BaseRecordingCanvas extends Canvas { 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 36c1c2133c2c..e35a3be6dbbf 100644 --- a/graphics/java/android/graphics/Canvas.java +++ b/graphics/java/android/graphics/Canvas.java @@ -1877,6 +1877,51 @@ public class Canvas extends BaseCanvas { } /** + * 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 6ce66bdc925b..009e042f74a7 100644 --- a/graphics/java/android/graphics/ImageDecoder.java +++ b/graphics/java/android/graphics/ImageDecoder.java @@ -59,6 +59,7 @@ import java.io.IOException; 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 @@ public final class ImageDecoder implements AutoCloseable { 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 @@ public final class ImageDecoder implements AutoCloseable { 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 @@ public final class ImageDecoder implements AutoCloseable { } } + 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 @@ public final class ImageDecoder implements AutoCloseable { } /** + * 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 c3449dd4fb47..de110c849338 100644 --- a/graphics/java/android/graphics/Insets.java +++ b/graphics/java/android/graphics/Insets.java @@ -73,6 +73,15 @@ public final class Insets { } /** + * 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/Layer.cpp b/libs/hwui/Layer.cpp index cc95051fc952..32aaa54e696c 100644 --- a/libs/hwui/Layer.cpp +++ b/libs/hwui/Layer.cpp @@ -77,5 +77,13 @@ void Layer::postDecStrong() { mRenderState.postDecStrong(this); } +SkBlendMode Layer::getMode() const { + if (mBlend || mode != SkBlendMode::kSrcOver) { + return mode; + } else { + return SkBlendMode::kSrc; + } +} + }; // namespace uirenderer }; // namespace android diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h index 6f07a43ceb58..e4f96e914c36 100644 --- a/libs/hwui/Layer.h +++ b/libs/hwui/Layer.h @@ -67,7 +67,7 @@ public: inline int getAlpha() const { return alpha; } - inline SkBlendMode getMode() const { return mode; } + SkBlendMode getMode() const; inline SkColorFilter* getColorFilter() const { return mColorFilter.get(); } diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp index c30af842ebbb..f928de9b92a6 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 DrawPicture final : Op { 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 DrawImageNine final : Op { 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 DrawImageRect final : Op { 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 @@ struct DrawImageLattice final : Op { 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 @@ struct DrawVectorDrawable final : Op { 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 @@ void DisplayListData::drawImageRect(sk_sp<const SkImage> image, const SkRect* sr 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 @@ constexpr bool has_palette = std::experimental::is_detected_v<has_palette_helper 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::onDrawBitmapNine(const SkBitmap& bm, const SkIRect& center } 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 @@ void RecordingCanvas::drawImage(const sk_sp<SkImage>& image, SkScalar x, SkScala 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::onDrawImageRect(const SkImage* img, const SkRect* src, con } 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 @@ void RecordingCanvas::onDrawPoints(SkCanvas::PointMode mode, size_t count, const 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 80c80ca46515..099e0be433ea 100644 --- a/libs/hwui/RecordingCanvas.h +++ b/libs/hwui/RecordingCanvas.h @@ -110,7 +110,7 @@ private: 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 @@ public: 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 d9a7cc3a575f..d2a8f02cc6a7 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -282,25 +282,45 @@ void RenderNode::syncDisplayList(TreeObserver& observer, TreeInfo* info) { 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; - } +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); } - mDisplayList->mDisplayList.applyColorTransform( - usage == UsageHint::Background ? ColorTransform::Dark : ColorTransform::Light); + 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 211dd2db5cf8..be0b46b1c45f 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -220,6 +220,7 @@ private: 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 17f1a3b4db3c..2e5aef5347d5 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -504,6 +504,11 @@ void SkiaCanvas::drawRoundRect(float left, float top, float right, float bottom, 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 24b7ec6d5c7b..3a877cf84010 100644 --- a/libs/hwui/SkiaCanvas.h +++ b/libs/hwui/SkiaCanvas.h @@ -107,6 +107,10 @@ public: 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 dbbe9f3acf53..6cf04bf5f811 100644 --- a/libs/hwui/VectorDrawable.cpp +++ b/libs/hwui/VectorDrawable.cpp @@ -470,10 +470,10 @@ void Tree::drawStaging(Canvas* outCanvas) { 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 af7f013e27b1..e2ea2bc37375 100644 --- a/libs/hwui/hwui/Canvas.cpp +++ b/libs/hwui/hwui/Canvas.cpp @@ -178,6 +178,41 @@ void Canvas::drawText(const uint16_t* text, int textSize, int start, int count, 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 b9af7de26721..e99742bc2eba 100644 --- a/libs/hwui/hwui/Canvas.h +++ b/libs/hwui/hwui/Canvas.h @@ -236,6 +236,8 @@ public: 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 @@ public: 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 fac07d74dad4..3fa73a4dadda 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp @@ -245,8 +245,9 @@ void SkiaRecordingCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& ch } 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 a2d811993f2f..2ca110f0d0b1 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp @@ -126,6 +126,12 @@ bool SkiaVulkanPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior, 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 83e9db359356..b0d45052ca49 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -78,7 +78,7 @@ bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe 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 @@ bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe 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 @@ bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe 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 7c59b6d340d2..6702649402e6 100644 --- a/libs/hwui/renderthread/VulkanManager.h +++ b/libs/hwui/renderthread/VulkanManager.h @@ -176,6 +176,7 @@ private: 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 52cb75e8e639..f60c338bf9c4 100644 --- a/libs/services/include/android/os/StatsLogEventWrapper.h +++ b/libs/services/include/android/os/StatsLogEventWrapper.h @@ -58,6 +58,11 @@ struct StatsLogValue { 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 04c4629b5432..a1a6d9fe0e22 100644 --- a/libs/services/src/os/StatsLogEventWrapper.cpp +++ b/libs/services/src/os/StatsLogEventWrapper.cpp @@ -85,6 +85,9 @@ status_t StatsLogEventWrapper::readFromParcel(const Parcel* in) { 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 a6099be60875..74eb4450d42c 100644 --- a/location/java/android/location/Criteria.java +++ b/location/java/android/location/Criteria.java @@ -21,9 +21,9 @@ import android.os.Parcelable; /** * 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 85eac4bd626f..c074ccebc62c 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -4607,7 +4607,7 @@ public class AudioManager { /** * 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 452ba0f2e8ec..2a575b626a44 100644 --- a/media/java/android/media/AudioRecord.java +++ b/media/java/android/media/AudioRecord.java @@ -473,7 +473,7 @@ public class AudioRecord implements AudioRouting * .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 dff5e9ae2e22..26b9b8cf85a7 100644 --- a/media/java/android/media/Image.java +++ b/media/java/android/media/Image.java @@ -75,7 +75,7 @@ public abstract class Image implements AutoCloseable { /** * 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 340f27950638..18d36eb1f753 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -227,7 +227,7 @@ import java.util.Vector; * 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 7492aa605dc6..8665f283ffb2 100644 --- a/media/java/android/media/MediaPlayer2.java +++ b/media/java/android/media/MediaPlayer2.java @@ -30,6 +30,8 @@ import android.os.PersistableBundle; 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 @@ import java.util.concurrent.Executor; /** * @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> - * <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> + * <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>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)}. + * + * <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>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>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>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> + * <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>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>{@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>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> + * + * <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> * - * <a name="Valid_and_Invalid_States"></a> - * <h3>Valid and invalid states</h3> + * <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 @@ public abstract class MediaPlayer2 implements SubtitleController.Listener * @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 @@ public abstract class MediaPlayer2 implements SubtitleController.Listener */ // 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 dfcbabeea4f3..678cb9af378d 100644 --- a/media/java/android/media/MediaPlayer2Impl.java +++ b/media/java/android/media/MediaPlayer2Impl.java @@ -53,9 +53,6 @@ import android.widget.VideoView; 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.LinkedList; 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 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { 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 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { @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 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { 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 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { */ @Override public void close() { - synchronized (mGuard) { - release(); - } + super.close(); + release(); } /** @@ -353,7 +349,7 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { 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 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { 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 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { 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 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { 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 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { 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 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { 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 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { // 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 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { resetDrmState(); _release(); + mReleased = true; } private native void _release(); @@ -3030,6 +3026,12 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { } } + 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 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { 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 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { 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 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { 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/jni/OWNERS b/media/jni/OWNERS new file mode 100644 index 000000000000..bb91d4b26ecc --- /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 6595baa594d2..7919723b80a8 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 @@ import android.util.Log; * <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 000000000000..1928ba811e7e --- /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/native/android/system_fonts.cpp b/native/android/system_fonts.cpp index b95adad78f89..761a475a2773 100644 --- a/native/android/system_fonts.cpp +++ b/native/android/system_fonts.cpp @@ -43,6 +43,9 @@ using XmlDocUniquePtr = std::unique_ptr<xmlDoc, XmlDocDeleter>; struct ASystemFontIterator { XmlDocUniquePtr mXmlDoc; xmlNode* mFontNode; + + // The OEM customization XML. + XmlDocUniquePtr mCustomizationXmlDoc; }; struct ASystemFont { @@ -93,29 +96,30 @@ xmlNode* nextSibling(xmlNode* node, const xmlChar* tag) { return nullptr; } -void copyFont(ASystemFontIterator* ite, ASystemFont* out) { +void copyFont(const XmlDocUniquePtr& xmlDoc, xmlNode* fontNode, ASystemFont* out, + const std::string& pathPrefix) { const xmlChar* LOCALE_ATTR_NAME = BAD_CAST("lang"); XmlCharUniquePtr filePathStr( - xmlNodeListGetString(ite->mXmlDoc.get(), ite->mFontNode->xmlChildrenNode, 1)); - out->mFilePath = "/system/fonts/" + xmlTrim( + xmlNodeListGetString(xmlDoc.get(), fontNode->xmlChildrenNode, 1)); + out->mFilePath = pathPrefix + xmlTrim( std::string(filePathStr.get(), filePathStr.get() + xmlStrlen(filePathStr.get()))); const xmlChar* WEIGHT_ATTR_NAME = BAD_CAST("weight"); - XmlCharUniquePtr weightStr(xmlGetProp(ite->mFontNode, WEIGHT_ATTR_NAME)); + XmlCharUniquePtr weightStr(xmlGetProp(fontNode, WEIGHT_ATTR_NAME)); out->mWeight = weightStr ? strtol(reinterpret_cast<const char*>(weightStr.get()), nullptr, 10) : 400; const xmlChar* STYLE_ATTR_NAME = BAD_CAST("style"); const xmlChar* ITALIC_ATTR_VALUE = BAD_CAST("italic"); - XmlCharUniquePtr styleStr(xmlGetProp(ite->mFontNode, STYLE_ATTR_NAME)); + XmlCharUniquePtr styleStr(xmlGetProp(fontNode, STYLE_ATTR_NAME)); out->mItalic = styleStr ? xmlStrEqual(styleStr.get(), ITALIC_ATTR_VALUE) : false; const xmlChar* INDEX_ATTR_NAME = BAD_CAST("index"); - XmlCharUniquePtr indexStr(xmlGetProp(ite->mFontNode, INDEX_ATTR_NAME)); + XmlCharUniquePtr indexStr(xmlGetProp(fontNode, INDEX_ATTR_NAME)); out->mCollectionIndex = indexStr ? strtol(reinterpret_cast<const char*>(indexStr.get()), nullptr, 10) : 0; - XmlCharUniquePtr localeStr(xmlGetProp(ite->mXmlDoc->parent, LOCALE_ATTR_NAME)); + XmlCharUniquePtr localeStr(xmlGetProp(xmlDoc->parent, LOCALE_ATTR_NAME)); out->mLocale.reset( localeStr ? new std::string(reinterpret_cast<const char*>(localeStr.get())) : nullptr); @@ -123,7 +127,7 @@ void copyFont(ASystemFontIterator* ite, ASystemFont* out) { const xmlChar* STYLEVALUE_ATTR_NAME = BAD_CAST("stylevalue"); const xmlChar* AXIS_TAG = BAD_CAST("axis"); out->mAxes.clear(); - for (xmlNode* axis = firstElement(ite->mFontNode, AXIS_TAG); axis; + for (xmlNode* axis = firstElement(fontNode, AXIS_TAG); axis; axis = nextSibling(axis, AXIS_TAG)) { XmlCharUniquePtr tagStr(xmlGetProp(axis, TAG_ATTR_NAME)); if (!tagStr || xmlStrlen(tagStr.get()) != 4) { @@ -154,8 +158,8 @@ bool isFontFileAvailable(const std::string& filePath) { return S_ISREG(st.st_mode); } -xmlNode* findFirstFontNode(xmlDoc* doc) { - xmlNode* familySet = xmlDocGetRootElement(doc); +xmlNode* findFirstFontNode(const XmlDocUniquePtr& doc) { + xmlNode* familySet = xmlDocGetRootElement(doc.get()); if (familySet == nullptr) { return nullptr; } @@ -180,6 +184,7 @@ xmlNode* findFirstFontNode(xmlDoc* doc) { ASystemFontIterator* ASystemFontIterator_open() { std::unique_ptr<ASystemFontIterator> ite(new ASystemFontIterator()); ite->mXmlDoc.reset(xmlReadFile("/system/etc/fonts.xml", nullptr, 0)); + ite->mCustomizationXmlDoc.reset(xmlReadFile("/product/etc/fonts_customization.xml", nullptr, 0)); return ite.release(); } @@ -187,45 +192,62 @@ void ASystemFontIterator_close(ASystemFontIterator* ite) { delete ite; } -ASystemFont* ASystemFontIterator_next(ASystemFontIterator* ite) { - LOG_ALWAYS_FATAL_IF(ite == nullptr, "nullptr has passed as iterator argument"); - if (ite->mFontNode == nullptr) { - if (ite->mXmlDoc == nullptr) { +xmlNode* findNextFontNode(const XmlDocUniquePtr& xmlDoc, xmlNode* fontNode) { + if (fontNode == nullptr) { + if (!xmlDoc) { return nullptr; // Already at the end. } else { // First time to query font. - ite->mFontNode = findFirstFontNode(ite->mXmlDoc.get()); - if (ite->mFontNode == nullptr) { - ite->mXmlDoc.reset(); - return nullptr; // No font node found. - } - std::unique_ptr<ASystemFont> font = std::make_unique<ASystemFont>(); - copyFont(ite, font.get()); - return font.release(); + return findFirstFontNode(xmlDoc); } } else { - xmlNode* nextNode = nextSibling(ite->mFontNode, FONT_TAG); + xmlNode* nextNode = nextSibling(fontNode, FONT_TAG); while (nextNode == nullptr) { - xmlNode* family = nextSibling(ite->mFontNode->parent, FAMILY_TAG); + xmlNode* family = nextSibling(fontNode->parent, FAMILY_TAG); if (family == nullptr) { break; } nextNode = firstElement(family, FONT_TAG); } - ite->mFontNode = nextNode; - if (nextNode == nullptr) { + return nextNode; + } +} + +ASystemFont* ASystemFontIterator_next(ASystemFontIterator* ite) { + LOG_ALWAYS_FATAL_IF(ite == nullptr, "nullptr has passed as iterator argument"); + if (ite->mXmlDoc) { + ite->mFontNode = findNextFontNode(ite->mXmlDoc, ite->mFontNode); + if (ite->mFontNode == nullptr) { + // Reached end of the XML file. Continue OEM customization. ite->mXmlDoc.reset(); - return nullptr; + ite->mFontNode = nullptr; + } else { + std::unique_ptr<ASystemFont> font = std::make_unique<ASystemFont>(); + copyFont(ite->mXmlDoc, ite->mFontNode, font.get(), "/system/fonts/"); + if (!isFontFileAvailable(font->mFilePath)) { + return ASystemFontIterator_next(ite); + } + return font.release(); } - - std::unique_ptr<ASystemFont> font = std::make_unique<ASystemFont>(); - copyFont(ite, font.get()); - if (!isFontFileAvailable(font->mFilePath)) { - // fonts.xml intentionally contains missing font configuration. Skip it. - return ASystemFontIterator_next(ite); + } + if (ite->mCustomizationXmlDoc) { + // TODO: Filter only customizationType="new-named-family" + ite->mFontNode = findNextFontNode(ite->mCustomizationXmlDoc, ite->mFontNode); + if (ite->mFontNode == nullptr) { + // Reached end of the XML file. Finishing + ite->mCustomizationXmlDoc.reset(); + ite->mFontNode = nullptr; + return nullptr; + } else { + std::unique_ptr<ASystemFont> font = std::make_unique<ASystemFont>(); + copyFont(ite->mCustomizationXmlDoc, ite->mFontNode, font.get(), "/product/fonts/"); + if (!isFontFileAvailable(font->mFilePath)) { + return ASystemFontIterator_next(ite); + } + return font.release(); } - return font.release(); } + return nullptr; } void ASystemFont_close(ASystemFont* font) { diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUidLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUidLoader.java index 95efb4cea491..cc970b93f601 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 @@ public class NetworkCycleDataForUidLoader extends 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 @@ public class NetworkCycleDataForUidLoader extends 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 @@ public class NetworkCycleDataForUidLoader extends extends NetworkCycleDataLoader.Builder<T> { private int mUid; + private boolean mRetrieveDetail = true; public Builder(Context context) { super(context); @@ -97,6 +102,11 @@ public class NetworkCycleDataForUidLoader extends 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 cc936d2485c5..b1c2c3a2d2e5 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 static android.net.NetworkStatsHistory.FIELD_TX_BYTES; 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.os.ServiceManager; 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 @@ public abstract class NetworkCycleDataLoader<D> extends AsyncTaskLoader<D> { protected NetworkCycleDataLoader(Builder<?> builder) { super(builder.mContext); - mPolicy = builder.mPolicy; mSubId = builder.mSubId; mNetworkType = builder.mNetworkType; mNetworkTemplate = builder.mNetworkTemplate; @@ -63,6 +65,10 @@ public abstract class NetworkCycleDataLoader<D> extends AsyncTaskLoader<D> { 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 @@ public abstract class NetworkCycleDataLoader<D> extends AsyncTaskLoader<D> { 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 abstract class NetworkCycleDataLoader<D> extends AsyncTaskLoader<D> { 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 @@ public abstract class NetworkCycleDataLoader<D> extends AsyncTaskLoader<D> { 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 3dc110d30e1e..cad88b1f97a4 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 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.os.RemoteException; import android.text.format.DateUtils; @@ -39,6 +41,8 @@ public class NetworkCycleChartDataLoaderTest { @Mock private NetworkStatsManager mNetworkStatsManager; @Mock + private NetworkPolicyManager mNetworkPolicyManager; + @Mock private Context mContext; private NetworkCycleChartDataLoader mLoader; @@ -48,6 +52,9 @@ public class NetworkCycleChartDataLoaderTest { 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 @@ public class NetworkCycleChartDataLoaderTest { 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 53fe45197236..2314f272c8ea 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 @@ public class NetworkCycleDataForUidLoaderTest { @Mock private NetworkStatsManager mNetworkStatsManager; @Mock + private NetworkPolicyManager mNetworkPolicyManager; + @Mock private Context mContext; private NetworkCycleDataForUidLoader mLoader; @@ -47,20 +58,42 @@ public class NetworkCycleDataForUidLoaderTest { 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 be7f1bbb280f..9d60a97f8584 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.ConnectivityManager; 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 @@ public class NetworkCycleDataLoaderTest { @Mock private NetworkStatsManager mNetworkStatsManager; @Mock + private NetworkPolicyManager mNetworkPolicyManager; + @Mock private Context mContext; @Mock private NetworkPolicy mPolicy; @@ -62,9 +65,6 @@ public class NetworkCycleDataLoaderTest { private Iterator<Range<ZonedDateTime>> mIterator; @Mock private INetworkStatsService mNetworkStatsService; - @Mock - private NetworkCycleDataLoader.Builder mBuilder; - private NetworkCycleDataTestLoader mLoader; @@ -73,7 +73,10 @@ public class NetworkCycleDataLoaderTest { 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/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 2b51aaab3805..e1c71fae7559 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 8442dd13a85b..6446367e19a5 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 d38cc0f608ce..69aea2c59029 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 android.util.DisplayMetrics; 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 f1b53fec0714..c7685f832cbb 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.admin.DevicePolicyManager; 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.IPackageManager; 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 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { 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 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { } }; + 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 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { private CancellationSignal mFingerprintCancelSignal; private CancellationSignal mFaceCancelSignal; private FingerprintManager mFpm; - private FaceManager mFaceAuthenticationManager; + private FaceManager mFaceManager; /** * When we receive a @@ -1434,7 +1401,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { 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 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { 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 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { 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 class KeyguardUpdateMonitor implements TrustManager.TrustListener { } 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 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { * 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 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { 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 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { 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 1af2156c4bbe..d351c4f3e3e6 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 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis private boolean mIsEnabled; private int mCurrentBoundedUserId = -1; private float mBackButtonAlpha; + private MotionEvent mStatusBarGestureDownEvent; private ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() { @@ -108,6 +113,9 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis } 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 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis 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 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis // 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 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis 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 646f69eede94..6dc2d67a1c78 100644 --- a/packages/SystemUI/src/com/android/systemui/RegionInterceptingFrameLayout.java +++ b/packages/SystemUI/src/com/android/systemui/RegionInterceptingFrameLayout.java @@ -76,7 +76,7 @@ public class RegionInterceptingFrameLayout extends FrameLayout { 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 48181bc629a8..4d24d82bd7ee 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; 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 @@ public class ScreenDecorations extends SystemUI implements Tunable { 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 @@ public class ScreenDecorations extends SystemUI implements Tunable { 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 @@ public class ScreenDecorations extends SystemUI implements Tunable { 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 class ScreenDecorations extends SystemUI implements Tunable { 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 @@ public class ScreenDecorations extends SystemUI implements Tunable { } 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 bde7f1bd86ad..512cd82731c2 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.Interpolators; 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 @@ import java.util.List; * 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 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, mEmergencyAffordanceManager = new EmergencyAffordanceManager(context); mScreenshotHelper = new ScreenshotHelper(context); + + Dependency.get(ConfigurationController.class).addCallback(this); } /** @@ -417,6 +421,15 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, || 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 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, * 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 1489c21c5a5c..039499858603 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 @@ public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks @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 0b9067e5dc1f..568a03954b36 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 @@ public class PowerUI extends SystemUI { // 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 4b5ab2a640ff..c398a4a68976 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.AnimatorSet; 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 @@ import java.util.Set; 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 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { 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 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { 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 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { 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 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { 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 79e508611750..42dfceed1e0d 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 @@ public class QSFragment extends Fragment implements QS, CommandQueue.Callbacks { 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 @@ public class QSFragment extends Fragment implements QS, CommandQueue.Callbacks { 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 e98ef4c09667..cf63e478312c 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.Context; 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 class QSPanel extends LinearLayout implements Tunable, Callback, Brightne } 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 556786a16caf..6f847c86f8da 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 @@ public class QuickQSPanel extends QSPanel { 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 6918a6309ae1..2ae53b5d81fe 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 @@ public class BrightnessDialog extends Activity { 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/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java index 8969acaac532..057784153dd6 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 @@ public class AmbientState { 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 9978ec364cdb..9ddab7cc0b2f 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.ObjectAnimator; 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.PorterDuff; 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.ExpandHelper; 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 @@ public class NotificationStackScrollLayout extends ViewGroup 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 class NotificationStackScrollLayout extends ViewGroup public void onPanelTrackingStarted() { mPanelTracking = true; mAmbientState.setPanelTracking(true); + resetExposedMenuView(true /* animate */, true /* force */); } @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) @@ -4271,8 +4271,10 @@ public class NotificationStackScrollLayout extends ViewGroup 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 @@ public class NotificationStackScrollLayout extends ViewGroup 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 cfc327116303..976327a49f58 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.Context; 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 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, // 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/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index 3a4c218d337e..6ea4c92e4143 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -733,7 +733,11 @@ public class NotificationPanelView extends PanelView implements mQsExpandImmediate = true; mNotificationStackScroller.setShouldShowShelfOnly(true); } - expand(true /* animate */); + if (isFullyCollapsed()){ + expand(true /* animate */); + } else { + flingSettings(0 /* velocity */, FLING_EXPAND); + } } public void expandWithoutQs() { 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 c3b87af612cc..90ed97f665d5 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 @@ public class StatusBar extends SystemUI implements DemoMode, } } else if (Intent.ACTION_SCREEN_OFF.equals(action)) { + mStatusBarWindowController.setNotTouchable(false); finishBarAnimations(); resetUserExpandedStates(); } 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 12b6f9de61ed..298a93eed55c 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.Bundle; 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 @@ public class KeyButtonView extends ImageView implements ButtonInterface { 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 ef51bf06e411..8d2552f9b166 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 @@ public class ZenModeControllerImpl extends CurrentUserTracker 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 @@ public class ZenModeControllerImpl extends CurrentUserTracker @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 @@ public class ZenModeControllerImpl extends CurrentUserTracker } 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 cc969177ab2e..b84f85bdbc4c 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 @@ package com.android.systemui; 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 static org.mockito.Mockito.when; 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.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.Collections; + @RunWithLooper @RunWith(AndroidTestingRunner.class) @SmallTest @@ -240,4 +244,11 @@ public class ScreenDecorationsTest extends SysuiTestCase { 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/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java index a9d49f91e44e..b44630a0cec0 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 @@ public class PowerUITest extends SysuiTestCase { 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 @@ public class PowerUITest extends SysuiTestCase { } @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 b545e61a446a..f8b24363da8f 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 @@ package com.android.systemui.statusbar.notification.stack; 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.service.dreams.IDreamManager; 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 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { 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/proto/src/wifi.proto b/proto/src/wifi.proto index 7f8989d8548e..033e99605d3f 100644 --- a/proto/src/wifi.proto +++ b/proto/src/wifi.proto @@ -479,6 +479,9 @@ message WifiLog { // Hardware revision (EVT, DVT, PVT etc.) optional string hardware_revision = 124; + + // Total wifi link layer usage data over the logging duration in ms. + optional WifiLinkLayerUsageStats wifi_link_layer_usage_stats = 125; } // Information that gets logged for every WiFi connection. @@ -1654,4 +1657,21 @@ message PasspointProfileTypeCount { // Num of installed Passpoint profile with same eap method optional int32 count = 2; +} + +message WifiLinkLayerUsageStats { + // Total logging duration in ms. + optional int64 logging_duration_ms = 1; + + // Total time the wifi radio is on in ms over the logging duration. + optional int64 radio_on_time_ms = 2; + + // Total time the wifi radio is doing tx in ms over the logging duration. + optional int64 radio_tx_time_ms = 3; + + // Total time the wifi radio is doing rx in ms over the logging duration. + optional int64 radio_rx_time_ms = 4; + + // Total time the wifi radio is scanning in ms over the logging duration. + optional int64 radio_scan_time_ms = 5; }
\ No newline at end of file diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 9bee8dba87ad..44ef8b6d6c3d 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 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @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 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub 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 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub 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 e0eb269ee728..9d84f5719dfc 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 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect 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 3c60eee4a2a9..328f8f7a37a7 100644 --- a/services/art-profile +++ b/services/art-profile @@ -2254,8 +2254,8 @@ HPLcom/android/server/wm/DisplayContent;->lambda$new$7(Lcom/android/server/wm/Di 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/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index ad2f82c9b19f..af33cbc0ac1c 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -142,6 +142,8 @@ class AlarmManagerService extends SystemService { 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 @@ class AlarmManagerService extends SystemService { 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 @@ class AlarmManagerService extends SystemService { 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 @@ class AlarmManagerService extends SystemService { 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 @@ class AlarmManagerService extends SystemService { if (alarm.alarmClock != null) { mNextAlarmClockMayChange = true; } - if (alarm.operation == mTimeTickSender) { + if (alarm.listener == mTimeTickTrigger) { mLastTickRemoved = mInjector.getCurrentTimeMillis(); } } else { @@ -690,8 +696,7 @@ class AlarmManagerService extends SystemService { 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 @@ class AlarmManagerService extends SystemService { } 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 @@ class AlarmManagerService extends SystemService { 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 @@ class AlarmManagerService extends SystemService { } 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 @@ class AlarmManagerService extends SystemService { } } - 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 @@ class AlarmManagerService extends SystemService { 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 @@ class AlarmManagerService extends SystemService { 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 @@ class AlarmManagerService extends SystemService { } // 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 @@ class AlarmManagerService extends SystemService { 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 @@ class AlarmManagerService extends SystemService { 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 AlarmManagerService extends SystemService { 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 @@ class AlarmManagerService extends SystemService { 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 @@ class AlarmManagerService extends SystemService { // PendingIntent alarm mSendCount++; - if (alarm.priorityClass.priority == PRIO_TICK) { - mLastTickIssued = nowELAPSED; - } - try { alarm.operation.send(getContext(), 0, mBackgroundIntent.putExtra( @@ -4344,13 +4362,10 @@ class AlarmManagerService extends SystemService { 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 @@ class AlarmManagerService extends SystemService { } 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 a1989e51ee51..5e8ffb79c493 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -393,9 +393,9 @@ public class ConnectivityService extends IConnectivityManager.Stub private static final int EVENT_PROMPT_UNVALIDATED = 29; /** - * used internally to (re)configure mobile data always-on settings. + * used internally to (re)configure always-on networks. */ - private static final int EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON = 30; + private static final int EVENT_CONFIGURE_ALWAYS_ON_NETWORKS = 30; /** * used to add a network listener with a pending intent @@ -751,6 +751,12 @@ public class ConnectivityService extends IConnectivityManager.Stub mDefaultMobileDataRequest = createDefaultInternetRequestForTransport( NetworkCapabilities.TRANSPORT_CELLULAR, NetworkRequest.Type.BACKGROUND_REQUEST); + // The default WiFi request is a background request so that apps using WiFi are + // migrated to a better network (typically ethernet) when one comes up, instead + // of staying on WiFi forever. + mDefaultWifiRequest = createDefaultInternetRequestForTransport( + NetworkCapabilities.TRANSPORT_WIFI, NetworkRequest.Type.BACKGROUND_REQUEST); + mHandlerThread = new HandlerThread("ConnectivityServiceThread"); mHandlerThread.start(); mHandler = new InternalHandler(mHandlerThread.getLooper()); @@ -948,8 +954,8 @@ public class ConnectivityService extends IConnectivityManager.Stub // 2. Give FakeSettingsProvider an alternative notification mechanism and have the test use it // by subclassing SettingsObserver. @VisibleForTesting - void updateMobileDataAlwaysOn() { - mHandler.sendEmptyMessage(EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON); + void updateAlwaysOnNetworks() { + mHandler.sendEmptyMessage(EVENT_CONFIGURE_ALWAYS_ON_NETWORKS); } // See FakeSettingsProvider comment above. @@ -958,22 +964,30 @@ public class ConnectivityService extends IConnectivityManager.Stub mHandler.sendEmptyMessage(EVENT_PRIVATE_DNS_SETTINGS_CHANGED); } - private void handleMobileDataAlwaysOn() { + private void handleAlwaysOnNetworkRequest( + NetworkRequest networkRequest, String settingName, boolean defaultValue) { final boolean enable = toBool(Settings.Global.getInt( - mContext.getContentResolver(), Settings.Global.MOBILE_DATA_ALWAYS_ON, 1)); - final boolean isEnabled = (mNetworkRequests.get(mDefaultMobileDataRequest) != null); + mContext.getContentResolver(), settingName, encodeBool(defaultValue))); + final boolean isEnabled = (mNetworkRequests.get(networkRequest) != null); if (enable == isEnabled) { return; // Nothing to do. } if (enable) { handleRegisterNetworkRequest(new NetworkRequestInfo( - null, mDefaultMobileDataRequest, new Binder())); + null, networkRequest, new Binder())); } else { - handleReleaseNetworkRequest(mDefaultMobileDataRequest, Process.SYSTEM_UID); + handleReleaseNetworkRequest(networkRequest, Process.SYSTEM_UID); } } + private void handleConfigureAlwaysOnNetworks() { + handleAlwaysOnNetworkRequest( + mDefaultMobileDataRequest,Settings.Global.MOBILE_DATA_ALWAYS_ON, true); + handleAlwaysOnNetworkRequest(mDefaultWifiRequest, Settings.Global.WIFI_ALWAYS_REQUESTED, + false); + } + private void registerSettingsCallbacks() { // Watch for global HTTP proxy changes. mSettingsObserver.observe( @@ -983,7 +997,12 @@ public class ConnectivityService extends IConnectivityManager.Stub // Watch for whether or not to keep mobile data always on. mSettingsObserver.observe( Settings.Global.getUriFor(Settings.Global.MOBILE_DATA_ALWAYS_ON), - EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON); + EVENT_CONFIGURE_ALWAYS_ON_NETWORKS); + + // Watch for whether or not to keep wifi always on. + mSettingsObserver.observe( + Settings.Global.getUriFor(Settings.Global.WIFI_ALWAYS_REQUESTED), + EVENT_CONFIGURE_ALWAYS_ON_NETWORKS); } private void registerPrivateDnsSettingsCallbacks() { @@ -1818,7 +1837,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } void systemReady() { - loadGlobalProxy(); + mProxyTracker.loadGlobalProxy(); registerNetdEventCallback(); synchronized (this) { @@ -1835,8 +1854,8 @@ public class ConnectivityService extends IConnectivityManager.Stub // for user to unlock device too. updateLockdownVpn(); - // Configure whether mobile data is always on. - mHandler.sendMessage(mHandler.obtainMessage(EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON)); + // Create network requests for always-on networks. + mHandler.sendMessage(mHandler.obtainMessage(EVENT_CONFIGURE_ALWAYS_ON_NETWORKS)); mHandler.sendMessage(mHandler.obtainMessage(EVENT_SYSTEM_READY)); @@ -3125,8 +3144,8 @@ public class ConnectivityService extends IConnectivityManager.Stub handlePromptUnvalidated((Network) msg.obj); break; } - case EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON: { - handleMobileDataAlwaysOn(); + case EVENT_CONFIGURE_ALWAYS_ON_NETWORKS: { + handleConfigureAlwaysOnNetworks(); break; } // Sent by KeepaliveTracker to process an app request on the state machine thread. @@ -3436,31 +3455,6 @@ public class ConnectivityService extends IConnectivityManager.Stub 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() { @@ -4554,6 +4548,10 @@ public class ConnectivityService extends IConnectivityManager.Stub // priority networks like Wi-Fi are active. private final NetworkRequest mDefaultMobileDataRequest; + // Request used to optionally keep wifi data active even when higher + // priority networks like ethernet are active. + private final NetworkRequest mDefaultWifiRequest; + private NetworkAgentInfo getNetworkForRequest(int requestId) { synchronized (mNetworkForRequestId) { return mNetworkForRequestId.get(requestId); diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java index a34c2b93e3bf..376bc0df1d87 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.TimeUtils; 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 @@ import java.util.Arrays; /** * 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 @@ public class DeviceIdleController extends SystemService 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 @@ public class DeviceIdleController extends SystemService } /** 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 class DeviceIdleController extends SystemService 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 @@ public class DeviceIdleController extends SystemService } }; - 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 @@ public class DeviceIdleController extends SystemService 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 class DeviceIdleController extends SystemService 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 @@ public class DeviceIdleController extends SystemService } } + 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 class DeviceIdleController extends SystemService } }; - 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 class DeviceIdleController extends SystemService 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 @@ public class DeviceIdleController extends SystemService 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 @@ public class DeviceIdleController extends SystemService 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 @@ public class DeviceIdleController extends SystemService } } + @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 @@ public class DeviceIdleController extends SystemService } } + @VisibleForTesting + boolean isCharging() { + return mCharging; + } + void updateChargingLocked(boolean charging) { if (DEBUG) Slog.i(TAG, "updateChargingLocked: charging=" + charging); if (!charging && mCharging) { @@ -2071,6 +2267,18 @@ public class DeviceIdleController extends SystemService } } + /** 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 @@ public class DeviceIdleController extends SystemService } } - void resetIdleManagementLocked() { + private void resetIdleManagementLocked() { mNextIdlePendingDelay = 0; mNextIdleDelay = 0; mNextLightIdleDelay = 0; @@ -2104,7 +2312,7 @@ public class DeviceIdleController extends SystemService mAnyMotionDetector.stop(); } - void resetLightIdleManagementLocked() { + private void resetLightIdleManagementLocked() { cancelLightAlarmLocked(); } @@ -2117,6 +2325,11 @@ public class DeviceIdleController extends SystemService } } + @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 @@ public class DeviceIdleController extends SystemService } } + /** 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 b7b5bd930f8e..8077e3499bb2 100644 --- a/services/core/java/com/android/server/GestureLauncherService.java +++ b/services/core/java/com/android/server/GestureLauncherService.java @@ -356,6 +356,12 @@ public class GestureLauncherService extends SystemService { 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/LooperStatsService.java b/services/core/java/com/android/server/LooperStatsService.java index 4f0e17055769..96ce6a4ee6a4 100644 --- a/services/core/java/com/android/server/LooperStatsService.java +++ b/services/core/java/com/android/server/LooperStatsService.java @@ -129,7 +129,12 @@ public class LooperStatsService extends Binder { } private void setSamplingInterval(int samplingInterval) { - mStats.setSamplingInterval(samplingInterval); + if (samplingInterval > 0) { + mStats.setSamplingInterval(samplingInterval); + } else { + Slog.w(TAG, "Ignored invalid sampling interval (value must be positive): " + + samplingInterval); + } } /** diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 461d39d0a29a..8e64b5063302 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 @@ public final class ActiveServices { + ") 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { 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 fab967c01086..fcb717ed51f0 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.annotation.Nullable; 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 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> */ 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 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> 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 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> 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 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> 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 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> if (ignoreCurrent && stack == currentFocus) { continue; } - if (!stack.isFocusable() || !stack.shouldBeVisible(null)) { + if (!stack.isFocusableAndVisible()) { continue; } @@ -911,6 +942,13 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> 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 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> && (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 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> 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 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> 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 aa14da0ad70a..9c96968e566f 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.DEBUG_APP; 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 @@ public class ActivityManagerService extends IActivityManager.Stub } 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 @@ public class ActivityManagerService extends IActivityManager.Stub // 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 @@ public class ActivityManagerService extends IActivityManager.Stub ? 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 5853ad976bbb..fe10baf33354 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 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo 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 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo 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 000000000000..b1ced29da4c2 --- /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 29b04ccdab08..ea807adc7d4f 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 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai 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 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai 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 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai 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 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai final String myReason = reason + " adjustFocus"; if (next == r) { - mStackSupervisor.moveFocusableActivityStackToFrontLocked( - mStackSupervisor.topRunningActivityLocked(), myReason); + mStackSupervisor.moveFocusableActivityToTop(mStackSupervisor.topRunningActivityLocked(), + myReason); return; } @@ -3482,7 +3483,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } // Whatever...go home. - mStackSupervisor.moveHomeStackTaskToTop(myReason); + getDisplay().moveHomeActivityToTop(myReason); } /** @@ -3511,7 +3512,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai 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 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai * 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 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai 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 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // 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 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai 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 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // 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 9688d263643c..a968ae4e0201 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_SPLIT_SCREEN_SECOND 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.DEFAULT_DISPLAY; 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.LOCK_TASK_AUTH_WHITELISTED; 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.PackageManager; 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.provider.MediaStore; 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D /** 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } ActivityRecord getTopResumedActivity() { - if (mWindowManager == null) { - return null; - } - final ActivityStack focusedStack = getTopDisplayFocusedStack(); if (focusedStack == null) { return null; @@ -782,7 +777,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D if (focusCandidate == null) { Slog.w(TAG, "setFocusStackUnchecked: No focusable stack found, focus home as default"); - focusCandidate = mHomeStack; + focusCandidate = getDefaultDisplay().getHomeStack(); } } @@ -803,10 +798,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } } - 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } } - /** 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, 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; } - return mService.mAm.startHomeActivityLocked(mCurrentUser, myReason); + + 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D */ 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return false; } - if (targetStack != null && targetStack.isTopStackOnDisplay()) { + if (targetStack != null && (targetStack.isTopStackOnDisplay() + || getTopDisplayFocusedStack() == targetStack)) { return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions); } @@ -2348,7 +2354,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D */ 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } /** 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D 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 class ActivityStackSupervisor extends ConfigurationContainer implements D } 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D private void handleDisplayAdded(int displayId) { synchronized (mService.mGlobalLock) { getActivityDisplayOrCreateLocked(displayId); + mService.mAm.startHomeActivityLocked(mCurrentUser, "displayAdded", displayId); } } @@ -4319,7 +4327,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D 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 6e3a79c3aa95..5e73bc3a2259 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 @@ public class ActivityStartController { /** 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 @@ public class ActivityStartController { 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 1fb8f871efcd..4789ff334398 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 @@ class ActivityStartInterceptor { 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 8236bd0e763e..de3b9cf3ccde 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 @@ class ActivityStarter { 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 @@ class ActivityStarter { 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 @@ class ActivityStarter { && 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 @@ class ActivityStarter { 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 36261b505a94..c1eab821d084 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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return; } final ActivityRecord r = stack.topRunningActivityLocked(); - if (mStackSupervisor.moveFocusableActivityStackToFrontLocked( - r, "setFocusedStack")) { + if (mStackSupervisor.moveFocusableActivityToTop(r, "setFocusedStack")) { mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); } } @@ -1714,7 +1713,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return; } final ActivityRecord r = task.topRunningActivityLocked(); - if (mStackSupervisor.moveFocusableActivityStackToFrontLocked(r, "setFocusedTask")) { + if (mStackSupervisor.moveFocusableActivityToTop(r, "setFocusedTask")) { mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); } } @@ -4846,8 +4845,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { 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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { 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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { 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 fa8e6c431770..1242ed626a18 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 @@ import java.io.PrintWriter; */ 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 @@ final class ConnectionRecord { 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 080fa2a2ec5e..3490b1d18953 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 @@ import java.io.PrintWriter; * 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 @@ public abstract class PersistentConnection<T> { private final long mRebindBackoffMs; private final double mRebindBackoffIncrease; private final long mRebindMaxBackoffMs; + private final long mResetBackoffDelay; private long mReconnectTime; @@ -109,6 +112,9 @@ public abstract class PersistentConnection<T> { @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 @@ public abstract class PersistentConnection<T> { mNumConnected++; mIsConnected = true; + mLastConnectedTime = injectUptimeMillis(); mService = asInterface(service); + + scheduleStableCheckLocked(); } } @@ -140,6 +149,9 @@ public abstract class PersistentConnection<T> { 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 abstract class PersistentConnection<T> { 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 @@ public abstract class PersistentConnection<T> { mRebindBackoffMs = rebindBackoffSeconds * 1000; mRebindBackoffIncrease = rebindBackoffIncrease; mRebindMaxBackoffMs = rebindMaxBackoffSeconds * 1000; + mResetBackoffDelay = resetBackoffDelay * 1000; mNextBackoffMs = mRebindBackoffMs; } @@ -242,6 +256,42 @@ public abstract class PersistentConnection<T> { } } + /** 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 @@ public abstract class PersistentConnection<T> { } 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 @@ public abstract class PersistentConnection<T> { private void cleanUpConnectionLocked() { mIsConnected = false; mService = null; + + unscheduleStableCheckLocked(); } /** @@ -297,6 +350,7 @@ public abstract class PersistentConnection<T> { mShouldBeBound = false; unbindLocked(); + unscheduleStableCheckLocked(); } } @@ -338,6 +392,33 @@ public abstract class PersistentConnection<T> { } } + 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 @@ public abstract class PersistentConnection<T> { 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 @@ public abstract class PersistentConnection<T> { } @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 000000000000..60ea0faaffee --- /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 e11e00368a78..4d0b1da98fde 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_EXCLUDE_FROM_RECENTS; 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.ParceledListSlice; 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 @@ class RecentTasks { void onSystemReadyLocked() { loadRecentsComponent(mService.mContext.getResources()); mTasks.clear(); - mTaskPersister.startPersisting(); + mTaskPersister.onSystemReady(); } Bitmap getTaskDescriptionIcon(String path) { @@ -1244,7 +1244,6 @@ class RecentTasks { */ 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 @@ class RecentTasks { } // 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 481bb2bbe118..fc5dfb4db1fc 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 android.util.Xml; 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.Collections; 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 @@ public class TaskPersister { 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 @@ public class TaskPersister { mStackSupervisor = stackSupervisor; mService = service; mRecentTasks = recentTasks; - mLazyTaskWriterThread = new LazyTaskWriterThread("LazyTaskWriterThread"); + mPersisterQueue = new PersisterQueue(); + mPersisterQueue.addListener(this); } @VisibleForTesting @@ -154,42 +113,21 @@ public class TaskPersister { 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 @@ public class TaskPersister { } 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 @@ public class TaskPersister { 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 @@ public class TaskPersister { 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 @@ public class TaskPersister { 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; } - @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()); - } - removeObsoleteFiles(persistentTaskIds); - } - writeTaskIdsFiles(); + 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); - processNextItem(); + if (DEBUG) { + xmlSerializer.setFeature( + "http://xmlpull.org/v1/doc/features.html#indent-output", true); } - } - 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 - - // 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 + ")"); - } + // save task + xmlSerializer.startDocument(null, true); - 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); + xmlSerializer.startTag(null, TAG_TASK); + task.saveToXml(xmlSerializer); + xmlSerializer.endTag(null, TAG_TASK); + + xmlSerializer.endDocument(); + xmlSerializer.flush(); + + return stringWriter; + } - long now = SystemClock.uptimeMillis(); - if (DEBUG) Slog.d(TAG, "LazyTaskWriter: now=" + now + " mNextWriteTime=" + - mNextWriteTime + " mWriteQueue.size=" + mWriteQueue.size()); - while (now < mNextWriteTime) { + @Override + 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, "LazyTaskWriter: waiting " + - (mNextWriteTime - now)); - TaskPersister.this.wait(mNextWriteTime - now); - } catch (InterruptedException e) { + if (DEBUG) Slog.d(TAG, "Saving task=" + task); + stringWriter = saveToXml(task); + } catch (IOException e) { + } catch (XmlPullParserException e) { } - now = SystemClock.uptimeMillis(); } - - // Got something to do. } - - 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; + if (stringWriter != null) { + // Write out xml file while not holding mService lock. + FileOutputStream file = null; + AtomicFile atomicFile = 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); + 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); } } } + + @Override + public String toString() { + return "TaskWriteQueueItem{task=" + mTask + "}"; + } + } + + private static class ImageWriteQueueItem implements PersisterQueue.WriteQueueItem { + final String mFilePath; + Bitmap mImage; + + ImageWriteQueueItem(String filePath, Bitmap image) { + mFilePath = filePath; + mImage = image; + } + + @Override + public void process() { + final String filePath = mFilePath; + if (!createParentDirectory(filePath)) { + Slog.e(TAG, "Error while creating images directory for file: " + filePath); + return; + } + 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 ef8cb1c07969..05b0d598f878 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.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VER 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.ORIG_ACTIVITY; 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 @@ public class TaskRecord extends ConfigurationContainer implements TaskWindowCont // 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 c2655a289b49..71847694b1d8 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 @@ import java.util.concurrent.TimeUnit; /** * 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 @@ class AppBindingConstants { 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 @@ class AppBindingConstants { */ 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 @@ class AppBindingConstants { } 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 @@ class AppBindingConstants { 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 @@ class AppBindingConstants { */ 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 @@ class AppBindingConstants { 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 c5cb2a4f5814..3131255a61cd 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.annotation.Nullable; 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 @@ import java.util.function.Consumer; * * <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 class AppBindingService extends Binder { 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 @@ public class AppBindingService extends Binder { 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 @@ public class AppBindingService extends Binder { }; 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 @@ public class AppBindingService extends Binder { 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 @@ public class AppBindingService extends Binder { 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 @@ public class AppBindingService extends Binder { 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 @@ public class AppBindingService extends Binder { 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 68c5e496cc32..a075c50733d5 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.content.pm.ServiceInfo; 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 @@ public abstract class AppServiceFinder<TServiceType, TServiceInterfaceType exten 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 @@ public abstract class AppServiceFinder<TServiceType, TServiceInterfaceType exten 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 @@ public abstract class AppServiceFinder<TServiceType, TServiceInterfaceType exten synchronized (mLock) { mTargetPackages.delete(userId); mTargetServices.delete(userId); + mLastMessages.delete(userId); } } @@ -87,10 +95,19 @@ public abstract class AppServiceFinder<TServiceType, TServiceInterfaceType exten * 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 @@ public abstract class AppServiceFinder<TServiceType, TServiceInterfaceType exten 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 @@ public abstract class AppServiceFinder<TServiceType, TServiceInterfaceType exten @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 @@ public abstract class AppServiceFinder<TServiceType, TServiceInterfaceType exten 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 abstract class AppServiceFinder<TServiceType, TServiceInterfaceType exten 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 c908bd919748..fcc28f8e2886 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.ComponentName; 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 @@ public class SmsAppServiceFinder extends AppServiceFinder<SmsAppService, ISmsApp } @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 class SmsAppServiceFinder extends AppServiceFinder<SmsAppService, ISmsApp 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 @@ public class SmsAppServiceFinder extends AppServiceFinder<SmsAppService, ISmsApp /* 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 aa4d34ef78b6..8a72a6d215ba 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 @@ public abstract class AuthenticationClient extends ClientMonitor { // 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 87cf9c434fc0..abc0107f8eaa 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.FaceManager; 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.RemoteException; 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 @@ public class BiometricService extends SystemService { 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 @@ public class BiometricService extends SystemService { } } + 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 class BiometricService extends SystemService { 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 @@ public class BiometricService extends SystemService { final int callingUserId = UserHandle.getCallingUserId(); mHandler.post(() -> { - mCurrentModality = checkAndGetBiometricModality(receiver); + final Pair<Integer, Integer> result = checkAndGetBiometricModality(); + 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 @@ public class BiometricService extends SystemService { } 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 @@ public class BiometricService extends SystemService { }); } - 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,33 +347,48 @@ public class BiometricService extends SystemService { } @Override // Binder call - public boolean hasEnrolledBiometrics(String opPackageName) { + public int canAuthenticate(String opPackageName) { checkPermission(); - - 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"); - } + checkAppOp(opPackageName, Binder.getCallingUid()); 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(); + 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() { if (getContext().checkCallingPermission(USE_FINGERPRINT) != PackageManager.PERMISSION_GRANTED) { @@ -270,11 +411,26 @@ public class BiometricService extends SystemService { 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 +461,91 @@ public class BiometricService extends SystemService { * 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 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 (firstHwAvailable == BIOMETRIC_NONE) { + // Store the first one since we want to return the error in correct priority + // order. + firstHwAvailable = modality; + } if (authenticator.hasEnrolledTemplates()) { hasTemplatesEnrolled = true; - modality = featureId; - break; + if (isEnabledForApp(modality)) { + 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; + 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); } - 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<>(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; } + } - return modality; + 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 dc65e1e69834..b7bbd422458f 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 @@ public class ProxyTracker { } /** + * 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 240592528565..7bfe9ce7017c 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 @@ package com.android.server.display; 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 @@ abstract class DisplayDevice { 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 512e85192d36..c51dc52b0515 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 @@ final class DisplayDeviceInfo { 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 0eff7f57895b..e70460aef3e0 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.BrightnessConfiguration; 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.DisplayInfo; 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 @@ public final class DisplayManagerService extends SystemService { // 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 @@ public final class DisplayManagerService extends SystemService { // 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 @@ public final class DisplayManagerService extends SystemService { } 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 @@ public final class DisplayManagerService extends SystemService { } 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 @@ public final class DisplayManagerService extends SystemService { 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 @@ public final class DisplayManagerService extends SystemService { 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 5b7c5205ce3a..6f726e605d5e 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 @@ package com.android.server.display; 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 @@ final class LogicalDisplay { 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 6111c23f252c..5aa585fcad75 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_ROTATES_WITH_CONTENT; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE; +import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH; -import 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 android.content.Context; import android.hardware.display.IVirtualDisplayCallback; @@ -33,10 +32,10 @@ import android.media.projection.IMediaProjection; 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 @@ public class VirtualDisplayAdapter extends DisplayAdapter { 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 @@ public class VirtualDisplayAdapter extends DisplayAdapter { 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 0f2843917420..4913e8bda6ad 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 @@ public class InputManagerService extends IInputManager.Stub 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 @@ public class InputManagerService extends IInputManager.Stub 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 @@ public class InputManagerService extends IInputManager.Stub 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 class InputManagerService extends IInputManager.Stub } } - public void setInputWindows(InputWindowHandle[] windowHandles, - InputWindowHandle focusedWindowHandle, int displayId) { + public void setInputWindows(InputWindowHandle[] windowHandles, int displayId) { + nativeSetInputWindows(mPtr, windowHandles, displayId); + } + + 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) { - mFocusedWindow = newFocusedWindow; if (mFocusedWindowHasCapture) { setPointerCapture(false); } + mFocusedWindow = newFocusedWindow; } - nativeSetInputWindows(mPtr, windowHandles, displayId); } - public void setFocusedApplication(InputApplicationHandle application) { - nativeSetFocusedApplication(mPtr, application); + public void setFocusedDisplay(int displayId) { + nativeSetFocusedDisplay(mPtr, displayId); } @Override @@ -1491,16 +1467,18 @@ public class InputManagerService extends IInputManager.Stub 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 @@ public class InputManagerService extends IInputManager.Stub 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 60e9eaab5721..3f031699bd7a 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } // 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { 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 class NetworkStatsService extends INetworkStatsService.Stub { 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 5005ea7e55f8..f9d49d7ce0d1 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 @@ public class NotificationManagerService extends SystemService { return mInternalService; } - private final IBinder mService = new INotificationManager.Stub() { + @VisibleForTesting + final IBinder mService = new INotificationManager.Stub() { // Toasts // ============================================================================ @@ -2015,21 +2017,30 @@ public class NotificationManagerService extends SystemService { } 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 471729ee649d..6bce78862087 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 @@ class PackageSignatures { 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 @@ class PackageSignatures { 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 @@ class PackageSignatures { "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 @@ class PackageSignatures { 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 @@ class PackageSignatures { } 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 @@ class PackageSignatures { 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 @@ class PackageSignatures { 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 @@ class PackageSignatures { + 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 @@ class PackageSignatures { + 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 @@ class PackageSignatures { 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 @@ class PackageSignatures { "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 @@ class PackageSignatures { 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 910ea738acd8..1f05dc966555 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.DexFile; 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 2557f46ba34b..21bf488f828c 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { } 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { 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 9f6b3dde5a49..b3f2a27cf99a 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -4404,8 +4404,11 @@ public final class PowerManagerService extends SystemService @Override // Binder call public boolean setPowerSaveMode(boolean enabled) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.DEVICE_POWER, null); + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.POWER_SAVER) + != PackageManager.PERMISSION_GRANTED) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.DEVICE_POWER, null); + } final long ident = Binder.clearCallingIdentity(); try { return setLowPowerModeInternal(enabled); diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java index d0de9409c49e..97992cf1232b 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.ArrayMap; 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; @@ -1336,12 +1339,28 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { } } + 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); @@ -1478,6 +1497,10 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { 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 +1562,8 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { 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 +1588,22 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { } /** - * 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 +1621,12 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { 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 +1640,8 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { 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 +1703,8 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { 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 +1712,8 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { 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 e4491113cfb8..74922f650ad8 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 @@ final class AccessibilityController { // 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 bcf9212464db..db0bd4f72fd4 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.service.voice.IVoiceInteractionSession; 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 @@ public abstract class ActivityTaskManagerInternal { 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 d73606f3003f..a9d09781d223 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.APP_TRANSITION_STATE; 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.Rect; 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 android.view.animation.TranslateAnimation; 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 @@ public class AppTransition implements Dump { 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 @@ public class AppTransition implements Dump { @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 @@ public class AppTransition implements Dump { 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 @@ public class AppTransition implements Dump { clear(); mNextAppTransitionType = NEXT_TRANSIT_TYPE_REMOTE; mRemoteAnimationController = new RemoteAnimationController(mService, - remoteAnimationAdapter, mService.mH); + remoteAnimationAdapter, mHandler); } } @@ -2162,8 +2169,8 @@ public class AppTransition implements Dump { } 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 @@ public class AppTransition implements Dump { 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 36280dd98180..330c54ca4c1d 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 @@ public class AppWindowContainerController 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 fc7610239fa3..e57fea31f1a8 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 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree 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 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } } + @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 a762fe9362c5..6f728fcf1f00 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_FRAMES; 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.SHOW_TRANSACTIONS; 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.WindowState.RESIZE_HANDLE_WIDTH_IN_DP; 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 android.view.SurfaceSession; 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 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo 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 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo 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 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo /** 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 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo }; 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 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo 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 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } } - 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 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo 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 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo 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 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // 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 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } 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 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo 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 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo 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 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo 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 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } // 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 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // can now be shown. atoken.updateAllDrawn(); } - - return mTmpApplySurfaceChangesTransactionState.focusDisplayed; } private void updateBounds() { @@ -3314,7 +3484,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo boolean displayHasContent; boolean obscured; boolean syswin; - boolean focusDisplayed; float preferredRefreshRate; int preferredModeId; @@ -3322,7 +3491,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo 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 76b6dbea36b9..ab8775983291 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 @@ public class DisplayWindowController } } + /** + * 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 ef3a770390ef..15f693872158 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 @@ final class InputMonitor { // 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 @@ final class InputMonitor { + 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 @@ final class InputMonitor { 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 @@ final class InputMonitor { 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 @@ final class InputMonitor { } // Send windows to native code. - mService.mInputManager.setInputWindows(mInputWindowHandles, mFocusedInputWindowHandle, - mDisplayId); + mService.mInputManager.setInputWindows(mInputWindowHandles, mDisplayId); clearInputWindowHandlesLw(); @@ -435,7 +428,6 @@ final class InputMonitor { 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 a9571be9599d..3fef87dce460 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.SHOW_TRANSACTIONS; 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 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { 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 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { 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 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { if (mService.updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES, false /*updateInputWindows*/)) { updateInputWindowsNeeded = true; - defaultDisplay.pendingLayoutChanges |= FINISH_LAYOUT_REDO_ANIM; } } @@ -646,7 +667,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { 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 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { 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 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { 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 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { /** * 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 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { } 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 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { } } + 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 33416f66f85b..b7e37b2fd47a 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 @@ class TaskPositioningController { // 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 f1e1592da83e..52f8510d6a99 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 @@ package com.android.server.wm; 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 @@ public class TaskTapPointerEventListener implements PointerEventListener { 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 @@ public class TaskTapPointerEventListener implements PointerEventListener { 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 @@ public class TaskTapPointerEventListener implements PointerEventListener { 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 4883f972f1e5..46999a2a847e 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 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< parent.mTreeWeight += child.mTreeWeight; parent = parent.getParent(); } + onChildPositionChanged(); } /** @@ -298,6 +299,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< parent.mTreeWeight -= child.mTreeWeight; parent = parent.getParent(); } + onChildPositionChanged(); } /** @@ -455,9 +457,15 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< 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 942e47b4725f..10ba63e0a9f7 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 @@ public class WindowManagerService extends IWindowManager.Stub 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 @@ public class WindowManagerService extends IWindowManager.Stub */ 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 @@ public class WindowManagerService extends IWindowManager.Stub } } - // TODO: Move to RootWindowContainer - AppWindowToken mFocusedApp = null; - PowerManager mPowerManager; PowerManagerInternal mPowerManagerInternal; @@ -1371,8 +1354,8 @@ public class WindowManagerService extends IWindowManager.Stub // 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 @@ public class WindowManagerService extends IWindowManager.Stub // 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 @@ public class WindowManagerService extends IWindowManager.Stub 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 @@ public class WindowManagerService extends IWindowManager.Stub 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 @@ public class WindowManagerService extends IWindowManager.Stub 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 @@ public class WindowManagerService extends IWindowManager.Stub 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 @@ public class WindowManagerService extends IWindowManager.Stub } result |= RELAYOUT_RES_SURFACE_CHANGED; if (!win.mWillReplaceWindow) { - focusMayChange = tryStartExitingAnimation(win, winAnimator, isDefaultDisplay, - focusMayChange); + focusMayChange = tryStartExitingAnimation(win, winAnimator, focusMayChange); } } @@ -2051,7 +2033,7 @@ public class WindowManagerService extends IWindowManager.Stub 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 @@ public class WindowManagerService extends IWindowManager.Stub } 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 @@ public class WindowManagerService extends IWindowManager.Stub 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 @@ public class WindowManagerService extends IWindowManager.Stub } } - 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 @@ public class WindowManagerService extends IWindowManager.Stub } private WindowState getFocusedWindowLocked() { - return mCurrentFocus; + // Return the focused window in the focused display. + return mRoot.getTopFocusedDisplayContent().mCurrentFocus; } TaskStack getImeFocusStackLocked() { @@ -4403,8 +4335,11 @@ public class WindowManagerService extends IWindowManager.Stub // 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 class WindowManagerService extends IWindowManager.Stub 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 class WindowManagerService extends IWindowManager.Stub 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 @@ public class WindowManagerService extends IWindowManager.Stub } switch (msg.what) { case REPORT_FOCUS_CHANGE: { + final DisplayContent displayContent = (DisplayContent) msg.obj; WindowState lastFocus; WindowState newFocus; @@ -4608,24 +4542,22 @@ public class WindowManagerService extends IWindowManager.Stub 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 @@ public class WindowManagerService extends IWindowManager.Stub 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 @@ public class WindowManagerService extends IWindowManager.Stub } 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 @@ public class WindowManagerService extends IWindowManager.Stub 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 @@ public class WindowManagerService extends IWindowManager.Stub 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 @@ public class WindowManagerService extends IWindowManager.Stub } } - // 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 @@ public class WindowManagerService extends IWindowManager.Stub 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 @@ public class WindowManagerService extends IWindowManager.Stub } } } - 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 @@ public class WindowManagerService extends IWindowManager.Stub 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 @@ public class WindowManagerService extends IWindowManager.Stub 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 @@ public class WindowManagerService extends IWindowManager.Stub @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 @@ public class WindowManagerService extends IWindowManager.Stub @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 @@ public class WindowManagerService extends IWindowManager.Stub // 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 a4bac31bbcee..8276952d8a6c 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 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP 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 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP 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 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP 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 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } } mPolicyVisibilityAfterAnim = false; + final boolean isFocused = isFocused(); if (!doAnimation) { if (DEBUG_VISIBILITY) Slog.v(TAG, "Policy visibility false: " + this); mPolicyVisibility = false; @@ -2489,7 +2490,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // 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 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (requestAnim) { mService.scheduleAnimationLocked(); } - if (mService.mCurrentFocus == this) { + if (isFocused) { mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /* updateImWindows */); } return true; @@ -2994,10 +2995,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } } - public boolean isFocused() { - synchronized(mService.mWindowMap) { - return mService.mCurrentFocus == this; - } + boolean isFocused() { + return getDisplayContent().mCurrentFocus == this; } @Override @@ -4435,7 +4434,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP @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 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP 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 080a3a269947..e13a70a399e2 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.WindowManager.TransitionType; 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 @@ class WindowSurfacePlacer { 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 @@ class WindowSurfacePlacer { 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 fefd305bc6d6..0cf79b63e9dc 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 @@ class WindowToken extends WindowContainer<WindowState> { 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 000000000000..9f307bb0f98f --- /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 157b6349f4ee..045f4ebf980f 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -8,6 +8,7 @@ cc_library_static { "-Wall", "-Werror", "-Wno-unused-parameter", + "-Wthread-safety", "-DEGL_EGLEXT_PROTOTYPES", "-DGL_GLEXT_PROTOTYPES", @@ -29,9 +30,7 @@ cc_library_static { "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 3901cebcc787..dc0d53b41595 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 @@ static void android_server_SystemServer_startSensorService(JNIEnv* /* env */, jo 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 42ade38fbe51..c66d03c3a5a3 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 inline const char* toString(bool value) { 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 getInputApplicationHandleObjLocalRef(JNIEnv* env, 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 @@ static void loadSystemIconAsSprite(JNIEnv* env, jobject contextObj, int32_t styl 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 @@ public: 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 @@ private: 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 @@ private: // Input devices to be disabled SortedVector<int32_t> disabledInputDevices; - } mLocked; + } mLocked GUARDED_BY(mLock); std::atomic<bool> mInteractive; @@ -384,8 +393,17 @@ bool NativeInputManager::checkAndClearExceptionFromCallback(JNIEnv* env, const c 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 @@ void NativeInputManager::setVirtualDisplayViewports(JNIEnv* env, jobjectArray vi 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 @@ void NativeInputManager::getReaderConfiguration(InputReaderConfiguration* outCon 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 @@ void NativeInputManager::getReaderConfiguration(InputReaderConfiguration* outCon 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 @@ sp<PointerControllerInterface> NativeInputManager::obtainPointerController(int32 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 @@ void NativeInputManager::notifyInputDevicesChanged(const Vector<InputDeviceInfo> 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 @@ void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowHandleO 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::setInputWindows(JNIEnv* env, jobjectArray windowHandleO } } -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 @@ void NativeInputManager::setSystemUiVisibility(int32_t visibility) { 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::reloadCalibration() { 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::setPointerIconType(int32_t iconId) { 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::reloadPointerIcons() { 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 @@ bool NativeInputManager::dispatchUnhandledKey(const sp<InputWindowHandle>& input 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 @@ int32_t NativeInputManager::getCustomPointerIconId() { 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 nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) { } } -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 @@ static jboolean nativeHasKeys(JNIEnv* env, jclass /* clazz */, 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 @@ static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */, sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env, inputChannelObj); - if (inputChannel == NULL) { + if (inputChannel == nullptr) { throwInputChannelNotInitialized(env); return; } @@ -1385,12 +1350,12 @@ static void nativeUnregisterInputChannel(JNIEnv* env, jclass /* clazz */, 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 nativeSetInputWindows(JNIEnv* env, jclass /* clazz */, } 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, displayId, applicationHandleObj); +} + +static void nativeSetFocusedDisplay(JNIEnv* env, jclass /* clazz */, + jlong ptr, jint displayId) { NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); - im->setFocusedApplication(env, applicationHandleObj); + im->setFocusedDisplay(env, displayId); } static void nativeSetPointerCapture(JNIEnv* env, jclass /* clazz */, jlong ptr, @@ -1490,7 +1462,7 @@ static jboolean nativeTransferTouchFocus(JNIEnv* env, 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 @@ static void nativeVibrate(JNIEnv* env, } 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 @@ static const JNINativeMethod gInputManagerMethods[] = { (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 @@ static const JNINativeMethod gInputManagerMethods[] = { (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 05052047e0fb..47790ce68dc1 100644 --- a/services/devicepolicy/Android.bp +++ b/services/devicepolicy/Android.bp @@ -3,7 +3,6 @@ java_library_static { 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 84de6b4b5281..66cf48c8ad24 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 class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub { */ 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 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub { 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 a8b9b0c910ac..85ca52e17ecb 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 @@ public class DeviceAdminServiceController { 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 616c669de939..71fea02c282f 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 @@ import java.util.concurrent.TimeUnit; 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 class DevicePolicyConstants { */ 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 @@ public class DevicePolicyConstants { 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 @@ public class DevicePolicyConstants { 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 @@ public class DevicePolicyConstants { 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 77a3e2102452..6ba7d94117b1 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 @@ public abstract class DhcpPacket { } /** + * 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 @@ public abstract class DhcpPacket { 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 @@ public abstract class DhcpPacket { 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 @@ public abstract class DhcpPacket { 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 @@ public abstract class DhcpPacket { 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 @@ public abstract class DhcpPacket { 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 2b3d577b0eae..cee6fa96bbc5 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.getBroadcastAddress; 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.Message; 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 @@ public class DhcpServer { 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 @@ public class DhcpServer { 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 @@ public class DhcpServer { // 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/fullbackup/AppMetadataBackupWriterTest.java b/services/robotests/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java index 112e1e385fed..b771039b2936 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 @@ public class AppMetadataBackupWriterTest { 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 000000000000..66650700f599 --- /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 index 39cab8d2888a..26e77eb166ca 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 @@ import java.util.Collections; @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 class PersistentConnectionTest extends AndroidTestCase { 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 @@ public class PersistentConnectionTest extends AndroidTestCase { 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 @@ public class PersistentConnectionTest extends AndroidTestCase { 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 @@ public class PersistentConnectionTest extends AndroidTestCase { 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/servicestests/Android.mk b/services/tests/servicestests/Android.mk index 80307eebf53b..2957267d2e5b 100644 --- a/services/tests/servicestests/Android.mk +++ b/services/tests/servicestests/Android.mk @@ -58,6 +58,7 @@ LOCAL_JNI_SHARED_LIBRARIES := \ 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 Binary files differnew file mode 100644 index 000000000000..509ea3b2e61d --- /dev/null +++ b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256-lineage-2-signers 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 Binary files differnew file mode 100644 index 000000000000..bee71c0189fa --- /dev/null +++ b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256-lineage-3-signers diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256.pk8 b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256.pk8 Binary files differnew file mode 100644 index 000000000000..f781c3083e54 --- /dev/null +++ b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256.pk8 diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256.x509.der b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256.x509.der Binary files differnew file mode 100644 index 000000000000..e611e3d9383f --- /dev/null +++ b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256.x509.der diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256_2.pk8 b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256_2.pk8 Binary files differnew file mode 100644 index 000000000000..5e73f27847d2 --- /dev/null +++ b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256_2.pk8 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 Binary files differnew file mode 100644 index 000000000000..7723beab5715 --- /dev/null +++ b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256_2.x509.der diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256_3.pk8 b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256_3.pk8 Binary files differnew file mode 100644 index 000000000000..d7309dd3f024 --- /dev/null +++ b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256_3.pk8 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 Binary files differnew file mode 100644 index 000000000000..cc82af9c0310 --- /dev/null +++ b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256_3.x509.der diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/README b/services/tests/servicestests/assets/PackageSignaturesTest/xml/README new file mode 100644 index 000000000000..43d5bb8864df --- /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 000000000000..4d55bad7cc90 --- /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 000000000000..f7882b1389b0 --- /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 000000000000..af2c293c0f4a --- /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 000000000000..893402d1004e --- /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 000000000000..1f81dacc2727 --- /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 000000000000..c38e4d94a6d0 --- /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 000000000000..8e8cbcf7174e --- /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 000000000000..57e96a8db0a0 --- /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 000000000000..d9f7a5f29d22 --- /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 000000000000..4eefdd9dbbbb --- /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 000000000000..2aeeb7124be9 --- /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 000000000000..14471f84cb1b --- /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 000000000000..2b2e383cd098 --- /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 000000000000..f992104ba00d --- /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 000000000000..6ef0fe5c38da --- /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 000000000000..d98573dbc08c --- /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 000000000000..2ccf5060f9e2 --- /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 000000000000..6d567e994d91 --- /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 000000000000..a2146b7b63ec --- /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 000000000000..90a4a8472e40 --- /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 000000000000..6525e48061ea --- /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 000000000000..e06892c6f7da --- /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 000000000000..8081d2e6d78d --- /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 000000000000..127000a160da --- /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 000000000000..6097ea6d7bd0 --- /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 000000000000..6ed3be8615cb --- /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 000000000000..ee4c4ebde3a9 --- /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 2ec6830b7755..4fbc587c8a87 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 @@ public class GestureLauncherServiceTest { 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 @@ public class GestureLauncherServiceTest { } @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 000000000000..44981b3a90cf --- /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 0345a81b5bec..81a0934a3460 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.assertEquals; 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 @@ public class ActivityStackSupervisorTests extends ActivityTestsBase { 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 @@ public class ActivityStackSupervisorTests extends ActivityTestsBase { 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 ab814ee15df0..5fcd2aa35e05 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.junit.Assert.assertTrue; 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 @@ public class ActivityStackTests extends ActivityTestsBase { 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 @@ public class ActivityStackTests extends ActivityTestsBase { 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 22add018f570..2008861907a6 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 @@ public class ActivityTestsBase { // 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 @@ public class ActivityTestsBase { // 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 @@ public class ActivityTestsBase { 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 000000000000..d7794b04adca --- /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 70cfad1d0f2f..1276f656f914 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 @@ public class RecentTasksTest extends ActivityTestsBase { 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 @@ public class RecentTasksTest extends ActivityTestsBase { 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 @@ public class RecentTasksTest extends ActivityTestsBase { 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 @@ public class RecentTasksTest extends ActivityTestsBase { 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 @@ public class RecentTasksTest extends ActivityTestsBase { @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 @@ public class RecentTasksTest extends ActivityTestsBase { 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 9fcdf2d517e9..d52051eec5bd 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 @@ public class AppBackupUtilsTest { new Signature[] {SIGNATURE_1}, PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3, null, - null, null)); packageInfo.applicationInfo = new ApplicationInfo(); @@ -456,7 +455,6 @@ public class AppBackupUtilsTest { new Signature[] {SIGNATURE_1}, PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3, null, - null, null)); packageInfo.applicationInfo = new ApplicationInfo(); @@ -537,7 +535,6 @@ public class AppBackupUtilsTest { 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 @@ public class AppBackupUtilsTest { 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 @@ public class AppBackupUtilsTest { new Signature[] {signature1Copy, signature2Copy}, PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3, null, - null, null)); packageInfo.applicationInfo = new ApplicationInfo(); @@ -606,7 +601,6 @@ public class AppBackupUtilsTest { 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 @@ public class AppBackupUtilsTest { new Signature[] {SIGNATURE_1}, PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3, null, - null, null)); packageInfo.applicationInfo = new ApplicationInfo(); @@ -654,8 +647,7 @@ public class AppBackupUtilsTest { 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 @@ public class AppBackupUtilsTest { 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 12f2991b3fea..47749856a7ec 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 @@ public class TarBackupReaderTest { new Signature[] {FAKE_SIGNATURE_2}, PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3, null, - null, null)); PackageManagerStub.sPackageInfo = packageInfo; @@ -414,7 +413,6 @@ public class TarBackupReaderTest { new Signature[] {FAKE_SIGNATURE_1}, PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3, null, - null, null)); PackageManagerStub.sPackageInfo = packageInfo; @@ -452,7 +450,6 @@ public class TarBackupReaderTest { new Signature[] {FAKE_SIGNATURE_1}, PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3, null, - null, null)); PackageManagerStub.sPackageInfo = packageInfo; @@ -493,7 +490,6 @@ public class TarBackupReaderTest { new Signature[] {FAKE_SIGNATURE_1}, PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3, null, - null, null)); packageInfo.versionCode = 2; PackageManagerStub.sPackageInfo = packageInfo; @@ -537,7 +533,6 @@ public class TarBackupReaderTest { new Signature[] {FAKE_SIGNATURE_1}, PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3, null, - null, null)); packageInfo.versionCode = 1; PackageManagerStub.sPackageInfo = packageInfo; @@ -577,7 +572,6 @@ public class TarBackupReaderTest { 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 ceee60c017e5..b4212808d585 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.display.IVirtualDisplayCallback; 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 @@ public class DisplayManagerServiceTest extends AndroidTestCase { @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 @@ public class DisplayManagerServiceTest extends AndroidTestCase { LocalServices.addService(WindowManagerInternal.class, mMockWindowManagerInternal); LocalServices.removeServiceForTest(LightsManager.class); LocalServices.addService(LightsManager.class, mMockLightsManager); - super.setUp(); - } - @Override - protected void tearDown() throws Exception { - super.tearDown(); + mContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); } - public void testCreateVirtualDisplay_sentToInputManager() throws Exception { + @Test + public void testCreateVirtualDisplay_sentToInputManager() { DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector); registerDefaultDisplays(displayManager); @@ -115,7 +127,7 @@ public class DisplayManagerServiceTest extends AndroidTestCase { 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 @@ public class DisplayManagerServiceTest extends AndroidTestCase { // flush the handler displayManager.getDisplayHandler().runWithScissors(() -> {}, 0 /* now */); - ArgumentCaptor<List<DisplayViewport>> virtualViewportCaptor = - ArgumentCaptor.forClass(List.class); - verify(mMockInputManagerInternal).setDisplayViewports( - any(), any(), virtualViewportCaptor.capture()); - - 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); + ArgumentCaptor<List<DisplayViewport>> viewportCaptor = ArgumentCaptor.forClass(List.class); + verify(mMockInputManagerInternal).setDisplayViewports(viewportCaptor.capture()); + List<DisplayViewport> viewports = viewportCaptor.getValue(); + + // 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 @@ public class DisplayManagerServiceTest extends AndroidTestCase { /** * 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 @@ public class DisplayManagerServiceTest extends AndroidTestCase { /** * 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 @@ public class DisplayManagerServiceTest extends AndroidTestCase { /** * 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 @@ public class DisplayManagerServiceTest extends AndroidTestCase { /** * 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 @@ public class DisplayManagerServiceTest extends AndroidTestCase { /** * 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 @@ public class DisplayManagerServiceTest extends AndroidTestCase { /** * 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 2de5d87c063c..a3348c26f772 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 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { 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 318ed3a5217c..9af7b1c911e1 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 @@ public class PackageParserTest { 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 000000000000..d3a77d3e80f1 --- /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 13612a1f93f7..182760b30005 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 @@ public class BackupUtilsTest { new Signature[] {SIGNATURE_1}, PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3, null, - null, null)); packageInfo.applicationInfo = new ApplicationInfo(); @@ -119,7 +118,6 @@ public class BackupUtilsTest { new Signature[] {SIGNATURE_1}, PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3, null, - null, null)); packageInfo.applicationInfo = new ApplicationInfo(); @@ -203,7 +201,6 @@ public class BackupUtilsTest { 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 @@ public class BackupUtilsTest { 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 @@ public class BackupUtilsTest { new Signature[] {SIGNATURE_1, SIGNATURE_2}, PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3, null, - null, null)); packageInfo.applicationInfo = new ApplicationInfo(); @@ -271,7 +266,6 @@ public class BackupUtilsTest { 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 @@ public class BackupUtilsTest { new Signature[] {SIGNATURE_1}, PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3, null, - null, null)); packageInfo.applicationInfo = new ApplicationInfo(); @@ -320,7 +313,6 @@ public class BackupUtilsTest { new Signature[] {SIGNATURE_1, SIGNATURE_2}, PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3, null, - null, null)); packageInfo.applicationInfo = new ApplicationInfo(); @@ -348,7 +340,6 @@ public class BackupUtilsTest { 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 acd065e90a0c..e16f118cd63b 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 @@ public class PhoneWindowManagerTestBase { 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 54ac6fc7db93..bd4a356fcb5c 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 @@ public class BatterySaverPolicyTest extends AndroidTestCase { } @Mock - Handler mHandler; - - @Mock MetricsLogger mMetricsLogger = mock(MetricsLogger.class); private BatterySaverPolicyForTest mBatterySaverPolicy; 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 12be0b3ae6f8..e6e08bb93c8e 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 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { 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 b3303049be39..3dcdd23acf1a 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_LANDSCAPE; 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_STATUS_BAR; 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 @@ public class DisplayContentTests extends WindowTestsBase { 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 @@ public class DisplayContentTests extends WindowTestsBase { 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 @@ public class DisplayContentTests extends WindowTestsBase { // 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 @@ public class DisplayContentTests extends WindowTestsBase { 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 @@ public class DisplayContentTests extends WindowTestsBase { 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 @@ public class DisplayContentTests extends WindowTestsBase { // | | ------------- 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 @@ public class DisplayContentTests extends WindowTestsBase { 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 088672973524..7cd131420049 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 @@ public class WindowFrameTests extends WindowTestsBase { 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 @@ public class WindowFrameTests extends WindowTestsBase { 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 b7cc9ce7a619..3637baf13c72 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.reset; 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 @@ public class WindowStateTests extends WindowTestsBase { 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 000000000000..ba8869b1692d --- /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 9ce3dca7e096..c5e35e7304c4 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 @@ package com.android.server.wm.utils; 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 @@ import java.util.Arrays; @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 @@ public class WmDisplayCutoutTest { @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 @@ public class WmDisplayCutoutTest { @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 @@ public class WmDisplayCutoutTest { @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 @@ public class WmDisplayCutoutTest { @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 @@ public class WmDisplayCutoutTest { @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 @@ public class WmDisplayCutoutTest { @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 @@ public class WmDisplayCutoutTest { @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 @@ public class WmDisplayCutoutTest { 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 840517954a0a..f3f43558115b 100644 --- a/services/tests/uiservicestests/Android.mk +++ b/services/tests/uiservicestests/Android.mk @@ -45,6 +45,7 @@ LOCAL_JNI_SHARED_LIBRARIES := \ 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 58aae2b12c21..4e007c2d9929 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.Notification.MessagingStyle.Message; 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 android.util.Log; 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 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { 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 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } } + 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 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { 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 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { 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 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { 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 c616685ba302..8946d251c61d 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 @@ import java.util.List; * 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 @@ public class UsageStatsDatabase { 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 000000000000..b3b09001e7ee --- /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 000000000000..1d38f4c1e23d --- /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 000000000000..f47a42cffdd8 --- /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 000000000000..1cd37b5546b9 --- /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 000000000000..34aedd7685d8 --- /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 000000000000..aa4eea716363 --- /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 000000000000..2c79319a1459 --- /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 000000000000..75d47f9e3d17 --- /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 000000000000..b77c03c1584a --- /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 000000000000..b5fd6d8d1c45 --- /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 000000000000..76057846e896 --- /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 000000000000..1b2aa46a6418 --- /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 000000000000..99f4add6579f --- /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 000000000000..4ba44a93f2a8 --- /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 000000000000..4abbb3e9f162 --- /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 8c37a21afa50..d33a537f2194 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -1905,6 +1905,22 @@ public class TelecomManager { 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 38247bc80e5c..df7d6832833a 100644 --- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl +++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl @@ -284,4 +284,9 @@ interface ITelecomService { * @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 57b652e4cc59..9b5b700f7572 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -1633,11 +1633,21 @@ public class CarrierConfigManager { * 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 @@ public class CarrierConfigManager { 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/NeighboringCellInfo.java b/telephony/java/android/telephony/NeighboringCellInfo.java index 5e4518f67538..79298fd54c50 100644 --- a/telephony/java/android/telephony/NeighboringCellInfo.java +++ b/telephony/java/android/telephony/NeighboringCellInfo.java @@ -33,8 +33,9 @@ import android.os.Parcelable; * 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 f2b73dccee2d..7469186a5d51 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -24,6 +24,7 @@ import android.os.Bundle; 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 @@ public class ServiceState implements Parcelable { /** * Get all of the available network registration states. * - * @return List of registration states + * @return List of {@link NetworkRegistrationState} * @hide */ @SystemApi @@ -1606,14 +1607,30 @@ public class ServiceState implements Parcelable { } /** - * 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> getNetworkRegistrationStatesForTransportType( + int transportType) { List<NetworkRegistrationState> list = new ArrayList<>(); synchronized (mNetworkRegistrationStates) { @@ -1628,16 +1645,57 @@ public class ServiceState implements Parcelable { } /** - * 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 @@ public class ServiceState implements Parcelable { mNetworkRegistrationStates.add(regState); } } - } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index ea9ac39c8111..80b6ead339d0 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -8381,6 +8381,29 @@ public class TelephonyManager { } /** + * 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 c2c93da306d9..8379f8cefda0 100644 --- a/telephony/java/android/telephony/data/ApnSetting.java +++ b/telephony/java/android/telephony/data/ApnSetting.java @@ -1206,7 +1206,8 @@ public class ApnSetting implements Parcelable { /** @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/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java index 5d6a8c158eed..89ef33914c12 100644 --- a/telephony/java/android/telephony/ims/ImsCallProfile.java +++ b/telephony/java/android/telephony/ims/ImsCallProfile.java @@ -117,12 +117,14 @@ public final class ImsCallProfile implements Parcelable { * @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/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java index 31381804d143..cecf2e26f139 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.annotation.SystemApi; 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 @@ public class ImsRegistrationImplBase { * 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 @@ public class ImsRegistrationImplBase { * @param imsRadioTech the radio access technology. Valid values are defined in * {@link ImsRegistrationTech}. */ + @Override public void onRegistering(@ImsRegistrationTech int imsRadioTech) { } @@ -105,6 +106,7 @@ public class ImsRegistrationImplBase { * * @param info the {@link ImsReasonInfo} associated with why registration was disconnected. */ + @Override public void onDeregistered(ImsReasonInfo info) { } @@ -115,6 +117,7 @@ public class ImsRegistrationImplBase { * @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 @@ public class ImsRegistrationImplBase { * @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 ca2bcff2f4cd..32eb12b49cf0 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -808,6 +808,13 @@ interface ITelephony { */ 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 39722c66e170..5ed283e8e4cb 100644 --- a/telephony/java/com/android/internal/telephony/SmsApplication.java +++ b/telephony/java/com/android/internal/telephony/SmsApplication.java @@ -584,7 +584,8 @@ public final class SmsApplication { // 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/NetworkSecurityConfigTest/Android.mk b/tests/NetworkSecurityConfigTest/Android.mk index fe65eccedd24..c225e170c377 100644 --- a/tests/NetworkSecurityConfigTest/Android.mk +++ b/tests/NetworkSecurityConfigTest/Android.mk @@ -7,7 +7,6 @@ LOCAL_CERTIFICATE := platform LOCAL_JAVA_LIBRARIES := \ android.test.runner \ - bouncycastle \ conscrypt \ android.test.base \ diff --git a/tests/net/Android.mk b/tests/net/Android.mk index e529b933bb80..750e2fb6f6b4 100644 --- a/tests/net/Android.mk +++ b/tests/net/Android.mk @@ -38,6 +38,7 @@ LOCAL_JNI_SHARED_LIBRARIES := \ 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 312b3d1878d6..a592809618e6 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.assertNull; 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 com.android.internal.util.HexDump; 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 @@ public class DhcpPacketTest { 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 @@ public class DhcpPacketTest { 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 @@ public class DhcpPacketTest { 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 @@ public class DhcpPacketTest { // 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 @@ public class DhcpPacketTest { @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 45a50d9a8b5f..df34c7310b63 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 @@ public class DhcpServerTest { 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 @@ public class DhcpServerTest { 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 class DhcpServerTest { 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 @@ public class DhcpServerTest { * - 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/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 1a053057540f..1c77fcc568f6 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -1070,13 +1070,13 @@ public class ConnectivityServiceTest { // Ensure that the default setting for Captive Portals is used for most tests setCaptivePortalMode(Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT); - setMobileDataAlwaysOn(false); + setAlwaysOnNetworks(false); setPrivateDnsSettings(PRIVATE_DNS_MODE_OFF, "ignored.example.com"); } @After public void tearDown() throws Exception { - setMobileDataAlwaysOn(false); + setAlwaysOnNetworks(false); if (mCellNetworkAgent != null) { mCellNetworkAgent.disconnect(); mCellNetworkAgent = null; @@ -2027,7 +2027,7 @@ public class ConnectivityServiceTest { @Test public void testNetworkGoesIntoBackgroundAfterLinger() { - setMobileDataAlwaysOn(true); + setAlwaysOnNetworks(true); NetworkRequest request = new NetworkRequest.Builder() .clearCapabilities() .build(); @@ -2772,10 +2772,10 @@ public class ConnectivityServiceTest { Settings.Global.putInt(cr, Settings.Global.CAPTIVE_PORTAL_MODE, mode); } - private void setMobileDataAlwaysOn(boolean enable) { + private void setAlwaysOnNetworks(boolean enable) { ContentResolver cr = mServiceContext.getContentResolver(); Settings.Global.putInt(cr, Settings.Global.MOBILE_DATA_ALWAYS_ON, enable ? 1 : 0); - mService.updateMobileDataAlwaysOn(); + mService.updateAlwaysOnNetworks(); waitForIdle(); } @@ -2797,7 +2797,7 @@ public class ConnectivityServiceTest { public void testBackgroundNetworks() throws Exception { // Create a background request. We can't do this ourselves because ConnectivityService // doesn't have an API for it. So just turn on mobile data always on. - setMobileDataAlwaysOn(true); + setAlwaysOnNetworks(true); final NetworkRequest request = new NetworkRequest.Builder().build(); final NetworkRequest fgRequest = new NetworkRequest.Builder() .addCapability(NET_CAPABILITY_FOREGROUND).build(); @@ -2995,7 +2995,7 @@ public class ConnectivityServiceTest { // Turn on mobile data always on. The factory starts looking again. testFactory.expectAddRequests(1); - setMobileDataAlwaysOn(true); + setAlwaysOnNetworks(true); testFactory.waitForNetworkRequests(2); assertTrue(testFactory.getMyStartRequested()); @@ -3015,7 +3015,7 @@ public class ConnectivityServiceTest { // Turn off mobile data always on and expect the request to disappear... testFactory.expectRemoveRequests(1); - setMobileDataAlwaysOn(false); + setAlwaysOnNetworks(false); testFactory.waitForNetworkRequests(1); // ... and cell data to be torn down. 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 9c4da1f72e38..14312cf84693 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 @@ public class WindowManagerPermissionTests extends TestCase { } 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 cf84c7926549..fff9635992d4 100644 --- a/tests/testables/src/android/testing/TestableContext.java +++ b/tests/testables/src/android/testing/TestableContext.java @@ -53,7 +53,7 @@ import org.junit.runners.model.Statement; * 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 c48765b7b947..de00fcaea07b 100644 --- a/tools/aapt2/ResourceUtils.cpp +++ b/tools/aapt2/ResourceUtils.cpp @@ -38,6 +38,8 @@ using ::android::base::StringPrintf; 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 @@ StringBuilder& StringBuilder::AppendText(const std::string& text, bool preserve_ 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 62c19fbfcdd3..8060a8de4629 100644 --- a/tools/aapt2/cmd/Compile.cpp +++ b/tools/aapt2/cmd/Compile.cpp @@ -73,9 +73,9 @@ struct ResourcePathData { }; // 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 @@ int Compile(IAaptContext* context, io::IFileCollection* inputs, IArchiveWriter* // 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 @@ int CompileCommand::Action(const std::vector<std::string>& args) { // 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 dd5198ce86da..c0c05cda35e7 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 @@ int TestCompile(const std::string& path, const std::string& outDir, bool legacy, 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 @@ TEST(CompilerTest, ZipInput) { 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 13c10478ba3e..20ea3cb6ffac 100644 --- a/tools/aapt2/cmd/Link.cpp +++ b/tools/aapt2/cmd/Link.cpp @@ -1260,7 +1260,7 @@ class Linker { 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 e58a93ec416c..a42c0f250e3e 100644 --- a/tools/aapt2/cmd/Link.h +++ b/tools/aapt2/cmd/Link.h @@ -49,6 +49,7 @@ struct LinkOptions { 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 @@ class LinkCommand : public Command { 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 158ef299f6e2..6aeff08e618e 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, LongVersionCodeUndefined) { } -TEST (UtilTest, ParseSplitParameter) { +TEST (UtilTest, ParseSplitParameters) { IDiagnostics* diagnostics = test::ContextBuilder().Build().get()->GetDiagnostics(); std::string path; SplitConstraints constraints; @@ -98,14 +108,14 @@ TEST (UtilTest, ParseSplitParameter) { // ========== 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 @@ TEST (UtilTest, ParseSplitParameter) { .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 (UtilTest, ParseSplitParameter) { // ========== 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 @@ TEST (UtilTest, ParseSplitParameter) { .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 @@ TEST (UtilTest, ParseSplitParameter) { // 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 @@ TEST (UtilTest, ParseSplitParameter) { .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 @@ TEST (UtilTest, ParseSplitParameter) { // 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 (UtilTest, ParseSplitParameter) { // ========== 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 @@ TEST (UtilTest, ParseSplitParameter) { // 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 @@ TEST (UtilTest, ParseSplitParameter) { .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 @@ TEST (UtilTest, ParseSplitParameter) { .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 @@ TEST (UtilTest, ParseSplitParameter) { .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 @@ TEST (UtilTest, ParseSplitParameter) { .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 @@ TEST (UtilTest, ParseSplitParameter) { .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 (UtilTest, ParseSplitParameter) { // ========== 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 @@ TEST (UtilTest, ParseSplitParameter) { .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 @@ TEST (UtilTest, ParseSplitParameter) { // 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 @@ TEST (UtilTest, ParseSplitParameter) { .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 @@ TEST (UtilTest, AdjustSplitConstraintsForMinSdk) { 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 2c356d1491d5..b6a984f9b156 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 @@ enum { 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 @@ class UsesLibrary : public ManifestExtractor::Element { } }; +/** 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 @@ class MetaData : public ManifestExtractor::Element { 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 PackageVerifier : public ManifestExtractor::Element { 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 @@ bool ManifestExtractor::Dump(text::Printer& printer, IDiagnostics* diag) { && 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 @@ T* ElementCast(ManifestExtractor::Element* element) { {"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 @@ std::unique_ptr<ManifestExtractor::Element> ManifestExtractor::Element::Inflate( {"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 21fdbd8a4c1e..74295ab85846 100644 --- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp +++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp @@ -33,6 +33,7 @@ class MockFileCollection : public io::IFileCollection { 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 f06e28c79c7b..565aad6f2284 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 @@ class IFileCollection { 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 16a20f4cb09d..51cc9032fb3e 100644 --- a/tools/aapt2/io/FileSystem.cpp +++ b/tools/aapt2/io/FileSystem.cpp @@ -128,5 +128,9 @@ std::unique_ptr<IFileCollectionIterator> FileCollection::Iterator() { 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 fb6bf6eeabbc..04c6fa15bc85 100644 --- a/tools/aapt2/io/FileSystem.h +++ b/tools/aapt2/io/FileSystem.h @@ -67,6 +67,7 @@ class FileCollection : public IFileCollection { 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 8e6d7137640a..427dc92505d4 100644 --- a/tools/aapt2/io/ZipArchive.cpp +++ b/tools/aapt2/io/ZipArchive.cpp @@ -33,6 +33,11 @@ ZipFile::ZipFile(ZipArchiveHandle handle, const ZipEntry& entry, : zip_handle_(handle), zip_entry_(entry), source_(source) {} std::unique_ptr<IData> ZipFile::OpenAsData() { + // The file will fail to be mmaped if it is empty + if (zip_entry_.uncompressed_length == 0) { + return util::make_unique<EmptyData>(); + } + if (zip_entry_.method == kCompressStored) { int fd = GetFileDescriptor(zip_handle_); @@ -154,6 +159,13 @@ std::unique_ptr<IFileCollectionIterator> ZipFileCollection::Iterator() { 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 8381259d77c5..b283e57d4011 100644 --- a/tools/aapt2/io/ZipArchive.h +++ b/tools/aapt2/io/ZipArchive.h @@ -66,6 +66,7 @@ class ZipFileCollection : public IFileCollection { 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 d40795accf79..52e168ed47aa 100644 --- a/tools/aapt2/java/ProguardRules.cpp +++ b/tools/aapt2/java/ProguardRules.cpp @@ -384,7 +384,7 @@ bool CollectProguardRules(IAaptContext* context_, xml::XmlResource* res, KeepSet 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 @@ void WriteKeepSet(const KeepSet& keep_set, OutputStream* out) { 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 01dad0b08aea..38b4860d1d61 100644 --- a/tools/aapt2/java/ProguardRules.h +++ b/tools/aapt2/java/ProguardRules.h @@ -70,7 +70,7 @@ class KeepSet { } 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 CollectProguardRules(IAaptContext* context, xml::XmlResource* res, KeepSet* 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 83c72d89bb62..3d93cb1dd43b 100644 --- a/tools/aapt2/java/ProguardRules_test.cpp +++ b/tools/aapt2/java/ProguardRules_test.cpp @@ -26,10 +26,10 @@ using ::testing::Not; 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 @@ TEST(ProguardRulesTest, ManifestRuleDefaultConstructorOnly) { 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 @@ TEST(ProguardRulesTest, FragmentNameRuleIsEmitted) { 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 @@ TEST(ProguardRulesTest, FragmentClassRuleIsEmitted) { 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 @@ TEST(ProguardRulesTest, FragmentNameAndClassRulesAreEmitted) { 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 @@ TEST(ProguardRulesTest, NavigationFragmentNameAndClassRulesAreEmitted) { 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 @@ TEST(ProguardRulesTest, CustomViewRulesAreEmitted) { 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 @@ TEST(ProguardRulesTest, IncludedLayoutRulesAreConditional) { 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 @@ TEST(ProguardRulesTest, AliasedLayoutRulesAreConditional) { 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>(...); }")); + 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); }")); + "-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 @@ TEST(ProguardRulesTest, NonLayoutReferencesAreUnconditional) { 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 @@ TEST(ProguardRulesTest, ViewOnClickRuleIsEmitted) { 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 @@ TEST(ProguardRulesTest, MenuRulesAreEmitted) { 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); }")); + 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); }")); + "-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 @@ TEST(ProguardRulesTest, TransitionPathMotionRulesAreEmitted) { 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 @@ TEST(ProguardRulesTest, TransitionRulesAreEmitted) { 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 fa6538d7b4e7..85bf6f218ba0 100644 --- a/tools/aapt2/link/ManifestFixer.cpp +++ b/tools/aapt2/link/ManifestFixer.cpp @@ -393,6 +393,10 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor, 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 219c18397358..202cc261ad89 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, AppendPath) { } 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 58c130017024..74722787f441 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -513,7 +513,7 @@ public class WifiConfiguration implements Parcelable { /** * @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 @@ public class WifiConfiguration implements Parcelable { /** * @hide * Universal name for app updating the configuration - * see {#link {@link PackageManager#getNameForUid(int)} + * see {@link PackageManager#getNameForUid(int)} */ @SystemApi public String lastUpdateName; |