diff options
153 files changed, 3447 insertions, 1609 deletions
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java index aad8f9da6226..9e131339595f 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -1895,11 +1895,14 @@ public class JobSchedulerService extends com.android.server.SystemService // The job ran past its expected run window. Have it count towards the current window // and schedule a new job for the next window. if (DEBUG) { - Slog.i(TAG, "Periodic job ran after its intended window."); + Slog.i(TAG, "Periodic job ran after its intended window by " + diffMs + " ms"); } long numSkippedWindows = (diffMs / period) + 1; // +1 to include original window - if (period != flex && diffMs > Math.min(PERIODIC_JOB_WINDOW_BUFFER, - (period - flex) / 2)) { + // Determine how far into a single period the job ran, and determine if it's too close + // to the start of the next period. If the difference between the start of the execution + // window and the previous execution time inside of the period is less than the + // threshold, then we say that the job ran too close to the next period. + if (period != flex && (period - flex - (diffMs % period)) <= flex / 6) { if (DEBUG) { Slog.d(TAG, "Custom flex job ran too close to next window."); } diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java index 52f883b5fbb7..d464e266ac36 100644 --- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java +++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java @@ -37,6 +37,8 @@ import com.android.internal.os.BaseCommand; import com.android.internal.telecom.ITelecomService; import java.io.PrintStream; +import java.util.Arrays; +import java.util.stream.Collectors; public final class Telecom extends BaseCommand { @@ -90,6 +92,10 @@ public final class Telecom extends BaseCommand { private static final String COMMAND_GET_MAX_PHONES = "get-max-phones"; private static final String COMMAND_SET_TEST_EMERGENCY_PHONE_ACCOUNT_PACKAGE_FILTER = "set-test-emergency-phone-account-package-filter"; + /** + * Command used to emit a distinct "mark" in the logs. + */ + private static final String COMMAND_LOG_MARK = "log-mark"; private ComponentName mComponent; private String mAccountId; @@ -161,6 +167,8 @@ public final class Telecom extends BaseCommand { + " package name that will be used for test emergency calls. To clear," + " send an empty package name. Real emergency calls will still be placed" + " over Telephony.\n" + + "telecom log-mark <MESSAGE>: emits a message into the telecom logs. Useful for " + + "testers to indicate where in the logs various test steps take place.\n" ); } @@ -265,6 +273,9 @@ public final class Telecom extends BaseCommand { case COMMAND_SET_TEST_EMERGENCY_PHONE_ACCOUNT_PACKAGE_FILTER: runSetEmergencyPhoneAccountPackageFilter(); break; + case COMMAND_LOG_MARK: + runLogMark(); + break; default: Log.w(this, "onRun: unknown command: %s", command); throw new IllegalArgumentException ("unknown command '" + command + "'"); @@ -442,6 +453,11 @@ public final class Telecom extends BaseCommand { } + private void runLogMark() throws RemoteException { + String message = Arrays.stream(mArgs.peekRemainingArgs()).collect(Collectors.joining(" ")); + mTelecomService.requestLogMark(message); + } + private PhoneAccountHandle getPhoneAccountHandleFromArgs() throws RemoteException { if (TextUtils.isEmpty(mArgs.peekNextArg())) { return null; diff --git a/core/api/current.txt b/core/api/current.txt index 2a6536ed218f..e260ad046918 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -335,15 +335,15 @@ package android { field public static final int allowClearUserData = 16842757; // 0x1010005 field public static final int allowClickWhenDisabled = 16844312; // 0x1010618 field public static final int allowEmbedded = 16843765; // 0x10103f5 - field public static final int allowGameAngleDriver; - field public static final int allowGameDownscaling; - field public static final int allowGameFpsOverride; + field public static final int allowGameAngleDriver = 16844376; // 0x1010658 + field public static final int allowGameDownscaling = 16844377; // 0x1010659 + field public static final int allowGameFpsOverride = 16844378; // 0x101065a field public static final int allowNativeHeapPointerTagging = 16844306; // 0x1010612 field public static final int allowParallelSyncs = 16843570; // 0x1010332 field public static final int allowSingleTap = 16843353; // 0x1010259 field public static final int allowTaskReparenting = 16843268; // 0x1010204 field public static final int allowUndo = 16843999; // 0x10104df - field public static final int allowUntrustedActivityEmbedding; + field public static final int allowUntrustedActivityEmbedding = 16844393; // 0x1010669 field public static final int alpha = 16843551; // 0x101031f field public static final int alphabeticModifiers = 16844110; // 0x101054e field public static final int alphabeticShortcut = 16843235; // 0x10101e3 @@ -374,7 +374,7 @@ package android { field public static final int authorities = 16842776; // 0x1010018 field public static final int autoAdvanceViewId = 16843535; // 0x101030f field public static final int autoCompleteTextViewStyle = 16842859; // 0x101006b - field public static final int autoHandwritingEnabled; + field public static final int autoHandwritingEnabled = 16844382; // 0x101065e field public static final int autoLink = 16842928; // 0x10100b0 field public static final int autoMirrored = 16843754; // 0x10103ea field public static final int autoRemoveFromRecents = 16843847; // 0x1010447 @@ -390,7 +390,7 @@ package android { field public static final int autoVerify = 16844014; // 0x10104ee field public static final int autofillHints = 16844118; // 0x1010556 field public static final int autofilledHighlight = 16844136; // 0x1010568 - field public static final int backdropColor; + field public static final int backdropColor = 16844402; // 0x1010672 field public static final int background = 16842964; // 0x10100d4 field public static final int backgroundDimAmount = 16842802; // 0x1010032 field public static final int backgroundDimEnabled = 16843295; // 0x101021f @@ -437,7 +437,7 @@ package android { field public static final int calendarViewShown = 16843596; // 0x101034c field public static final int calendarViewStyle = 16843613; // 0x101035d field public static final int canControlMagnification = 16844039; // 0x1010507 - field public static final int canDisplayOnRemoteDevices; + field public static final int canDisplayOnRemoteDevices = 16844368; // 0x1010650 field public static final int canPauseRecording = 16844314; // 0x101061a field public static final int canPerformGestures = 16844045; // 0x101050d field public static final int canRecord = 16844060; // 0x101051c @@ -632,7 +632,7 @@ package android { field public static final int elevation = 16843840; // 0x1010440 field public static final int ellipsize = 16842923; // 0x10100ab field public static final int ems = 16843096; // 0x1010158 - field public static final int enableOnBackInvokedCallback; + field public static final int enableOnBackInvokedCallback = 16844396; // 0x101066c field public static final int enableVrMode = 16844069; // 0x1010525 field public static final int enabled = 16842766; // 0x101000e field public static final int end = 16843996; // 0x10104dc @@ -744,10 +744,10 @@ package android { field public static final int freezesText = 16843116; // 0x101016c field public static final int fromAlpha = 16843210; // 0x10101ca field public static final int fromDegrees = 16843187; // 0x10101b3 - field public static final int fromExtendBottom; - field public static final int fromExtendLeft; - field public static final int fromExtendRight; - field public static final int fromExtendTop; + field public static final int fromExtendBottom = 16844386; // 0x1010662 + field public static final int fromExtendLeft = 16844383; // 0x101065f + field public static final int fromExtendRight = 16844385; // 0x1010661 + field public static final int fromExtendTop = 16844384; // 0x1010660 field public static final int fromId = 16843850; // 0x101044a field public static final int fromScene = 16843741; // 0x10103dd field public static final int fromXDelta = 16843206; // 0x10101c6 @@ -867,7 +867,7 @@ package android { field public static final int installLocation = 16843447; // 0x10102b7 field public static final int interactiveUiTimeout = 16844181; // 0x1010595 field public static final int interpolator = 16843073; // 0x1010141 - field public static final int intro; + field public static final int intro = 16844395; // 0x101066b field public static final int isAccessibilityTool = 16844353; // 0x1010641 field public static final int isAlwaysSyncable = 16843571; // 0x1010333 field public static final int isAsciiCapable = 16843753; // 0x10103e9 @@ -910,7 +910,7 @@ package android { field public static final int keyboardNavigationCluster = 16844096; // 0x1010540 field public static final int keycode = 16842949; // 0x10100c5 field public static final int killAfterRestore = 16843420; // 0x101029c - field public static final int knownActivityEmbeddingCerts; + field public static final int knownActivityEmbeddingCerts = 16844394; // 0x101066a field public static final int knownCerts = 16844330; // 0x101062a field public static final int lStar = 16844359; // 0x1010647 field public static final int label = 16842753; // 0x1010001 @@ -978,8 +978,8 @@ package android { field public static final int left = 16843181; // 0x10101ad field public static final int letterSpacing = 16843958; // 0x10104b6 field public static final int level = 16844032; // 0x1010500 - field public static final int lineBreakStyle; - field public static final int lineBreakWordStyle; + field public static final int lineBreakStyle = 16844398; // 0x101066e + field public static final int lineBreakWordStyle = 16844399; // 0x101066f field public static final int lineHeight = 16844159; // 0x101057f field public static final int lineSpacingExtra = 16843287; // 0x1010217 field public static final int lineSpacingMultiplier = 16843288; // 0x1010218 @@ -1003,7 +1003,7 @@ package android { field public static final int listSeparatorTextViewStyle = 16843272; // 0x1010208 field public static final int listViewStyle = 16842868; // 0x1010074 field public static final int listViewWhiteStyle = 16842869; // 0x1010075 - field public static final int localeConfig; + field public static final int localeConfig = 16844379; // 0x101065b field public static final int lockTaskMode = 16844013; // 0x10104ed field public static final int logo = 16843454; // 0x10102be field public static final int logoDescription = 16844009; // 0x10104e9 @@ -1162,7 +1162,7 @@ package android { field public static final int popupWindowStyle = 16842870; // 0x1010076 field public static final int port = 16842793; // 0x1010029 field public static final int positiveButtonText = 16843253; // 0x10101f5 - field public static final int preferKeepClear; + field public static final int preferKeepClear = 16844381; // 0x101065d field public static final int preferMinimalPostProcessing = 16844300; // 0x101060c field public static final int preferenceCategoryStyle = 16842892; // 0x101008c field public static final int preferenceFragmentStyle = 16844038; // 0x1010506 @@ -1238,10 +1238,10 @@ package android { field public static final int requiredFeature = 16844116; // 0x1010554 field public static final int requiredForAllUsers = 16843728; // 0x10103d0 field public static final int requiredNotFeature = 16844117; // 0x1010555 - field public static final int requiredSplitTypes; + field public static final int requiredSplitTypes = 16844366; // 0x101064e field public static final int requiresFadingEdge = 16843685; // 0x10103a5 field public static final int requiresSmallestWidthDp = 16843620; // 0x1010364 - field public static final int resetEnabledSettingsOnAppDataCleared; + field public static final int resetEnabledSettingsOnAppDataCleared = 16844370; // 0x1010652 field public static final int resizeClip = 16843983; // 0x10104cf field public static final int resizeMode = 16843619; // 0x1010363 field public static final int resizeable = 16843405; // 0x101028d @@ -1337,7 +1337,7 @@ package android { field public static final int shareInterpolator = 16843195; // 0x10101bb field @Deprecated public static final int sharedUserId = 16842763; // 0x101000b field @Deprecated public static final int sharedUserLabel = 16843361; // 0x1010261 - field public static final int sharedUserMaxSdkVersion; + field public static final int sharedUserMaxSdkVersion = 16844365; // 0x101064d field public static final int shell = 16844180; // 0x1010594 field public static final int shortcutDisabledMessage = 16844075; // 0x101052b field public static final int shortcutId = 16844072; // 0x1010528 @@ -1346,8 +1346,8 @@ package android { field public static final int shouldDisableView = 16843246; // 0x10101ee field public static final int shouldUseDefaultUnfoldTransition = 16844364; // 0x101064c field public static final int showAsAction = 16843481; // 0x10102d9 - field public static final int showBackdrop; - field public static final int showClockAndComplications; + field public static final int showBackdrop = 16844380; // 0x101065c + field public static final int showClockAndComplications = 16844372; // 0x1010654 field public static final int showDefault = 16843258; // 0x10101fa field public static final int showDividers = 16843561; // 0x1010329 field public static final int showForAllUsers = 16844015; // 0x10104ef @@ -1378,7 +1378,7 @@ package android { field public static final int splitMotionEvents = 16843503; // 0x10102ef field public static final int splitName = 16844105; // 0x1010549 field public static final int splitTrack = 16843852; // 0x101044c - field public static final int splitTypes; + field public static final int splitTypes = 16844367; // 0x101064f field public static final int spotShadowAlpha = 16843967; // 0x10104bf field public static final int src = 16843033; // 0x1010119 field public static final int ssp = 16843747; // 0x10103e3 @@ -1449,18 +1449,18 @@ 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 supportedTypes; + field public static final int supportedTypes = 16844369; // 0x1010651 field public static final int supportsAssist = 16844016; // 0x10104f0 - field public static final int supportsBatteryGameMode; + field public static final int supportsBatteryGameMode = 16844374; // 0x1010656 field public static final int supportsInlineSuggestions = 16844301; // 0x101060d - field public static final int supportsInlineSuggestionsWithTouchExploration; + field public static final int supportsInlineSuggestionsWithTouchExploration = 16844397; // 0x101066d field public static final int supportsLaunchVoiceAssistFromKeyguard = 16844017; // 0x10104f1 field public static final int supportsLocalInteraction = 16844047; // 0x101050f field public static final int supportsMultipleDisplays = 16844182; // 0x1010596 - field public static final int supportsPerformanceGameMode; + field public static final int supportsPerformanceGameMode = 16844375; // 0x1010657 field public static final int supportsPictureInPicture = 16844023; // 0x10104f7 field public static final int supportsRtl = 16843695; // 0x10103af - field public static final int supportsStylusHandwriting; + field public static final int supportsStylusHandwriting = 16844371; // 0x1010653 field public static final int supportsSwitchingToNextInputMethod = 16843755; // 0x10103eb field public static final int supportsUploading = 16843419; // 0x101029b field public static final int suppressesSpellChecker = 16844355; // 0x1010643 @@ -1579,7 +1579,7 @@ package android { field public static final int tileMode = 16843265; // 0x1010201 field public static final int tileModeX = 16843895; // 0x1010477 field public static final int tileModeY = 16843896; // 0x1010478 - field public static final int tileService; + field public static final int tileService = 16844391; // 0x1010667 field public static final int timePickerDialogTheme = 16843934; // 0x101049e field public static final int timePickerMode = 16843956; // 0x10104b4 field public static final int timePickerStyle = 16843933; // 0x101049d @@ -1598,10 +1598,10 @@ package android { field public static final int titleTextStyle = 16843512; // 0x10102f8 field public static final int toAlpha = 16843211; // 0x10101cb field public static final int toDegrees = 16843188; // 0x10101b4 - field public static final int toExtendBottom; - field public static final int toExtendLeft; - field public static final int toExtendRight; - field public static final int toExtendTop; + field public static final int toExtendBottom = 16844390; // 0x1010666 + field public static final int toExtendLeft = 16844387; // 0x1010663 + field public static final int toExtendRight = 16844389; // 0x1010665 + field public static final int toExtendTop = 16844388; // 0x1010664 field public static final int toId = 16843849; // 0x1010449 field public static final int toScene = 16843742; // 0x10103de field public static final int toXDelta = 16843207; // 0x10101c7 @@ -1753,7 +1753,7 @@ package android { field public static final int windowSplashScreenAnimatedIcon = 16844333; // 0x101062d field @Deprecated public static final int windowSplashScreenAnimationDuration = 16844334; // 0x101062e field public static final int windowSplashScreenBackground = 16844332; // 0x101062c - field public static final int windowSplashScreenBehavior; + field public static final int windowSplashScreenBehavior = 16844392; // 0x1010668 field public static final int windowSplashScreenBrandingImage = 16844335; // 0x101062f field public static final int windowSplashScreenIconBackgroundColor = 16844336; // 0x1010630 field @Deprecated public static final int windowSplashscreenContent = 16844132; // 0x1010564 @@ -2092,7 +2092,7 @@ package android { field public static final int accessibilityActionScrollUp = 16908344; // 0x1020038 field public static final int accessibilityActionSetProgress = 16908349; // 0x102003d field public static final int accessibilityActionShowOnScreen = 16908342; // 0x1020036 - field public static final int accessibilityActionShowTextSuggestions; + field public static final int accessibilityActionShowTextSuggestions = 16908376; // 0x1020058 field public static final int accessibilityActionShowTooltip = 16908356; // 0x1020044 field public static final int accessibilitySystemActionBack = 16908363; // 0x102004b field public static final int accessibilitySystemActionHome = 16908364; // 0x102004c @@ -2128,8 +2128,8 @@ package android { field public static final int icon_frame = 16908350; // 0x102003e field public static final int input = 16908297; // 0x1020009 field public static final int inputArea = 16908318; // 0x102001e - field public static final int inputExtractAccessories; - field public static final int inputExtractAction; + field public static final int inputExtractAccessories = 16908378; // 0x102005a + field public static final int inputExtractAction = 16908377; // 0x1020059 field public static final int inputExtractEditText = 16908325; // 0x1020025 field @Deprecated public static final int keyboardView = 16908326; // 0x1020026 field public static final int list = 16908298; // 0x102000a @@ -2301,7 +2301,7 @@ package android { field public static final int TextAppearance = 16973886; // 0x103003e field public static final int TextAppearance_DeviceDefault = 16974253; // 0x10301ad field public static final int TextAppearance_DeviceDefault_DialogWindowTitle = 16974264; // 0x10301b8 - field public static final int TextAppearance_DeviceDefault_Headline; + field public static final int TextAppearance_DeviceDefault_Headline = 16974565; // 0x10302e5 field public static final int TextAppearance_DeviceDefault_Inverse = 16974254; // 0x10301ae field public static final int TextAppearance_DeviceDefault_Large = 16974255; // 0x10301af field public static final int TextAppearance_DeviceDefault_Large_Inverse = 16974256; // 0x10301b0 @@ -30977,7 +30977,7 @@ package android.os { field public static final int R = 30; // 0x1e field public static final int S = 31; // 0x1f field public static final int S_V2 = 32; // 0x20 - field public static final int TIRAMISU = 10000; // 0x2710 + field public static final int TIRAMISU = 33; // 0x21 } public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable { diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 66af50c24db2..b25d1e302da5 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -367,12 +367,12 @@ package android { public static final class R.array { field public static final int config_keySystemUuidMapping = 17235973; // 0x1070005 - field public static final int config_optionalIpSecAlgorithms; + field public static final int config_optionalIpSecAlgorithms = 17235974; // 0x1070006 } public static final class R.attr { field public static final int allowClearUserDataOnFailedRestore = 16844288; // 0x1010600 - field public static final int gameSessionService; + field public static final int gameSessionService = 16844373; // 0x1010655 field public static final int hotwordDetectionService = 16844326; // 0x1010626 field public static final int isVrOnly = 16844152; // 0x1010578 field public static final int minExtensionVersion = 16844305; // 0x1010611 @@ -385,7 +385,7 @@ package android { } public static final class R.bool { - field public static final int config_enableQrCodeScannerOnLockScreen; + field public static final int config_enableQrCodeScannerOnLockScreen = 17891336; // 0x1110008 field public static final int config_sendPackageName = 17891328; // 0x1110000 field public static final int config_showDefaultAssistant = 17891329; // 0x1110001 field public static final int config_showDefaultEmergency = 17891330; // 0x1110002 @@ -402,7 +402,7 @@ package android { public static final class R.drawable { field public static final int ic_info = 17301684; // 0x10800b4 - field public static final int ic_safety_protection; + field public static final int ic_safety_protection = 17301685; // 0x10800b5 } public static final class R.raw { @@ -414,13 +414,13 @@ package android { field public static final int config_customMediaKeyDispatcher = 17039404; // 0x104002c field public static final int config_customMediaSessionPolicyProvider = 17039405; // 0x104002d field public static final int config_defaultAssistant = 17039393; // 0x1040021 - field public static final int config_defaultAutomotiveNavigation; + field public static final int config_defaultAutomotiveNavigation = 17039424; // 0x1040040 field public static final int config_defaultBrowser = 17039394; // 0x1040022 field public static final int config_defaultCallRedirection = 17039397; // 0x1040025 field public static final int config_defaultCallScreening = 17039398; // 0x1040026 field public static final int config_defaultDialer = 17039395; // 0x1040023 field public static final int config_defaultSms = 17039396; // 0x1040024 - field public static final int config_devicePolicyManagement; + field public static final int config_devicePolicyManagement = 17039421; // 0x104003d field public static final int config_feedbackIntentExtraKey = 17039391; // 0x104001f field public static final int config_feedbackIntentNameKey = 17039392; // 0x1040020 field public static final int config_helpIntentExtraKey = 17039389; // 0x104001d @@ -429,19 +429,19 @@ package android { field public static final int config_helpPackageNameValue = 17039388; // 0x104001c field public static final int config_systemActivityRecognizer = 17039416; // 0x1040038 field public static final int config_systemAmbientAudioIntelligence = 17039411; // 0x1040033 - field public static final int config_systemAppProtectionService; + field public static final int config_systemAppProtectionService = 17039422; // 0x104003e field public static final int config_systemAudioIntelligence = 17039412; // 0x1040034 - field public static final int config_systemAutomotiveCalendarSyncManager; + field public static final int config_systemAutomotiveCalendarSyncManager = 17039423; // 0x104003f field public static final int config_systemAutomotiveCluster = 17039400; // 0x1040028 field public static final int config_systemAutomotiveProjection = 17039401; // 0x1040029 field public static final int config_systemCompanionDeviceProvider = 17039417; // 0x1040039 field public static final int config_systemContacts = 17039403; // 0x104002b field public static final int config_systemGallery = 17039399; // 0x1040027 field public static final int config_systemNotificationIntelligence = 17039413; // 0x1040035 - field public static final int config_systemSettingsIntelligence; + field public static final int config_systemSettingsIntelligence = 17039426; // 0x1040042 field public static final int config_systemShell = 17039402; // 0x104002a field public static final int config_systemSpeechRecognizer = 17039406; // 0x104002e - field public static final int config_systemSupervision; + field public static final int config_systemSupervision = 17039420; // 0x104003c field public static final int config_systemTelevisionNotificationHandler = 17039409; // 0x1040031 field public static final int config_systemTextIntelligence = 17039414; // 0x1040036 field public static final int config_systemUi = 17039418; // 0x104003a @@ -449,7 +449,7 @@ package android { field public static final int config_systemVisualIntelligence = 17039415; // 0x1040037 field public static final int config_systemWellbeing = 17039408; // 0x1040030 field public static final int config_systemWifiCoexManager = 17039407; // 0x104002f - field public static final int safety_protection_display_text; + field public static final int safety_protection_display_text = 17039425; // 0x1040041 } public static final class R.style { diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 27f5b926411c..f4a12a5a6a0f 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -63,14 +63,14 @@ package android { public static final class R.bool { field public static final int config_assistantOnTopOfDream = 17891333; // 0x1110005 field public static final int config_perDisplayFocusEnabled = 17891332; // 0x1110004 - field public static final int config_preventImeStartupUnlessTextEditor; + field public static final int config_preventImeStartupUnlessTextEditor = 17891335; // 0x1110007 field public static final int config_remoteInsetsControllerControlsSystemBars = 17891334; // 0x1110006 } public static final class R.string { field public static final int config_defaultAssistant = 17039393; // 0x1040021 field public static final int config_defaultDialer = 17039395; // 0x1040023 - field public static final int config_systemAutomotiveCalendarSyncManager; + field public static final int config_systemAutomotiveCalendarSyncManager = 17039423; // 0x104003f field public static final int config_systemAutomotiveCluster = 17039400; // 0x1040028 field public static final int config_systemAutomotiveProjection = 17039401; // 0x1040029 field public static final int config_systemGallery = 17039399; // 0x1040027 diff --git a/core/java/android/accessibilityservice/InputMethod.java b/core/java/android/accessibilityservice/InputMethod.java index c0e5e84d369a..a3936040f2af 100644 --- a/core/java/android/accessibilityservice/InputMethod.java +++ b/core/java/android/accessibilityservice/InputMethod.java @@ -624,7 +624,6 @@ public class InputMethod { @Override public void invalidateInputInternal(EditorInfo editorInfo, IInputContext inputContext, int sessionId) { - // TODO(b/217788708): Add automated test. if (mStartedInputConnection instanceof RemoteInputConnection) { final RemoteInputConnection ric = (RemoteInputConnection) mStartedInputConnection; diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 6bb35db6e712..bfb11682e89c 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -628,6 +628,9 @@ public class Notification implements Parcelable * Bit to be bitwise-ored into the {@link #flags} field that should be * set if you would only like the sound, vibrate and ticker to be played * if the notification was not already showing. + * + * Note that using this flag will stop any ongoing alerting behaviour such + * as sound, vibration or blinking notification LED. */ public static final int FLAG_ONLY_ALERT_ONCE = 0x00000008; @@ -4633,6 +4636,9 @@ public class Notification implements Parcelable * Set this flag if you would only like the sound, vibrate * and ticker to be played if the notification is not already showing. * + * Note that using this flag will stop any ongoing alerting behaviour such + * as sound, vibration or blinking notification LED. + * * @see Notification#FLAG_ONLY_ALERT_ONCE */ @NonNull diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index f5cf73b152c8..d11b23cc871b 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -2542,7 +2542,7 @@ public class DevicePolicyManager { * that has this delegation. If another app already had delegated security logging access, it * will lose the delegation when a new app is delegated. * - * <p> Can only be granted by Device Owner or Profile Owner of an organnization owned and + * <p> Can only be granted by Device Owner or Profile Owner of an organization-owned * managed profile. */ public static final String DELEGATION_SECURITY_LOGGING = "delegation-security-logging"; diff --git a/core/java/android/app/admin/DevicePolicyResourcesManager.java b/core/java/android/app/admin/DevicePolicyResourcesManager.java index 06729222dea1..e8eb792f7fd7 100644 --- a/core/java/android/app/admin/DevicePolicyResourcesManager.java +++ b/core/java/android/app/admin/DevicePolicyResourcesManager.java @@ -70,6 +70,7 @@ public class DevicePolicyResourcesManager { * * <p>Important notes to consider when using this API: * <ul> + * <li> Updated resources are persisted over reboots. * <li>{@link #getDrawable} references the resource * {@link DevicePolicyDrawableResource#getResourceIdInCallingPackage()} in the * calling package each time it gets called. You have to ensure that the resource is always @@ -381,7 +382,9 @@ public class DevicePolicyResourcesManager { * * <p>Important notes to consider when using this API: * <ul> - * <li> {@link #getString} references the resource {@code callingPackageResourceId} in the + * <li> Updated resources are persisted over reboots. + * <li> {@link #getString} references the resource + * {@link DevicePolicyStringResource#getResourceIdInCallingPackage()} in the * calling package each time it gets called. You have to ensure that the resource is always * available in the calling package as long as it is used as an updated resource. * <li> You still have to re-call {@code setStrings} even if you only make changes to the diff --git a/core/java/android/hardware/CameraSessionStats.java b/core/java/android/hardware/CameraSessionStats.java index 698cc76f6325..9ef63065c057 100644 --- a/core/java/android/hardware/CameraSessionStats.java +++ b/core/java/android/hardware/CameraSessionStats.java @@ -61,6 +61,7 @@ public class CameraSessionStats implements Parcelable { private boolean mDeviceError; private float mMaxPreviewFps; private ArrayList<CameraStreamStats> mStreamStats; + private String mUserTag; public CameraSessionStats() { mFacing = -1; @@ -131,6 +132,7 @@ public class CameraSessionStats implements Parcelable { dest.writeLong(mResultErrorCount); dest.writeBoolean(mDeviceError); dest.writeTypedList(mStreamStats); + dest.writeString(mUserTag); } public void readFromParcel(Parcel in) { @@ -151,6 +153,8 @@ public class CameraSessionStats implements Parcelable { ArrayList<CameraStreamStats> streamStats = new ArrayList<CameraStreamStats>(); in.readTypedList(streamStats, CameraStreamStats.CREATOR); mStreamStats = streamStats; + + mUserTag = in.readString(); } public String getCameraId() { @@ -208,4 +212,8 @@ public class CameraSessionStats implements Parcelable { public List<CameraStreamStats> getStreamStats() { return mStreamStats; } + + public String getUserTag() { + return mUserTag; + } } diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java index 9884c382cd7c..15e59e03ee70 100644 --- a/core/java/android/hardware/camera2/CaptureRequest.java +++ b/core/java/android/hardware/camera2/CaptureRequest.java @@ -266,6 +266,8 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> private int mRequestType = -1; + private static final String SET_TAG_STRING_PREFIX = + "android.hardware.camera2.CaptureRequest.setTag."; /** * Get the type of the capture request * @@ -614,6 +616,11 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> throw new RuntimeException("Reading cached CaptureRequest is not supported"); } } + + boolean hasUserTagStr = (in.readInt() == 1) ? true : false; + if (hasUserTagStr) { + mUserTag = in.readString(); + } } @Override @@ -656,6 +663,19 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> dest.writeInt(0); } } + + // Write string for user tag if set to something in the same namespace + if (mUserTag != null) { + String userTagStr = mUserTag.toString(); + if (userTagStr != null && userTagStr.startsWith(SET_TAG_STRING_PREFIX)) { + dest.writeInt(1); + dest.writeString(userTagStr.substring(SET_TAG_STRING_PREFIX.length())); + } else { + dest.writeInt(0); + } + } else { + dest.writeInt(0); + } } /** @@ -938,7 +958,10 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * <p>This tag is not used for anything by the camera device, but can be * used by an application to easily identify a CaptureRequest when it is * returned by - * {@link CameraCaptureSession.CaptureCallback#onCaptureCompleted CaptureCallback.onCaptureCompleted} + * {@link CameraCaptureSession.CaptureCallback#onCaptureCompleted CaptureCallback.onCaptureCompleted}.</p> + * + * <p>If the application overrides the tag object's {@link Object#toString} function, the + * returned string must not contain personal identifiable information.</p> * * @param tag an arbitrary Object to store with this request * @see CaptureRequest#getTag diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 4fdd53425328..6ece5efae537 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -101,6 +101,7 @@ import android.view.BatchedInputEventReceiver.SimpleBatchedInputEventReceiver; import android.view.Choreographer; import android.view.Gravity; import android.view.InputChannel; +import android.view.InputDevice; import android.view.InputEventReceiver; import android.view.KeyCharacterMap; import android.view.KeyEvent; @@ -134,7 +135,10 @@ import android.widget.FrameLayout; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.TextView; +import android.window.OnBackInvokedCallback; +import android.window.OnBackInvokedDispatcher; import android.window.WindowMetricsHelper; +import android.window.WindowOnBackInvokedDispatcher; import com.android.internal.annotations.GuardedBy; import com.android.internal.inputmethod.IInputContentUriToken; @@ -345,6 +349,9 @@ public class InputMethodService extends AbstractInputMethodService { **/ private RingBuffer<MotionEvent> mPendingEvents; + /** Callback to handle back invocation when IME window is shown. */ + private OnBackInvokedCallback mBackCallback; + /** * Returns whether {@link InputMethodService} is responsible for rendering the back button and * the IME switcher button or not when the gestural navigation is enabled. @@ -1605,6 +1612,7 @@ public class InputMethodService extends AbstractInputMethodService { @Override public void onDestroy() { mDestroyed = true; super.onDestroy(); + unregisterOnBackInvokedCallback(); mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener( mInsetsComputer); doFinishInput(); @@ -2579,6 +2587,7 @@ public class InputMethodService extends AbstractInputMethodService { cancelImeSurfaceRemoval(); mInShowWindow = false; Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + registerOnBackInvokedCallback(); } @@ -2625,6 +2634,56 @@ public class InputMethodService extends AbstractInputMethodService { } /** + * Registers an {@link OnBackInvokedCallback} to handle back invocation when ahead-of-time + * back dispatching is enabled. We keep the KEYCODE_BACK based legacy code around to handle + * back on older devices. + */ + private void registerOnBackInvokedCallback() { + if (mBackCallback != null) { + // A back callback has already been registered. + return; + } + final ViewRootImpl viewRootImpl = mRootView == null ? null : mRootView.getViewRootImpl(); + if (viewRootImpl != null && WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled( + viewRootImpl.mContext)) { + final OnBackInvokedCallback callback = () -> { + KeyEvent downEvent = createKeyEvent( + KeyEvent.ACTION_DOWN, false /* isTracking */); + onKeyDown(KeyEvent.KEYCODE_BACK, downEvent); + boolean hasStartedTracking = + (downEvent.getFlags() & KeyEvent.FLAG_START_TRACKING) != 0; + KeyEvent upEvent = createKeyEvent(KeyEvent.ACTION_UP, hasStartedTracking); + onKeyUp(KeyEvent.KEYCODE_BACK, upEvent); + }; + viewRootImpl.getOnBackInvokedDispatcher().registerOnBackInvokedCallback( + OnBackInvokedDispatcher.PRIORITY_DEFAULT, callback); + mBackCallback = callback; + } + } + + private KeyEvent createKeyEvent(int action, boolean isTracking) { + final long when = SystemClock.uptimeMillis(); + return new KeyEvent(when, when, action, + KeyEvent.KEYCODE_BACK, 0 /* repeat */, 0 /* metaState */, + KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */, + KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY + | (isTracking ? KeyEvent.FLAG_TRACKING : 0), + InputDevice.SOURCE_KEYBOARD); + } + + private void unregisterOnBackInvokedCallback() { + final ViewRootImpl viewRootImpl = mRootView == null ? null : mRootView.getViewRootImpl(); + if (viewRootImpl != null + && mBackCallback != null + && WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled( + viewRootImpl.mContext)) { + viewRootImpl.getOnBackInvokedDispatcher() + .unregisterOnBackInvokedCallback(mBackCallback); + } + mBackCallback = null; + } + + /** * Applies the IME visibility in {@link android.view.ImeInsetsSourceConsumer}. * * @param setVisible {@code true} to make it visible, false to hide it. @@ -2669,6 +2728,7 @@ public class InputMethodService extends AbstractInputMethodService { } mLastWasInFullscreenMode = mIsFullscreen; updateFullscreenMode(); + unregisterOnBackInvokedCallback(); } /** diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index 42e6ac4df8af..0b956f8bf9e0 100755 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -1166,7 +1166,7 @@ public class Build { /** * Tiramisu. */ - public static final int TIRAMISU = CUR_DEVELOPMENT; + public static final int TIRAMISU = 33; } /** The type of build, like "user" or "eng". */ diff --git a/core/java/android/view/AccessibilityEmbeddedConnection.java b/core/java/android/view/AccessibilityEmbeddedConnection.java index de895d9c2e8e..a7d3164198a1 100644 --- a/core/java/android/view/AccessibilityEmbeddedConnection.java +++ b/core/java/android/view/AccessibilityEmbeddedConnection.java @@ -33,7 +33,7 @@ import java.lang.ref.WeakReference; */ final class AccessibilityEmbeddedConnection extends IAccessibilityEmbeddedConnection.Stub { private final WeakReference<ViewRootImpl> mViewRootImpl; - private final Matrix mTmpScreenMatrix = new Matrix(); + private final Matrix mTmpWindowMatrix = new Matrix(); AccessibilityEmbeddedConnection(ViewRootImpl viewRootImpl) { mViewRootImpl = new WeakReference<>(viewRootImpl); @@ -70,14 +70,14 @@ final class AccessibilityEmbeddedConnection extends IAccessibilityEmbeddedConnec } @Override - public void setScreenMatrix(float[] matrixValues) { + public void setWindowMatrix(float[] matrixValues) { final ViewRootImpl viewRootImpl = mViewRootImpl.get(); if (viewRootImpl != null) { - mTmpScreenMatrix.setValues(matrixValues); - if (viewRootImpl.mAttachInfo.mScreenMatrixInEmbeddedHierarchy == null) { - viewRootImpl.mAttachInfo.mScreenMatrixInEmbeddedHierarchy = new Matrix(); + mTmpWindowMatrix.setValues(matrixValues); + if (viewRootImpl.mAttachInfo.mWindowMatrixInEmbeddedHierarchy == null) { + viewRootImpl.mAttachInfo.mWindowMatrixInEmbeddedHierarchy = new Matrix(); } - viewRootImpl.mAttachInfo.mScreenMatrixInEmbeddedHierarchy.set(mTmpScreenMatrix); + viewRootImpl.mAttachInfo.mWindowMatrixInEmbeddedHierarchy.set(mTmpWindowMatrix); } } } diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java index 7e0b79470cf2..23e1505e1a4c 100644 --- a/core/java/android/view/AccessibilityInteractionController.java +++ b/core/java/android/view/AccessibilityInteractionController.java @@ -22,7 +22,6 @@ import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_REQUES import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY; import android.graphics.Matrix; -import android.graphics.Point; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; @@ -112,10 +111,7 @@ public final class AccessibilityInteractionController { private final ArrayList<View> mTempArrayList = new ArrayList<View>(); - private final Point mTempPoint = new Point(); private final Rect mTempRect = new Rect(); - private final Rect mTempRect1 = new Rect(); - private final Rect mTempRect2 = new Rect(); private final RectF mTempRectF = new RectF(); private AddNodeInfosForViewId mAddNodeInfosForViewId; @@ -131,7 +127,7 @@ public final class AccessibilityInteractionController { private int mActiveRequestPreparerId; public AccessibilityInteractionController(ViewRootImpl viewRootImpl) { - Looper looper = viewRootImpl.mHandler.getLooper(); + Looper looper = viewRootImpl.mHandler.getLooper(); mMyLooperThreadId = looper.getThread().getId(); mMyProcessId = Process.myPid(); mHandler = new PrivateHandler(looper); @@ -173,7 +169,8 @@ public final class AccessibilityInteractionController { public void findAccessibilityNodeInfoByAccessibilityIdClientThread( long accessibilityNodeId, Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, - long interrogatingTid, MagnificationSpec spec, Bundle arguments) { + long interrogatingTid, MagnificationSpec spec, float[] matrixValues, + Bundle arguments) { final Message message = mHandler.obtainMessage(); message.what = PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID; message.arg1 = flags; @@ -186,6 +183,7 @@ public final class AccessibilityInteractionController { args.arg2 = spec; args.arg3 = interactiveRegion; args.arg4 = arguments; + args.arg5 = matrixValues; message.obj = args; synchronized (mLock) { @@ -344,6 +342,7 @@ public final class AccessibilityInteractionController { final MagnificationSpec spec = (MagnificationSpec) args.arg2; final Region interactiveRegion = (Region) args.arg3; final Bundle arguments = (Bundle) args.arg4; + final float[] matrixValues = (float[]) args.arg5; args.recycle(); @@ -378,7 +377,7 @@ public final class AccessibilityInteractionController { if (!interruptPrefetch) { // Return found node and prefetched nodes in one IPC. updateInfosForViewportAndReturnFindNodeResult(infos, callback, interactionId, spec, - interactiveRegion); + matrixValues, interactiveRegion); final SatisfiedFindAccessibilityNodeByAccessibilityIdRequest satisfiedRequest = getSatisfiedRequestInPrefetch(requestedNode == null ? null : requestedNode, @@ -391,13 +390,13 @@ public final class AccessibilityInteractionController { // Return found node. updateInfoForViewportAndReturnFindNodeResult( requestedNode == null ? null : new AccessibilityNodeInfo(requestedNode), - callback, interactionId, spec, interactiveRegion); + callback, interactionId, spec, matrixValues, interactiveRegion); } } mPrefetcher.prefetchAccessibilityNodeInfos(requestedView, requestedNode == null ? null : new AccessibilityNodeInfo(requestedNode), infos); mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0; - updateInfosForViewPort(infos, spec, interactiveRegion); + updateInfosForViewPort(infos, spec, matrixValues, interactiveRegion); final SatisfiedFindAccessibilityNodeByAccessibilityIdRequest satisfiedRequest = getSatisfiedRequestInPrefetch(requestedNode == null ? null : requestedNode, infos, flags); @@ -439,7 +438,7 @@ public final class AccessibilityInteractionController { public void findAccessibilityNodeInfosByViewIdClientThread(long accessibilityNodeId, String viewId, Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, - long interrogatingTid, MagnificationSpec spec) { + long interrogatingTid, MagnificationSpec spec, float[] matrixValues) { Message message = mHandler.obtainMessage(); message.what = PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID; message.arg1 = flags; @@ -451,6 +450,7 @@ public final class AccessibilityInteractionController { args.arg2 = spec; args.arg3 = viewId; args.arg4 = interactiveRegion; + args.arg5 = matrixValues; message.obj = args; scheduleMessage(message, interrogatingPid, interrogatingTid, CONSIDER_REQUEST_PREPARERS); @@ -467,6 +467,7 @@ public final class AccessibilityInteractionController { final MagnificationSpec spec = (MagnificationSpec) args.arg2; final String viewId = (String) args.arg3; final Region interactiveRegion = (Region) args.arg4; + final float[] matrixValues = (float[]) args.arg5; args.recycle(); final List<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList; @@ -494,14 +495,14 @@ public final class AccessibilityInteractionController { } finally { mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0; updateInfosForViewportAndReturnFindNodeResult( - infos, callback, interactionId, spec, interactiveRegion); + infos, callback, interactionId, spec, matrixValues, interactiveRegion); } } public void findAccessibilityNodeInfosByTextClientThread(long accessibilityNodeId, String text, Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, - long interrogatingTid, MagnificationSpec spec) { + long interrogatingTid, MagnificationSpec spec, float[] matrixValues) { Message message = mHandler.obtainMessage(); message.what = PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT; message.arg1 = flags; @@ -514,6 +515,7 @@ public final class AccessibilityInteractionController { args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId); args.argi3 = interactionId; args.arg4 = interactiveRegion; + args.arg5 = matrixValues; message.obj = args; scheduleMessage(message, interrogatingPid, interrogatingTid, CONSIDER_REQUEST_PREPARERS); @@ -531,6 +533,7 @@ public final class AccessibilityInteractionController { final int virtualDescendantId = args.argi2; final int interactionId = args.argi3; final Region interactiveRegion = (Region) args.arg4; + final float[] matrixValues = (float[]) args.arg5; args.recycle(); List<AccessibilityNodeInfo> infos = null; @@ -577,14 +580,14 @@ public final class AccessibilityInteractionController { } finally { mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0; updateInfosForViewportAndReturnFindNodeResult( - infos, callback, interactionId, spec, interactiveRegion); + infos, callback, interactionId, spec, matrixValues, interactiveRegion); } } public void findFocusClientThread(long accessibilityNodeId, int focusType, Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, - long interrogatingTid, MagnificationSpec spec) { + long interrogatingTid, MagnificationSpec spec, float[] matrixValues) { Message message = mHandler.obtainMessage(); message.what = PrivateHandler.MSG_FIND_FOCUS; message.arg1 = flags; @@ -597,7 +600,7 @@ public final class AccessibilityInteractionController { args.arg1 = callback; args.arg2 = spec; args.arg3 = interactiveRegion; - + args.arg4 = matrixValues; message.obj = args; scheduleMessage(message, interrogatingPid, interrogatingTid, CONSIDER_REQUEST_PREPARERS); @@ -615,6 +618,7 @@ public final class AccessibilityInteractionController { (IAccessibilityInteractionConnectionCallback) args.arg1; final MagnificationSpec spec = (MagnificationSpec) args.arg2; final Region interactiveRegion = (Region) args.arg3; + final float[] matrixValues = (float[]) args.arg4; args.recycle(); AccessibilityNodeInfo focused = null; @@ -672,14 +676,14 @@ public final class AccessibilityInteractionController { } finally { mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0; updateInfoForViewportAndReturnFindNodeResult( - focused, callback, interactionId, spec, interactiveRegion); + focused, callback, interactionId, spec, matrixValues, interactiveRegion); } } public void focusSearchClientThread(long accessibilityNodeId, int direction, Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, - long interrogatingTid, MagnificationSpec spec) { + long interrogatingTid, MagnificationSpec spec, float[] matrixValues) { Message message = mHandler.obtainMessage(); message.what = PrivateHandler.MSG_FOCUS_SEARCH; message.arg1 = flags; @@ -691,6 +695,7 @@ public final class AccessibilityInteractionController { args.arg1 = callback; args.arg2 = spec; args.arg3 = interactiveRegion; + args.arg4 = matrixValues; message.obj = args; @@ -708,7 +713,7 @@ public final class AccessibilityInteractionController { (IAccessibilityInteractionConnectionCallback) args.arg1; final MagnificationSpec spec = (MagnificationSpec) args.arg2; final Region interactiveRegion = (Region) args.arg3; - + final float[] matrixValues = (float[]) args.arg4; args.recycle(); AccessibilityNodeInfo next = null; @@ -727,7 +732,7 @@ public final class AccessibilityInteractionController { } finally { mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0; updateInfoForViewportAndReturnFindNodeResult( - next, callback, interactionId, spec, interactiveRegion); + next, callback, interactionId, spec, matrixValues, interactiveRegion); } } @@ -882,13 +887,20 @@ public final class AccessibilityInteractionController { } } + // The boundInScreen includes magnification effect, so we need to normalize it before + // determine the visibility. private void adjustIsVisibleToUserIfNeeded(AccessibilityNodeInfo info, - Region interactiveRegion) { + Region interactiveRegion, MagnificationSpec spec) { if (interactiveRegion == null || info == null) { return; } Rect boundsInScreen = mTempRect; info.getBoundsInScreen(boundsInScreen); + if (spec != null && !spec.isNop()) { + boundsInScreen.offset((int) -spec.offsetX, (int) -spec.offsetY); + boundsInScreen.scale(1 / spec.scale); + } + if (interactiveRegion.quickReject(boundsInScreen) && !shouldBypassAdjustIsVisible()) { info.setVisibleToUser(false); } @@ -902,36 +914,30 @@ public final class AccessibilityInteractionController { return false; } - private void applyScreenMatrixIfNeeded(List<AccessibilityNodeInfo> infos) { - if (infos == null || shouldBypassApplyScreenMatrix()) { - return; - } - final int infoCount = infos.size(); - for (int i = 0; i < infoCount; i++) { - final AccessibilityNodeInfo info = infos.get(i); - applyScreenMatrixIfNeeded(info); - } - } - - private void applyScreenMatrixIfNeeded(AccessibilityNodeInfo info) { - if (info == null || shouldBypassApplyScreenMatrix()) { + /** + * Applies the host-window matrix to the embedded node. After this transform, The node bounds + * will be transformed from embedded window coordinates to host-window coordinates. + * + */ + private void applyHostWindowMatrixIfNeeded(AccessibilityNodeInfo info) { + if (info == null || shouldBypassApplyWindowMatrix()) { return; } final Rect boundsInScreen = mTempRect; final RectF transformedBounds = mTempRectF; - final Matrix screenMatrix = mViewRootImpl.mAttachInfo.mScreenMatrixInEmbeddedHierarchy; + final Matrix windowMatrix = mViewRootImpl.mAttachInfo.mWindowMatrixInEmbeddedHierarchy; info.getBoundsInScreen(boundsInScreen); transformedBounds.set(boundsInScreen); - screenMatrix.mapRect(transformedBounds); + windowMatrix.mapRect(transformedBounds); boundsInScreen.set((int) transformedBounds.left, (int) transformedBounds.top, (int) transformedBounds.right, (int) transformedBounds.bottom); info.setBoundsInScreen(boundsInScreen); } - private boolean shouldBypassApplyScreenMatrix() { - final Matrix screenMatrix = mViewRootImpl.mAttachInfo.mScreenMatrixInEmbeddedHierarchy; - return screenMatrix == null || screenMatrix.isIdentity(); + private boolean shouldBypassApplyWindowMatrix() { + final Matrix windowMatrix = mViewRootImpl.mAttachInfo.mWindowMatrixInEmbeddedHierarchy; + return windowMatrix == null || windowMatrix.isIdentity(); } private void associateLeashedParentIfNeeded(AccessibilityNodeInfo info) { @@ -963,46 +969,17 @@ public final class AccessibilityInteractionController { if (!shouldApplyAppScaleAndMagnificationSpec(applicationScale, spec)) { return; } - Rect boundsInParent = mTempRect; - Rect boundsInScreen = mTempRect1; info.getBoundsInParent(boundsInParent); - info.getBoundsInScreen(boundsInScreen); if (applicationScale != 1.0f) { boundsInParent.scale(applicationScale); - boundsInScreen.scale(applicationScale); } if (spec != null) { boundsInParent.scale(spec.scale); // boundsInParent must not be offset. - boundsInScreen.scale(spec.scale); - boundsInScreen.offset((int) spec.offsetX, (int) spec.offsetY); } info.setBoundsInParent(boundsInParent); - info.setBoundsInScreen(boundsInScreen); - - // Scale text locations if they are present - if (info.hasExtras()) { - Bundle extras = info.getExtras(); - Parcelable[] textLocations = - extras.getParcelableArray(EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY); - if (textLocations != null) { - for (int i = 0; i < textLocations.length; i++) { - // Unchecked cast - an app that puts other objects in this bundle with this - // key will crash. - RectF textLocation = ((RectF) textLocations[i]); - if (textLocation == null) { - continue; - } - textLocation.scale(applicationScale); - if (spec != null) { - textLocation.scale(spec.scale); - textLocation.offset(spec.offsetX, spec.offsetY); - } - } - } - } } private boolean shouldApplyAppScaleAndMagnificationSpec(float appScale, @@ -1011,27 +988,88 @@ public final class AccessibilityInteractionController { } private void updateInfosForViewPort(List<AccessibilityNodeInfo> infos, MagnificationSpec spec, - Region interactiveRegion) { + float[] matrixValues, Region interactiveRegion) { for (int i = 0; i < infos.size(); i++) { - updateInfoForViewPort(infos.get(i), spec, interactiveRegion); + updateInfoForViewPort(infos.get(i), spec, matrixValues, interactiveRegion); } } private void updateInfoForViewPort(AccessibilityNodeInfo info, MagnificationSpec spec, - Region interactiveRegion) { + float[] matrixValues, Region interactiveRegion) { associateLeashedParentIfNeeded(info); - applyScreenMatrixIfNeeded(info); - // To avoid applyAppScaleAndMagnificationSpecIfNeeded changing the bounds of node, - // then impact the visibility result, we need to adjust visibility before apply scale. - adjustIsVisibleToUserIfNeeded(info, interactiveRegion); + + applyHostWindowMatrixIfNeeded(info); + // Transform view bounds from window coordinates to screen coordinates. + transformBoundsWithScreenMatrix(info, matrixValues); + adjustIsVisibleToUserIfNeeded(info, interactiveRegion, spec); applyAppScaleAndMagnificationSpecIfNeeded(info, spec); } + + /** + * Transforms the regions from local screen coordinate to global screen coordinate with the + * given transform matrix used in on-screen coordinate. + * + * @param info the AccessibilityNodeInfo that has the region in application screen coordinate + * @param matrixValues the matrix to be applied + */ + private void transformBoundsWithScreenMatrix(AccessibilityNodeInfo info, + float[] matrixValues) { + if (info == null || matrixValues == null) { + return; + } + final Rect boundInScreen = mTempRect; + final RectF transformedBounds = mTempRectF; + + info.getBoundsInScreen(boundInScreen); + transformedBounds.set(boundInScreen); + + final Matrix transformMatrix = new Matrix(); + transformMatrix.setValues(matrixValues); + final float applicationScale = mViewRootImpl.mAttachInfo.mApplicationScale; + if (applicationScale != 1f) { + transformMatrix.preScale(applicationScale, applicationScale); + } + // Transform the bounds from application screen coordinates to global window coordinates. + // For the embedded node, the bounds we get is already in window coordinates, so we don't + // need to do it. + if (mViewRootImpl.mAttachInfo.mWindowMatrixInEmbeddedHierarchy == null) { + transformMatrix.preTranslate(-mViewRootImpl.mAttachInfo.mWindowLeft, + -mViewRootImpl.mAttachInfo.mWindowTop); + } + + if (transformMatrix.isIdentity()) { + return; + } + transformMatrix.mapRect(transformedBounds); + // Offset 0.5f to round after casting. + transformedBounds.offset(0.5f, 0.5f); + boundInScreen.set((int) (transformedBounds.left), (int) transformedBounds.top, + (int) transformedBounds.right, (int) transformedBounds.bottom); + info.setBoundsInScreen(boundInScreen); + // Scale text locations if they are present + if (info.hasExtras()) { + final Bundle extras = info.getExtras(); + final RectF[] textLocations = + extras.getParcelableArray(EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY, RectF.class); + if (textLocations != null) { + for (int i = 0; i < textLocations.length; i++) { + // Unchecked cast - an app that puts other objects in this bundle with this + // key will crash. + final RectF textLocation = textLocations[i]; + if (textLocation != null) { + transformMatrix.mapRect(textLocation); + } + } + } + } + } + private void updateInfosForViewportAndReturnFindNodeResult(List<AccessibilityNodeInfo> infos, IAccessibilityInteractionConnectionCallback callback, int interactionId, - MagnificationSpec spec, Region interactiveRegion) { + MagnificationSpec spec, float[] matrixValues, Region interactiveRegion) { if (infos != null) { - updateInfosForViewPort(infos, spec, interactiveRegion); + updateInfosForViewPort(infos, spec, matrixValues, interactiveRegion); } returnFindNodesResult(infos, callback, interactionId); } @@ -1141,8 +1179,8 @@ public final class AccessibilityInteractionController { private void updateInfoForViewportAndReturnFindNodeResult(AccessibilityNodeInfo info, IAccessibilityInteractionConnectionCallback callback, int interactionId, - MagnificationSpec spec, Region interactiveRegion) { - updateInfoForViewPort(info, spec, interactiveRegion); + MagnificationSpec spec, float[] matrixValues, Region interactiveRegion) { + updateInfoForViewPort(info, spec, matrixValues, interactiveRegion); returnFindNodeResult(info, callback, interactionId); } diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index 7b6a0d64f980..cce3e8c84451 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -833,10 +833,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } /** - * @see InsetsState#calculateVisibleInsets(Rect, int) + * @see InsetsState#calculateVisibleInsets(Rect, int, int, int, int) */ - public Insets calculateVisibleInsets(@SoftInputModeFlags int softInputMode) { - return mState.calculateVisibleInsets(mFrame, softInputMode); + public Insets calculateVisibleInsets(int windowType, int windowingMode, + @SoftInputModeFlags int softInputMode, int windowFlags) { + return mState.calculateVisibleInsets(mFrame, windowType, windowingMode, softInputMode, + windowFlags); } /** diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java index b1b630ed7353..eb746080de15 100644 --- a/core/java/android/view/InsetsState.java +++ b/core/java/android/view/InsetsState.java @@ -23,11 +23,11 @@ import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_STABLE; import static android.view.WindowInsets.Type.displayCutout; import static android.view.WindowInsets.Type.ime; import static android.view.WindowInsets.Type.indexOf; -import static android.view.WindowInsets.Type.isVisibleInsetsType; import static android.view.WindowInsets.Type.statusBars; import static android.view.WindowInsets.Type.systemBars; import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; +import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR; @@ -257,7 +257,7 @@ public class InsetsState implements Parcelable { if ((legacyWindowFlags & FLAG_FULLSCREEN) != 0) { compatInsetsTypes &= ~statusBars(); } - if (clearCompatInsets(windowType, legacyWindowFlags, windowingMode)) { + if (clearsCompatInsets(windowType, legacyWindowFlags, windowingMode)) { compatInsetsTypes = 0; } @@ -358,17 +358,23 @@ public class InsetsState implements Parcelable { return insets; } - public Insets calculateVisibleInsets(Rect frame, @SoftInputModeFlags int softInputMode) { + public Insets calculateVisibleInsets(Rect frame, int windowType, int windowingMode, + @SoftInputModeFlags int softInputMode, int windowFlags) { + if (clearsCompatInsets(windowType, windowFlags, windowingMode)) { + return Insets.NONE; + } + final int softInputAdjustMode = softInputMode & SOFT_INPUT_MASK_ADJUST; + final int visibleInsetsTypes = softInputAdjustMode != SOFT_INPUT_ADJUST_NOTHING + ? systemBars() | ime() + : systemBars(); Insets insets = Insets.NONE; for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) { InsetsSource source = mSources[type]; if (source == null) { continue; } - - // Ignore everything that's not a system bar or IME. - int publicType = InsetsState.toPublicType(type); - if (!isVisibleInsetsType(publicType, softInputMode)) { + final int publicType = InsetsState.toPublicType(type); + if ((publicType & visibleInsetsTypes) == 0) { continue; } insets = Insets.max(source.calculateVisibleInsets(frame), insets); @@ -676,7 +682,7 @@ public class InsetsState implements Parcelable { mSources[source.getType()] = source; } - public static boolean clearCompatInsets(int windowType, int windowFlags, int windowingMode) { + public static boolean clearsCompatInsets(int windowType, int windowFlags, int windowingMode) { return (windowFlags & FLAG_LAYOUT_NO_LIMITS) != 0 && windowType != TYPE_WALLPAPER && windowType != TYPE_SYSTEM_ERROR && !WindowConfiguration.inMultiWindowMode(windowingMode); diff --git a/core/java/android/view/RemoteAccessibilityController.java b/core/java/android/view/RemoteAccessibilityController.java index bc0fab1bcf8d..8855fb584ef3 100644 --- a/core/java/android/view/RemoteAccessibilityController.java +++ b/core/java/android/view/RemoteAccessibilityController.java @@ -28,7 +28,7 @@ class RemoteAccessibilityController { private static final String TAG = "RemoteAccessibilityController"; private int mHostId; private RemoteAccessibilityEmbeddedConnection mConnectionWrapper; - private Matrix mScreenMatrixForEmbeddedHierarchy = new Matrix(); + private Matrix mWindowMatrixForEmbeddedHierarchy = new Matrix(); private final float[] mMatrixValues = new float[9]; private View mHostView; @@ -140,9 +140,9 @@ class RemoteAccessibilityController { return mConnectionWrapper; } - void setScreenMatrix(Matrix m) { - // If the screen matrix is identity or doesn't change, do nothing. - if (m.isIdentity() || m.equals(mScreenMatrixForEmbeddedHierarchy)) { + void setWindowMatrix(Matrix m) { + // If the window matrix is identity or doesn't change, do nothing. + if (m.isIdentity() || m.equals(mWindowMatrixForEmbeddedHierarchy)) { return; } @@ -153,8 +153,8 @@ class RemoteAccessibilityController { return; } m.getValues(mMatrixValues); - wrapper.getConnection().setScreenMatrix(mMatrixValues); - mScreenMatrixForEmbeddedHierarchy.set(m); + wrapper.getConnection().setWindowMatrix(mMatrixValues); + mWindowMatrixForEmbeddedHierarchy.set(m); } catch (RemoteException e) { Log.d(TAG, "Error while setScreenMatrix " + e); } diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index ed57136b1d35..017f2e4e9af1 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -1781,11 +1781,16 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall return; } getBoundsOnScreen(mTmpRect); + + // To compute the node bounds of the node on the embedded window, + // apply this matrix to get the bounds in host window-relative coordinates, + // then using the global transform to get the actual bounds on screen. + mTmpRect.offset(-mAttachInfo.mWindowLeft, -mAttachInfo.mWindowTop); mTmpMatrix.reset(); mTmpMatrix.setTranslate(mTmpRect.left, mTmpRect.top); mTmpMatrix.postScale(mScreenRect.width() / (float) mSurfaceWidth, mScreenRect.height() / (float) mSurfaceHeight); - mRemoteAccessibilityController.setScreenMatrix(mTmpMatrix); + mRemoteAccessibilityController.setWindowMatrix(mTmpMatrix); } @Override diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 8b9a86b9eec6..569461e81111 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -29938,10 +29938,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, boolean mHandlingPointerEvent; /** - * The screen matrix of this view when it's on a {@link SurfaceControlViewHost} that is + * The window matrix of this view when it's on a {@link SurfaceControlViewHost} that is * embedded within a SurfaceView. */ - Matrix mScreenMatrixInEmbeddedHierarchy; + Matrix mWindowMatrixInEmbeddedHierarchy; /** * Global to the view hierarchy used as a temporary for dealing with diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index a3d0bf79416b..92289d9c72b6 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -2570,7 +2570,8 @@ public final class ViewRootImpl implements ViewParent, mAttachInfo.mContentInsets.set(mLastWindowInsets.getSystemWindowInsets().toRect()); mAttachInfo.mStableInsets.set(mLastWindowInsets.getStableInsets().toRect()); mAttachInfo.mVisibleInsets.set(mInsetsController.calculateVisibleInsets( - mWindowAttributes.softInputMode).toRect()); + mWindowAttributes.type, config.windowConfiguration.getWindowingMode(), + mWindowAttributes.softInputMode, mWindowAttributes.flags).toRect()); } return mLastWindowInsets; } @@ -10249,13 +10250,14 @@ public final class ViewRootImpl implements ViewParent, public void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId, Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, - int interrogatingPid, long interrogatingTid, MagnificationSpec spec, Bundle args) { + int interrogatingPid, long interrogatingTid, MagnificationSpec spec, float[] matrix, + Bundle args) { ViewRootImpl viewRootImpl = mViewRootImpl.get(); if (viewRootImpl != null && viewRootImpl.mView != null) { viewRootImpl.getAccessibilityInteractionController() .findAccessibilityNodeInfoByAccessibilityIdClientThread(accessibilityNodeId, interactiveRegion, interactionId, callback, flags, interrogatingPid, - interrogatingTid, spec, args); + interrogatingTid, spec, matrix, args); } else { // We cannot make the call and notify the caller so it does not wait. try { @@ -10290,13 +10292,14 @@ public final class ViewRootImpl implements ViewParent, public void findAccessibilityNodeInfosByViewId(long accessibilityNodeId, String viewId, Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, - int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { + int interrogatingPid, long interrogatingTid, MagnificationSpec spec, + float[] matrix) { ViewRootImpl viewRootImpl = mViewRootImpl.get(); if (viewRootImpl != null && viewRootImpl.mView != null) { viewRootImpl.getAccessibilityInteractionController() .findAccessibilityNodeInfosByViewIdClientThread(accessibilityNodeId, viewId, interactiveRegion, interactionId, callback, flags, - interrogatingPid, interrogatingTid, spec); + interrogatingPid, interrogatingTid, spec, matrix); } else { // We cannot make the call and notify the caller so it does not wait. try { @@ -10311,13 +10314,14 @@ public final class ViewRootImpl implements ViewParent, public void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text, Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, - int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { + int interrogatingPid, long interrogatingTid, MagnificationSpec spec, + float[] matrix) { ViewRootImpl viewRootImpl = mViewRootImpl.get(); if (viewRootImpl != null && viewRootImpl.mView != null) { viewRootImpl.getAccessibilityInteractionController() .findAccessibilityNodeInfosByTextClientThread(accessibilityNodeId, text, interactiveRegion, interactionId, callback, flags, interrogatingPid, - interrogatingTid, spec); + interrogatingTid, spec, matrix); } else { // We cannot make the call and notify the caller so it does not wait. try { @@ -10331,13 +10335,14 @@ public final class ViewRootImpl implements ViewParent, @Override public void findFocus(long accessibilityNodeId, int focusType, Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, - int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { + int interrogatingPid, long interrogatingTid, MagnificationSpec spec, + float[] matrix) { ViewRootImpl viewRootImpl = mViewRootImpl.get(); if (viewRootImpl != null && viewRootImpl.mView != null) { viewRootImpl.getAccessibilityInteractionController() .findFocusClientThread(accessibilityNodeId, focusType, interactiveRegion, interactionId, callback, flags, interrogatingPid, interrogatingTid, - spec); + spec, matrix); } else { // We cannot make the call and notify the caller so it does not wait. try { @@ -10351,13 +10356,14 @@ public final class ViewRootImpl implements ViewParent, @Override public void focusSearch(long accessibilityNodeId, int direction, Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, - int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { + int interrogatingPid, long interrogatingTid, MagnificationSpec spec, + float[] matrix) { ViewRootImpl viewRootImpl = mViewRootImpl.get(); if (viewRootImpl != null && viewRootImpl.mView != null) { viewRootImpl.getAccessibilityInteractionController() .focusSearchClientThread(accessibilityNodeId, direction, interactiveRegion, interactionId, callback, flags, interrogatingPid, interrogatingTid, - spec); + spec, matrix); } else { // We cannot make the call and notify the caller so it does not wait. try { diff --git a/core/java/android/view/WindowInfo.java b/core/java/android/view/WindowInfo.java index 1edbbba09eef..a5b8720c7dec 100644 --- a/core/java/android/view/WindowInfo.java +++ b/core/java/android/view/WindowInfo.java @@ -17,6 +17,7 @@ package android.view; import android.app.ActivityTaskManager; +import android.graphics.Matrix; import android.graphics.Region; import android.os.IBinder; import android.os.Parcel; @@ -53,6 +54,11 @@ public class WindowInfo implements Parcelable { public boolean hasFlagWatchOutsideTouch; public int displayId = Display.INVALID_DISPLAY; public int taskId = ActivityTaskManager.INVALID_TASK_ID; + // The matrix applied to the bounds in window coordinate to get the corresponding values in + // screen coordinates. + public float[] mTransformMatrix = new float[9]; + + public MagnificationSpec mMagnificationSpec = new MagnificationSpec(); private WindowInfo() { /* do nothing - hide constructor */ @@ -81,6 +87,9 @@ public class WindowInfo implements Parcelable { window.accessibilityIdOfAnchor = other.accessibilityIdOfAnchor; window.inPictureInPicture = other.inPictureInPicture; window.hasFlagWatchOutsideTouch = other.hasFlagWatchOutsideTouch; + for (int i = 0; i < window.mTransformMatrix.length; i++) { + window.mTransformMatrix[i] = other.mTransformMatrix[i]; + } if (other.childTokens != null && !other.childTokens.isEmpty()) { if (window.childTokens == null) { @@ -89,7 +98,7 @@ public class WindowInfo implements Parcelable { window.childTokens.addAll(other.childTokens); } } - + window.mMagnificationSpec.setTo(other.mMagnificationSpec); return window; } @@ -118,6 +127,7 @@ public class WindowInfo implements Parcelable { parcel.writeLong(accessibilityIdOfAnchor); parcel.writeInt(inPictureInPicture ? 1 : 0); parcel.writeInt(hasFlagWatchOutsideTouch ? 1 : 0); + parcel.writeFloatArray(mTransformMatrix); if (childTokens != null && !childTokens.isEmpty()) { parcel.writeInt(1); @@ -125,6 +135,7 @@ public class WindowInfo implements Parcelable { } else { parcel.writeInt(0); } + mMagnificationSpec.writeToParcel(parcel, flags); } @Override @@ -145,6 +156,10 @@ public class WindowInfo implements Parcelable { builder.append(", accessibility anchor=").append(accessibilityIdOfAnchor); builder.append(", pictureInPicture=").append(inPictureInPicture); builder.append(", watchOutsideTouch=").append(hasFlagWatchOutsideTouch); + Matrix matrix = new Matrix(); + matrix.setValues(mTransformMatrix); + builder.append(", mTransformMatrix=").append(matrix); + builder.append(", mMagnificationSpec=").append(mMagnificationSpec); builder.append(']'); return builder.toString(); } @@ -163,7 +178,7 @@ public class WindowInfo implements Parcelable { accessibilityIdOfAnchor = parcel.readLong(); inPictureInPicture = (parcel.readInt() == 1); hasFlagWatchOutsideTouch = (parcel.readInt() == 1); - + parcel.readFloatArray(mTransformMatrix); final boolean hasChildren = (parcel.readInt() == 1); if (hasChildren) { if (childTokens == null) { @@ -171,6 +186,7 @@ public class WindowInfo implements Parcelable { } parcel.readBinderList(childTokens); } + mMagnificationSpec = MagnificationSpec.CREATOR.createFromParcel(parcel); } private void clear() { @@ -188,6 +204,10 @@ public class WindowInfo implements Parcelable { } inPictureInPicture = false; hasFlagWatchOutsideTouch = false; + for (int i = 0; i < mTransformMatrix.length; i++) { + mTransformMatrix[i] = 0; + } + mMagnificationSpec.clear(); } public static final @android.annotation.NonNull Parcelable.Creator<WindowInfo> CREATOR = diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java index 1c6d93da092c..c846175699f2 100644 --- a/core/java/android/view/WindowInsets.java +++ b/core/java/android/view/WindowInsets.java @@ -32,8 +32,6 @@ import static android.view.WindowInsets.Type.all; import static android.view.WindowInsets.Type.ime; import static android.view.WindowInsets.Type.indexOf; import static android.view.WindowInsets.Type.systemBars; -import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; -import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; import android.annotation.IntDef; import android.annotation.IntRange; @@ -46,7 +44,6 @@ import android.graphics.Rect; import android.util.SparseArray; import android.view.View.OnApplyWindowInsetsListener; import android.view.WindowInsets.Type.InsetsType; -import android.view.WindowManager.LayoutParams.SoftInputModeFlags; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethod; @@ -1600,17 +1597,6 @@ public final class WindowInsets { public static @InsetsType int all() { return 0xFFFFFFFF; } - - /** - * Checks whether the specified type is considered to be part of visible insets. - * @hide - */ - public static boolean isVisibleInsetsType(int type, - @SoftInputModeFlags int softInputModeFlags) { - int softInputMode = softInputModeFlags & SOFT_INPUT_MASK_ADJUST; - return (type & Type.systemBars()) != 0 - || (softInputMode != SOFT_INPUT_ADJUST_NOTHING && (type & Type.ime()) != 0); - } } /** diff --git a/core/java/android/view/accessibility/IAccessibilityEmbeddedConnection.aidl b/core/java/android/view/accessibility/IAccessibilityEmbeddedConnection.aidl index 707099ef09f4..75d81ed6f667 100644 --- a/core/java/android/view/accessibility/IAccessibilityEmbeddedConnection.aidl +++ b/core/java/android/view/accessibility/IAccessibilityEmbeddedConnection.aidl @@ -28,5 +28,5 @@ interface IAccessibilityEmbeddedConnection { void disassociateEmbeddedHierarchy(); - oneway void setScreenMatrix(in float[] matrixValues); + oneway void setWindowMatrix(in float[] matrixValues); } diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl index deb0d2a3fd3d..472a36361585 100644 --- a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl +++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl @@ -34,23 +34,24 @@ oneway interface IAccessibilityInteractionConnection { void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId, in Region bounds, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, in MagnificationSpec spec, - in Bundle arguments); + in float[] matrixValues, in Bundle arguments); void findAccessibilityNodeInfosByViewId(long accessibilityNodeId, String viewId, in Region bounds, int interactionId, IAccessibilityInteractionConnectionCallback callback, - int flags, int interrogatingPid, long interrogatingTid, in MagnificationSpec spec); + int flags, int interrogatingPid, long interrogatingTid, in MagnificationSpec spec, + in float[] matrix); void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text, in Region bounds, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, - int interrogatingPid, long interrogatingTid, in MagnificationSpec spec); + int interrogatingPid, long interrogatingTid, in MagnificationSpec spec, in float[] matrixValues); void findFocus(long accessibilityNodeId, int focusType, in Region bounds, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, - long interrogatingTid, in MagnificationSpec spec); + long interrogatingTid, in MagnificationSpec spec, in float[] matrixValues); void focusSearch(long accessibilityNodeId, int direction, in Region bounds, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, - long interrogatingTid, in MagnificationSpec spec); + long interrogatingTid, in MagnificationSpec spec, in float[] matrixValues); void performAccessibilityAction(long accessibilityNodeId, int action, in Bundle arguments, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, diff --git a/core/java/android/window/TaskFragmentInfo.java b/core/java/android/window/TaskFragmentInfo.java index a118f9a8188f..f72164e1f53f 100644 --- a/core/java/android/window/TaskFragmentInfo.java +++ b/core/java/android/window/TaskFragmentInfo.java @@ -52,9 +52,6 @@ public final class TaskFragmentInfo implements Parcelable { @NonNull private final Configuration mConfiguration = new Configuration(); - /** Whether the TaskFragment contains any child Window Container. */ - private final boolean mIsEmpty; - /** The number of the running activities in the TaskFragment. */ private final int mRunningActivityCount; @@ -77,21 +74,27 @@ public final class TaskFragmentInfo implements Parcelable { */ private final boolean mIsTaskClearedForReuse; + /** + * Whether the last running activity in the TaskFragment was reparented to a different Task + * because it is entering PiP. + */ + private final boolean mIsTaskFragmentClearedForPip; + /** @hide */ public TaskFragmentInfo( @NonNull IBinder fragmentToken, @NonNull WindowContainerToken token, - @NonNull Configuration configuration, boolean isEmpty, int runningActivityCount, + @NonNull Configuration configuration, int runningActivityCount, boolean isVisible, @NonNull List<IBinder> activities, @NonNull Point positionInParent, - boolean isTaskClearedForReuse) { + boolean isTaskClearedForReuse, boolean isTaskFragmentClearedForPip) { mFragmentToken = requireNonNull(fragmentToken); mToken = requireNonNull(token); mConfiguration.setTo(configuration); - mIsEmpty = isEmpty; mRunningActivityCount = runningActivityCount; mIsVisible = isVisible; mActivities.addAll(activities); mPositionInParent = requireNonNull(positionInParent); mIsTaskClearedForReuse = isTaskClearedForReuse; + mIsTaskFragmentClearedForPip = isTaskFragmentClearedForPip; } @NonNull @@ -110,7 +113,7 @@ public final class TaskFragmentInfo implements Parcelable { } public boolean isEmpty() { - return mIsEmpty; + return mRunningActivityCount == 0; } public boolean hasRunningActivity() { @@ -140,6 +143,11 @@ public final class TaskFragmentInfo implements Parcelable { return mIsTaskClearedForReuse; } + /** @hide */ + public boolean isTaskFragmentClearedForPip() { + return mIsTaskFragmentClearedForPip; + } + @WindowingMode public int getWindowingMode() { return mConfiguration.windowConfiguration.getWindowingMode(); @@ -156,25 +164,25 @@ public final class TaskFragmentInfo implements Parcelable { return mFragmentToken.equals(that.mFragmentToken) && mToken.equals(that.mToken) - && mIsEmpty == that.mIsEmpty && mRunningActivityCount == that.mRunningActivityCount && mIsVisible == that.mIsVisible && getWindowingMode() == that.getWindowingMode() && mActivities.equals(that.mActivities) && mPositionInParent.equals(that.mPositionInParent) - && mIsTaskClearedForReuse == that.mIsTaskClearedForReuse; + && mIsTaskClearedForReuse == that.mIsTaskClearedForReuse + && mIsTaskFragmentClearedForPip == that.mIsTaskFragmentClearedForPip; } private TaskFragmentInfo(Parcel in) { mFragmentToken = in.readStrongBinder(); mToken = in.readTypedObject(WindowContainerToken.CREATOR); mConfiguration.readFromParcel(in); - mIsEmpty = in.readBoolean(); mRunningActivityCount = in.readInt(); mIsVisible = in.readBoolean(); in.readBinderList(mActivities); mPositionInParent = requireNonNull(in.readTypedObject(Point.CREATOR)); mIsTaskClearedForReuse = in.readBoolean(); + mIsTaskFragmentClearedForPip = in.readBoolean(); } /** @hide */ @@ -183,12 +191,12 @@ public final class TaskFragmentInfo implements Parcelable { dest.writeStrongBinder(mFragmentToken); dest.writeTypedObject(mToken, flags); mConfiguration.writeToParcel(dest, flags); - dest.writeBoolean(mIsEmpty); dest.writeInt(mRunningActivityCount); dest.writeBoolean(mIsVisible); dest.writeBinderList(mActivities); dest.writeTypedObject(mPositionInParent, flags); dest.writeBoolean(mIsTaskClearedForReuse); + dest.writeBoolean(mIsTaskFragmentClearedForPip); } @NonNull @@ -210,12 +218,12 @@ public final class TaskFragmentInfo implements Parcelable { return "TaskFragmentInfo{" + " fragmentToken=" + mFragmentToken + " token=" + mToken - + " isEmpty=" + mIsEmpty + " runningActivityCount=" + mRunningActivityCount + " isVisible=" + mIsVisible + " activities=" + mActivities + " positionInParent=" + mPositionInParent + " isTaskClearedForReuse=" + mIsTaskClearedForReuse + + " isTaskFragmentClearedForPip" + mIsTaskFragmentClearedForPip + "}"; } diff --git a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java index 68b8968fe399..18fde4794969 100644 --- a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java +++ b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java @@ -217,22 +217,18 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable { break; case TYPE_SYSTEM_LANGUAGE_FOR_APP_LANGUAGE_PICKER: if (!(convertView instanceof ViewGroup)) { + TextView title; if (((LocaleStore.LocaleInfo)getItem(position)).isAppCurrentLocale()) { convertView = mInflater.inflate( - R.layout.app_language_picker_system_current, parent, false); + R.layout.app_language_picker_current_locale_item, parent, false); + title = convertView.findViewById(R.id.language_picker_item); } else { convertView = mInflater.inflate( - R.layout.app_language_picker_system_default, parent, false); + R.layout.language_picker_item, parent, false); + title = convertView.findViewById(R.id.locale); } + title.setText(R.string.system_locale_title); } - - Locale defaultLocale = Locale.getDefault(); - TextView title = convertView.findViewById(R.id.locale); - title.setText(R.string.system_locale_title); - title.setTextLocale(defaultLocale); - TextView subtitle = convertView.findViewById(R.id.system_locale_subtitle); - subtitle.setText(defaultLocale.getDisplayName()); - subtitle.setTextLocale(defaultLocale); break; case TYPE_CURRENT_LOCALE: if (!(convertView instanceof ViewGroup)) { diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index 2f7c0152207f..1db4bbba9ad5 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -22,7 +22,7 @@ import static android.os.Build.VERSION_CODES.M; import static android.os.Build.VERSION_CODES.N; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_STATUS_BAR; -import static android.view.InsetsState.clearCompatInsets; +import static android.view.InsetsState.clearsCompatInsets; import static android.view.View.MeasureSpec.AT_MOST; import static android.view.View.MeasureSpec.EXACTLY; import static android.view.View.MeasureSpec.getMode; @@ -1120,11 +1120,11 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind : controller.getSystemBarsAppearance(); if (insets != null) { - final boolean clearCompatInsets = clearCompatInsets(attrs.type, attrs.flags, + final boolean clearsCompatInsets = clearsCompatInsets(attrs.type, attrs.flags, getResources().getConfiguration().windowConfiguration.getWindowingMode()); final Insets stableBarInsets = insets.getInsetsIgnoringVisibility( WindowInsets.Type.systemBars()); - final Insets systemInsets = clearCompatInsets + final Insets systemInsets = clearsCompatInsets ? Insets.NONE : Insets.min(insets.getInsets(WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout()), stableBarInsets); diff --git a/core/java/com/android/internal/security/OWNERS b/core/java/com/android/internal/security/OWNERS index 41d1d6687c42..b702df80cc7e 100644 --- a/core/java/com/android/internal/security/OWNERS +++ b/core/java/com/android/internal/security/OWNERS @@ -1,3 +1,3 @@ # Bug component: 36824 -per-file VerityUtils.java = victorhsieh@google.com +per-file VerityUtils.java = file:platform/system/security:/fsverity/OWNERS diff --git a/core/jni/OWNERS b/core/jni/OWNERS index 9e5f6ea666ba..ac6b80f6d4a3 100644 --- a/core/jni/OWNERS +++ b/core/jni/OWNERS @@ -84,7 +84,7 @@ per-file LayoutlibLoader.cpp = file:/graphics/java/android/graphics/OWNERS per-file LayoutlibLoader.cpp = diegoperez@google.com, jgaillard@google.com # Verity -per-file com_android_internal_security_Verity* = ebiggers@google.com, victorhsieh@google.com +per-file com_android_internal_security_Verity* = file:platform/system/security:/fsverity/OWNERS # VINTF per-file android_os_VintfObject* = file:platform/system/libvintf:/OWNERS diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp index bce4ed78eda1..8012e0c5ac57 100644 --- a/core/jni/android_media_AudioRecord.cpp +++ b/core/jni/android_media_AudioRecord.cpp @@ -33,12 +33,13 @@ #include <nativehelper/ScopedUtfChars.h> +#include "android_media_AudioAttributes.h" #include "android_media_AudioFormat.h" #include "android_media_AudioErrors.h" #include "android_media_DeviceCallback.h" +#include "android_media_JNIUtils.h" #include "android_media_MediaMetricsJNI.h" #include "android_media_MicrophoneInfo.h" -#include "android_media_AudioAttributes.h" // ---------------------------------------------------------------------------- @@ -57,8 +58,7 @@ struct audio_record_fields_t { // these fields provide access from C++ to the... jmethodID postNativeEventInJava; //... event post callback method jfieldID nativeRecorderInJavaObj; // provides access to the C++ AudioRecord object - jfieldID nativeCallbackCookie; // provides access to the AudioRecord callback data - jfieldID nativeDeviceCallback; // provides access to the JNIDeviceCallback instance + jfieldID jniData; // provides access to AudioRecord JNI Handle }; static audio_record_fields_t javaAudioRecordFields; static struct { @@ -66,122 +66,81 @@ static struct { jfieldID fieldNanoTime; // AudioTimestamp.nanoTime } javaAudioTimestampFields; -struct audiorecord_callback_cookie { - jclass audioRecord_class; - jobject audioRecord_ref; - bool busy; - Condition cond; -}; -static Mutex sLock; -static SortedVector <audiorecord_callback_cookie *> sAudioRecordCallBackCookies; +class AudioRecordJNIStorage : public AudioRecord::IAudioRecordCallback { + private: + // Keep in sync with frameworks/base/media/java/android/media/AudioRecord.java NATIVE_EVENT_*. + enum class EventType { + EVENT_MORE_DATA = 0, // Request to read available data from buffer. + // If this event is delivered but the callback handler + // does not want to read the available data, the handler must + // explicitly ignore the event by setting frameCount to zero. + EVENT_OVERRUN = 1, // Buffer overrun occurred. + EVENT_MARKER = 2, // Record head is at the specified marker position + // (See setMarkerPosition()). + EVENT_NEW_POS = 3, // Record head is at a new position + // (See setPositionUpdatePeriod()). + EVENT_NEW_IAUDIORECORD = 4, // IAudioRecord was re-created, either due to re-routing and + // voluntary invalidation by mediaserver, or mediaserver crash. + }; -// ---------------------------------------------------------------------------- + public: + AudioRecordJNIStorage(jclass audioRecordClass, jobject audioRecordWeakRef) + : mAudioRecordClass(audioRecordClass), mAudioRecordWeakRef(audioRecordWeakRef) {} + AudioRecordJNIStorage(const AudioRecordJNIStorage &) = delete; + AudioRecordJNIStorage& operator=(const AudioRecordJNIStorage &) = delete; -#define AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT (-16) -#define AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK (-17) -#define AUDIORECORD_ERROR_SETUP_INVALIDFORMAT (-18) -#define AUDIORECORD_ERROR_SETUP_INVALIDSOURCE (-19) -#define AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED (-20) - -// ---------------------------------------------------------------------------- -static void recorderCallback(int event, void* user, void *info) { + void onMarker(uint32_t) override { + postEvent(EventType::EVENT_MARKER); + } - audiorecord_callback_cookie *callbackInfo = (audiorecord_callback_cookie *)user; - { - Mutex::Autolock l(sLock); - if (sAudioRecordCallBackCookies.indexOf(callbackInfo) < 0) { - return; - } - callbackInfo->busy = true; - } - - switch (event) { - case AudioRecord::EVENT_MARKER: { - JNIEnv *env = AndroidRuntime::getJNIEnv(); - if (user != NULL && env != NULL) { - env->CallStaticVoidMethod( - callbackInfo->audioRecord_class, - javaAudioRecordFields.postNativeEventInJava, - callbackInfo->audioRecord_ref, event, 0,0, NULL); - if (env->ExceptionCheck()) { - env->ExceptionDescribe(); - env->ExceptionClear(); - } - } - } break; - - case AudioRecord::EVENT_NEW_POS: { - JNIEnv *env = AndroidRuntime::getJNIEnv(); - if (user != NULL && env != NULL) { - env->CallStaticVoidMethod( - callbackInfo->audioRecord_class, - javaAudioRecordFields.postNativeEventInJava, - callbackInfo->audioRecord_ref, event, 0,0, NULL); - if (env->ExceptionCheck()) { - env->ExceptionDescribe(); - env->ExceptionClear(); - } - } - } break; + void onNewPos(uint32_t) override { + postEvent(EventType::EVENT_NEW_POS); } - { - Mutex::Autolock l(sLock); - callbackInfo->busy = false; - callbackInfo->cond.broadcast(); + void setDeviceCallback(const sp<JNIDeviceCallback>& callback) { + mDeviceCallback = callback; } -} -static sp<JNIDeviceCallback> getJniDeviceCallback(JNIEnv* env, jobject thiz) -{ - Mutex::Autolock l(sLock); - JNIDeviceCallback* const cb = - (JNIDeviceCallback*)env->GetLongField(thiz, - javaAudioRecordFields.nativeDeviceCallback); - return sp<JNIDeviceCallback>(cb); -} + sp<JNIDeviceCallback> getDeviceCallback() const { return mDeviceCallback; } -static sp<JNIDeviceCallback> setJniDeviceCallback(JNIEnv* env, - jobject thiz, - const sp<JNIDeviceCallback>& cb) -{ - Mutex::Autolock l(sLock); - sp<JNIDeviceCallback> old = - (JNIDeviceCallback*)env->GetLongField(thiz, - javaAudioRecordFields.nativeDeviceCallback); - if (cb.get()) { - cb->incStrong((void*)setJniDeviceCallback); - } - if (old != 0) { - old->decStrong((void*)setJniDeviceCallback); + jobject getAudioTrackWeakRef() const & { return mAudioRecordWeakRef.get(); } + + // If we attempt to get a jobject from a rvalue, it will soon go out of + // scope, and the reference count can drop to zero, which is unsafe. + jobject getAudioTrackWeakRef() const && = delete; + + private: + void postEvent(EventType event, int arg = 0) const { + JNIEnv *env = getJNIEnvOrDie(); + env->CallStaticVoidMethod( + static_cast<jclass>(mAudioRecordClass.get()), + javaAudioRecordFields.postNativeEventInJava, + mAudioRecordWeakRef.get(), static_cast<int>(event), arg, 0, nullptr); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + } } - env->SetLongField(thiz, javaAudioRecordFields.nativeDeviceCallback, (jlong)cb.get()); - return old; -} + + // Mutation of this object is protected using Java concurrency constructs + sp<JNIDeviceCallback> mDeviceCallback; + const GlobalRef mAudioRecordClass; + const GlobalRef mAudioRecordWeakRef; +}; // ---------------------------------------------------------------------------- -static sp<AudioRecord> getAudioRecord(JNIEnv* env, jobject thiz) -{ - Mutex::Autolock l(sLock); - AudioRecord* const ar = - (AudioRecord*)env->GetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj); - return sp<AudioRecord>(ar); -} -static sp<AudioRecord> setAudioRecord(JNIEnv* env, jobject thiz, const sp<AudioRecord>& ar) +#define AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT (-16) +#define AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK (-17) +#define AUDIORECORD_ERROR_SETUP_INVALIDFORMAT (-18) +#define AUDIORECORD_ERROR_SETUP_INVALIDSOURCE (-19) +#define AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED (-20) + +// ---------------------------------------------------------------------------- +static sp<AudioRecord> getAudioRecord(JNIEnv* env, jobject thiz) { - Mutex::Autolock l(sLock); - sp<AudioRecord> old = - (AudioRecord*)env->GetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj); - if (ar.get()) { - ar->incStrong((void*)setAudioRecord); - } - if (old != 0) { - old->decStrong((void*)setAudioRecord); - } - env->SetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, (jlong)ar.get()); - return old; + return getFieldSp<AudioRecord>(env, thiz, javaAudioRecordFields.nativeRecorderInJavaObj); } // ---------------------------------------------------------------------------- @@ -211,9 +170,8 @@ static jint android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject w env->ReleasePrimitiveArrayCritical(jSession, nSession, 0); nSession = NULL; - sp<AudioRecord> lpRecorder = 0; - audiorecord_callback_cookie *lpCallbackData = NULL; - + sp<AudioRecord> lpRecorder; + sp<AudioRecordJNIStorage> callbackData; jclass clazz = env->GetObjectClass(thiz); if (clazz == NULL) { ALOGE("Can't find %s when setting up callback.", kClassPathName); @@ -287,18 +245,14 @@ static jint android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject w } // create the callback information: // this data will be passed with every AudioRecord callback - lpCallbackData = new audiorecord_callback_cookie; - lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz); // we use a weak reference so the AudioRecord object can be garbage collected. - lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this); - lpCallbackData->busy = false; + callbackData = sp<AudioRecordJNIStorage>::make(clazz, weak_this); const status_t status = lpRecorder->set(paa->source, sampleRateInHertz, format, // word length, PCM localChanMask, frameCount, - recorderCallback, // callback_t - lpCallbackData, // void* user + callbackData, // callback 0, // notificationFrames, true, // threadCanCallJava sessionId, AudioRecord::TRANSFER_DEFAULT, flags, -1, @@ -330,11 +284,8 @@ static jint android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject w // create the callback information: // this data will be passed with every AudioRecord callback - lpCallbackData = new audiorecord_callback_cookie; - lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz); - // we use a weak reference so the AudioRecord object can be garbage collected. - lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this); - lpCallbackData->busy = false; + // This next line makes little sense + // callbackData = sp<AudioRecordJNIStorage>::make(clazz, weak_this); } nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL); @@ -352,26 +303,20 @@ static jint android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject w env->SetIntArrayRegion(jSampleRate, 0, 1, elements); } - { // scope for the lock - Mutex::Autolock l(sLock); - sAudioRecordCallBackCookies.add(lpCallbackData); - } // save our newly created C++ AudioRecord in the "nativeRecorderInJavaObj" field // of the Java object - setAudioRecord(env, thiz, lpRecorder); + setFieldSp(env, thiz, lpRecorder, javaAudioRecordFields.nativeRecorderInJavaObj); - // save our newly created callback information in the "nativeCallbackCookie" field - // of the Java object (in mNativeCallbackCookie) so we can free the memory in finalize() - env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, (jlong)lpCallbackData); + // save our newly created callback information in the "jniData" field + // of the Java object (in mNativeJNIDataHandle) so we can free the memory in finalize() + setFieldSp(env, thiz, callbackData, javaAudioRecordFields.jniData); return (jint) AUDIO_JAVA_SUCCESS; // failure: native_init_failure: - env->DeleteGlobalRef(lpCallbackData->audioRecord_class); - env->DeleteGlobalRef(lpCallbackData->audioRecord_ref); - delete lpCallbackData; - env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0); + setFieldSp(env, thiz, sp<AudioRecord>{}, javaAudioRecordFields.nativeRecorderInJavaObj); + setFieldSp(env, thiz, sp<AudioRecordJNIStorage>{}, javaAudioRecordFields.jniData); // lpRecorder goes out of scope, so reference count drops to zero return (jint) AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED; @@ -411,36 +356,9 @@ android_media_AudioRecord_stop(JNIEnv *env, jobject thiz) #define CALLBACK_COND_WAIT_TIMEOUT_MS 1000 static void android_media_AudioRecord_release(JNIEnv *env, jobject thiz) { - sp<AudioRecord> lpRecorder = setAudioRecord(env, thiz, 0); - if (lpRecorder == NULL) { - return; - } - ALOGV("About to delete lpRecorder: %p", lpRecorder.get()); - lpRecorder->stop(); - audiorecord_callback_cookie *lpCookie = (audiorecord_callback_cookie *)env->GetLongField( - thiz, javaAudioRecordFields.nativeCallbackCookie); - - // reset the native resources in the Java object so any attempt to access - // them after a call to release fails. - env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0); - - // delete the callback information - if (lpCookie) { - Mutex::Autolock l(sLock); - ALOGV("deleting lpCookie: %p", lpCookie); - while (lpCookie->busy) { - if (lpCookie->cond.waitRelative(sLock, - milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) != - NO_ERROR) { - break; - } - } - sAudioRecordCallBackCookies.remove(lpCookie); - env->DeleteGlobalRef(lpCookie->audioRecord_class); - env->DeleteGlobalRef(lpCookie->audioRecord_ref); - delete lpCookie; - } + setFieldSp(env, thiz, sp<AudioRecord>{}, javaAudioRecordFields.nativeRecorderInJavaObj); + setFieldSp(env, thiz, sp<AudioRecordJNIStorage>{}, javaAudioRecordFields.jniData); } @@ -685,43 +603,40 @@ static jint android_media_AudioRecord_getRoutedDeviceId( return (jint)lpRecorder->getRoutedDeviceId(); } +// Enable and Disable Callback methods are synchronized on the Java side static void android_media_AudioRecord_enableDeviceCallback( JNIEnv *env, jobject thiz) { sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); - if (lpRecorder == 0) { - return; - } - sp<JNIDeviceCallback> cb = getJniDeviceCallback(env, thiz); - if (cb != 0) { + if (lpRecorder == nullptr) { return; } - audiorecord_callback_cookie *cookie = - (audiorecord_callback_cookie *)env->GetLongField(thiz, - javaAudioRecordFields.nativeCallbackCookie); - if (cookie == NULL) { + const auto pJniStorage = + getFieldSp<AudioRecordJNIStorage>(env, thiz, javaAudioRecordFields.jniData); + if (pJniStorage == nullptr || pJniStorage->getDeviceCallback() != nullptr) { return; } - cb = new JNIDeviceCallback(env, thiz, cookie->audioRecord_ref, - javaAudioRecordFields.postNativeEventInJava); - status_t status = lpRecorder->addAudioDeviceCallback(cb); - if (status == NO_ERROR) { - setJniDeviceCallback(env, thiz, cb); - } + pJniStorage->setDeviceCallback( + sp<JNIDeviceCallback>::make(env, thiz, pJniStorage->getAudioTrackWeakRef(), + javaAudioRecordFields.postNativeEventInJava)); + lpRecorder->addAudioDeviceCallback(pJniStorage->getDeviceCallback()); } static void android_media_AudioRecord_disableDeviceCallback( JNIEnv *env, jobject thiz) { - sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); - if (lpRecorder == 0) { + if (lpRecorder == nullptr) { return; } - sp<JNIDeviceCallback> cb = setJniDeviceCallback(env, thiz, 0); - if (cb != 0) { - lpRecorder->removeAudioDeviceCallback(cb); + const auto pJniStorage = + getFieldSp<AudioRecordJNIStorage>(env, thiz, javaAudioRecordFields.jniData); + + if (pJniStorage == nullptr || pJniStorage->getDeviceCallback() == nullptr) { + return; } + lpRecorder->removeAudioDeviceCallback(pJniStorage->getDeviceCallback()); + pJniStorage->setDeviceCallback(nullptr); } // ---------------------------------------------------------------------------- @@ -962,17 +877,15 @@ static const JNINativeMethod gMethods[] = { // field names found in android/media/AudioRecord.java #define JAVA_POSTEVENT_CALLBACK_NAME "postEventFromNative" -#define JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME "mNativeRecorderInJavaObj" -#define JAVA_NATIVECALLBACKINFO_FIELD_NAME "mNativeCallbackCookie" -#define JAVA_NATIVEDEVICECALLBACK_FIELD_NAME "mNativeDeviceCallback" +#define JAVA_NATIVEAUDIORECORDERHANDLE_FIELD_NAME "mNativeAudioRecordHandle" +#define JAVA_NATIVEJNIDATAHANDLE_FIELD_NAME "mNativeJNIDataHandle" // ---------------------------------------------------------------------------- int register_android_media_AudioRecord(JNIEnv *env) { javaAudioRecordFields.postNativeEventInJava = NULL; javaAudioRecordFields.nativeRecorderInJavaObj = NULL; - javaAudioRecordFields.nativeCallbackCookie = NULL; - javaAudioRecordFields.nativeDeviceCallback = NULL; + javaAudioRecordFields.jniData = NULL; // Get the AudioRecord class @@ -983,15 +896,12 @@ int register_android_media_AudioRecord(JNIEnv *env) "(Ljava/lang/Object;IIILjava/lang/Object;)V"); // Get the variables - // mNativeRecorderInJavaObj + // mNativeAudioRecordHandle javaAudioRecordFields.nativeRecorderInJavaObj = GetFieldIDOrDie(env, - audioRecordClass, JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME, "J"); - // mNativeCallbackCookie - javaAudioRecordFields.nativeCallbackCookie = GetFieldIDOrDie(env, - audioRecordClass, JAVA_NATIVECALLBACKINFO_FIELD_NAME, "J"); - - javaAudioRecordFields.nativeDeviceCallback = GetFieldIDOrDie(env, - audioRecordClass, JAVA_NATIVEDEVICECALLBACK_FIELD_NAME, "J"); + audioRecordClass, JAVA_NATIVEAUDIORECORDERHANDLE_FIELD_NAME, "J"); + // mNativeJNIDataHandle + javaAudioRecordFields.jniData = GetFieldIDOrDie(env, + audioRecordClass, JAVA_NATIVEJNIDATAHANDLE_FIELD_NAME, "J"); // Get the RecordTimestamp class and fields jclass audioTimestampClass = FindClassOrDie(env, "android/media/AudioTimestamp"); diff --git a/core/jni/android_media_JNIUtils.h b/core/jni/android_media_JNIUtils.h index 9da5fa37a8f3..a413d096421c 100644 --- a/core/jni/android_media_JNIUtils.h +++ b/core/jni/android_media_JNIUtils.h @@ -64,5 +64,39 @@ inline JNIEnv* getJNIEnvOrDie() { return env; } +class GlobalRef { + public: + GlobalRef(jobject object) : GlobalRef(object, AndroidRuntime::getJNIEnv()) {} + + GlobalRef(jobject object, JNIEnv* env) { + LOG_ALWAYS_FATAL_IF(env == nullptr, "Invalid JNIEnv when attempting to create a GlobalRef"); + mGlobalRef = env->NewGlobalRef(object); + LOG_ALWAYS_FATAL_IF(env->IsSameObject(object, nullptr) == JNI_TRUE, + "Creating GlobalRef from null object"); + } + + GlobalRef(const GlobalRef& other) : GlobalRef(other.mGlobalRef) {} + + GlobalRef(GlobalRef&& other) : mGlobalRef(other.mGlobalRef) { other.mGlobalRef = nullptr; } + + // Logically const + GlobalRef& operator=(const GlobalRef& other) = delete; + + GlobalRef& operator=(GlobalRef&& other) = delete; + + ~GlobalRef() { + if (mGlobalRef == nullptr) return; // No reference to decrement + getJNIEnvOrDie()->DeleteGlobalRef(mGlobalRef); + } + + // Valid as long as this wrapper is in scope. + jobject get() const { + return mGlobalRef; + } + + private: + // Logically const. Not actually const so we can move from GlobalRef + jobject mGlobalRef; +}; } // namespace android diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 518fc09dee5a..51a708b76801 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -24,7 +24,9 @@ #include <memory> +#include <aidl/android/hardware/graphics/common/PixelFormat.h> #include <android-base/chrono_utils.h> +#include <android/graphics/properties.h> #include <android/graphics/region.h> #include <android/gui/BnScreenCaptureListener.h> #include <android/hardware/display/IDeviceProductInfoConstants.h> @@ -1888,6 +1890,11 @@ static jobject nativeGetDisplayDecorationSupport(JNIEnv* env, jclass clazz, return nullptr; } + using aidl::android::hardware::graphics::common::PixelFormat; + if (support.value().format == PixelFormat::R_8 && !hwui_uses_vulkan()) { + return nullptr; + } + jobject jDisplayDecorationSupport = env->NewObject(gDisplayDecorationSupportInfo.clazz, gDisplayDecorationSupportInfo.ctor); if (jDisplayDecorationSupport == nullptr) { diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index cd3ba1ef14f7..0e3840a39e25 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -5678,8 +5678,8 @@ restricted level. --> <array name="config_bg_current_drain_threshold_to_bg_restricted"> - <item>4.0</item> <!-- regular device --> - <item>8.0</item> <!-- low ram device --> + <item>10.0</item> <!-- regular device --> + <item>20.0</item> <!-- low ram device --> </array> <!-- The background current drain monitoring window size. --> diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml index cd3578c727f0..77500c42c6bf 100644 --- a/core/res/res/values/config_telephony.xml +++ b/core/res/res/values/config_telephony.xml @@ -17,9 +17,9 @@ <resources> <!-- This file defines Android telephony related resources --> - <!-- Whether force to enable telephony new data stack or not --> - <bool name="config_force_enable_telephony_new_data_stack">true</bool> - <java-symbol type="bool" name="config_force_enable_telephony_new_data_stack" /> + <!-- Whether force disabling telephony new data stack or not --> + <bool name="config_force_disable_telephony_new_data_stack">false</bool> + <java-symbol type="bool" name="config_force_disable_telephony_new_data_stack" /> <!-- Configure tcp buffer sizes per network type in the form: network-type:rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max diff --git a/core/res/res/values/public-final.xml b/core/res/res/values/public-final.xml index 19a484293b0a..85325fec7277 100644 --- a/core/res/res/values/public-final.xml +++ b/core/res/res/values/public-final.xml @@ -3222,4 +3222,184 @@ <public type="id" name="accessibilityActionDragDrop" id="0x01020056" /> <public type="id" name="accessibilityActionDragCancel" id="0x01020057" /> + <!-- =============================================================== + Resources added in version T of the platform + + NOTE: After this version of the platform is forked, changes cannot be made to the root + branch's groups for that release. Only merge changes to the forked platform branch. + =============================================================== --> + <eat-comment/> + + <staging-public-group-final type="attr" first-id="0x01df0000"> + <public name="sharedUserMaxSdkVersion" /> + <public name="requiredSplitTypes" /> + <public name="splitTypes" /> + <public name="canDisplayOnRemoteDevices" /> + <public name="supportedTypes" /> + <public name="resetEnabledSettingsOnAppDataCleared" /> + <public name="supportsStylusHandwriting" /> + <public name="showClockAndComplications" /> + <!-- @hide @SystemApi --> + <public name="gameSessionService" /> + <public name="supportsBatteryGameMode" /> + <public name="supportsPerformanceGameMode" /> + <public name="allowGameAngleDriver" /> + <public name="allowGameDownscaling" /> + <public name="allowGameFpsOverride" /> + <public name="localeConfig" /> + <public name="showBackdrop" /> + <public name="removed_useTargetActivityForQuickAccess"/> + <public name="removed_inheritKeyStoreKeys" /> + <public name="preferKeepClear" /> + <public name="autoHandwritingEnabled" /> + <public name="fromExtendLeft" /> + <public name="fromExtendTop" /> + <public name="fromExtendRight" /> + <public name="fromExtendBottom" /> + <public name="toExtendLeft" /> + <public name="toExtendTop" /> + <public name="toExtendRight" /> + <public name="toExtendBottom" /> + <public name="tileService" /> + <public name="windowSplashScreenBehavior" /> + <public name="allowUntrustedActivityEmbedding" /> + <public name="knownActivityEmbeddingCerts" /> + <public name="intro" /> + <public name="enableOnBackInvokedCallback" /> + <public name="supportsInlineSuggestionsWithTouchExploration" /> + <public name="lineBreakStyle" /> + <public name="lineBreakWordStyle" /> + <!-- @hide --> + <public name="maxDrawableWidth" /> + <!-- @hide --> + <public name="maxDrawableHeight" /> + <public name="backdropColor" /> + </staging-public-group-final> + + <public type="attr" name="sharedUserMaxSdkVersion" id="0x0101064d" /> + <public type="attr" name="requiredSplitTypes" id="0x0101064e" /> + <public type="attr" name="splitTypes" id="0x0101064f" /> + <public type="attr" name="canDisplayOnRemoteDevices" id="0x01010650" /> + <public type="attr" name="supportedTypes" id="0x01010651" /> + <public type="attr" name="resetEnabledSettingsOnAppDataCleared" id="0x01010652" /> + <public type="attr" name="supportsStylusHandwriting" id="0x01010653" /> + <public type="attr" name="showClockAndComplications" id="0x01010654" /> + <!-- @hide @SystemApi --> + <public type="attr" name="gameSessionService" id="0x01010655" /> + <public type="attr" name="supportsBatteryGameMode" id="0x01010656" /> + <public type="attr" name="supportsPerformanceGameMode" id="0x01010657" /> + <public type="attr" name="allowGameAngleDriver" id="0x01010658" /> + <public type="attr" name="allowGameDownscaling" id="0x01010659" /> + <public type="attr" name="allowGameFpsOverride" id="0x0101065a" /> + <public type="attr" name="localeConfig" id="0x0101065b" /> + <public type="attr" name="showBackdrop" id="0x0101065c" /> + <public type="attr" name="preferKeepClear" id="0x0101065d" /> + <public type="attr" name="autoHandwritingEnabled" id="0x0101065e" /> + <public type="attr" name="fromExtendLeft" id="0x0101065f" /> + <public type="attr" name="fromExtendTop" id="0x01010660" /> + <public type="attr" name="fromExtendRight" id="0x01010661" /> + <public type="attr" name="fromExtendBottom" id="0x01010662" /> + <public type="attr" name="toExtendLeft" id="0x01010663" /> + <public type="attr" name="toExtendTop" id="0x01010664" /> + <public type="attr" name="toExtendRight" id="0x01010665" /> + <public type="attr" name="toExtendBottom" id="0x01010666" /> + <public type="attr" name="tileService" id="0x01010667" /> + <public type="attr" name="windowSplashScreenBehavior" id="0x01010668" /> + <public type="attr" name="allowUntrustedActivityEmbedding" id="0x01010669" /> + <public type="attr" name="knownActivityEmbeddingCerts" id="0x0101066a" /> + <public type="attr" name="intro" id="0x0101066b" /> + <public type="attr" name="enableOnBackInvokedCallback" id="0x0101066c" /> + <public type="attr" name="supportsInlineSuggestionsWithTouchExploration" id="0x0101066d" /> + <public type="attr" name="lineBreakStyle" id="0x0101066e" /> + <public type="attr" name="lineBreakWordStyle" id="0x0101066f" /> + <!-- @hide --> + <public type="attr" name="maxDrawableWidth" id="0x01010670" /> + <!-- @hide --> + <public type="attr" name="maxDrawableHeight" id="0x01010671" /> + <public type="attr" name="backdropColor" id="0x01010672" /> + + <staging-public-group-final type="id" first-id="0x01de0000"> + <public name="removed_accessibilityActionSwipeLeft" /> + <public name="removed_accessibilityActionSwipeRight" /> + <public name="removed_accessibilityActionSwipeUp" /> + <public name="removed_accessibilityActionSwipeDown" /> + <public name="accessibilityActionShowTextSuggestions" /> + <public name="inputExtractAction" /> + <public name="inputExtractAccessories" /> + </staging-public-group-final> + + <public type="id" name="accessibilityActionShowTextSuggestions" id="0x01020058" /> + <public type="id" name="inputExtractAction" id="0x01020059" /> + <public type="id" name="inputExtractAccessories" id="0x0102005a" /> + + <staging-public-group-final type="style" first-id="0x01dd0000"> + <public name="TextAppearance.DeviceDefault.Headline" /> + </staging-public-group-final> + + <public type="style" name="TextAppearance.DeviceDefault.Headline" id="0x010302e5" /> + + <staging-public-group-final type="string" first-id="0x01dc0000"> + <!-- @hide @SystemApi --> + <public name="config_systemSupervision" /> + <!-- @hide @SystemApi --> + <public name="config_devicePolicyManagement" /> + <!-- @hide @SystemApi --> + <public name="config_systemAppProtectionService" /> + <!-- @hide @SystemApi @TestApi --> + <public name="config_systemAutomotiveCalendarSyncManager" /> + <!-- @hide @SystemApi --> + <public name="config_defaultAutomotiveNavigation" /> + <!-- @hide @SystemApi --> + <public name="safety_protection_display_text" /> + <!-- @hide @SystemApi --> + <public name="config_systemSettingsIntelligence" /> + <!-- @hide --> + <public name="config_systemBluetoothStack" /> + </staging-public-group-final> + + <!-- @hide @SystemApi --> + <public type="string" name="config_systemSupervision" id="0x0104003c" /> + <!-- @hide @SystemApi --> + <public type="string" name="config_devicePolicyManagement" id="0x0104003d" /> + <!-- @hide @SystemApi --> + <public type="string" name="config_systemAppProtectionService" id="0x0104003e" /> + <!-- @hide @SystemApi @TestApi --> + <public type="string" name="config_systemAutomotiveCalendarSyncManager" id="0x0104003f" /> + <!-- @hide @SystemApi --> + <public type="string" name="config_defaultAutomotiveNavigation" id="0x01040040" /> + <!-- @hide @SystemApi --> + <public type="string" name="safety_protection_display_text" id="0x01040041" /> + <!-- @hide @SystemApi --> + <public type="string" name="config_systemSettingsIntelligence" id="0x01040042" /> + <!-- @hide --> + <public type="string" name="config_systemBluetoothStack" id="0x01040043" /> + + <staging-public-group-final type="array" first-id="0x01d90000"> + <!-- @hide @SystemApi --> + <public name="config_optionalIpSecAlgorithms" /> + </staging-public-group-final> + + <!-- @hide @SystemApi --> + <public type="array" name="config_optionalIpSecAlgorithms" id="0x01070006" /> + + <staging-public-group-final type="drawable" first-id="0x01d80000"> + <!-- @hide @SystemApi --> + <public name="ic_safety_protection" /> + </staging-public-group-final> + + <!-- @hide @SystemApi --> + <public type="drawable" name="ic_safety_protection" id="0x010800b5" /> + + <staging-public-group-final type="bool" first-id="0x01cf0000"> + <!-- @hide @TestApi --> + <public name="config_preventImeStartupUnlessTextEditor" /> + <!-- @hide @SystemApi --> + <public name="config_enableQrCodeScannerOnLockScreen" /> + </staging-public-group-final> + + <!-- @hide @TestApi --> + <public type="bool" name="config_preventImeStartupUnlessTextEditor" id="0x01110007" /> + <!-- @hide @SystemApi --> + <public type="bool" name="config_enableQrCodeScannerOnLockScreen" id="0x01110008" /> + </resources> diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml index 86bad7f12258..f09ffbe4466b 100644 --- a/core/res/res/values/public-staging.xml +++ b/core/res/res/values/public-staging.xml @@ -102,140 +102,65 @@ <resources> <!-- =============================================================== - Resources added in version T of the platform + Resources added in version U of the platform NOTE: After this version of the platform is forked, changes cannot be made to the root branch's groups for that release. Only merge changes to the forked platform branch. =============================================================== --> <eat-comment/> - <staging-public-group type="attr" first-id="0x01df0000"> - <public name="sharedUserMaxSdkVersion" /> - <public name="requiredSplitTypes" /> - <public name="splitTypes" /> - <public name="canDisplayOnRemoteDevices" /> - <public name="supportedTypes" /> - <public name="resetEnabledSettingsOnAppDataCleared" /> - <public name="supportsStylusHandwriting" /> - <public name="showClockAndComplications" /> - <!-- @hide @SystemApi --> - <public name="gameSessionService" /> - <public name="supportsBatteryGameMode" /> - <public name="supportsPerformanceGameMode" /> - <public name="allowGameAngleDriver" /> - <public name="allowGameDownscaling" /> - <public name="allowGameFpsOverride" /> - <public name="localeConfig" /> - <public name="showBackdrop" /> - <public name="removed_useTargetActivityForQuickAccess"/> - <public name="removed_inheritKeyStoreKeys" /> - <public name="preferKeepClear" /> - <public name="autoHandwritingEnabled" /> - <public name="fromExtendLeft" /> - <public name="fromExtendTop" /> - <public name="fromExtendRight" /> - <public name="fromExtendBottom" /> - <public name="toExtendLeft" /> - <public name="toExtendTop" /> - <public name="toExtendRight" /> - <public name="toExtendBottom" /> - <public name="tileService" /> - <public name="windowSplashScreenBehavior" /> - <public name="allowUntrustedActivityEmbedding" /> - <public name="knownActivityEmbeddingCerts" /> - <public name="intro" /> - <public name="enableOnBackInvokedCallback" /> - <public name="supportsInlineSuggestionsWithTouchExploration" /> - <public name="lineBreakStyle" /> - <public name="lineBreakWordStyle" /> - <!-- @hide --> - <public name="maxDrawableWidth" /> - <!-- @hide --> - <public name="maxDrawableHeight" /> - <public name="backdropColor" /> + <staging-public-group type="attr" first-id="0x01ce0000"> </staging-public-group> - <staging-public-group type="id" first-id="0x01de0000"> - <public name="removed_accessibilityActionSwipeLeft" /> - <public name="removed_accessibilityActionSwipeRight" /> - <public name="removed_accessibilityActionSwipeUp" /> - <public name="removed_accessibilityActionSwipeDown" /> - <public name="accessibilityActionShowTextSuggestions" /> - <public name="inputExtractAction" /> - <public name="inputExtractAccessories" /> + <staging-public-group type="id" first-id="0x01cd0000"> </staging-public-group> - <staging-public-group type="style" first-id="0x01dd0000"> - <public name="TextAppearance.DeviceDefault.Headline" /> + <staging-public-group type="style" first-id="0x01cc0000"> </staging-public-group> - <staging-public-group type="string" first-id="0x01dc0000"> - <!-- @hide @SystemApi --> - <public name="config_systemSupervision" /> - <!-- @hide @SystemApi --> - <public name="config_devicePolicyManagement" /> - <!-- @hide @SystemApi --> - <public name="config_systemAppProtectionService" /> - <!-- @hide @SystemApi @TestApi --> - <public name="config_systemAutomotiveCalendarSyncManager" /> - <!-- @hide @SystemApi --> - <public name="config_defaultAutomotiveNavigation" /> - <!-- @hide @SystemApi --> - <public name="safety_protection_display_text" /> - <!-- @hide @SystemApi --> - <public name="config_systemSettingsIntelligence" /> - <!-- @hide --> - <public name="config_systemBluetoothStack" /> + <staging-public-group type="string" first-id="0x01cb0000"> </staging-public-group> - <staging-public-group type="dimen" first-id="0x01db0000"> + <staging-public-group type="dimen" first-id="0x01ca0000"> </staging-public-group> - <staging-public-group type="color" first-id="0x01da0000"> + <staging-public-group type="color" first-id="0x01c90000"> </staging-public-group> - <staging-public-group type="array" first-id="0x01d90000"> - <!-- @hide @SystemApi --> - <public name="config_optionalIpSecAlgorithms" /> + <staging-public-group type="array" first-id="0x01c80000"> </staging-public-group> - <staging-public-group type="drawable" first-id="0x01d80000"> - <!-- @hide @SystemApi --> - <public name="ic_safety_protection" /> + <staging-public-group type="drawable" first-id="0x01c70000"> </staging-public-group> - <staging-public-group type="layout" first-id="0x01d70000"> + <staging-public-group type="layout" first-id="0x01c60000"> </staging-public-group> - <staging-public-group type="anim" first-id="0x01d60000"> + <staging-public-group type="anim" first-id="0x01c50000"> </staging-public-group> - <staging-public-group type="animator" first-id="0x01d50000"> + <staging-public-group type="animator" first-id="0x01c40000"> </staging-public-group> - <staging-public-group type="interpolator" first-id="0x01d40000"> + <staging-public-group type="interpolator" first-id="0x01c30000"> </staging-public-group> - <staging-public-group type="mipmap" first-id="0x01d30000"> + <staging-public-group type="mipmap" first-id="0x01c20000"> </staging-public-group> - <staging-public-group type="integer" first-id="0x01d20000"> + <staging-public-group type="integer" first-id="0x01c10000"> </staging-public-group> - <staging-public-group type="transition" first-id="0x01d10000"> + <staging-public-group type="transition" first-id="0x01c00000"> </staging-public-group> - <staging-public-group type="raw" first-id="0x01d00000"> + <staging-public-group type="raw" first-id="0x01bf0000"> </staging-public-group> - <staging-public-group type="bool" first-id="0x01cf0000"> - <!-- @hide @TestApi --> - <public name="config_preventImeStartupUnlessTextEditor" /> - <!-- @hide @SystemApi --> - <public name="config_enableQrCodeScannerOnLockScreen" /> + <staging-public-group type="bool" first-id="0x01be0000"> </staging-public-group> - <staging-public-group type="fraction" first-id="0x01ce0000"> + <staging-public-group type="fraction" first-id="0x01bd0000"> </staging-public-group> </resources> diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java index bf8bb76891d7..be9da11057a2 100644 --- a/core/tests/coretests/src/android/view/InsetsStateTest.java +++ b/core/tests/coretests/src/android/view/InsetsStateTest.java @@ -217,7 +217,8 @@ public class InsetsStateTest { mState.getSource(ITYPE_CAPTION_BAR).setVisible(true); Insets visibleInsets = mState.calculateVisibleInsets( - new Rect(0, 0, 100, 400), SOFT_INPUT_ADJUST_NOTHING); + new Rect(0, 0, 100, 400), TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, + SOFT_INPUT_ADJUST_NOTHING, 0 /* windowFlags */); assertEquals(Insets.of(0, 300, 0, 0), visibleInsets); } @@ -227,7 +228,8 @@ public class InsetsStateTest { mState.getSource(ITYPE_CAPTION_BAR).setVisible(true); Insets visibleInsets = mState.calculateVisibleInsets( - new Rect(0, 0, 150, 400), SOFT_INPUT_ADJUST_NOTHING); + new Rect(0, 0, 150, 400), TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, + SOFT_INPUT_ADJUST_NOTHING, 0 /* windowFlags */); assertEquals(Insets.of(0, 300, 0, 0), visibleInsets); } @@ -414,7 +416,8 @@ public class InsetsStateTest { mState.getSource(ITYPE_BOTTOM_GESTURES).setFrame(new Rect(0, 100, 100, 300)); mState.getSource(ITYPE_BOTTOM_GESTURES).setVisible(true); Insets visibleInsets = mState.calculateVisibleInsets( - new Rect(0, 0, 100, 300), SOFT_INPUT_ADJUST_PAN); + new Rect(0, 0, 100, 300), TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, + SOFT_INPUT_ADJUST_PAN, 0 /* windowFlags */); assertEquals(Insets.of(0, 100, 0, 100), visibleInsets); } @@ -429,11 +432,28 @@ public class InsetsStateTest { mState.getSource(ITYPE_BOTTOM_GESTURES).setFrame(new Rect(0, 100, 100, 300)); mState.getSource(ITYPE_BOTTOM_GESTURES).setVisible(true); Insets visibleInsets = mState.calculateVisibleInsets( - new Rect(0, 0, 100, 300), SOFT_INPUT_ADJUST_NOTHING); + new Rect(0, 0, 100, 300), TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, + SOFT_INPUT_ADJUST_NOTHING, 0 /* windowFlags */); assertEquals(Insets.of(0, 100, 0, 0), visibleInsets); } @Test + public void testCalculateVisibleInsets_layoutNoLimits() { + mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100)); + mState.getSource(ITYPE_STATUS_BAR).setVisible(true); + mState.getSource(ITYPE_IME).setFrame(new Rect(0, 200, 100, 300)); + mState.getSource(ITYPE_IME).setVisible(true); + + // Make sure bottom gestures are ignored + mState.getSource(ITYPE_BOTTOM_GESTURES).setFrame(new Rect(0, 100, 100, 300)); + mState.getSource(ITYPE_BOTTOM_GESTURES).setVisible(true); + Insets visibleInsets = mState.calculateVisibleInsets( + new Rect(0, 0, 100, 300), TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, + SOFT_INPUT_ADJUST_PAN, FLAG_LAYOUT_NO_LIMITS); + assertEquals(Insets.NONE, visibleInsets); + } + + @Test public void testCalculateUncontrollableInsets() { mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 200, 100)); mState.getSource(ITYPE_STATUS_BAR).setVisible(true); diff --git a/core/tests/coretests/src/android/view/WindowInfoTest.java b/core/tests/coretests/src/android/view/WindowInfoTest.java index 0a99b08f58ff..afc2c002db08 100644 --- a/core/tests/coretests/src/android/view/WindowInfoTest.java +++ b/core/tests/coretests/src/android/view/WindowInfoTest.java @@ -26,6 +26,7 @@ import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import android.app.ActivityTaskManager; +import android.graphics.Matrix; import android.os.IBinder; import android.os.Parcel; import android.platform.test.annotations.Presubmit; @@ -38,6 +39,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.util.ArrayList; +import java.util.Arrays; /** * Class for testing {@link WindowInfo}. @@ -94,6 +96,8 @@ public class WindowInfoTest { assertFalse(w.inPictureInPicture); assertFalse(w.hasFlagWatchOutsideTouch); assertTrue(w.regionInScreen.isEmpty()); + assertEquals(w.mTransformMatrix.length, 9); + assertTrue(w.mMagnificationSpec.isNop()); } @SmallTest @@ -117,6 +121,8 @@ public class WindowInfoTest { equality &= w1.parentToken == w2.parentToken; equality &= w1.activityToken == w2.activityToken; equality &= w1.regionInScreen.equals(w2.regionInScreen); + equality &= w1.mMagnificationSpec.equals(w2.mMagnificationSpec); + equality &= Arrays.equals(w1.mTransformMatrix, w2.mTransformMatrix); return equality; } @@ -136,5 +142,9 @@ public class WindowInfoTest { windowInfo.inPictureInPicture = true; windowInfo.hasFlagWatchOutsideTouch = true; windowInfo.regionInScreen.set(0, 0, 1080, 1080); + windowInfo.mMagnificationSpec.scale = 2.0f; + windowInfo.mMagnificationSpec.offsetX = 100f; + windowInfo.mMagnificationSpec.offsetY = 200f; + Matrix.IDENTITY_MATRIX.getValues(windowInfo.mTransformMatrix); } } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java index 3ec8843838fe..33a41ecd49fa 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -155,13 +155,18 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen // Check if there are no running activities - consider the container empty if there are no // non-finishing activities left. if (!taskFragmentInfo.hasRunningActivity()) { - // TODO(b/225371112): Don't finish dependent if the last activity is moved to the PIP - // Task. - // Do not finish the dependents if this TaskFragment was cleared due to launching - // activity in the Task. - final boolean shouldFinishDependent = - !taskFragmentInfo.isTaskClearedForReuse(); - mPresenter.cleanupContainer(container, shouldFinishDependent); + if (taskFragmentInfo.isTaskFragmentClearedForPip()) { + // Do not finish the dependents if the last activity is reparented to PiP. + // Instead, the original split should be cleanup, and the dependent may be expanded + // to fullscreen. + cleanupForEnterPip(container); + mPresenter.cleanupContainer(container, false /* shouldFinishDependent */); + } else { + // Do not finish the dependents if this TaskFragment was cleared due to launching + // activity in the Task. + final boolean shouldFinishDependent = !taskFragmentInfo.isTaskClearedForReuse(); + mPresenter.cleanupContainer(container, shouldFinishDependent); + } } else if (wasInPip && isInPip) { // No update until exit PIP. return; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java index f60b6599e350..322c0bf80816 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java @@ -913,8 +913,10 @@ public class BubbleStackView extends FrameLayout afterExpandedViewAnimation(); showManageMenu(mShowingManage); } /* after */); + PointF p = mPositioner.getExpandedBubbleXY(getBubbleIndex(mExpandedBubble), + getState()); final float translationY = mPositioner.getExpandedViewY(mExpandedBubble, - getBubbleIndex(mExpandedBubble)); + mPositioner.showBubblesVertically() ? p.y : p.x); mExpandedViewContainer.setTranslationX(0f); mExpandedViewContainer.setTranslationY(translationY); mExpandedViewContainer.setAlpha(1f); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java index 6f4e22fa8a04..79d795ee613f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java @@ -32,6 +32,7 @@ import static android.view.Surface.ROTATION_90; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.ContentResolver; import android.content.Context; import android.content.res.Resources; @@ -336,6 +337,12 @@ public class DisplayLayout { return navigationBarPosition(res, mWidth, mHeight, mRotation); } + /** @return {@link DisplayCutout} instance. */ + @Nullable + public DisplayCutout getDisplayCutout() { + return mCutout; + } + /** * Calculates the stable insets if we already have the non-decor insets. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java index e28c58c2e7cd..9754a0369b67 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java @@ -37,7 +37,6 @@ import android.graphics.Region; import android.graphics.Region.Op; import android.hardware.display.DisplayManager; import android.os.Bundle; -import android.os.RemoteException; import android.util.AttributeSet; import android.util.Slog; import android.view.Choreographer; @@ -241,22 +240,6 @@ public class DividerView extends FrameLayout implements OnTouchListener, } }; - private Runnable mUpdateEmbeddedMatrix = () -> { - if (getViewRootImpl() == null) { - return; - } - if (isHorizontalDivision()) { - mTmpMatrix.setTranslate(0, mDividerPositionY - mDividerInsets); - } else { - mTmpMatrix.setTranslate(mDividerPositionX - mDividerInsets, 0); - } - mTmpMatrix.getValues(mTmpValues); - try { - getViewRootImpl().getAccessibilityEmbeddedConnection().setScreenMatrix(mTmpValues); - } catch (RemoteException e) { - } - }; - public DividerView(Context context) { this(context, null); } @@ -1045,10 +1028,6 @@ public class DividerView extends FrameLayout implements OnTouchListener, t.setPosition(dividerCtrl, mDividerPositionX - mDividerInsets, 0); } } - if (getViewRootImpl() != null) { - getHandler().removeCallbacks(mUpdateEmbeddedMatrix); - getHandler().post(mUpdateEmbeddedMatrix); - } } void setResizeDimLayer(Transaction t, boolean primary, float alpha) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java index 337900088c92..95bb65c5873e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java @@ -250,7 +250,6 @@ public class PipAnimationController { protected T mCurrentValue; protected T mStartValue; private T mEndValue; - private float mStartingAngle; private PipAnimationCallback mPipAnimationCallback; private PipTransactionHandler mPipTransactionHandler; private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory @@ -260,8 +259,8 @@ public class PipAnimationController { protected SurfaceControl mContentOverlay; private PipTransitionAnimator(TaskInfo taskInfo, SurfaceControl leash, - @AnimationType int animationType, Rect destinationBounds, T baseValue, T startValue, - T endValue, float startingAngle) { + @AnimationType int animationType, + Rect destinationBounds, T baseValue, T startValue, T endValue) { mTaskInfo = taskInfo; mLeash = leash; mAnimationType = animationType; @@ -269,7 +268,6 @@ public class PipAnimationController { mBaseValue = baseValue; mStartValue = startValue; mEndValue = endValue; - mStartingAngle = startingAngle; addListener(this); addUpdateListener(this); mSurfaceControlTransactionFactory = @@ -480,7 +478,7 @@ public class PipAnimationController { static PipTransitionAnimator<Float> ofAlpha(TaskInfo taskInfo, SurfaceControl leash, Rect destinationBounds, float startValue, float endValue) { return new PipTransitionAnimator<Float>(taskInfo, leash, ANIM_TYPE_ALPHA, - destinationBounds, startValue, startValue, endValue, 0) { + destinationBounds, startValue, startValue, endValue) { @Override void applySurfaceControlTransaction(SurfaceControl leash, SurfaceControl.Transaction tx, float fraction) { @@ -520,7 +518,7 @@ public class PipAnimationController { @PipAnimationController.TransitionDirection int direction, float startingAngle, @Surface.Rotation int rotationDelta) { final boolean isOutPipDirection = isOutPipDirection(direction); - + final boolean isInPipDirection = isInPipDirection(direction); // Just for simplicity we'll interpolate between the source rect hint insets and empty // insets to calculate the window crop final Rect initialSourceValue; @@ -559,8 +557,7 @@ public class PipAnimationController { // construct new Rect instances in case they are recycled return new PipTransitionAnimator<Rect>(taskInfo, leash, ANIM_TYPE_BOUNDS, - endValue, new Rect(baseValue), new Rect(startValue), new Rect(endValue), - startingAngle) { + endValue, new Rect(baseValue), new Rect(startValue), new Rect(endValue)) { private final RectEvaluator mRectEvaluator = new RectEvaluator(new Rect()); private final RectEvaluator mInsetsEvaluator = new RectEvaluator(new Rect()); @@ -595,7 +592,8 @@ public class PipAnimationController { } else { final Rect insets = computeInsets(fraction); getSurfaceTransactionHelper().scaleAndCrop(tx, leash, - initialSourceValue, bounds, insets); + sourceHintRect, initialSourceValue, bounds, insets, + isInPipDirection); if (shouldApplyCornerRadius()) { final Rect sourceBounds = new Rect(initialContainerRect); sourceBounds.inset(insets); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java index 00f62d435b68..b349010be1fe 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java @@ -103,21 +103,31 @@ public class PipSurfaceTransactionHelper { * @return same {@link PipSurfaceTransactionHelper} instance for method chaining */ public PipSurfaceTransactionHelper scaleAndCrop(SurfaceControl.Transaction tx, - SurfaceControl leash, - Rect sourceBounds, Rect destinationBounds, Rect insets) { + SurfaceControl leash, Rect sourceRectHint, + Rect sourceBounds, Rect destinationBounds, Rect insets, + boolean isInPipDirection) { mTmpSourceRectF.set(sourceBounds); mTmpDestinationRect.set(sourceBounds); mTmpDestinationRect.inset(insets); // Scale by the shortest edge and offset such that the top/left of the scaled inset source // rect aligns with the top/left of the destination bounds - final float scale = sourceBounds.width() <= sourceBounds.height() - ? (float) destinationBounds.width() / sourceBounds.width() - : (float) destinationBounds.height() / sourceBounds.height(); + final float scale; + if (isInPipDirection + && sourceRectHint != null && sourceRectHint.width() < sourceBounds.width()) { + // scale by sourceRectHint if it's not edge-to-edge, for entering PiP transition only. + scale = sourceBounds.width() <= sourceBounds.height() + ? (float) destinationBounds.width() / sourceRectHint.width() + : (float) destinationBounds.height() / sourceRectHint.height(); + } else { + scale = sourceBounds.width() <= sourceBounds.height() + ? (float) destinationBounds.width() / sourceBounds.width() + : (float) destinationBounds.height() / sourceBounds.height(); + } final float left = destinationBounds.left - insets.left * scale; final float top = destinationBounds.top - insets.top * scale; mTmpTransform.setScale(scale, scale); tx.setMatrix(leash, mTmpTransform, mTmpFloat9) - .setWindowCrop(leash, mTmpDestinationRect) + .setCrop(leash, mTmpDestinationRect) .setPosition(leash, left, top); return this; } @@ -163,7 +173,7 @@ public class PipSurfaceTransactionHelper { mTmpTransform.setScale(scale, scale); mTmpTransform.postRotate(degrees); mTmpTransform.postTranslate(positionX, positionY); - tx.setMatrix(leash, mTmpTransform, mTmpFloat9).setWindowCrop(leash, crop); + tx.setMatrix(leash, mTmpTransform, mTmpFloat9).setCrop(leash, crop); return this; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java index 5e4c12ed37ed..f73b81e9d3f3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java @@ -135,19 +135,6 @@ public class PhonePipMenuController implements PipMenuController { } }; - private final float[] mTmpValues = new float[9]; - private final Runnable mUpdateEmbeddedMatrix = () -> { - if (mPipMenuView == null || mPipMenuView.getViewRootImpl() == null) { - return; - } - mMoveTransform.getValues(mTmpValues); - try { - mPipMenuView.getViewRootImpl().getAccessibilityEmbeddedConnection() - .setScreenMatrix(mTmpValues); - } catch (RemoteException e) { - } - }; - public PhonePipMenuController(Context context, PipBoundsState pipBoundsState, PipMediaController mediaController, SystemWindows systemWindows, Optional<SplitScreenController> splitScreenOptional, @@ -348,11 +335,6 @@ public class PhonePipMenuController implements PipMenuController { } else { mApplier.scheduleApply(params); } - - if (mPipMenuView.getViewRootImpl() != null) { - mPipMenuView.getHandler().removeCallbacks(mUpdateEmbeddedMatrix); - mPipMenuView.getHandler().post(mUpdateEmbeddedMatrix); - } } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java index 69ae45d12795..7365b9525919 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java @@ -285,7 +285,7 @@ public class PipAccessibilityInteractionConnection { Region bounds, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec, - Bundle arguments) throws RemoteException { + float[] matrixValues, Bundle arguments) throws RemoteException { mMainExcutor.execute(() -> { PipAccessibilityInteractionConnection.this .findAccessibilityNodeInfoByAccessibilityId(accessibilityNodeId, bounds, @@ -298,7 +298,8 @@ public class PipAccessibilityInteractionConnection { public void findAccessibilityNodeInfosByViewId(long accessibilityNodeId, String viewId, Region bounds, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, - int interrogatingPid, long interrogatingTid, MagnificationSpec spec) + int interrogatingPid, long interrogatingTid, MagnificationSpec spec, + float[] matrixValues) throws RemoteException { mMainExcutor.execute(() -> { PipAccessibilityInteractionConnection.this.findAccessibilityNodeInfosByViewId( @@ -311,7 +312,8 @@ public class PipAccessibilityInteractionConnection { public void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text, Region bounds, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, - int interrogatingPid, long interrogatingTid, MagnificationSpec spec) + int interrogatingPid, long interrogatingTid, MagnificationSpec spec, + float[] matrixValues) throws RemoteException { mMainExcutor.execute(() -> { PipAccessibilityInteractionConnection.this.findAccessibilityNodeInfosByText( @@ -323,7 +325,8 @@ public class PipAccessibilityInteractionConnection { @Override public void findFocus(long accessibilityNodeId, int focusType, Region bounds, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, - int interrogatingPid, long interrogatingTid, MagnificationSpec spec) + int interrogatingPid, long interrogatingTid, MagnificationSpec spec, + float[] matrixValues) throws RemoteException { mMainExcutor.execute(() -> { PipAccessibilityInteractionConnection.this.findFocus(accessibilityNodeId, focusType, @@ -335,7 +338,8 @@ public class PipAccessibilityInteractionConnection { @Override public void focusSearch(long accessibilityNodeId, int direction, Region bounds, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, - int interrogatingPid, long interrogatingTid, MagnificationSpec spec) + int interrogatingPid, long interrogatingTid, MagnificationSpec spec, + float[] matrixValues) throws RemoteException { mMainExcutor.execute(() -> { PipAccessibilityInteractionConnection.this.focusSearch(accessibilityNodeId, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java index 147a272f4645..ac7b9033b2b9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java @@ -36,6 +36,7 @@ import android.graphics.PointF; import android.graphics.Rect; import android.provider.DeviceConfig; import android.util.Size; +import android.view.DisplayCutout; import android.view.InputEvent; import android.view.MotionEvent; import android.view.ViewConfiguration; @@ -959,21 +960,38 @@ public class PipTouchHandler { } private boolean shouldStash(PointF vel, Rect motionBounds) { + final boolean flingToLeft = vel.x < -mStashVelocityThreshold; + final boolean flingToRight = vel.x > mStashVelocityThreshold; + final int offset = motionBounds.width() / 2; + final boolean droppingOnLeft = + motionBounds.left < mPipBoundsState.getDisplayBounds().left - offset; + final boolean droppingOnRight = + motionBounds.right > mPipBoundsState.getDisplayBounds().right + offset; + + // Do not allow stash if the destination edge contains display cutout. We only + // compare the left and right edges since we do not allow stash on top / bottom. + final DisplayCutout displayCutout = + mPipBoundsState.getDisplayLayout().getDisplayCutout(); + if (displayCutout != null) { + if ((flingToLeft || droppingOnLeft) + && !displayCutout.getBoundingRectLeft().isEmpty()) { + return false; + } else if ((flingToRight || droppingOnRight) + && !displayCutout.getBoundingRectRight().isEmpty()) { + return false; + } + } + // If user flings the PIP window above the minimum velocity, stash PIP. // Only allow stashing to the edge if PIP wasn't previously stashed on the opposite // edge. - final boolean stashFromFlingToEdge = ((vel.x < -mStashVelocityThreshold - && mPipBoundsState.getStashedState() != STASH_TYPE_RIGHT) - || (vel.x > mStashVelocityThreshold - && mPipBoundsState.getStashedState() != STASH_TYPE_LEFT)); + final boolean stashFromFlingToEdge = + (flingToLeft && mPipBoundsState.getStashedState() != STASH_TYPE_RIGHT) + || (flingToRight && mPipBoundsState.getStashedState() != STASH_TYPE_LEFT); // If User releases the PIP window while it's out of the display bounds, put // PIP into stashed mode. - final int offset = motionBounds.width() / 2; - final boolean stashFromDroppingOnEdge = - (motionBounds.right > mPipBoundsState.getDisplayBounds().right + offset - || motionBounds.left - < mPipBoundsState.getDisplayBounds().left - offset); + final boolean stashFromDroppingOnEdge = droppingOnLeft || droppingOnRight; return stashFromFlingToEdge || stashFromDroppingOnEdge; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java index 7b8dcf70cff0..2d67254f3610 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java @@ -30,7 +30,6 @@ import android.graphics.Matrix; import android.graphics.Rect; import android.graphics.RectF; import android.os.Handler; -import android.os.RemoteException; import android.view.LayoutInflater; import android.view.SurfaceControl; import android.view.SyncRtSurfaceTransactionApplier; @@ -88,19 +87,6 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis RectF mTmpDestinationRectF = new RectF(); Matrix mMoveTransform = new Matrix(); - private final float[] mTmpValues = new float[9]; - private final Runnable mUpdateEmbeddedMatrix = () -> { - if (mPipMenuView == null || mPipMenuView.getViewRootImpl() == null) { - return; - } - mMoveTransform.getValues(mTmpValues); - try { - mPipMenuView.getViewRootImpl().getAccessibilityEmbeddedConnection() - .setScreenMatrix(mTmpValues); - } catch (RemoteException e) { - if (DEBUG) e.printStackTrace(); - } - }; private final Runnable mCloseEduTextRunnable = this::closeEduText; public TvPipMenuController(Context context, TvPipBoundsState tvPipBoundsState, @@ -525,11 +511,6 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis mApplier.scheduleApply(frontParams); } - if (mPipMenuView.getViewRootImpl() != null) { - mPipMenuView.getHandler().removeCallbacks(mUpdateEmbeddedMatrix); - mPipMenuView.getHandler().post(mUpdateEmbeddedMatrix); - } - updateMenuBounds(pipDestBounds); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index aec51baa4af7..10dfdc30d0d6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -190,7 +190,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, mStageCoordinator = new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue, mTaskOrganizer, mDisplayController, mDisplayImeController, mDisplayInsetsController, mTransitions, mTransactionPool, mLogger, - mIconProvider, mRecentTasksOptional, mUnfoldControllerProvider); + mIconProvider, mMainExecutor, mRecentTasksOptional, mUnfoldControllerProvider); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index 45931de5e35e..9d6e34ddd9d7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -94,6 +94,7 @@ import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.DisplayInsetsController; import com.android.wm.shell.common.DisplayLayout; +import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.split.SplitLayout; @@ -157,6 +158,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, private final TransactionPool mTransactionPool; private final SplitScreenTransitions mSplitTransitions; private final SplitscreenEventLogger mLogger; + private final ShellExecutor mMainExecutor; private final Optional<RecentTasksController> mRecentTasks; /** @@ -196,13 +198,15 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, DisplayImeController displayImeController, DisplayInsetsController displayInsetsController, Transitions transitions, TransactionPool transactionPool, SplitscreenEventLogger logger, - IconProvider iconProvider, Optional<RecentTasksController> recentTasks, + IconProvider iconProvider, ShellExecutor mainExecutor, + Optional<RecentTasksController> recentTasks, Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) { mContext = context; mDisplayId = displayId; mSyncQueue = syncQueue; mTaskOrganizer = taskOrganizer; mLogger = logger; + mMainExecutor = mainExecutor; mRecentTasks = recentTasks; mMainUnfoldController = unfoldControllerProvider.get().orElse(null); mSideUnfoldController = unfoldControllerProvider.get().orElse(null); @@ -247,7 +251,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, DisplayController displayController, DisplayImeController displayImeController, DisplayInsetsController displayInsetsController, SplitLayout splitLayout, Transitions transitions, TransactionPool transactionPool, - SplitscreenEventLogger logger, + SplitscreenEventLogger logger, ShellExecutor mainExecutor, Optional<RecentTasksController> recentTasks, Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) { mContext = context; @@ -266,6 +270,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mMainUnfoldController = unfoldControllerProvider.get().orElse(null); mSideUnfoldController = unfoldControllerProvider.get().orElse(null); mLogger = logger; + mMainExecutor = mainExecutor; mRecentTasks = recentTasks; mDisplayController.addDisplayWindowListener(this); mDisplayLayout = new DisplayLayout(); @@ -394,6 +399,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, @Nullable PendingIntent pendingIntent, @Nullable Intent fillInIntent, @Nullable Bundle mainOptions, @Nullable Bundle sideOptions, @SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter) { + final boolean withIntent = pendingIntent != null && fillInIntent != null; // Init divider first to make divider leash for remote animation target. mSplitLayout.init(); // Set false to avoid record new bounds with old task still on top; @@ -423,10 +429,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, new IRemoteAnimationFinishedCallback.Stub() { @Override public void onAnimationFinished() throws RemoteException { - mIsDividerRemoteAnimating = false; - mShouldUpdateRecents = true; - mSyncQueue.queue(evictWct); - mSyncQueue.runInSync(t -> setDividerVisibility(true, t)); + onRemoteAnimationFinishedOrCancelled(evictWct); finishedCallback.onAnimationFinished(); } }; @@ -447,10 +450,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, @Override public void onAnimationCancelled() { - mIsDividerRemoteAnimating = false; - mShouldUpdateRecents = true; - mSyncQueue.queue(evictWct); - mSyncQueue.runInSync(t -> setDividerVisibility(true, t)); + onRemoteAnimationFinishedOrCancelled(evictWct); try { adapter.getRunner().onAnimationCancelled(); } catch (RemoteException e) { @@ -486,20 +486,37 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, addActivityOptions(sideOptions, mSideStage); // Add task launch requests - if (pendingIntent != null && fillInIntent != null) { - wct.startTask(mainTaskId, mainOptions); + wct.startTask(mainTaskId, mainOptions); + if (withIntent) { wct.sendPendingIntent(pendingIntent, fillInIntent, sideOptions); } else { - wct.startTask(mainTaskId, mainOptions); wct.startTask(sideTaskId, sideOptions); } - // Using legacy transitions, so we can't use blast sync since it conflicts. mTaskOrganizer.applyTransaction(wct); mSyncQueue.runInSync(t -> updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */)); } + private void onRemoteAnimationFinishedOrCancelled(WindowContainerTransaction evictWct) { + mIsDividerRemoteAnimating = false; + mShouldUpdateRecents = true; + // If any stage has no child after animation finished, it means that split will display + // nothing, such status will happen if task and intent is same app but not support + // multi-instagce, we should exit split and expand that app as full screen. + if (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0) { + mMainExecutor.execute(() -> + exitSplitScreen(mMainStage.getChildCount() == 0 + ? mSideStage : mMainStage, EXIT_REASON_UNKNOWN)); + } else { + mSyncQueue.queue(evictWct); + mSyncQueue.runInSync(t -> { + setDividerVisibility(true, t); + updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */); + }); + } + } + /** * Collects all the current child tasks of a specific split and prepares transaction to evict * them to display. diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt index 274d34ba3c5b..0640ac526bd0 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt @@ -26,9 +26,7 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.LAUNCHER_COMPONENT import com.android.server.wm.flicker.annotation.Group3 import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.rules.WMFlickerServiceRuleForTestSpec import org.junit.FixMethodOrder -import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -57,8 +55,6 @@ import org.junit.runners.Parameterized @FixMethodOrder(MethodSorters.NAME_ASCENDING) @Group3 class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { - @get:Rule - val flickerRule = WMFlickerServiceRuleForTestSpec(testSpec) /** * Defines the transition used to run the test diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt index 9a8c8942d2c9..8da6224d990c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt @@ -25,9 +25,7 @@ import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.annotation.Group3 import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.rules.WMFlickerServiceRuleForTestSpec import org.junit.FixMethodOrder -import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -57,8 +55,6 @@ import org.junit.runners.Parameterized @FixMethodOrder(MethodSorters.NAME_ASCENDING) @Group3 class ExitPipViaIntentTest(testSpec: FlickerTestParameter) : ExitPipToAppTransition(testSpec) { - @get:Rule - val flickerRule = WMFlickerServiceRuleForTestSpec(testSpec) /** * Defines the transition used to run the test diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt index 9c095a2a039f..437ad893f1d9 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt @@ -25,9 +25,7 @@ import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.annotation.Group3 import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.rules.WMFlickerServiceRuleForTestSpec import org.junit.FixMethodOrder -import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -57,8 +55,6 @@ import org.junit.runners.Parameterized @FixMethodOrder(MethodSorters.NAME_ASCENDING) @Group3 class ExitPipWithDismissButtonTest(testSpec: FlickerTestParameter) : ExitPipTransition(testSpec) { - @get:Rule - val flickerRule = WMFlickerServiceRuleForTestSpec(testSpec) override val transition: FlickerBuilder.() -> Unit get() = { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java index 49f36a4be35f..eb9d3a11d285 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java @@ -31,6 +31,7 @@ import com.android.wm.shell.TestRunningTaskInfoBuilder; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.DisplayInsetsController; +import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.split.SplitLayout; @@ -72,11 +73,13 @@ public class SplitTestUtils { DisplayController displayController, DisplayImeController imeController, DisplayInsetsController insetsController, SplitLayout splitLayout, Transitions transitions, TransactionPool transactionPool, - SplitscreenEventLogger logger, Optional<RecentTasksController> recentTasks, + SplitscreenEventLogger logger, ShellExecutor mainExecutor, + Optional<RecentTasksController> recentTasks, Provider<Optional<StageTaskUnfoldController>> unfoldController) { super(context, displayId, syncQueue, taskOrganizer, mainStage, sideStage, displayController, imeController, insetsController, splitLayout, - transitions, transactionPool, logger, recentTasks, unfoldController); + transitions, transactionPool, logger, mainExecutor, recentTasks, + unfoldController); // Prepare root task for testing. mRootTask = new TestRunningTaskInfoBuilder().build(); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java index f847e6eb5e96..a55f737f2f25 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java @@ -97,6 +97,7 @@ public class SplitTransitionTests extends ShellTestCase { @Mock private SurfaceSession mSurfaceSession; @Mock private SplitscreenEventLogger mLogger; @Mock private IconProvider mIconProvider; + @Mock private ShellExecutor mMainExecutor; private SplitLayout mSplitLayout; private MainStage mMainStage; private SideStage mSideStage; @@ -126,7 +127,7 @@ public class SplitTransitionTests extends ShellTestCase { mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue, mTaskOrganizer, mMainStage, mSideStage, mDisplayController, mDisplayImeController, mDisplayInsetsController, mSplitLayout, mTransitions, - mTransactionPool, mLogger, Optional.empty(), Optional::empty); + mTransactionPool, mLogger, mMainExecutor, Optional.empty(), Optional::empty); mSplitScreenTransitions = mStageCoordinator.getSplitTransitions(); doAnswer((Answer<IBinder>) invocation -> mock(IBinder.class)) .when(mTransitions).startTransition(anyInt(), any(), any()); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java index c571d44d3da9..42d998f6b0ee 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java @@ -54,6 +54,7 @@ import com.android.wm.shell.TestRunningTaskInfoBuilder; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.DisplayInsetsController; +import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.split.SplitLayout; @@ -101,6 +102,8 @@ public class StageCoordinatorTests extends ShellTestCase { private TransactionPool mTransactionPool; @Mock private SplitscreenEventLogger mLogger; + @Mock + private ShellExecutor mMainExecutor; private final Rect mBounds1 = new Rect(10, 20, 30, 40); private final Rect mBounds2 = new Rect(5, 10, 15, 20); @@ -116,7 +119,7 @@ public class StageCoordinatorTests extends ShellTestCase { mStageCoordinator = spy(new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue, mTaskOrganizer, mMainStage, mSideStage, mDisplayController, mDisplayImeController, mDisplayInsetsController, mSplitLayout, mTransitions, mTransactionPool, mLogger, - Optional.empty(), new UnfoldControllerProvider())); + mMainExecutor, Optional.empty(), new UnfoldControllerProvider())); doNothing().when(mStageCoordinator).updateActivityOptions(any(), anyInt()); when(mSplitLayout.getBounds1()).thenReturn(mBounds1); diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index ece150a4bd45..d8b077b32420 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -249,6 +249,7 @@ cc_defaults { "apex/android_matrix.cpp", "apex/android_paint.cpp", "apex/android_region.cpp", + "apex/properties.cpp", ], header_libs: ["android_graphics_apex_headers"], diff --git a/libs/hwui/apex/include/android/graphics/properties.h b/libs/hwui/apex/include/android/graphics/properties.h new file mode 100644 index 000000000000..f24f840710f9 --- /dev/null +++ b/libs/hwui/apex/include/android/graphics/properties.h @@ -0,0 +1,32 @@ +/* + * Copyright 2022 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. + */ + +#ifndef ANDROID_GRAPHICS_PROPERTIES_H +#define ANDROID_GRAPHICS_PROPERTIES_H + +#include <cutils/compiler.h> +#include <sys/cdefs.h> + +__BEGIN_DECLS + +/** + * Returns true if libhwui is using the vulkan backend. + */ +ANDROID_API bool hwui_uses_vulkan(); + +__END_DECLS + +#endif // ANDROID_GRAPHICS_PROPERTIES_H diff --git a/libs/hwui/apex/properties.cpp b/libs/hwui/apex/properties.cpp new file mode 100644 index 000000000000..abb333be159a --- /dev/null +++ b/libs/hwui/apex/properties.cpp @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2022 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. + */ + +#include "android/graphics/properties.h" + +#include <Properties.h> + +bool hwui_uses_vulkan() { + return android::uirenderer::Properties::peekRenderPipelineType() == + android::uirenderer::RenderPipelineType::SkiaVulkan; +} diff --git a/libs/hwui/libhwui.map.txt b/libs/hwui/libhwui.map.txt index 087c006a8680..fdb237387098 100644 --- a/libs/hwui/libhwui.map.txt +++ b/libs/hwui/libhwui.map.txt @@ -39,6 +39,7 @@ LIBHWUI { # platform-only /* HWUI isn't current a module, so all of these are st ARegionIterator_next; ARegionIterator_getRect; ARegionIterator_getTotalBounds; + hwui_uses_vulkan; local: *; }; diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java index 52838898146d..8be1bcdc9479 100644 --- a/media/java/android/media/AudioRecord.java +++ b/media/java/android/media/AudioRecord.java @@ -187,22 +187,14 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection, */ @SuppressWarnings("unused") @UnsupportedAppUsage - private long mNativeRecorderInJavaObj; + private long mNativeAudioRecordHandle; /** * Accessed by native methods: provides access to the callback data. */ @SuppressWarnings("unused") @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - private long mNativeCallbackCookie; - - /** - * Accessed by native methods: provides access to the JNIDeviceCallback instance. - */ - @SuppressWarnings("unused") - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - private long mNativeDeviceCallback; - + private long mNativeJNIDataHandle; //--------------------------------------------------------- // Member variables @@ -492,9 +484,8 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection, * value here as no error checking is or can be done. */ /*package*/ AudioRecord(long nativeRecordInJavaObj) { - mNativeRecorderInJavaObj = 0; - mNativeCallbackCookie = 0; - mNativeDeviceCallback = 0; + mNativeAudioRecordHandle = 0; + mNativeJNIDataHandle = 0; // other initialization... if (nativeRecordInJavaObj != 0) { @@ -2131,7 +2122,7 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection, * @hide */ public int getPortId() { - if (mNativeRecorderInJavaObj == 0) { + if (mNativeAudioRecordHandle == 0) { return 0; } try { diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java index 62e53d62fd2a..a41399fb0d0d 100644 --- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java +++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java @@ -16,17 +16,18 @@ package com.android.dynsystem; +import android.annotation.NonNull; import android.content.Context; import android.gsi.AvbPublicKey; import android.gsi.IGsiService; import android.net.Uri; import android.os.AsyncTask; import android.os.Build; -import android.os.MemoryFile; -import android.os.ParcelFileDescriptor; +import android.os.SharedMemory; import android.os.SystemProperties; import android.os.image.DynamicSystemManager; import android.service.persistentdata.PersistentDataBlockManager; +import android.system.ErrnoException; import android.util.Log; import android.util.Pair; import android.util.Range; @@ -39,6 +40,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URL; +import java.nio.ByteBuffer; import java.util.Arrays; import java.util.Enumeration; import java.util.List; @@ -154,6 +156,22 @@ class InstallationAsyncTask extends AsyncTask<String, Long, Throwable> { void onResult(int resultCode, Throwable detail); } + private static class MappedMemoryBuffer implements AutoCloseable { + public ByteBuffer mBuffer; + + MappedMemoryBuffer(@NonNull ByteBuffer buffer) { + mBuffer = buffer; + } + + @Override + public void close() { + if (mBuffer != null) { + SharedMemory.unmap(mBuffer); + mBuffer = null; + } + } + } + private final int mSharedMemorySize; private final String mUrl; private final String mDsuSlot; @@ -674,59 +692,66 @@ class InstallationAsyncTask extends AsyncTask<String, Long, Throwable> { Log.d(TAG, "Start installing: " + partitionName); - MemoryFile memoryFile = new MemoryFile("dsu_" + partitionName, mSharedMemorySize); - ParcelFileDescriptor pfd = new ParcelFileDescriptor(memoryFile.getFileDescriptor()); - - mInstallationSession.setAshmem(pfd, memoryFile.length()); - - initPartitionProgress(partitionName, partitionSize, /* readonly = */ true); - publishProgress(/* installedSize = */ 0L); - long prevInstalledSize = 0; - long installedSize = 0; - byte[] bytes = new byte[memoryFile.length()]; - ExecutorService executor = Executors.newSingleThreadExecutor(); - Future<Boolean> submitPromise = null; - - while (true) { - final int numBytesRead = sis.read(bytes, 0, bytes.length); - - if (submitPromise != null) { - // Wait until the previous submit task is complete. - while (true) { - try { - if (!submitPromise.get()) { - throw new IOException("Failed submitFromAshmem() to DynamicSystem"); + try (SharedMemory sharedMemory = + SharedMemory.create("dsu_buffer_" + partitionName, mSharedMemorySize); + MappedMemoryBuffer mappedBuffer = + new MappedMemoryBuffer(sharedMemory.mapReadWrite())) { + mInstallationSession.setAshmem(sharedMemory.getFdDup(), sharedMemory.getSize()); + + initPartitionProgress(partitionName, partitionSize, /* readonly = */ true); + publishProgress(/* installedSize = */ 0L); + + long installedSize = 0; + byte[] readBuffer = new byte[sharedMemory.getSize()]; + ByteBuffer buffer = mappedBuffer.mBuffer; + ExecutorService executor = Executors.newSingleThreadExecutor(); + Future<Boolean> submitPromise = null; + + while (true) { + final int numBytesRead = sis.read(readBuffer, 0, readBuffer.length); + + if (submitPromise != null) { + // Wait until the previous submit task is complete. + while (true) { + try { + if (!submitPromise.get()) { + throw new IOException("Failed submitFromAshmem() to DynamicSystem"); + } + break; + } catch (InterruptedException e) { + // Ignore. } - break; - } catch (InterruptedException e) { - // Ignore. } - } - // Publish the progress of the previous submit task. - if (installedSize > prevInstalledSize + MIN_PROGRESS_TO_PUBLISH) { - publishProgress(installedSize); - prevInstalledSize = installedSize; + // Publish the progress of the previous submit task. + if (installedSize > prevInstalledSize + MIN_PROGRESS_TO_PUBLISH) { + publishProgress(installedSize); + prevInstalledSize = installedSize; + } } - } - // Ensure the previous submit task (submitPromise) is complete before exiting the loop. - if (numBytesRead < 0) { - break; - } + // Ensure the previous submit task (submitPromise) is complete before exiting the + // loop. + if (numBytesRead < 0) { + break; + } - if (isCancelled()) { - return; - } + if (isCancelled()) { + return; + } - memoryFile.writeBytes(bytes, 0, 0, numBytesRead); - submitPromise = - executor.submit(() -> mInstallationSession.submitFromAshmem(numBytesRead)); + buffer.position(0); + buffer.put(readBuffer, 0, numBytesRead); + submitPromise = + executor.submit(() -> mInstallationSession.submitFromAshmem(numBytesRead)); - // Even though we update the bytes counter here, the actual progress is updated only - // after the submit task (submitPromise) is complete. - installedSize += numBytesRead; + // Even though we update the bytes counter here, the actual progress is updated only + // after the submit task (submitPromise) is complete. + installedSize += numBytesRead; + } + } catch (ErrnoException e) { + e.rethrowAsIOException(); } AvbPublicKey avbPublicKey = new AvbPublicKey(); diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 6d7172ec0fbe..6887d037c6f4 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -523,7 +523,8 @@ android:launchMode="singleTop" android:permission="android.permission.MANAGE_SENSOR_PRIVACY" android:theme="@style/Theme.SystemUI.Dialog.Alert" - android:finishOnCloseSystemDialogs="true"> + android:finishOnCloseSystemDialogs="true" + android:showForAllUsers="true"> </activity> <!-- started from SensoryPrivacyService --> @@ -532,7 +533,8 @@ android:launchMode="singleTop" android:permission="android.permission.MANAGE_SENSOR_PRIVACY" android:theme="@style/BottomSheet" - android:finishOnCloseSystemDialogs="true"> + android:finishOnCloseSystemDialogs="true" + android:showForAllUsers="true"> </activity> diff --git a/packages/SystemUI/res/anim/media_metadata_enter.xml b/packages/SystemUI/res/anim/media_metadata_enter.xml new file mode 100644 index 000000000000..fccb7667c41c --- /dev/null +++ b/packages/SystemUI/res/anim/media_metadata_enter.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2022 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. +--> +<set xmlns:android="http://schemas.android.com/apk/res/android" + android:ordering="together"> + <objectAnimator + android:propertyName="translationX" + android:valueFrom="32dp" + android:valueTo="0dp" + android:duration="600" + android:valueType="floatType" /> + <objectAnimator + android:propertyName="transitionAlpha" + android:valueFrom="0" + android:valueTo="1" + android:duration="167" + android:valueType="floatType" /> +</set>
\ No newline at end of file diff --git a/packages/SystemUI/res/anim/media_metadata_exit.xml b/packages/SystemUI/res/anim/media_metadata_exit.xml new file mode 100644 index 000000000000..0ee1171c3bf0 --- /dev/null +++ b/packages/SystemUI/res/anim/media_metadata_exit.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2022 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. +--> +<set xmlns:android="http://schemas.android.com/apk/res/android" + android:ordering="together"> + <objectAnimator + android:propertyName="translationX" + android:valueFrom="0dp" + android:valueTo="-32dp" + android:duration="167" + android:valueType="floatType" /> + <objectAnimator + android:propertyName="transitionAlpha" + android:valueFrom="1" + android:valueTo="0" + android:duration="83" + android:startOffset="83" + android:valueType="floatType" /> +</set> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java index 238690cd48e2..938b1cae845e 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java @@ -80,21 +80,29 @@ public class PipSurfaceTransactionHelper { public PictureInPictureSurfaceTransaction scaleAndCrop( SurfaceControl.Transaction tx, SurfaceControl leash, - Rect sourceBounds, Rect destinationBounds, Rect insets) { + Rect sourceRectHint, Rect sourceBounds, Rect destinationBounds, Rect insets) { mTmpSourceRectF.set(sourceBounds); mTmpDestinationRect.set(sourceBounds); mTmpDestinationRect.inset(insets); // Scale by the shortest edge and offset such that the top/left of the scaled inset // source rect aligns with the top/left of the destination bounds - final float scale = sourceBounds.width() <= sourceBounds.height() - ? (float) destinationBounds.width() / sourceBounds.width() - : (float) destinationBounds.height() / sourceBounds.height(); + final float scale; + if (sourceRectHint.isEmpty() || sourceRectHint.width() == sourceBounds.width()) { + scale = sourceBounds.width() <= sourceBounds.height() + ? (float) destinationBounds.width() / sourceBounds.width() + : (float) destinationBounds.height() / sourceBounds.height(); + } else { + // scale by sourceRectHint if it's not edge-to-edge + scale = sourceRectHint.width() <= sourceRectHint.height() + ? (float) destinationBounds.width() / sourceRectHint.width() + : (float) destinationBounds.height() / sourceRectHint.height(); + } final float left = destinationBounds.left - (insets.left + sourceBounds.left) * scale; final float top = destinationBounds.top - (insets.top + sourceBounds.top) * scale; mTmpTransform.setScale(scale, scale); final float cornerRadius = getScaledCornerRadius(mTmpDestinationRect, destinationBounds); tx.setMatrix(leash, mTmpTransform, mTmpFloat9) - .setWindowCrop(leash, mTmpDestinationRect) + .setCrop(leash, mTmpDestinationRect) .setPosition(leash, left, top) .setCornerRadius(leash, cornerRadius) .setShadowRadius(leash, mShadowRadius); @@ -127,7 +135,7 @@ public class PipSurfaceTransactionHelper { adjustedPositionY = positionY - insets.left * scale; } tx.setMatrix(leash, mTmpTransform, mTmpFloat9) - .setWindowCrop(leash, mTmpDestinationRect) + .setCrop(leash, mTmpDestinationRect) .setPosition(leash, adjustedPositionX, adjustedPositionY) .setCornerRadius(leash, cornerRadius) .setShadowRadius(leash, mShadowRadius); diff --git a/packages/SystemUI/src/com/android/systemui/media/AnimationBindHandler.kt b/packages/SystemUI/src/com/android/systemui/media/AnimationBindHandler.kt new file mode 100644 index 000000000000..013683e962a4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/media/AnimationBindHandler.kt @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.media + +import android.graphics.drawable.Animatable2 +import android.graphics.drawable.Drawable + +/** + * AnimationBindHandler is responsible for tracking the bound animation state and preventing + * jank and conflicts due to media notifications arriving at any time during an animation. It + * does this in two parts. + * - Exit animations fired as a result of user input are tracked. When these are running, any + * bind actions are delayed until the animation completes (and then fired in sequence). + * - Continuous animations are tracked using their rebind id. Later calls using the same + * rebind id will be totally ignored to prevent the continuous animation from restarting. + */ +internal class AnimationBindHandler : Animatable2.AnimationCallback() { + private val onAnimationsComplete = mutableListOf<() -> Unit>() + private val registrations = mutableListOf<Animatable2>() + private var rebindId: Int? = null + + val isAnimationRunning: Boolean + get() = registrations.any { it.isRunning } + + /** + * This check prevents rebinding to the action button if the identifier has not changed. A + * null value is always considered to be changed. This is used to prevent the connecting + * animation from rebinding (and restarting) if multiple buffer PlaybackStates are pushed by + * an application in a row. + */ + fun updateRebindId(newRebindId: Int?): Boolean { + if (rebindId == null || newRebindId == null || rebindId != newRebindId) { + rebindId = newRebindId + return true + } + return false + } + + fun tryRegister(drawable: Drawable?) { + if (drawable is Animatable2) { + val anim = drawable as Animatable2 + anim.registerAnimationCallback(this) + registrations.add(anim) + } + } + + fun unregisterAll() { + registrations.forEach { it.unregisterAnimationCallback(this) } + registrations.clear() + } + + fun tryExecute(action: () -> Unit) { + if (isAnimationRunning) { + onAnimationsComplete.add(action) + } else { + action() + } + } + + override fun onAnimationEnd(drawable: Drawable) { + super.onAnimationEnd(drawable) + if (!isAnimationRunning) { + onAnimationsComplete.forEach { it() } + onAnimationsComplete.clear() + } + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/media/ColorSchemeTransition.kt b/packages/SystemUI/src/com/android/systemui/media/ColorSchemeTransition.kt new file mode 100644 index 000000000000..8f0305f6dc6d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/media/ColorSchemeTransition.kt @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.media + +import android.animation.ArgbEvaluator +import android.animation.ValueAnimator.AnimatorUpdateListener +import android.animation.ValueAnimator +import android.content.Context +import android.content.res.ColorStateList +import com.android.internal.R +import com.android.internal.annotations.VisibleForTesting +import com.android.settingslib.Utils +import com.android.systemui.monet.ColorScheme + +/** + * ColorTransition is responsible for managing the animation between two specific colors. + * It uses a ValueAnimator to execute the animation and interpolate between the source color and + * the target color. + * + * Selection of the target color from the scheme, and application of the interpolated color + * are delegated to callbacks. + */ +open class ColorTransition( + private val defaultColor: Int, + private val extractColor: (ColorScheme) -> Int, + private val applyColor: (Int) -> Unit +) : AnimatorUpdateListener { + + private val argbEvaluator = ArgbEvaluator() + private val valueAnimator = buildAnimator() + var sourceColor: Int = defaultColor + var currentColor: Int = defaultColor + var targetColor: Int = defaultColor + + override fun onAnimationUpdate(animation: ValueAnimator) { + currentColor = argbEvaluator.evaluate( + animation.animatedFraction, sourceColor, targetColor + ) as Int + applyColor(currentColor) + } + + fun updateColorScheme(scheme: ColorScheme?) { + val newTargetColor = if (scheme == null) defaultColor else extractColor(scheme) + if (newTargetColor != targetColor) { + sourceColor = currentColor + targetColor = newTargetColor + valueAnimator.cancel() + valueAnimator.start() + } + } + + init { + applyColor(defaultColor) + } + + @VisibleForTesting + open fun buildAnimator(): ValueAnimator { + val animator = ValueAnimator.ofFloat(0f, 1f) + animator.duration = 333 + animator.addUpdateListener(this) + return animator + } +} + +typealias ColorTransitionFactory = (Int, (ColorScheme) -> Int, (Int) -> Unit) -> ColorTransition + +/** + * ColorSchemeTransition constructs a ColorTransition for each color in the scheme + * that needs to be transitioned when changed. It also sets up the assignment functions for sending + * the sending the interpolated colors to the appropriate views. + */ +class ColorSchemeTransition internal constructor( + private val context: Context, + bgColor: Int, + mediaViewHolder: MediaViewHolder, + colorTransitionFactory: ColorTransitionFactory +) { + constructor(context: Context, bgColor: Int, mediaViewHolder: MediaViewHolder) : + this(context, bgColor, mediaViewHolder, ::ColorTransition) + + val surfaceColor = colorTransitionFactory( + bgColor, + { colorScheme -> colorScheme.accent2[9] }, // A2-800 + { surfaceColor -> + val colorList = ColorStateList.valueOf(surfaceColor) + mediaViewHolder.player.backgroundTintList = colorList + mediaViewHolder.albumView.foregroundTintList = colorList + mediaViewHolder.albumView.backgroundTintList = colorList + mediaViewHolder.seamlessIcon.imageTintList = colorList + mediaViewHolder.seamlessText.setTextColor(surfaceColor) + mediaViewHolder.dismissText.setTextColor(surfaceColor) + }) + + val accentPrimary = colorTransitionFactory( + loadDefaultColor(R.attr.textColorPrimary), + { colorScheme -> colorScheme.accent1[2] }, // A1-100 + { accentPrimary -> + val accentColorList = ColorStateList.valueOf(accentPrimary) + mediaViewHolder.actionPlayPause.backgroundTintList = accentColorList + mediaViewHolder.seamlessButton.backgroundTintList = accentColorList + mediaViewHolder.settings.imageTintList = accentColorList + mediaViewHolder.cancelText.backgroundTintList = accentColorList + mediaViewHolder.dismissText.backgroundTintList = accentColorList + }) + + val textPrimary = colorTransitionFactory( + loadDefaultColor(R.attr.textColorPrimary), + { colorScheme -> colorScheme.neutral1[1] }, // N1-50 + { textPrimary -> + mediaViewHolder.titleText.setTextColor(textPrimary) + val textColorList = ColorStateList.valueOf(textPrimary) + mediaViewHolder.seekBar.thumb.setTintList(textColorList) + mediaViewHolder.seekBar.progressTintList = textColorList + mediaViewHolder.longPressText.setTextColor(textColorList) + mediaViewHolder.cancelText.setTextColor(textColorList) + mediaViewHolder.scrubbingElapsedTimeView.setTextColor(textColorList) + mediaViewHolder.scrubbingTotalTimeView.setTextColor(textColorList) + for (button in mediaViewHolder.getTransparentActionButtons()) { + button.imageTintList = textColorList + } + }) + + val textPrimaryInverse = colorTransitionFactory( + loadDefaultColor(R.attr.textColorPrimaryInverse), + { colorScheme -> colorScheme.neutral1[10] }, // N1-900 + { textPrimaryInverse -> + mediaViewHolder.actionPlayPause.imageTintList = + ColorStateList.valueOf(textPrimaryInverse) + }) + + val textSecondary = colorTransitionFactory( + loadDefaultColor(R.attr.textColorSecondary), + { colorScheme -> colorScheme.neutral2[3] }, // N2-200 + { textSecondary -> mediaViewHolder.artistText.setTextColor(textSecondary) }) + + val textTertiary = colorTransitionFactory( + loadDefaultColor(R.attr.textColorTertiary), + { colorScheme -> colorScheme.neutral2[5] }, // N2-400 + { textTertiary -> + mediaViewHolder.seekBar.progressBackgroundTintList = + ColorStateList.valueOf(textTertiary) + }) + + val colorTransitions = arrayOf( + surfaceColor, accentPrimary, textPrimary, + textPrimaryInverse, textSecondary, textTertiary) + + private fun loadDefaultColor(id: Int): Int { + return Utils.getColorAttr(context, id).defaultColor + } + + fun updateColorScheme(colorScheme: ColorScheme?) { + colorTransitions.forEach { it.updateColorScheme(colorScheme) } + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java index c95678311093..1b6d1d5825da 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java @@ -18,6 +18,9 @@ package com.android.systemui.media; import static android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS; +import android.animation.Animator; +import android.animation.AnimatorInflater; +import android.animation.AnimatorSet; import android.app.PendingIntent; import android.app.WallpaperColors; import android.app.smartspace.SmartspaceAction; @@ -31,20 +34,22 @@ import android.graphics.ColorMatrix; import android.graphics.ColorMatrixColorFilter; import android.graphics.Rect; import android.graphics.drawable.Animatable; -import android.graphics.drawable.Animatable2; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; +import android.graphics.drawable.TransitionDrawable; import android.media.session.MediaController; import android.media.session.MediaSession; import android.media.session.PlaybackState; import android.os.Process; import android.text.TextUtils; import android.util.Log; +import android.util.Pair; import android.view.View; import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import android.view.animation.Interpolator; import android.widget.ImageButton; import android.widget.ImageView; -import android.widget.SeekBar; import android.widget.TextView; import androidx.annotation.NonNull; @@ -52,12 +57,14 @@ import androidx.annotation.Nullable; import androidx.annotation.UiThread; import androidx.constraintlayout.widget.ConstraintSet; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.logging.InstanceId; import com.android.settingslib.widget.AdaptiveIcon; import com.android.systemui.R; import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.animation.GhostedViewLaunchAnimatorController; +import com.android.systemui.animation.Interpolators; import com.android.systemui.broadcast.BroadcastSender; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; @@ -148,6 +155,11 @@ public class MediaControlPanel { private MediaCarouselController mMediaCarouselController; private final MediaOutputDialogFactory mMediaOutputDialogFactory; private final FalsingManager mFalsingManager; + private MetadataAnimationHandler mMetadataAnimationHandler; + private ColorSchemeTransition mColorSchemeTransition; + private Drawable mPrevArtwork = null; + private int mArtworkBoundId = 0; + private int mArtworkNextBindRequestId = 0; // Used for logging. protected boolean mIsImpressed = false; @@ -171,15 +183,20 @@ public class MediaControlPanel { * @param activityStarter activity starter */ @Inject - public MediaControlPanel(Context context, + public MediaControlPanel( + Context context, @Background Executor backgroundExecutor, @Main Executor mainExecutor, - ActivityStarter activityStarter, BroadcastSender broadcastSender, - MediaViewController mediaViewController, SeekBarViewModel seekBarViewModel, + ActivityStarter activityStarter, + BroadcastSender broadcastSender, + MediaViewController mediaViewController, + SeekBarViewModel seekBarViewModel, Lazy<MediaDataManager> lazyMediaDataManager, MediaOutputDialogFactory mediaOutputDialogFactory, MediaCarouselController mediaCarouselController, - FalsingManager falsingManager, SystemClock systemClock, MediaUiEventLogger logger) { + FalsingManager falsingManager, + SystemClock systemClock, + MediaUiEventLogger logger) { mContext = context; mBackgroundExecutor = backgroundExecutor; mMainExecutor = mainExecutor; @@ -306,6 +323,33 @@ public class MediaControlPanel { mActivityStarter.startActivity(SETTINGS_INTENT, true /* dismissShade */); } }); + + TextView titleText = mMediaViewHolder.getTitleText(); + TextView artistText = mMediaViewHolder.getArtistText(); + AnimatorSet enter = loadAnimator(R.anim.media_metadata_enter, + Interpolators.EMPHASIZED_DECELERATE, titleText, artistText); + AnimatorSet exit = loadAnimator(R.anim.media_metadata_exit, + Interpolators.EMPHASIZED_ACCELERATE, titleText, artistText); + + mColorSchemeTransition = new ColorSchemeTransition( + mContext, mBackgroundColor, mMediaViewHolder); + mMetadataAnimationHandler = new MetadataAnimationHandler(exit, enter); + } + + @VisibleForTesting + protected AnimatorSet loadAnimator(int animId, Interpolator motionInterpolator, + View... targets) { + ArrayList<Animator> animators = new ArrayList<>(); + for (View target : targets) { + AnimatorSet animator = (AnimatorSet) AnimatorInflater.loadAnimator(mContext, animId); + animator.getChildAnimations().get(0).setInterpolator(motionInterpolator); + animator.setTarget(target); + animators.add(animator); + } + + AnimatorSet result = new AnimatorSet(); + result.playTogether(animators); + return result; } /** Attaches the recommendations to the recommendation view holder. */ @@ -377,20 +421,6 @@ public class MediaControlPanel { }); } - // Accessibility label - mMediaViewHolder.getPlayer().setContentDescription( - mContext.getString( - R.string.controls_media_playing_item_description, - data.getSong(), data.getArtist(), data.getApp())); - - // Song name - TextView titleText = mMediaViewHolder.getTitleText(); - titleText.setText(data.getSong()); - - // Artist name - TextView artistText = mMediaViewHolder.getArtistText(); - artistText.setText(data.getArtist()); - // Seek Bar final MediaController controller = getController(); mBackgroundExecutor.execute(() -> mSeekBarViewModel.updateController(controller)); @@ -399,11 +429,16 @@ public class MediaControlPanel { bindLongPressMenu(data); bindActionButtons(data); bindScrubbingTime(data); - bindArtworkAndColors(data); + + boolean isSongUpdated = bindSongMetadata(data); + bindArtworkAndColors(data, isSongUpdated); // TODO: We don't need to refresh this state constantly, only if the state actually changed // to something which might impact the measurement - mMediaViewController.refreshState(); + // State refresh interferes with the translation animation, only run it if it's not running. + if (!mMetadataAnimationHandler.isRunning()) { + mMediaViewController.refreshState(); + } } private void bindOutputSwitcherChip(MediaData data) { @@ -494,120 +529,135 @@ public class MediaControlPanel { }); } - private void bindArtworkAndColors(MediaData data) { - // Default colors - int surfaceColor = mBackgroundColor; - int accentPrimary = com.android.settingslib.Utils.getColorAttr(mContext, - com.android.internal.R.attr.textColorPrimary).getDefaultColor(); - int textPrimary = com.android.settingslib.Utils.getColorAttr(mContext, - com.android.internal.R.attr.textColorPrimary).getDefaultColor(); - int textPrimaryInverse = com.android.settingslib.Utils.getColorAttr(mContext, - com.android.internal.R.attr.textColorPrimaryInverse).getDefaultColor(); - int textSecondary = com.android.settingslib.Utils.getColorAttr(mContext, - com.android.internal.R.attr.textColorSecondary).getDefaultColor(); - int textTertiary = com.android.settingslib.Utils.getColorAttr(mContext, - com.android.internal.R.attr.textColorTertiary).getDefaultColor(); - - // Album art - ColorScheme colorScheme = null; - ImageView albumView = mMediaViewHolder.getAlbumView(); - boolean hasArtwork = data.getArtwork() != null; - if (hasArtwork) { - colorScheme = new ColorScheme(WallpaperColors.fromBitmap(data.getArtwork().getBitmap()), - true); - - // Scale artwork to fit background - int width = mMediaViewHolder.getPlayer().getWidth(); - int height = mMediaViewHolder.getPlayer().getHeight(); - Drawable artwork = getScaledBackground(data.getArtwork(), width, height); - albumView.setPadding(0, 0, 0, 0); - albumView.setImageDrawable(artwork); - albumView.setClipToOutline(true); - } else { - // If there's no artwork, use colors from the app icon - try { - Drawable icon = mContext.getPackageManager().getApplicationIcon( - data.getPackageName()); - colorScheme = new ColorScheme(WallpaperColors.fromDrawable(icon), true); - } catch (PackageManager.NameNotFoundException e) { - Log.w(TAG, "Cannot find icon for package " + data.getPackageName(), e); - } - } + private boolean bindSongMetadata(MediaData data) { + // Accessibility label + mMediaViewHolder.getPlayer().setContentDescription( + mContext.getString( + R.string.controls_media_playing_item_description, + data.getSong(), data.getArtist(), data.getApp())); - // Get colors for player - if (colorScheme != null) { - surfaceColor = colorScheme.getAccent2().get(9); // A2-800 - accentPrimary = colorScheme.getAccent1().get(2); // A1-100 - textPrimary = colorScheme.getNeutral1().get(1); // N1-50 - textPrimaryInverse = colorScheme.getNeutral1().get(10); // N1-900 - textSecondary = colorScheme.getNeutral2().get(3); // N2-200 - textTertiary = colorScheme.getNeutral2().get(5); // N2-400 - } + TextView titleText = mMediaViewHolder.getTitleText(); + TextView artistText = mMediaViewHolder.getArtistText(); + return mMetadataAnimationHandler.setNext( + Pair.create(data.getSong(), data.getArtist()), + () -> { + titleText.setText(data.getSong()); + artistText.setText(data.getArtist()); + + // refreshState is required here to resize the text views (and prevent ellipsis) + mMediaViewController.refreshState(); + + // Use OnPreDrawListeners to enforce zero alpha on these views for a frame. + // TransitionLayout insists on resetting the alpha of these views to 1 when onLayout + // is called which causes the animation to look bad. These suppress that behavior. + titleText.getViewTreeObserver().addOnPreDrawListener( + new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + titleText.setAlpha(0); + titleText.getViewTreeObserver().removeOnPreDrawListener(this); + return true; + } + }); + + artistText.getViewTreeObserver().addOnPreDrawListener( + new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + artistText.setAlpha(0); + artistText.getViewTreeObserver().removeOnPreDrawListener(this); + return true; + } + }); + + return Unit.INSTANCE; + }, + () -> { + // After finishing the enter animation, we refresh state. This could pop if + // something is incorrectly bound, but needs to be run if other elements were + // updated while the enter animation was running + mMediaViewController.refreshState(); + return Unit.INSTANCE; + }); + } - ColorStateList bgColorList = ColorStateList.valueOf(surfaceColor); - ColorStateList accentColorList = ColorStateList.valueOf(accentPrimary); - ColorStateList textColorList = ColorStateList.valueOf(textPrimary); - - // Gradient and background (visible when there is no art) - albumView.setForegroundTintList(ColorStateList.valueOf(surfaceColor)); - albumView.setBackgroundTintList( - ColorStateList.valueOf(surfaceColor)); - mMediaViewHolder.getPlayer().setBackgroundTintList(bgColorList); - - // App icon - use notification icon - ImageView appIconView = mMediaViewHolder.getAppIcon(); - appIconView.clearColorFilter(); - if (data.getAppIcon() != null && !data.getResumption()) { - appIconView.setImageIcon(data.getAppIcon()); - appIconView.setColorFilter(accentPrimary); - } else { - // Resume players use launcher icon - appIconView.setColorFilter(getGrayscaleFilter()); - try { - Drawable icon = mContext.getPackageManager().getApplicationIcon( - data.getPackageName()); - appIconView.setImageDrawable(icon); - } catch (PackageManager.NameNotFoundException e) { - Log.w(TAG, "Cannot find icon for package " + data.getPackageName(), e); - appIconView.setImageResource(R.drawable.ic_music_note); + private void bindArtworkAndColors(MediaData data, boolean updateBackground) { + final int reqId = mArtworkNextBindRequestId++; + + // Capture width & height from views in foreground for artwork scaling in background + int width = mMediaViewHolder.getPlayer().getWidth(); + int height = mMediaViewHolder.getPlayer().getHeight(); + + // WallpaperColors.fromBitmap takes a good amount of time. We do that work + // on the background executor to avoid stalling animations on the UI Thread. + mBackgroundExecutor.execute(() -> { + // Album art + ColorScheme mutableColorScheme = null; + Drawable artwork; + Icon artworkIcon = data.getArtwork(); + if (artworkIcon != null) { + WallpaperColors wallpaperColors = WallpaperColors + .fromBitmap(artworkIcon.getBitmap()); + mutableColorScheme = new ColorScheme(wallpaperColors, true); + artwork = getScaledBackground(artworkIcon, width, height); + } else { + // If there's no artwork, use colors from the app icon + artwork = null; + try { + Drawable icon = mContext.getPackageManager() + .getApplicationIcon(data.getPackageName()); + mutableColorScheme = new ColorScheme(WallpaperColors.fromDrawable(icon), true); + } catch (PackageManager.NameNotFoundException e) { + Log.w(TAG, "Cannot find icon for package " + data.getPackageName(), e); + } } - } - // Metadata text - mMediaViewHolder.getTitleText().setTextColor(textPrimary); - mMediaViewHolder.getArtistText().setTextColor(textSecondary); - - // Seekbar - SeekBar seekbar = mMediaViewHolder.getSeekBar(); - seekbar.getThumb().setTintList(textColorList); - seekbar.setProgressTintList(textColorList); - seekbar.setProgressBackgroundTintList(ColorStateList.valueOf(textTertiary)); - mMediaViewHolder.getScrubbingElapsedTimeView().setTextColor(textColorList); - mMediaViewHolder.getScrubbingTotalTimeView().setTextColor(textColorList); - - // Action buttons - mMediaViewHolder.getActionPlayPause().setBackgroundTintList(accentColorList); - mMediaViewHolder.getActionPlayPause().setImageTintList( - ColorStateList.valueOf(textPrimaryInverse)); - for (ImageButton button : mMediaViewHolder.getTransparentActionButtons()) { - button.setImageTintList(textColorList); - } + final ColorScheme colorScheme = mutableColorScheme; + mMainExecutor.execute(() -> { + // Cancel the request if a later one arrived first + if (reqId < mArtworkBoundId) return; + mArtworkBoundId = reqId; + + // Bind the album view to the artwork or a transition drawable + ImageView albumView = mMediaViewHolder.getAlbumView(); + albumView.setPadding(0, 0, 0, 0); + albumView.setClipToOutline(true); + if (updateBackground) { + if (mPrevArtwork == null || artwork == null) { + albumView.setImageDrawable(artwork); + } else { + TransitionDrawable transitionDrawable = new TransitionDrawable( + new Drawable[] { mPrevArtwork, artwork }); + albumView.setImageDrawable(transitionDrawable); + transitionDrawable.startTransition(333); + } + mPrevArtwork = artwork; + } - // Output switcher - View seamlessView = mMediaViewHolder.getSeamlessButton(); - seamlessView.setBackgroundTintList(accentColorList); - ImageView seamlessIconView = mMediaViewHolder.getSeamlessIcon(); - seamlessIconView.setImageTintList(bgColorList); - TextView seamlessText = mMediaViewHolder.getSeamlessText(); - seamlessText.setTextColor(surfaceColor); - - // Long press buttons - mMediaViewHolder.getLongPressText().setTextColor(textColorList); - mMediaViewHolder.getSettings().setImageTintList(accentColorList); - mMediaViewHolder.getCancelText().setTextColor(textColorList); - mMediaViewHolder.getCancelText().setBackgroundTintList(accentColorList); - mMediaViewHolder.getDismissText().setTextColor(surfaceColor); - mMediaViewHolder.getDismissText().setBackgroundTintList(accentColorList); + // Transition Colors to current color scheme + mColorSchemeTransition.updateColorScheme(colorScheme); + + // App icon - use notification icon + ImageView appIconView = mMediaViewHolder.getAppIcon(); + appIconView.clearColorFilter(); + if (data.getAppIcon() != null && !data.getResumption()) { + appIconView.setImageIcon(data.getAppIcon()); + appIconView.setColorFilter( + mColorSchemeTransition.getAccentPrimary().getTargetColor()); + } else { + // Resume players use launcher icon + appIconView.setColorFilter(getGrayscaleFilter()); + try { + Drawable icon = mContext.getPackageManager() + .getApplicationIcon(data.getPackageName()); + appIconView.setImageDrawable(icon); + } catch (PackageManager.NameNotFoundException e) { + Log.w(TAG, "Cannot find icon for package " + data.getPackageName(), e); + appIconView.setImageResource(R.drawable.ic_music_note); + } + } + }); + }); } private void bindActionButtons(MediaData data) { @@ -714,6 +764,7 @@ public class MediaControlPanel { animHandler.tryExecute(() -> { bindButtonWithAnimations(button, mediaAction, animHandler); setSemanticButtonVisibleAndAlpha(button.getId(), mediaAction, semanticActions); + return Unit.INSTANCE; }); } @@ -795,11 +846,12 @@ public class MediaControlPanel { private void updateDisplayForScrubbingChange(@NonNull MediaButton semanticActions) { // Update visibilities of the scrubbing time views and the scrubbing-dependent buttons. bindScrubbingTime(mMediaData); - SEMANTIC_ACTIONS_HIDE_WHEN_SCRUBBING.forEach((id) -> - setSemanticButtonVisibleAndAlpha( - id, semanticActions.getActionById(id), semanticActions)); - // Trigger a state refresh so that we immediately update visibilities. - mMediaViewController.refreshState(); + SEMANTIC_ACTIONS_HIDE_WHEN_SCRUBBING.forEach((id) -> setSemanticButtonVisibleAndAlpha( + id, semanticActions.getActionById(id), semanticActions)); + if (!mMetadataAnimationHandler.isRunning()) { + // Trigger a state refresh so that we immediately update visibilities. + mMediaViewController.refreshState(); + } } private void bindScrubbingTime(MediaData data) { @@ -824,74 +876,6 @@ public class MediaControlPanel { ); } - // AnimationBindHandler is responsible for tracking the bound animation state and preventing - // jank and conflicts due to media notifications arriving at any time during an animation. It - // does this in two parts. - // - Exit animations fired as a result of user input are tracked. When these are running, any - // bind actions are delayed until the animation completes (and then fired in sequence). - // - Continuous animations are tracked using their rebind id. Later calls using the same - // rebind id will be totally ignored to prevent the continuous animation from restarting. - private static class AnimationBindHandler extends Animatable2.AnimationCallback { - private ArrayList<Runnable> mOnAnimationsComplete = new ArrayList<>(); - private ArrayList<Animatable2> mRegistrations = new ArrayList<>(); - private Integer mRebindId = null; - - // This check prevents rebinding to the action button if the identifier has not changed. A - // null value is always considered to be changed. This is used to prevent the connecting - // animation from rebinding (and restarting) if multiple buffer PlaybackStates are pushed by - // an application in a row. - public boolean updateRebindId(Integer rebindId) { - if (mRebindId == null || rebindId == null || !mRebindId.equals(rebindId)) { - mRebindId = rebindId; - return true; - } - return false; - } - - public void tryRegister(Drawable drawable) { - if (drawable instanceof Animatable2) { - Animatable2 anim = (Animatable2) drawable; - anim.registerAnimationCallback(this); - mRegistrations.add(anim); - } - } - - public void unregisterAll() { - for (Animatable2 anim : mRegistrations) { - anim.unregisterAnimationCallback(this); - } - mRegistrations.clear(); - } - - public boolean isAnimationRunning() { - for (Animatable2 anim : mRegistrations) { - if (anim.isRunning()) { - return true; - } - } - return false; - } - - public void tryExecute(Runnable action) { - if (isAnimationRunning()) { - mOnAnimationsComplete.add(action); - } else { - action.run(); - } - } - - @Override - public void onAnimationEnd(Drawable drawable) { - super.onAnimationEnd(drawable); - if (!isAnimationRunning()) { - for (Runnable action : mOnAnimationsComplete) { - action.run(); - } - mOnAnimationsComplete.clear(); - } - } - } - @Nullable private ActivityLaunchAnimator.Controller buildLaunchAnimatorController( TransitionLayout player) { @@ -1097,7 +1081,9 @@ public class MediaControlPanel { }); mController = null; - mMediaViewController.refreshState(); + if (mMetadataAnimationHandler == null || !mMetadataAnimationHandler.isRunning()) { + mMediaViewController.refreshState(); + } } /** diff --git a/packages/SystemUI/src/com/android/systemui/media/MetadataAnimationHandler.kt b/packages/SystemUI/src/com/android/systemui/media/MetadataAnimationHandler.kt new file mode 100644 index 000000000000..9a1a6d35e3e3 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/media/MetadataAnimationHandler.kt @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.media + +import android.animation.Animator +import android.animation.AnimatorListenerAdapter +import android.animation.AnimatorSet +import com.android.internal.annotations.VisibleForTesting + +/** + * MetadataAnimationHandler controls the current state of the MediaControlPanel's transition motion. + * + * It checks for a changed data object (artist & title from MediaControlPanel) and runs the + * animation if necessary. When the motion has fully transitioned the elements out, it runs the + * update callback to modify the view data, before the enter animation runs. + */ +internal open class MetadataAnimationHandler( + private val exitAnimator: Animator, + private val enterAnimator: Animator +) : AnimatorListenerAdapter() { + + private val animator: AnimatorSet + private var postExitUpdate: (() -> Unit)? = null + private var postEnterUpdate: (() -> Unit)? = null + private var targetData: Any? = null + + val isRunning: Boolean + get() = animator.isRunning + + fun setNext(targetData: Any, postExit: () -> Unit, postEnter: () -> Unit): Boolean { + if (targetData != this.targetData) { + this.targetData = targetData + postExitUpdate = postExit + postEnterUpdate = postEnter + if (!animator.isRunning) { + animator.start() + } + return true + } + return false + } + + override fun onAnimationEnd(animator: Animator) { + if (animator === exitAnimator) { + postExitUpdate?.let { it() } + postExitUpdate = null + } + + if (animator === enterAnimator) { + // Another new update appeared while entering + if (postExitUpdate != null) { + this.animator.start() + } else { + postEnterUpdate?.let { it() } + postEnterUpdate = null + } + } + } + + init { + exitAnimator.addListener(this) + enterAnimator.addListener(this) + animator = buildAnimatorSet(exitAnimator, enterAnimator) + } + + @VisibleForTesting + protected open fun buildAnimatorSet(exit: Animator, enter: Animator): AnimatorSet { + val result = AnimatorSet() + result.playSequentially(exitAnimator, enterAnimator) + return result + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt index 612a7f92fc33..c9d300bad5e0 100644 --- a/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt +++ b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt @@ -16,20 +16,29 @@ package com.android.systemui.media +import android.animation.Animator +import android.animation.ObjectAnimator import android.text.format.DateUtils import androidx.annotation.UiThread import androidx.lifecycle.Observer +import com.android.internal.annotations.VisibleForTesting import com.android.systemui.R +import com.android.systemui.animation.Interpolators /** * Observer for changes from SeekBarViewModel. * * <p>Updates the seek bar views in response to changes to the model. */ -class SeekBarObserver( +open class SeekBarObserver( private val holder: MediaViewHolder ) : Observer<SeekBarViewModel.Progress> { + companion object { + @JvmStatic val RESET_ANIMATION_DURATION_MS: Int = 750 + @JvmStatic val RESET_ANIMATION_THRESHOLD_MS: Int = 250 + } + val seekBarEnabledMaxHeight = holder.seekBar.context.resources .getDimensionPixelSize(R.dimen.qs_media_enabled_seekbar_height) val seekBarDisabledHeight = holder.seekBar.context.resources @@ -38,6 +47,7 @@ class SeekBarObserver( .getDimensionPixelSize(R.dimen.qs_media_session_enabled_seekbar_vertical_padding) val seekBarDisabledVerticalPadding = holder.seekBar.context.resources .getDimensionPixelSize(R.dimen.qs_media_session_disabled_seekbar_vertical_padding) + var seekBarResetAnimator: Animator? = null init { val seekBarProgressWavelength = holder.seekBar.context.resources @@ -91,7 +101,17 @@ class SeekBarObserver( holder.scrubbingTotalTimeView.text = totalTimeString data.elapsedTime?.let { - holder.seekBar.setProgress(it) + if (!data.scrubbing && !(seekBarResetAnimator?.isRunning ?: false)) { + if (it <= RESET_ANIMATION_THRESHOLD_MS && + holder.seekBar.progress > RESET_ANIMATION_THRESHOLD_MS) { + // This animation resets for every additional update to zero. + val animator = buildResetAnimator(it) + animator.start() + seekBarResetAnimator = animator + } else { + holder.seekBar.progress = it + } + } val elapsedTimeString = DateUtils.formatElapsedTime( it / DateUtils.SECOND_IN_MILLIS) holder.scrubbingElapsedTimeView.text = elapsedTimeString @@ -104,6 +124,16 @@ class SeekBarObserver( } } + @VisibleForTesting + open fun buildResetAnimator(targetTime: Int): Animator { + val animator = ObjectAnimator.ofInt(holder.seekBar, "progress", + holder.seekBar.progress, targetTime + RESET_ANIMATION_DURATION_MS) + animator.setAutoCancel(true) + animator.duration = RESET_ANIMATION_DURATION_MS.toLong() + animator.interpolator = Interpolators.EMPHASIZED + return animator + } + @UiThread fun setVerticalPadding(padding: Int) { val leftPadding = holder.seekBar.paddingLeft diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index 72488f3dc823..740ecff6b06e 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java @@ -197,6 +197,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements private final Optional<Pip> mPipOptional; private final Optional<Recents> mRecentsOptional; private final DeviceConfigProxy mDeviceConfigProxy; + private final NavigationBarTransitions mNavigationBarTransitions; private final Optional<BackAnimation> mBackAnimation; private final Handler mHandler; private final NavigationBarOverlayController mNavbarOverlayController; @@ -513,6 +514,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements InputMethodManager inputMethodManager, DeadZone deadZone, DeviceConfigProxy deviceConfigProxy, + NavigationBarTransitions navigationBarTransitions, Optional<BackAnimation> backAnimation) { super(navigationBarView); mFrame = navigationBarFrame; @@ -537,6 +539,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements mRecentsOptional = recentsOptional; mDeadZone = deadZone; mDeviceConfigProxy = deviceConfigProxy; + mNavigationBarTransitions = navigationBarTransitions; mBackAnimation = backAnimation; mHandler = mainHandler; mNavbarOverlayController = navbarOverlayController; @@ -561,6 +564,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements public void onInit() { // TODO: A great deal of this code should probably live in onViewAttached. // It should also has corresponding cleanup in onViewDetached. + mView.setBarTransitions(mNavigationBarTransitions); mView.setTouchHandler(mTouchHandler); mView.setNavBarMode(mNavBarMode); mView.updateRotationButton(); @@ -632,7 +636,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements mView.setOnVerticalChangedListener(this::onVerticalChanged); mView.setOnTouchListener(this::onNavigationTouch); if (mSavedState != null) { - mView.getLightTransitionsController().restoreState(mSavedState); + getBarTransitions().getLightTransitionsController().restoreState(mSavedState); } setNavigationIconHints(mNavigationIconHints); mView.setWindowVisible(isNavBarWindowVisible()); @@ -705,8 +709,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements mView.getRotationButtonController(); rotationButtonController.setRotationCallback(null); mView.setUpdateActiveTouchRegionsCallback(null); - mView.getBarTransitions().destroy(); - mView.getLightTransitionsController().destroy(mContext); + getBarTransitions().destroy(); mOverviewProxyService.removeCallback(mOverviewProxyListener); mBroadcastDispatcher.unregisterReceiver(mBroadcastReceiver); if (mOrientationHandle != null) { @@ -732,7 +735,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements outState.putInt(EXTRA_APPEARANCE, mAppearance); outState.putInt(EXTRA_BEHAVIOR, mBehavior); outState.putBoolean(EXTRA_TRANSIENT_STATE, mTransientShown); - mView.getLightTransitionsController().saveState(outState); + getBarTransitions().getLightTransitionsController().saveState(outState); } /** @@ -893,7 +896,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements pw.println(" mTransientShown=" + mTransientShown); pw.println(" mTransientShownFromGestureOnSystemBar=" + mTransientShownFromGestureOnSystemBar); - dumpBarTransitions(pw, "mNavigationBarView", mView.getBarTransitions()); + dumpBarTransitions(pw, "mNavigationBarView", getBarTransitions()); mView.dump(pw); } @@ -1430,7 +1433,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements mLightBarController = lightBarController; if (mLightBarController != null) { mLightBarController.setNavigationBar( - mView.getLightTransitionsController()); + getBarTransitions().getLightTransitionsController()); } } @@ -1472,7 +1475,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements mCentralSurfacesOptionalLazy.get().map(CentralSurfaces::isDeviceInteractive) .orElse(false) && mNavigationBarWindowState != WINDOW_STATE_HIDDEN; - mView.getBarTransitions().transitionTo(mTransitionMode, anim); + getBarTransitions().transitionTo(mTransitionMode, anim); } public void disableAnimationsDuringHide(long delay) { @@ -1492,11 +1495,11 @@ public class NavigationBar extends ViewController<NavigationBarView> implements } public NavigationBarTransitions getBarTransitions() { - return mView.getBarTransitions(); + return mNavigationBarTransitions; } public void finishBarAnimations() { - mView.getBarTransitions().finishAnimations(); + getBarTransitions().finishAnimations(); } private WindowManager.LayoutParams getBarLayoutParams(int rotation) { diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java index 58e07db09c62..e625501d8961 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java @@ -31,17 +31,19 @@ import android.view.View; import com.android.systemui.Dependency; import com.android.systemui.R; +import com.android.systemui.navigationbar.NavigationBarComponent.NavigationBarScope; import com.android.systemui.navigationbar.buttons.ButtonDispatcher; -import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.phone.BarTransitions; import com.android.systemui.statusbar.phone.LightBarTransitionsController; -import com.android.systemui.statusbar.policy.KeyguardStateController; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; +import javax.inject.Inject; + +/** */ +@NavigationBarScope public final class NavigationBarTransitions extends BarTransitions implements LightBarTransitionsController.DarkIntensityApplier { @@ -81,15 +83,13 @@ public final class NavigationBarTransitions extends BarTransitions implements } }; - public NavigationBarTransitions(NavigationBarView view, CommandQueue commandQueue) { + @Inject + public NavigationBarTransitions( + NavigationBarView view, + LightBarTransitionsController.Factory lightBarTransitionsControllerFactory) { super(view, R.drawable.nav_background); mView = view; - mLightTransitionsController = new LightBarTransitionsController( - view.getContext(), - this, - commandQueue, - Dependency.get(KeyguardStateController.class), - Dependency.get(StatusBarStateController.class)); + mLightTransitionsController = lightBarTransitionsControllerFactory.create(this); mAllowAutoDimWallpaperNotVisible = view.getContext().getResources() .getBoolean(R.bool.config_navigation_bar_enable_auto_dim_no_visible_wallpaper); mDarkIntensityListeners = new ArrayList(); @@ -127,6 +127,7 @@ public final class NavigationBarTransitions extends BarTransitions implements Display.DEFAULT_DISPLAY); } catch (RemoteException e) { } + mLightTransitionsController.destroy(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java index abff914693d4..8878c2decb87 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java @@ -85,7 +85,6 @@ import com.android.systemui.shared.rotation.RotationButtonController; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.WindowManagerWrapper; -import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.phone.AutoHideController; import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.phone.LightBarTransitionsController; @@ -140,7 +139,7 @@ public class NavigationBarView extends FrameLayout { private EdgeBackGestureHandler mEdgeBackGestureHandler; private final DeadZone mDeadZone; private boolean mDeadZoneConsuming = false; - private final NavigationBarTransitions mBarTransitions; + private NavigationBarTransitions mBarTransitions; @Nullable private AutoHideController mAutoHideController; @@ -370,7 +369,6 @@ public class NavigationBarView extends FrameLayout { mConfiguration.updateFrom(context.getResources().getConfiguration()); mScreenPinningNotify = new ScreenPinningNotify(mContext); - mBarTransitions = new NavigationBarTransitions(this, Dependency.get(CommandQueue.class)); mButtonDispatchers.put(R.id.back, new ButtonDispatcher(R.id.back)); mButtonDispatchers.put(R.id.home, new ButtonDispatcher(R.id.home)); @@ -418,12 +416,12 @@ public class NavigationBarView extends FrameLayout { } } - public void setAutoHideController(AutoHideController autoHideController) { - mAutoHideController = autoHideController; + void setBarTransitions(NavigationBarTransitions navigationBarTransitions) { + mBarTransitions = navigationBarTransitions; } - public NavigationBarTransitions getBarTransitions() { - return mBarTransitions; + public void setAutoHideController(AutoHideController autoHideController) { + mAutoHideController = autoHideController; } public LightBarTransitionsController getLightTransitionsController() { diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java index cdc6b3b89f0c..363baaa5ef70 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java @@ -264,7 +264,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks, mWindowContext = null; } mAutoHideController.setNavigationBar(null); - mLightBarTransitionsController.destroy(mContext); + mLightBarTransitionsController.destroy(); mLightBarController.setNavigationBar(null); mPipOptional.ifPresent(this::removePipExclusionBoundsChangeListener); mInitialized = false; diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java index 097cf3583149..00aa1381ace1 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java @@ -706,7 +706,7 @@ public class PeopleTileViewHelper { Drawable drawable = resolveImage(imageUri, mContext); Bitmap bitmap = convertDrawableToBitmap(drawable); views.setImageViewBitmap(R.id.image, bitmap); - } catch (IOException e) { + } catch (IOException | SecurityException e) { Log.e(TAG, "Could not decode image: " + e); // If we couldn't load the image, show text that we have a new image. views.setTextViewText(R.id.text_content, newImageDescription); @@ -754,7 +754,7 @@ public class PeopleTileViewHelper { return views; } - private Drawable resolveImage(Uri uri, Context context) throws IOException { + Drawable resolveImage(Uri uri, Context context) throws IOException { final ImageDecoder.Source source = ImageDecoder.createSource(context.getContentResolver(), uri); final Drawable drawable = diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java index a64b67023da8..76ec6bd086db 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java @@ -519,7 +519,10 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca @Override public void setOverScrollAmount(int overScrollAmount) { mOverScrolling = overScrollAmount != 0; - getView().setTranslationY(overScrollAmount); + View view = getView(); + if (view != null) { + view.setTranslationY(overScrollAmount); + } } @Override 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 15a93c871c80..b52fd6107701 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 @@ -417,7 +417,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private NotificationShelf mShelf; private int mMaxDisplayedNotifications = -1; private float mKeyguardBottomPadding = -1; - private float mKeyguardNotificationAvailableSpace = -1; @VisibleForTesting int mStatusBarHeight; private int mMinInteractionHeight; private final Rect mClipRect = new Rect(); @@ -775,9 +774,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable y = (int) mMaxLayoutHeight; drawDebugInfo(canvas, y, Color.MAGENTA, /* label= */ "mMaxLayoutHeight = " + y); + // The space between mTopPadding and mKeyguardBottomPadding determines the available space + // for notifications on keyguard. if (mKeyguardBottomPadding >= 0) { y = getHeight() - (int) mKeyguardBottomPadding; - drawDebugInfo(canvas, y, Color.GRAY, + drawDebugInfo(canvas, y, Color.RED, /* label= */ "getHeight() - mKeyguardBottomPadding = " + y); } @@ -789,7 +790,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable drawDebugInfo(canvas, y, Color.CYAN, /* label= */ "mAmbientState.getStackY() = " + y); y = (int) (mAmbientState.getStackY() + mAmbientState.getStackHeight()); - drawDebugInfo(canvas, y, Color.BLUE, + drawDebugInfo(canvas, y, Color.LTGRAY, /* label= */ "mAmbientState.getStackY() + mAmbientState.getStackHeight() = " + y); y = (int) mAmbientState.getStackY() + mContentHeight; @@ -800,10 +801,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable drawDebugInfo(canvas, y, Color.YELLOW, /* label= */ "mAmbientState.getStackY() + mIntrinsicContentHeight = " + y); - y = (int) (mAmbientState.getStackY() + mKeyguardNotificationAvailableSpace); - drawDebugInfo(canvas, y, Color.RED, /* label= */ - "mAmbientState.getStackY() + mKeyguardNotificationAvailableSpace = " + y); - drawDebugInfo(canvas, mRoundedRectClippingBottom, Color.DKGRAY, /* label= */ "mRoundedRectClippingBottom) = " + y); } @@ -2267,10 +2264,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) private void updateContentHeight() { final float scrimTopPadding = mAmbientState.isOnKeyguard() ? 0 : mMinimumPaddings; + final int shelfIntrinsicHeight = mShelf != null ? mShelf.getIntrinsicHeight() : 0; final int height = (int) scrimTopPadding + (int) mNotificationStackSizeCalculator.computeHeight( /* notificationStackScrollLayout= */ this, mMaxDisplayedNotifications, - mShelf != null ? mShelf.getIntrinsicHeight() : 0); + shelfIntrinsicHeight); mIntrinsicContentHeight = height; // The topPadding can be bigger than the regular padding when qs is expanded, in that @@ -4914,15 +4912,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mKeyguardBottomPadding = keyguardBottomPadding; } - /** - * For debugging only. Enables to draw a line related to the available size for notifications in - * keyguard. - */ - public void setKeyguardAvailableSpaceForDebug(float keyguardNotificationAvailableSpace) { - mKeyguardNotificationAvailableSpace = keyguardNotificationAvailableSpace; - } - - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public void setShouldShowShelfOnly(boolean shouldShowShelfOnly) { mShouldShowShelfOnly = shouldShowShelfOnly; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index 440bea638231..ea6987a62e52 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -1305,12 +1305,6 @@ public class NotificationStackScrollLayoutController { mView.setKeyguardBottomPadding(keyguardBottomPadding); } - /** For debugging only. */ - public void mKeyguardNotificationAvailableSpaceForDebug( - float keyguardNotificationAvailableSpace) { - mView.setKeyguardAvailableSpaceForDebug(keyguardNotificationAvailableSpace); - } - public RemoteInputController.Delegate createDelegate() { return new RemoteInputController.Delegate() { public void setRemoteInputActive(NotificationEntry entry, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt index 7fb115d21fa5..9417aac49989 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt @@ -29,6 +29,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableView import com.android.systemui.util.children import javax.inject.Inject import kotlin.math.max +import kotlin.math.min import kotlin.properties.Delegates.notNull private const val TAG = "NotificationStackSizeCalculator" @@ -51,9 +52,7 @@ constructor( */ private var maxKeyguardNotifications by notNull<Int>() - /** - * Minimum space between two notifications, see [calculateGapAndDividerHeight]. - */ + /** Minimum space between two notifications, see [calculateGapAndDividerHeight]. */ private var dividerHeight by notNull<Int>() init { @@ -61,55 +60,34 @@ constructor( } /** - * Given the [availableSpace] constraint, calculates how many notification to show. + * Given the [totalAvailableSpace] constraint, calculates how many notification to show. * * This number is only valid in keyguard. * - * @param availableSpace space for notifications. This doesn't include the space for the shelf. + * @param totalAvailableSpace space for notifications. This includes the space for the shelf. */ fun computeMaxKeyguardNotifications( stack: NotificationStackScrollLayout, - availableSpace: Float, - shelfHeight: Float + totalAvailableSpace: Float, + shelfIntrinsicHeight: Float ): Int { - log { - "computeMaxKeyguardNotifications(" + - "availableSpace=$availableSpace shelfHeight=$shelfHeight)" + val stackHeightSequence = computeHeightPerNotificationLimit(stack, shelfIntrinsicHeight) + + var maxNotifications = + stackHeightSequence.lastIndexWhile { stackHeight -> stackHeight <= totalAvailableSpace } + + if (onLockscreen()) { + maxNotifications = min(maxKeyguardNotifications, maxNotifications) } - val children: Sequence<ExpandableView> = stack.childrenSequence - var remainingSpace: Float = availableSpace - var count = 0 - var previous: ExpandableView? = null - val onLockscreen = true - val showableRows = children.filter { it.isShowable(onLockscreen) } - val showableRowsCount = showableRows.count() - log { "\tshowableRowsCount=$showableRowsCount "} - - showableRows.forEachIndexed { i, current -> - val spaceNeeded = current.spaceNeeded(count, previous, stack, onLockscreen) - val spaceAfter = remainingSpace - spaceNeeded - previous = current - log { "\ti=$i spaceNeeded=$spaceNeeded remainingSpace=$remainingSpace " + - "spaceAfter=$spaceAfter" } - - if (remainingSpace - spaceNeeded >= 0 && count < maxKeyguardNotifications) { - count += 1 - remainingSpace -= spaceNeeded - } else if (remainingSpace - spaceNeeded > -shelfHeight && i == showableRowsCount - 1) { - log { "Show all notifications. Shelf not needed." } - // If this is the last one, and it fits using the space shelf would use, then we can - // display it, as the shelf will not be needed (as all notifications are shown). - return count + 1 - } else { - log { - "No more fit. Returning $count. Space used: ${availableSpace - remainingSpace}" - } - return count - } + // Could be < 0 if the space available is less than the shelf size. Returns 0 in this case. + maxNotifications = max(0, maxNotifications) + log { + "computeMaxKeyguardNotifications(" + + "availableSpace=$totalAvailableSpace" + + " shelfHeight=$shelfIntrinsicHeight) -> $maxNotifications" } - log { "All fit. Returning $count" } - return count + return maxNotifications } /** @@ -119,47 +97,60 @@ constructor( * @param stack stack containing notifications as children. * @param maxNotifications Maximum number of notifications. When reached, the others will go * into the shelf. - * @param shelfHeight height of the shelf. It might be zero. + * @param shelfIntrinsicHeight height of the shelf, without any padding. It might be zero. * * @return height of the stack, including shelf height, if needed. */ fun computeHeight( stack: NotificationStackScrollLayout, maxNotifications: Int, - shelfHeight: Float + shelfIntrinsicHeight: Float ): Float { - val children: Sequence<ExpandableView> = stack.childrenSequence - val maxNotificationsArg = infiniteIfNegative(maxNotifications) + val heightPerMaxNotifications = + computeHeightPerNotificationLimit(stack, shelfIntrinsicHeight) + val height = + heightPerMaxNotifications.elementAtOrElse(maxNotifications) { + heightPerMaxNotifications.last() // Height with all notifications visible. + } + log { "computeHeight(maxNotifications=$maxNotifications) -> $height" } + return height + } + + /** The ith result in the sequence is the height with ith max notifications. */ + private fun computeHeightPerNotificationLimit( + stack: NotificationStackScrollLayout, + shelfIntrinsicHeight: Float + ): Sequence<Float> = sequence { + val children = stack.showableChildren().toList() var height = 0f var previous: ExpandableView? = null - var count = 0 val onLockscreen = onLockscreen() - log { "computeHeight(maxNotification=$maxNotifications, shelf=$shelfHeight" } - children.filter { it.isShowable(onLockscreen) }.forEach { current -> - if (count < maxNotificationsArg) { - val spaceNeeded = current.spaceNeeded(count, previous, stack, onLockscreen) - log { "\ti=$count spaceNeeded=$spaceNeeded" } - height += spaceNeeded - count += 1 - } else { - height += current.calculateGapAndDividerHeight(stack, previous, count) - height += shelfHeight - log { "returning height with shelf -> $height" } - return height - } - previous = current + yield(dividerHeight + shelfIntrinsicHeight) // Only shelf. + + children.forEachIndexed { i, currentNotification -> + height += currentNotification.spaceNeeded(i, previous, stack, onLockscreen) + previous = currentNotification + + val shelfHeight = + if (i == children.lastIndex) { + 0f // No shelf needed. + } else { + val spaceBeforeShelf = + calculateGapAndDividerHeight( + stack, previous = currentNotification, current = children[i + 1], i) + spaceBeforeShelf + shelfIntrinsicHeight + } + + yield(height + shelfHeight) } - log { "Returning height without shelf -> $height" } - return height } fun updateResources() { maxKeyguardNotifications = infiniteIfNegative(resources.getInteger(R.integer.keyguard_max_notification_count)) - dividerHeight = - max(1, resources.getDimensionPixelSize(R.dimen.notification_divider_height)) + dividerHeight = max(1, resources.getDimensionPixelSize(R.dimen.notification_divider_height)) } private val NotificationStackScrollLayout.childrenSequence: Sequence<ExpandableView> @@ -180,7 +171,7 @@ constructor( } else { intrinsicHeight.toFloat() } - size += calculateGapAndDividerHeight(stack, previousView, visibleIndex) + size += calculateGapAndDividerHeight(stack, previousView, current = this, visibleIndex) return size } @@ -200,18 +191,22 @@ constructor( return true } - private fun ExpandableView.calculateGapAndDividerHeight( + private fun calculateGapAndDividerHeight( stack: NotificationStackScrollLayout, previous: ExpandableView?, + current: ExpandableView?, visibleIndex: Int - ) : Float { - var height = stack.calculateGapHeight(previous, /* current= */ this, visibleIndex) + ): Float { + var height = stack.calculateGapHeight(previous, current, visibleIndex) if (visibleIndex != 0) { height += dividerHeight } return height } + private fun NotificationStackScrollLayout.showableChildren() = + this.childrenSequence.filter { it.isShowable(onLockscreen()) } + /** * Can a view be shown on the lockscreen when calculating the number of allowed notifications to * show? @@ -240,4 +235,8 @@ constructor( } else { v } + + /** Returns the last index where [predicate] returns true, or -1 if it was always false. */ + private fun <T> Sequence<T>.lastIndexWhile(predicate: (T) -> Boolean): Int = + takeWhile(predicate).count() - 1 } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java index a19d5f162db9..54d39fd673f6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java @@ -31,8 +31,6 @@ import android.provider.Settings; import android.util.Log; import android.util.MathUtils; -import android.util.Log; - import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; @@ -293,11 +291,6 @@ public class DozeParameters implements } public void updateControlScreenOff() { - Log.i("TEST", "Display needs blanking?" + getDisplayNeedsBlanking()); - Log.i("TEST", "Should control screen off?" + shouldControlUnlockedScreenOff()); - Log.i("TEST", "alwaysOn?" + getAlwaysOn()); - Log.i("TEST", "keyguard showing?" + mKeyguardShowing); - Log.i("TEST", "Flag enabled? " + mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ANIMATIONS)); if (!getDisplayNeedsBlanking()) { final boolean controlScreenOff = getAlwaysOn() && (mKeyguardShowing || shouldControlUnlockedScreenOff()); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java index b6ad9f704bcd..16fddb420fc4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java @@ -93,7 +93,8 @@ public class LightBarTransitionsController implements Dumpable, Callbacks, mDisplayId = mContext.getDisplayId(); } - public void destroy(Context context) { + /** Call to cleanup the LightBarTransitionsController when done with it. */ + public void destroy() { mCommandQueue.removeCallback(this); mStatusBarStateController.removeCallback(this); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index a6b5c4d2c92c..cb2d5b265f8c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -317,8 +317,6 @@ public class NotificationPanelViewController extends PanelViewController { private boolean mShouldUseSplitNotificationShade; // The bottom padding reserved for elements of the keyguard measuring notifications private float mKeyguardNotificationBottomPadding; - // Space available for notifications. - private float mKeyguardNotificationAvailableSpace; // Current max allowed keyguard notifications determined by measuring the panel private int mMaxAllowedKeyguardNotifications; @@ -1245,8 +1243,6 @@ public class NotificationPanelViewController extends PanelViewController { mMaxAllowedKeyguardNotifications); mNotificationStackScrollLayoutController.setKeyguardBottomPaddingForDebug( mKeyguardNotificationBottomPadding); - mNotificationStackScrollLayoutController.mKeyguardNotificationAvailableSpaceForDebug( - mKeyguardNotificationAvailableSpace); } else { // no max when not on the keyguard mNotificationStackScrollLayoutController.setMaxDisplayedNotifications(-1); @@ -1468,13 +1464,11 @@ public class NotificationPanelViewController extends PanelViewController { * @return the maximum keyguard notifications that can fit on the screen */ private int computeMaxKeyguardNotifications() { - int notificationPadding = Math.max( - 1, mResources.getDimensionPixelSize(R.dimen.notification_divider_height)); float topPadding = mNotificationStackScrollLayoutController.getTopPadding(); - float shelfHeight = + float shelfIntrinsicHeight = mNotificationShelfController.getVisibility() == View.GONE ? 0 - : mNotificationShelfController.getIntrinsicHeight() + notificationPadding; + : mNotificationShelfController.getIntrinsicHeight(); // Padding to add to the bottom of the stack to keep a minimum distance from the top of // the lock icon. @@ -1493,13 +1487,11 @@ public class NotificationPanelViewController extends PanelViewController { float availableSpace = mNotificationStackScrollLayoutController.getHeight() - topPadding - - shelfHeight - bottomPadding; - mKeyguardNotificationAvailableSpace = availableSpace; return mNotificationStackSizeCalculator.computeMaxKeyguardNotifications( mNotificationStackScrollLayoutController.getView(), availableSpace, - shelfHeight); + shelfIntrinsicHeight); } private void updateClock() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/AnimationBindHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/AnimationBindHandlerTest.kt new file mode 100644 index 000000000000..e4cab1810822 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/media/AnimationBindHandlerTest.kt @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.media + +import org.mockito.Mockito.`when` as whenever +import android.graphics.drawable.Animatable2 +import android.graphics.drawable.Drawable +import android.test.suitebuilder.annotation.SmallTest +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import com.android.systemui.SysuiTestCase +import junit.framework.Assert.assertTrue +import junit.framework.Assert.assertFalse +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.Mockito.times +import org.mockito.Mockito.never +import org.mockito.junit.MockitoJUnit + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +class AnimationBindHandlerTest : SysuiTestCase() { + + private interface Callback : () -> Unit + private abstract class AnimatedDrawable : Drawable(), Animatable2 + private lateinit var handler: AnimationBindHandler + + @Mock private lateinit var animatable: AnimatedDrawable + @Mock private lateinit var animatable2: AnimatedDrawable + @Mock private lateinit var callback: Callback + + @JvmField @Rule val mockito = MockitoJUnit.rule() + + @Before + fun setUp() { + handler = AnimationBindHandler() + } + + @After + fun tearDown() {} + + @Test + fun registerNoAnimations_executeCallbackImmediately() { + handler.tryExecute(callback) + verify(callback).invoke() + } + + @Test + fun registerStoppedAnimations_executeCallbackImmediately() { + whenever(animatable.isRunning).thenReturn(false) + whenever(animatable2.isRunning).thenReturn(false) + + handler.tryExecute(callback) + verify(callback).invoke() + } + + @Test + fun registerRunningAnimations_executeCallbackDelayed() { + whenever(animatable.isRunning).thenReturn(true) + whenever(animatable2.isRunning).thenReturn(true) + + handler.tryRegister(animatable) + handler.tryRegister(animatable2) + handler.tryExecute(callback) + + verify(callback, never()).invoke() + + whenever(animatable.isRunning).thenReturn(false) + handler.onAnimationEnd(animatable) + verify(callback, never()).invoke() + + whenever(animatable2.isRunning).thenReturn(false) + handler.onAnimationEnd(animatable2) + verify(callback, times(1)).invoke() + } + + @Test + fun repeatedEndCallback_executeSingleCallback() { + whenever(animatable.isRunning).thenReturn(true) + + handler.tryRegister(animatable) + handler.tryExecute(callback) + + verify(callback, never()).invoke() + + whenever(animatable.isRunning).thenReturn(false) + handler.onAnimationEnd(animatable) + handler.onAnimationEnd(animatable) + handler.onAnimationEnd(animatable) + verify(callback, times(1)).invoke() + } + + @Test + fun registerUnregister_executeImmediately() { + whenever(animatable.isRunning).thenReturn(true) + + handler.tryRegister(animatable) + handler.unregisterAll() + handler.tryExecute(callback) + + verify(callback).invoke() + } + + @Test + fun updateRebindId_returnsAsExpected() { + // Previous or current call is null, returns true + assertTrue(handler.updateRebindId(null)) + assertTrue(handler.updateRebindId(null)) + assertTrue(handler.updateRebindId(null)) + assertTrue(handler.updateRebindId(10)) + assertTrue(handler.updateRebindId(null)) + assertTrue(handler.updateRebindId(20)) + + // Different integer from prevoius, returns true + assertTrue(handler.updateRebindId(10)) + assertTrue(handler.updateRebindId(20)) + + // Matches previous call, returns false + assertFalse(handler.updateRebindId(20)) + assertFalse(handler.updateRebindId(20)) + assertTrue(handler.updateRebindId(10)) + assertFalse(handler.updateRebindId(10)) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/ColorSchemeTransitionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/ColorSchemeTransitionTest.kt new file mode 100644 index 000000000000..86527d9558fd --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/media/ColorSchemeTransitionTest.kt @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.media + +import org.mockito.Mockito.`when` as whenever +import android.animation.ValueAnimator +import android.graphics.Color +import android.test.suitebuilder.annotation.SmallTest +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import com.android.systemui.SysuiTestCase +import com.android.systemui.monet.ColorScheme +import junit.framework.Assert.assertEquals +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.never +import org.mockito.Mockito.times +import org.mockito.Mockito.verify +import org.mockito.junit.MockitoJUnit + +private const val DEFAULT_COLOR = Color.RED +private const val TARGET_COLOR = Color.BLUE +private const val BG_COLOR = Color.GREEN + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +class ColorSchemeTransitionTest : SysuiTestCase() { + + private interface ExtractCB : (ColorScheme) -> Int + private interface ApplyCB : (Int) -> Unit + private lateinit var colorTransition: ColorTransition + private lateinit var colorSchemeTransition: ColorSchemeTransition + + @Mock private lateinit var mockTransition: ColorTransition + @Mock private lateinit var valueAnimator: ValueAnimator + @Mock private lateinit var colorScheme: ColorScheme + @Mock private lateinit var extractColor: ExtractCB + @Mock private lateinit var applyColor: ApplyCB + + private lateinit var transitionFactory: ColorTransitionFactory + @Mock private lateinit var mediaViewHolder: MediaViewHolder + + @JvmField @Rule val mockitoRule = MockitoJUnit.rule() + + @Before + fun setUp() { + transitionFactory = { default, extractColor, applyColor -> mockTransition } + whenever(extractColor.invoke(colorScheme)).thenReturn(TARGET_COLOR) + + colorSchemeTransition = ColorSchemeTransition(context, + BG_COLOR, mediaViewHolder, transitionFactory) + + colorTransition = object : ColorTransition(DEFAULT_COLOR, extractColor, applyColor) { + override fun buildAnimator(): ValueAnimator { + return valueAnimator + } + } + } + + @After + fun tearDown() {} + + @Test + fun testColorTransition_nullColorScheme_keepsDefault() { + colorTransition.updateColorScheme(null) + verify(applyColor, times(1)).invoke(DEFAULT_COLOR) + verify(valueAnimator, never()).start() + assertEquals(DEFAULT_COLOR, colorTransition.sourceColor) + assertEquals(DEFAULT_COLOR, colorTransition.targetColor) + } + + @Test + fun testColorTransition_newColor_startsAnimation() { + colorTransition.updateColorScheme(colorScheme) + verify(applyColor, times(1)).invoke(DEFAULT_COLOR) + verify(valueAnimator, times(1)).start() + assertEquals(DEFAULT_COLOR, colorTransition.sourceColor) + assertEquals(TARGET_COLOR, colorTransition.targetColor) + } + + @Test + fun testColorTransition_sameColor_noAnimation() { + whenever(extractColor.invoke(colorScheme)).thenReturn(DEFAULT_COLOR) + colorTransition.updateColorScheme(colorScheme) + verify(valueAnimator, never()).start() + assertEquals(DEFAULT_COLOR, colorTransition.sourceColor) + assertEquals(DEFAULT_COLOR, colorTransition.targetColor) + } + + @Test + fun testColorTransition_colorAnimation_startValues() { + val expectedColor = DEFAULT_COLOR + whenever(valueAnimator.animatedFraction).thenReturn(0f) + colorTransition.updateColorScheme(colorScheme) + colorTransition.onAnimationUpdate(valueAnimator) + + assertEquals(expectedColor, colorTransition.currentColor) + assertEquals(expectedColor, colorTransition.sourceColor) + verify(applyColor, times(2)).invoke(expectedColor) // applied once in constructor + } + + @Test + fun testColorTransition_colorAnimation_endValues() { + val expectedColor = TARGET_COLOR + whenever(valueAnimator.animatedFraction).thenReturn(1f) + colorTransition.updateColorScheme(colorScheme) + colorTransition.onAnimationUpdate(valueAnimator) + + assertEquals(expectedColor, colorTransition.currentColor) + assertEquals(expectedColor, colorTransition.targetColor) + verify(applyColor).invoke(expectedColor) + } + + @Test + fun testColorTransition_colorAnimation_interpolatedMidpoint() { + val expectedColor = Color.rgb(186, 0, 186) + whenever(valueAnimator.animatedFraction).thenReturn(0.5f) + colorTransition.updateColorScheme(colorScheme) + colorTransition.onAnimationUpdate(valueAnimator) + + assertEquals(expectedColor, colorTransition.currentColor) + verify(applyColor).invoke(expectedColor) + } + + @Test + fun testColorSchemeTransition_update() { + colorSchemeTransition.updateColorScheme(colorScheme) + verify(mockTransition, times(6)).updateColorScheme(colorScheme) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt index a58a28e3920b..a39ae6c231bd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt @@ -16,6 +16,8 @@ package com.android.systemui.media +import android.animation.Animator +import android.animation.AnimatorSet import android.app.PendingIntent import android.app.smartspace.SmartspaceAction import android.content.Context @@ -39,6 +41,7 @@ import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.View import android.view.ViewGroup +import android.view.animation.Interpolator import android.widget.FrameLayout import android.widget.ImageButton import android.widget.ImageView @@ -58,6 +61,7 @@ import com.android.systemui.plugins.FalsingManager import com.android.systemui.util.animation.TransitionLayout import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.KotlinArgumentCaptor +import com.android.systemui.util.mockito.argumentCaptor import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.withArgCaptor import com.android.systemui.util.time.FakeSystemClock @@ -140,6 +144,7 @@ public class MediaControlPanelTest : SysuiTestCase() { private lateinit var actionsTopBarrier: Barrier @Mock private lateinit var longPressText: TextView @Mock private lateinit var handler: Handler + @Mock private lateinit var mockAnimator: AnimatorSet private lateinit var settings: ImageButton private lateinit var cancel: View private lateinit var cancelText: TextView @@ -181,7 +186,7 @@ public class MediaControlPanelTest : SysuiTestCase() { whenever(packageManager.getApplicationLabel(any())).thenReturn(PACKAGE) context.setMockPackageManager(packageManager) - player = MediaControlPanel( + player = object : MediaControlPanel( context, bgExecutor, mainExecutor, @@ -194,8 +199,15 @@ public class MediaControlPanelTest : SysuiTestCase() { mediaCarouselController, falsingManager, clock, - logger - ) + logger) { + override fun loadAnimator( + animId: Int, + otionInterpolator: Interpolator, + vararg targets: View + ): AnimatorSet { + return mockAnimator + } + } initMediaViewHolderMocks() @@ -470,7 +482,7 @@ public class MediaControlPanelTest : SysuiTestCase() { val icon = context.getDrawable(android.R.drawable.ic_media_play) val semanticActions = MediaButton( prevOrCustom = MediaAction(icon, {}, "prev", null), - nextOrCustom = MediaAction(icon, {}, "next", null), + nextOrCustom = MediaAction(icon, {}, "next", null) ) val state = mediaData.copy(semanticActions = semanticActions) @@ -504,7 +516,7 @@ public class MediaControlPanelTest : SysuiTestCase() { val icon = context.getDrawable(android.R.drawable.ic_media_play) val semanticActions = MediaButton( prevOrCustom = null, - nextOrCustom = MediaAction(icon, {}, "next", null), + nextOrCustom = MediaAction(icon, {}, "next", null) ) val state = mediaData.copy(semanticActions = semanticActions) player.attachPlayer(viewHolder) @@ -524,7 +536,7 @@ public class MediaControlPanelTest : SysuiTestCase() { val icon = context.getDrawable(android.R.drawable.ic_media_play) val semanticActions = MediaButton( prevOrCustom = MediaAction(icon, {}, "prev", null), - nextOrCustom = null, + nextOrCustom = null ) val state = mediaData.copy(semanticActions = semanticActions) player.attachPlayer(viewHolder) @@ -544,7 +556,7 @@ public class MediaControlPanelTest : SysuiTestCase() { val icon = context.getDrawable(android.R.drawable.ic_media_play) val semanticActions = MediaButton( prevOrCustom = MediaAction(icon, {}, "prev", null), - nextOrCustom = MediaAction(icon, {}, "next", null), + nextOrCustom = MediaAction(icon, {}, "next", null) ) val state = mediaData.copy(semanticActions = semanticActions) player.attachPlayer(viewHolder) @@ -566,7 +578,7 @@ public class MediaControlPanelTest : SysuiTestCase() { val icon = context.getDrawable(android.R.drawable.ic_media_play) val semanticActions = MediaButton( prevOrCustom = MediaAction(icon, {}, "prev", null), - nextOrCustom = MediaAction(icon, {}, "next", null), + nextOrCustom = MediaAction(icon, {}, "next", null) ) val state = mediaData.copy(semanticActions = semanticActions) @@ -709,8 +721,53 @@ public class MediaControlPanelTest : SysuiTestCase() { fun bindText() { player.attachPlayer(viewHolder) player.bindPlayer(mediaData, PACKAGE) + + // Capture animation handler + val captor = argumentCaptor<Animator.AnimatorListener>() + verify(mockAnimator, times(2)).addListener(captor.capture()) + val handler = captor.value + + // Validate text views unchanged but animation started + assertThat(titleText.getText()).isEqualTo("") + assertThat(artistText.getText()).isEqualTo("") + verify(mockAnimator, times(1)).start() + + // Binding only after animator runs + handler.onAnimationEnd(mockAnimator) assertThat(titleText.getText()).isEqualTo(TITLE) assertThat(artistText.getText()).isEqualTo(ARTIST) + + // Rebinding should not trigger animation + player.bindPlayer(mediaData, PACKAGE) + verify(mockAnimator, times(1)).start() + } + + @Test + fun bindTextInterrupted() { + val data0 = mediaData.copy(artist = "ARTIST_0") + val data1 = mediaData.copy(artist = "ARTIST_1") + val data2 = mediaData.copy(artist = "ARTIST_2") + + player.attachPlayer(viewHolder) + player.bindPlayer(data0, PACKAGE) + + // Capture animation handler + val captor = argumentCaptor<Animator.AnimatorListener>() + verify(mockAnimator, times(2)).addListener(captor.capture()) + val handler = captor.value + + handler.onAnimationEnd(mockAnimator) + assertThat(artistText.getText()).isEqualTo("ARTIST_0") + + // Bind trigges new animation + player.bindPlayer(data1, PACKAGE) + verify(mockAnimator, times(2)).start() + whenever(mockAnimator.isRunning()).thenReturn(true) + + // Rebind before animation end binds corrct data + player.bindPlayer(data2, PACKAGE) + handler.onAnimationEnd(mockAnimator) + assertThat(artistText.getText()).isEqualTo("ARTIST_2") } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MetadataAnimationHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MetadataAnimationHandlerTest.kt new file mode 100644 index 000000000000..52cb902a4f38 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MetadataAnimationHandlerTest.kt @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.media + +import org.mockito.Mockito.`when` as whenever +import android.animation.Animator +import android.animation.AnimatorSet +import android.test.suitebuilder.annotation.SmallTest +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import com.android.systemui.SysuiTestCase +import junit.framework.Assert.fail +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.Mockito.times +import org.mockito.Mockito.mock +import org.mockito.Mockito.never +import org.mockito.junit.MockitoJUnit + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +class MetadataAnimationHandlerTest : SysuiTestCase() { + + private interface Callback : () -> Unit + private lateinit var handler: MetadataAnimationHandler + + @Mock private lateinit var animatorSet: AnimatorSet + @Mock private lateinit var enterAnimator: Animator + @Mock private lateinit var exitAnimator: Animator + @Mock private lateinit var postExitCB: Callback + @Mock private lateinit var postEnterCB: Callback + + @JvmField @Rule val mockito = MockitoJUnit.rule() + + @Before + fun setUp() { + handler = object : MetadataAnimationHandler(exitAnimator, enterAnimator) { + override fun buildAnimatorSet(exit: Animator, enter: Animator): AnimatorSet { + return animatorSet + } + } + } + + @After + fun tearDown() {} + + @Test + fun firstBind_startsAnimationSet() { + val cb = { fail("Unexpected callback") } + handler.setNext("data-1", cb, cb) + + verify(animatorSet).start() + } + + @Test + fun executeAnimationEnd_runsCallacks() { + handler.setNext("data-1", postExitCB, postEnterCB) + verify(animatorSet, times(1)).start() + verify(postExitCB, never()).invoke() + + handler.onAnimationEnd(exitAnimator) + verify(animatorSet, times(1)).start() + verify(postExitCB, times(1)).invoke() + verify(postEnterCB, never()).invoke() + + handler.onAnimationEnd(enterAnimator) + verify(animatorSet, times(1)).start() + verify(postExitCB, times(1)).invoke() + verify(postEnterCB, times(1)).invoke() + } + + @Test + fun rebindSameData_executesFirstCallback() { + val postExitCB2 = mock(Callback::class.java) + + handler.setNext("data-1", postExitCB, postEnterCB) + handler.setNext("data-1", postExitCB2, postEnterCB) + handler.onAnimationEnd(exitAnimator) + + verify(postExitCB, times(1)).invoke() + verify(postExitCB2, never()).invoke() + verify(postEnterCB, never()).invoke() + } + + @Test + fun rebindDifferentData_executesSecondCallback() { + val postExitCB2 = mock(Callback::class.java) + + handler.setNext("data-1", postExitCB, postEnterCB) + handler.setNext("data-2", postExitCB2, postEnterCB) + handler.onAnimationEnd(exitAnimator) + + verify(postExitCB, never()).invoke() + verify(postExitCB2, times(1)).invoke() + verify(postEnterCB, never()).invoke() + } + + @Test + fun rebindBeforeEnterComplete_animationRestarts() { + val postExitCB2 = mock(Callback::class.java) + val postEnterCB2 = mock(Callback::class.java) + + handler.setNext("data-1", postExitCB, postEnterCB) + verify(animatorSet, times(1)).start() + verify(postExitCB, never()).invoke() + verify(postExitCB2, never()).invoke() + verify(postEnterCB, never()).invoke() + verify(postEnterCB2, never()).invoke() + + whenever(animatorSet.isRunning()).thenReturn(true) + handler.onAnimationEnd(exitAnimator) + verify(animatorSet, times(1)).start() + verify(postExitCB, times(1)).invoke() + verify(postExitCB2, never()).invoke() + verify(postEnterCB, never()).invoke() + verify(postEnterCB2, never()).invoke() + + handler.setNext("data-2", postExitCB2, postEnterCB2) + handler.onAnimationEnd(enterAnimator) + verify(animatorSet, times(2)).start() + verify(postExitCB, times(1)).invoke() + verify(postExitCB2, never()).invoke() + verify(postEnterCB, never()).invoke() + verify(postEnterCB2, never()).invoke() + + handler.onAnimationEnd(exitAnimator) + verify(animatorSet, times(2)).start() + verify(postExitCB, times(1)).invoke() + verify(postExitCB2, times(1)).invoke() + verify(postEnterCB, never()).invoke() + verify(postEnterCB2, never()).invoke() + + handler.onAnimationEnd(enterAnimator) + verify(animatorSet, times(2)).start() + verify(postExitCB, times(1)).invoke() + verify(postExitCB2, times(1)).invoke() + verify(postEnterCB, never()).invoke() + verify(postEnterCB2, times(1)).invoke() + } + + @Test + fun exitAnimationEndMultipleCalls_singleCallbackExecution() { + handler.setNext("data-1", postExitCB, postEnterCB) + handler.onAnimationEnd(exitAnimator) + handler.onAnimationEnd(exitAnimator) + handler.onAnimationEnd(exitAnimator) + + verify(postExitCB, times(1)).invoke() + } + + @Test + fun enterAnimatorEndsWithoutCallback_noAnimatiorStart() { + handler.onAnimationEnd(enterAnimator) + + verify(animatorSet, never()).start() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt index c48d84698b3b..49be669bb4a5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt @@ -16,6 +16,8 @@ package com.android.systemui.media +import android.animation.Animator +import android.animation.ObjectAnimator import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.View @@ -43,6 +45,7 @@ class SeekBarObserverTest : SysuiTestCase() { private val enabledHeight = 2 private lateinit var observer: SeekBarObserver + @Mock private lateinit var mockSeekbarAnimator: ObjectAnimator @Mock private lateinit var mockHolder: MediaViewHolder @Mock private lateinit var mockSquigglyProgress: SquigglyProgress private lateinit var seekBarView: SeekBar @@ -66,7 +69,11 @@ class SeekBarObserverTest : SysuiTestCase() { whenever(mockHolder.scrubbingElapsedTimeView).thenReturn(scrubbingElapsedTimeView) whenever(mockHolder.scrubbingTotalTimeView).thenReturn(scrubbingTotalTimeView) - observer = SeekBarObserver(mockHolder) + observer = object : SeekBarObserver(mockHolder) { + override fun buildResetAnimator(targetTime: Int): Animator { + return mockSeekbarAnimator + } + } } @Test @@ -189,4 +196,20 @@ class SeekBarObserverTest : SysuiTestCase() { assertThat(scrubbingElapsedTimeView.text).isEqualTo("") assertThat(scrubbingTotalTimeView.text).isEqualTo("") } + + @Test + fun seekBarJumpAnimation() { + val data0 = SeekBarViewModel.Progress(true, true, true, false, 4000, 120000) + val data1 = SeekBarViewModel.Progress(true, true, true, false, 10, 120000) + + // Set initial position of progress bar + observer.onChanged(data0) + assertThat(seekBarView.progress).isEqualTo(4000) + assertThat(seekBarView.max).isEqualTo(120000) + + // Change to second data & confirm no change to position (due to animation delay) + observer.onChanged(data1) + assertThat(seekBarView.progress).isEqualTo(4000) + verify(mockSeekbarAnimator).start() + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java index f5b006d732fd..4a740f6c5571 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java @@ -205,10 +205,9 @@ public class NavigationBarTest extends SysuiTestCase { when(mNavigationBarView.getAccessibilityButton()).thenReturn(mAccessibilityButton); when(mNavigationBarView.getImeSwitchButton()).thenReturn(mImeSwitchButton); when(mNavigationBarView.getBackButton()).thenReturn(mBackButton); - when(mNavigationBarView.getBarTransitions()).thenReturn(mNavigationBarTransitions); when(mNavigationBarView.getRotationButtonController()) .thenReturn(mRotationButtonController); - when(mNavigationBarView.getLightTransitionsController()) + when(mNavigationBarTransitions.getLightTransitionsController()) .thenReturn(mLightBarTransitionsController); when(mStatusBarKeyguardViewManager.isNavBarVisible()).thenReturn(true); setupSysuiDependency(); @@ -459,6 +458,7 @@ public class NavigationBarTest extends SysuiTestCase { mInputMethodManager, mDeadZone, mDeviceConfigProxyFake, + mNavigationBarTransitions, Optional.of(mock(BackAnimation.class)))); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTransitionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTransitionsTest.java index 6a2a78b40d2d..084eca82ce98 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTransitionsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTransitionsTest.java @@ -21,7 +21,6 @@ import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; @@ -37,8 +36,8 @@ import com.android.systemui.assist.AssistManager; import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.recents.OverviewProxyService; -import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.phone.BarTransitions; +import com.android.systemui.statusbar.phone.LightBarTransitionsController; import com.android.systemui.statusbar.policy.KeyguardStateController; import org.junit.Before; @@ -53,6 +52,10 @@ import org.mockito.MockitoAnnotations; public class NavigationBarTransitionsTest extends SysuiTestCase { @Mock + LightBarTransitionsController.Factory mLightBarTransitionsFactory; + @Mock + LightBarTransitionsController mLightBarTransitions; + @Mock EdgeBackGestureHandler.Factory mEdgeBackGestureHandlerFactory; @Mock EdgeBackGestureHandler mEdgeBackGestureHandler; @@ -76,10 +79,11 @@ public class NavigationBarTransitionsTest extends SysuiTestCase { .when(mDependency.injectMockDependency(NavigationModeController.class)) .getCurrentUserContext(); + when(mLightBarTransitionsFactory.create(any())).thenReturn(mLightBarTransitions); NavigationBarView navBar = spy(new NavigationBarView(mContext, null)); when(navBar.getCurrentView()).thenReturn(navBar); when(navBar.findViewById(anyInt())).thenReturn(navBar); - mTransitions = new NavigationBarTransitions(navBar, mock(CommandQueue.class)); + mTransitions = new NavigationBarTransitions(navBar, mLightBarTransitionsFactory); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java index b4b459752bc2..e6b960d75215 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java @@ -33,8 +33,12 @@ import static com.android.systemui.people.PeopleSpaceUtils.VALID_CONTACT; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import android.app.people.ConversationStatus; @@ -44,6 +48,7 @@ import android.content.Intent; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.content.res.Resources; +import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Icon; import android.net.Uri; import android.os.UserHandle; @@ -581,6 +586,27 @@ public class PeopleTileViewHelperTest extends SysuiTestCase { } @Test + public void testCreateRemoteViewsWithNotificationContent() throws Exception { + PeopleTileViewHelper helper = spy(getPeopleTileViewHelper(PERSON_TILE)); + doReturn(new BitmapDrawable()).when(helper).resolveImage(any(), any()); + RemoteViews views = helper.getViews(); + View result = views.apply(mContext, null); + + assertEquals(View.VISIBLE, result.findViewById(R.id.image).getVisibility()); + } + + @Test + public void testCreateRemoteViewsWithInvalidNotificationContent() throws Exception { + PeopleTileViewHelper helper = spy(getPeopleTileViewHelper(PERSON_TILE)); + doThrow(SecurityException.class).when(helper).resolveImage(any(), any()); + RemoteViews views = helper.getViews(); + View result = views.apply(mContext, null); + + assertEquals(View.GONE, result.findViewById(R.id.image).getVisibility()); + assertEquals(View.VISIBLE, result.findViewById(R.id.text_content).getVisibility()); + } + + @Test public void testCreateRemoteViewsWithUserQuieted() { PeopleSpaceTile tile = PERSON_TILE.toBuilder() .setIsUserQuieted(true) diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java index f5d19e24bfa6..4fbdb7c512bd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java @@ -315,6 +315,24 @@ public class QSFragmentTest extends SysuiBaseFragmentTest { verify(mQuickQSPanelController).setCollapseExpandAction(action); } + @Test + public void setOverScrollAmount_setsTranslationOnView() { + QSFragment fragment = resumeAndGetFragment(); + + fragment.setOverScrollAmount(123); + + assertThat(mQsFragmentView.getTranslationY()).isEqualTo(123); + } + + @Test + public void setOverScrollAmount_beforeViewCreated_translationIsNotSet() { + QSFragment fragment = getFragment(); + + fragment.setOverScrollAmount(123); + + assertThat(mQsFragmentView.getTranslationY()).isEqualTo(0); + } + @Override protected Fragment instantiate(Context context, String className, Bundle arguments) { MockitoAnnotations.initMocks(this); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt index dfd70a2e810b..968e16aab14e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.notification.stack +import android.annotation.DimenRes import android.service.notification.StatusBarNotification import android.testing.AndroidTestingRunner import android.view.View.VISIBLE @@ -27,6 +28,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.ExpandableView import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.nullable import com.google.common.truth.Truth.assertThat import org.junit.Before @@ -34,8 +36,8 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.mock -import org.mockito.MockitoAnnotations import org.mockito.Mockito.`when` as whenever +import org.mockito.MockitoAnnotations @SmallTest @RunWith(AndroidTestingRunner::class) @@ -49,17 +51,15 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { private lateinit var sizeCalculator: NotificationStackSizeCalculator + private val gapHeight = px(R.dimen.notification_section_divider_height) + private val dividerHeight = px(R.dimen.notification_divider_height) + private val shelfHeight = px(R.dimen.notification_shelf_height) + private val rowHeight = px(R.dimen.notification_max_height) + @Before fun setUp() { MockitoAnnotations.initMocks(this) - whenever(stackLayout.calculateGapHeight(nullable(), nullable(), any())) - .thenReturn(GAP_HEIGHT) - with(testableResources) { - addOverride(R.integer.keyguard_max_notification_count, -1) - addOverride(R.dimen.notification_divider_height, DIVIDER_HEIGHT.toInt()) - } - sizeCalculator = NotificationStackSizeCalculator( statusBarStateController = sysuiStatusBarStateController, @@ -68,7 +68,7 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { @Test fun computeMaxKeyguardNotifications_zeroSpace_returnZero() { - val rows = listOf(createMockRow(height = ROW_HEIGHT)) + val rows = listOf(createMockRow(height = rowHeight)) val maxNotifications = computeMaxKeyguardNotifications(rows, availableSpace = 0f, shelfHeight = 0f) @@ -87,105 +87,78 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { } @Test - fun computeMaxKeyguardNotifications_spaceForOne_returnsOne() { - val rowHeight = ROW_HEIGHT - val totalSpaceForEachRow = GAP_HEIGHT + rowHeight - val shelfHeight = - totalSpaceForEachRow / 2 // In this way shelf absence will not leave room for another. - val spaceForOne = totalSpaceForEachRow - val rows = - listOf( - createMockRow(rowHeight), - createMockRow(rowHeight)) - - val maxNotifications = - computeMaxKeyguardNotifications( - rows, availableSpace = spaceForOne, shelfHeight = shelfHeight) - - assertThat(maxNotifications).isEqualTo(1) - } - - @Test - fun computeMaxKeyguardNotifications_spaceForOne_shelfUsableForLastNotification_returnsTwo() { - val rowHeight = ROW_HEIGHT - val totalSpaceForEachRow = GAP_HEIGHT + rowHeight - val shelfHeight = totalSpaceForEachRow + DIVIDER_HEIGHT - val spaceForOne = totalSpaceForEachRow - val rows = - listOf( - createMockRow(rowHeight), - createMockRow(rowHeight)) + fun computeMaxKeyguardNotifications_spaceForOneAndShelf_returnsOne() { + setGapHeight(gapHeight) + val shelfHeight = rowHeight / 2 // Shelf absence won't leave room for another row. + val availableSpace = + listOf(rowHeight + dividerHeight, gapHeight + dividerHeight + shelfHeight).sum() + val rows = listOf(createMockRow(rowHeight), createMockRow(rowHeight)) - val maxNotifications = - computeMaxKeyguardNotifications( - rows, availableSpace = spaceForOne, shelfHeight = shelfHeight) + val maxNotifications = computeMaxKeyguardNotifications(rows, availableSpace, shelfHeight) assertThat(maxNotifications).isEqualTo(1) } @Test fun computeMaxKeyguardNotifications_spaceForTwo_returnsTwo() { - val rowHeight = ROW_HEIGHT - val totalSpaceForEachRow = GAP_HEIGHT + rowHeight - val spaceForTwo = totalSpaceForEachRow * 2 + DIVIDER_HEIGHT - val rows = + setGapHeight(gapHeight) + val shelfHeight = shelfHeight + dividerHeight + val availableSpace = listOf( - createMockRow(rowHeight), - createMockRow(rowHeight), - createMockRow(rowHeight)) + rowHeight + dividerHeight, + gapHeight + rowHeight + dividerHeight, + gapHeight + dividerHeight + shelfHeight) + .sum() + val rows = + listOf(createMockRow(rowHeight), createMockRow(rowHeight), createMockRow(rowHeight)) - val maxNotifications = computeMaxKeyguardNotifications(rows, spaceForTwo, shelfHeight = 0f) + val maxNotifications = computeMaxKeyguardNotifications(rows, availableSpace, shelfHeight) assertThat(maxNotifications).isEqualTo(2) } @Test fun computeHeight_returnsAtMostSpaceAvailable_withGapBeforeShelf() { - val rowHeight = ROW_HEIGHT - val shelfHeight = SHELF_HEIGHT - val totalSpaceForEachRow = GAP_HEIGHT + rowHeight + DIVIDER_HEIGHT - val availableSpace = totalSpaceForEachRow * 2 + setGapHeight(gapHeight) + val shelfHeight = shelfHeight + val availableSpace = + listOf( + rowHeight + dividerHeight, + gapHeight + rowHeight + dividerHeight, + gapHeight + dividerHeight + shelfHeight) + .sum() // All rows in separate sections (default setup). val rows = - listOf( - createMockRow(rowHeight), - createMockRow(rowHeight), - createMockRow(rowHeight)) + listOf(createMockRow(rowHeight), createMockRow(rowHeight), createMockRow(rowHeight)) val maxNotifications = computeMaxKeyguardNotifications(rows, availableSpace, shelfHeight) assertThat(maxNotifications).isEqualTo(2) - val height = sizeCalculator.computeHeight(stackLayout, maxNotifications, SHELF_HEIGHT) - assertThat(height).isAtMost(availableSpace + GAP_HEIGHT + SHELF_HEIGHT) + val height = sizeCalculator.computeHeight(stackLayout, maxNotifications, this.shelfHeight) + assertThat(height).isAtMost(availableSpace) } @Test - fun computeHeight_returnsAtMostSpaceAvailable_noGapBeforeShelf() { - val rowHeight = ROW_HEIGHT - val shelfHeight = SHELF_HEIGHT - val totalSpaceForEachRow = GAP_HEIGHT + rowHeight + DIVIDER_HEIGHT - val availableSpace = totalSpaceForEachRow * 1 - + fun computeHeight_noGapBeforeShelf_returnsAtMostSpaceAvailable() { // Both rows are in the same section. - whenever(stackLayout.calculateGapHeight(nullable(), nullable(), any())) - .thenReturn(0f) - val rows = - listOf( - createMockRow(rowHeight), - createMockRow(rowHeight)) + setGapHeight(0f) + val rowHeight = rowHeight + val shelfHeight = shelfHeight + val availableSpace = listOf(rowHeight + dividerHeight, dividerHeight + shelfHeight).sum() + val rows = listOf(createMockRow(rowHeight), createMockRow(rowHeight)) val maxNotifications = computeMaxKeyguardNotifications(rows, availableSpace, shelfHeight) assertThat(maxNotifications).isEqualTo(1) - val height = sizeCalculator.computeHeight(stackLayout, maxNotifications, SHELF_HEIGHT) - assertThat(height).isAtMost(availableSpace + SHELF_HEIGHT) + val height = sizeCalculator.computeHeight(stackLayout, maxNotifications, this.shelfHeight) + assertThat(height).isAtMost(availableSpace) } private fun computeMaxKeyguardNotifications( rows: List<ExpandableView>, availableSpace: Float, - shelfHeight: Float = SHELF_HEIGHT + shelfHeight: Float = this.shelfHeight ): Int { setupChildren(rows) return sizeCalculator.computeMaxKeyguardNotifications( @@ -204,9 +177,9 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { (1..number).map { createMockRow() }.toList() private fun createMockRow( - height: Float = ROW_HEIGHT, + height: Float = rowHeight, isRemoved: Boolean = false, - visibility: Int = VISIBLE, + visibility: Int = VISIBLE ): ExpandableNotificationRow { val row = mock(ExpandableNotificationRow::class.java) val entry = mock(NotificationEntry::class.java) @@ -220,11 +193,12 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { return row } - /** Default dimensions for tests that don't overwrite them. */ - companion object { - const val GAP_HEIGHT = 12f - const val DIVIDER_HEIGHT = 3f - const val SHELF_HEIGHT = 14f - const val ROW_HEIGHT = SHELF_HEIGHT * 3 + private fun setGapHeight(height: Float) { + whenever(stackLayout.calculateGapHeight(nullable(), nullable(), any())).thenReturn(height) + whenever(stackLayout.calculateGapHeight(nullable(), nullable(), /* visibleIndex= */ eq(0))) + .thenReturn(0f) } + + private fun px(@DimenRes id: Int): Float = + testableResources.resources.getDimensionPixelSize(id).toFloat() } diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java index 5ef1008afad5..562d11a0b197 100644 --- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java @@ -69,6 +69,7 @@ import android.os.ServiceManager; import android.os.SystemClock; import android.os.Trace; import android.provider.Settings; +import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.view.Display; @@ -218,12 +219,6 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ @NonNull KeyEventDispatcher getKeyEventDispatcher(); /** - * @param windowId The id of the window of interest - * @return The magnification spec for the window, or {@code null} if none is available - */ - @Nullable MagnificationSpec getCompatibleMagnificationSpecLocked(int windowId); - - /** * @param displayId The display id. * @return The current injector of motion events used on the display, if one exists. */ @@ -557,7 +552,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ final int resolvedWindowId; RemoteAccessibilityConnection connection; Region partialInteractiveRegion = Region.obtain(); - MagnificationSpec spec; + final MagnificationSpec spec; + final float[] transformMatrix; synchronized (mLock) { mUsesAccessibilityCache = true; if (!hasRightsToCurrentUserLocked()) { @@ -581,7 +577,10 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ partialInteractiveRegion.recycle(); partialInteractiveRegion = null; } - spec = mSystemSupport.getCompatibleMagnificationSpecLocked(resolvedWindowId); + final Pair<float[], MagnificationSpec> transformMatrixAndSpec = + getTransformMatrixAndSpecLocked(resolvedWindowId); + transformMatrix = transformMatrixAndSpec.first; + spec = transformMatrixAndSpec.second; } if (!mSecurityPolicy.checkAccessibilityAccess(this)) { return null; @@ -594,12 +593,12 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ logTraceIntConn("findAccessibilityNodeInfosByViewId", accessibilityNodeId + ";" + viewIdResName + ";" + partialInteractiveRegion + ";" + interactionId + ";" + callback + ";" + mFetchFlags + ";" + interrogatingPid - + ";" + interrogatingTid + ";" + spec); + + ";" + interrogatingTid + ";" + spec + ";" + Arrays.toString(transformMatrix)); } try { connection.getRemote().findAccessibilityNodeInfosByViewId(accessibilityNodeId, viewIdResName, partialInteractiveRegion, interactionId, callback, mFetchFlags, - interrogatingPid, interrogatingTid, spec); + interrogatingPid, interrogatingTid, spec, transformMatrix); return mSecurityPolicy.computeValidReportedPackages( connection.getPackageName(), connection.getUid()); } catch (RemoteException re) { @@ -630,7 +629,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ final int resolvedWindowId; RemoteAccessibilityConnection connection; Region partialInteractiveRegion = Region.obtain(); - MagnificationSpec spec; + final MagnificationSpec spec; + final float [] transformMatrix; synchronized (mLock) { mUsesAccessibilityCache = true; if (!hasRightsToCurrentUserLocked()) { @@ -654,7 +654,10 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ partialInteractiveRegion.recycle(); partialInteractiveRegion = null; } - spec = mSystemSupport.getCompatibleMagnificationSpecLocked(resolvedWindowId); + final Pair<float[], MagnificationSpec> transformMatrixAndSpec = + getTransformMatrixAndSpecLocked(resolvedWindowId); + transformMatrix = transformMatrixAndSpec.first; + spec = transformMatrixAndSpec.second; } if (!mSecurityPolicy.checkAccessibilityAccess(this)) { return null; @@ -667,12 +670,12 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ logTraceIntConn("findAccessibilityNodeInfosByText", accessibilityNodeId + ";" + text + ";" + partialInteractiveRegion + ";" + interactionId + ";" + callback + ";" + mFetchFlags + ";" + interrogatingPid - + ";" + interrogatingTid + ";" + spec); + + ";" + interrogatingTid + ";" + spec + ";" + Arrays.toString(transformMatrix)); } try { connection.getRemote().findAccessibilityNodeInfosByText(accessibilityNodeId, text, partialInteractiveRegion, interactionId, callback, mFetchFlags, - interrogatingPid, interrogatingTid, spec); + interrogatingPid, interrogatingTid, spec, transformMatrix); return mSecurityPolicy.computeValidReportedPackages( connection.getPackageName(), connection.getUid()); } catch (RemoteException re) { @@ -704,7 +707,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ final int resolvedWindowId; RemoteAccessibilityConnection connection; Region partialInteractiveRegion = Region.obtain(); - MagnificationSpec spec; + final MagnificationSpec spec; + final float[] transformMatrix; synchronized (mLock) { mUsesAccessibilityCache = true; if (!hasRightsToCurrentUserLocked()) { @@ -728,7 +732,10 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ partialInteractiveRegion.recycle(); partialInteractiveRegion = null; } - spec = mSystemSupport.getCompatibleMagnificationSpecLocked(resolvedWindowId); + final Pair<float[], MagnificationSpec> transformMatrixAndSpec = + getTransformMatrixAndSpecLocked(resolvedWindowId); + transformMatrix = transformMatrixAndSpec.first; + spec = transformMatrixAndSpec.second; } if (!mSecurityPolicy.checkAccessibilityAccess(this)) { return null; @@ -741,12 +748,14 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ logTraceIntConn("findAccessibilityNodeInfoByAccessibilityId", accessibilityNodeId + ";" + partialInteractiveRegion + ";" + interactionId + ";" + callback + ";" + (mFetchFlags | flags) + ";" + interrogatingPid + ";" - + interrogatingTid + ";" + spec + ";" + arguments); + + interrogatingTid + ";" + spec + ";" + Arrays.toString(transformMatrix) + + ";" + arguments); } try { connection.getRemote().findAccessibilityNodeInfoByAccessibilityId( accessibilityNodeId, partialInteractiveRegion, interactionId, callback, - mFetchFlags | flags, interrogatingPid, interrogatingTid, spec, arguments); + mFetchFlags | flags, interrogatingPid, interrogatingTid, spec, transformMatrix, + arguments); return mSecurityPolicy.computeValidReportedPackages( connection.getPackageName(), connection.getUid()); } catch (RemoteException re) { @@ -778,7 +787,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ final int resolvedWindowId; RemoteAccessibilityConnection connection; Region partialInteractiveRegion = Region.obtain(); - MagnificationSpec spec; + final MagnificationSpec spec; + final float[] transformMatrix; synchronized (mLock) { if (!hasRightsToCurrentUserLocked()) { return null; @@ -802,7 +812,10 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ partialInteractiveRegion.recycle(); partialInteractiveRegion = null; } - spec = mSystemSupport.getCompatibleMagnificationSpecLocked(resolvedWindowId); + final Pair<float[], MagnificationSpec> transformMatrixAndSpec = + getTransformMatrixAndSpecLocked(resolvedWindowId); + transformMatrix = transformMatrixAndSpec.first; + spec = transformMatrixAndSpec.second; } if (!mSecurityPolicy.checkAccessibilityAccess(this)) { return null; @@ -815,12 +828,13 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ logTraceIntConn("findFocus", accessibilityNodeId + ";" + focusType + ";" + partialInteractiveRegion + ";" + interactionId + ";" + callback + ";" + mFetchFlags + ";" + interrogatingPid - + ";" + interrogatingTid + ";" + spec); + + ";" + interrogatingTid + ";" + spec + ";" + + Arrays.toString(transformMatrix)); } try { connection.getRemote().findFocus(accessibilityNodeId, focusType, partialInteractiveRegion, interactionId, callback, mFetchFlags, - interrogatingPid, interrogatingTid, spec); + interrogatingPid, interrogatingTid, spec, transformMatrix); return mSecurityPolicy.computeValidReportedPackages( connection.getPackageName(), connection.getUid()); } catch (RemoteException re) { @@ -852,7 +866,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ final int resolvedWindowId; RemoteAccessibilityConnection connection; Region partialInteractiveRegion = Region.obtain(); - MagnificationSpec spec; + final MagnificationSpec spec; + final float[] transformMatrix; synchronized (mLock) { if (!hasRightsToCurrentUserLocked()) { return null; @@ -875,7 +890,11 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ partialInteractiveRegion.recycle(); partialInteractiveRegion = null; } - spec = mSystemSupport.getCompatibleMagnificationSpecLocked(resolvedWindowId); + + final Pair<float[], MagnificationSpec> transformMatrixAndSpec = + getTransformMatrixAndSpecLocked(resolvedWindowId); + transformMatrix = transformMatrixAndSpec.first; + spec = transformMatrixAndSpec.second; } if (!mSecurityPolicy.checkAccessibilityAccess(this)) { return null; @@ -888,12 +907,13 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ logTraceIntConn("focusSearch", accessibilityNodeId + ";" + direction + ";" + partialInteractiveRegion + ";" + interactionId + ";" + callback + ";" + mFetchFlags + ";" - + interrogatingPid + ";" + interrogatingTid + ";" + spec); + + interrogatingPid + ";" + interrogatingTid + ";" + spec + ";" + + Arrays.toString(transformMatrix)); } try { connection.getRemote().focusSearch(accessibilityNodeId, direction, partialInteractiveRegion, interactionId, callback, mFetchFlags, - interrogatingPid, interrogatingTid, spec); + interrogatingPid, interrogatingTid, spec, transformMatrix); return mSecurityPolicy.computeValidReportedPackages( connection.getPackageName(), connection.getUid()); } catch (RemoteException re) { @@ -1652,6 +1672,23 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ mInvocationHandler.startInputLocked(startInputToken, inputContext, editorInfo, restarting); } + + + @Nullable + Pair<float[], MagnificationSpec> getTransformMatrixAndSpecLocked(int resolvedWindowId) { + final WindowInfo windowInfo = + mA11yWindowManager.findWindowInfoByIdLocked(resolvedWindowId); + if (windowInfo == null) { + Slog.w(LOG_TAG, "getTransformMatrixAndSpec, windowInfo is null window id = " + + resolvedWindowId); + return new Pair<>(null, null); + } + + final MagnificationSpec spec = new MagnificationSpec(); + spec.setTo(windowInfo.mMagnificationSpec); + return new Pair<>(windowInfo.mTransformMatrix, spec); + } + /** * Called by the invocation handler to notify the service that the * state of magnification has changed. diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index e3226c7e2a7b..ac0c051794b3 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -106,6 +106,7 @@ import android.view.IWindow; import android.view.KeyEvent; import android.view.MagnificationSpec; import android.view.MotionEvent; +import android.view.WindowInfo; import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityInteractionClient; @@ -3045,22 +3046,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub userState.setInteractiveUiTimeoutLocked(newInteractiveUiTimeout); } - @GuardedBy("mLock") - @Override - public MagnificationSpec getCompatibleMagnificationSpecLocked(int windowId) { - IBinder windowToken = mA11yWindowManager.getWindowTokenForUserAndWindowIdLocked( - mCurrentUserId, windowId); - if (windowToken != null) { - if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MANAGER_INTERNAL)) { - mTraceManager.logTrace(LOG_TAG + ".getCompatibleMagnificationSpecForWindow", - FLAGS_WINDOW_MANAGER_INTERNAL, "windowToken=" + windowToken); - } - - return mWindowManagerService.getCompatibleMagnificationSpecForWindow(windowToken); - } - return null; - } - @Override public KeyEventDispatcher getKeyEventDispatcher() { if (mKeyEventDispatcher == null) { @@ -3741,7 +3726,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub boundsInScreenBeforeMagnification.centerY()); // Invert magnification if needed. - MagnificationSpec spec = getCompatibleMagnificationSpecLocked(focus.getWindowId()); + final WindowInfo windowInfo = mA11yWindowManager.findWindowInfoByIdLocked( + focus.getWindowId()); + MagnificationSpec spec = null; + if (windowInfo != null) { + spec = new MagnificationSpec(); + spec.setTo(windowInfo.mMagnificationSpec); + } + if (spec != null && !spec.isNop()) { boundsInScreenBeforeMagnification.offset((int) -spec.offsetX, (int) -spec.offsetY); diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java index aba32ec465b5..e30639cf416a 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java @@ -52,6 +52,7 @@ import com.android.server.wm.WindowManagerInternal; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -478,6 +479,9 @@ public class AccessibilityWindowManager { if (oldWindow.taskId != newWindow.taskId) { return true; } + if (!Arrays.equals(oldWindow.mTransformMatrix, newWindow.mTransformMatrix)) { + return true; + } return false; } @@ -800,14 +804,24 @@ public class AccessibilityWindowManager { pw.append(','); pw.println(); } - pw.append("Window["); + pw.append("A11yWindow["); AccessibilityWindowInfo window = mWindows.get(j); pw.append(window.toString()); pw.append(']'); + pw.println(); + final WindowInfo windowInfo = findWindowInfoByIdLocked(window.getId()); + if (windowInfo != null) { + pw.append("WindowInfo["); + pw.append(windowInfo.toString()); + pw.append("]"); + pw.println(); + } + } pw.println(); } } + } /** * Interface to send {@link AccessibilityEvent}. diff --git a/services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java b/services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java index 6828dd916701..6958b667da37 100644 --- a/services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java +++ b/services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java @@ -83,7 +83,7 @@ public class ActionReplacingCallback extends IAccessibilityInteractionConnection mConnectionWithReplacementActions.findAccessibilityNodeInfoByAccessibilityId( AccessibilityNodeInfo.ROOT_NODE_ID, null, mNodeWithReplacementActionsInteractionId, this, 0, - interrogatingPid, interrogatingTid, null, null); + interrogatingPid, interrogatingTid, null, null, null); } catch (RemoteException re) { if (DEBUG) { Slog.e(LOG_TAG, "Error calling findAccessibilityNodeInfoByAccessibilityId()"); diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java index f944d4fd8478..e529010e6b7d 100644 --- a/services/core/java/com/android/server/GestureLauncherService.java +++ b/services/core/java/com/android/server/GestureLauncherService.java @@ -75,6 +75,13 @@ public class GestureLauncherService extends SystemService { @VisibleForTesting static final long CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS = 300; /** + * Min time in milliseconds to complete the emergency gesture for it count. If the gesture is + * completed faster than this, we assume it's not performed by human and the + * event gets ignored. + */ + @VisibleForTesting static final int EMERGENCY_GESTURE_TAP_DETECTION_MIN_TIME_MS = 160; + + /** * Interval in milliseconds in which the power button must be depressed in succession to be * considered part of an extended sequence of taps. Note that this is a looser threshold than * the camera launch gesture, because the purpose of this threshold is to measure the @@ -184,6 +191,7 @@ public class GestureLauncherService extends SystemService { private int mEmergencyGesturePowerButtonCooldownPeriodMs; private long mLastPowerDown; + private long mFirstPowerDown; private long mLastEmergencyGestureTriggered; private int mPowerButtonConsecutiveTaps; private int mPowerButtonSlowConsecutiveTaps; @@ -553,10 +561,12 @@ public class GestureLauncherService extends SystemService { mLastPowerDown = event.getEventTime(); if (powerTapInterval >= POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS) { // Tap too slow, reset consecutive tap counts. + mFirstPowerDown = event.getEventTime(); mPowerButtonConsecutiveTaps = 1; mPowerButtonSlowConsecutiveTaps = 1; } else if (powerTapInterval >= CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS) { // Tap too slow for shortcuts + mFirstPowerDown = event.getEventTime(); mPowerButtonConsecutiveTaps = 1; mPowerButtonSlowConsecutiveTaps++; } else { @@ -575,7 +585,26 @@ public class GestureLauncherService extends SystemService { intercept = interactive; } if (mPowerButtonConsecutiveTaps == EMERGENCY_GESTURE_POWER_TAP_COUNT_THRESHOLD) { - launchEmergencyGesture = true; + long emergencyGestureSpentTime = event.getEventTime() - mFirstPowerDown; + long emergencyGestureTapDetectionMinTimeMs = Settings.Global.getInt( + mContext.getContentResolver(), + Settings.Global.EMERGENCY_GESTURE_TAP_DETECTION_MIN_TIME_MS, + EMERGENCY_GESTURE_TAP_DETECTION_MIN_TIME_MS); + if (emergencyGestureSpentTime <= emergencyGestureTapDetectionMinTimeMs) { + Slog.i(TAG, "Emergency gesture detected but it's too fast. Gesture time: " + + emergencyGestureSpentTime + " ms"); + // Reset consecutive tap counts. + mFirstPowerDown = event.getEventTime(); + mPowerButtonConsecutiveTaps = 1; + mPowerButtonSlowConsecutiveTaps = 1; + } else { + Slog.i(TAG, "Emergency gesture detected. Gesture time: " + + emergencyGestureSpentTime + " ms"); + launchEmergencyGesture = true; + mMetricsLogger.histogram("emergency_gesture_spent_time", + (int) emergencyGestureSpentTime); + + } } } if (mCameraDoubleTapPowerEnabled diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index e29f11a42c18..35f7e064e358 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -4821,7 +4821,7 @@ public class ActivityManagerService extends IActivityManager.Stub } checkTime(startTime, "attachApplicationLocked: immediately before bindApplication"); - bindApplicationTimeMillis = SystemClock.elapsedRealtime(); + bindApplicationTimeMillis = SystemClock.uptimeMillis(); mAtmInternal.preBindApplication(app.getWindowProcessController()); final ActiveInstrumentation instr2 = app.getActiveInstrumentation(); if (mPlatformCompat != null) { @@ -4969,9 +4969,9 @@ public class ActivityManagerService extends IActivityManager.Stub pid, app.info.packageName, FrameworkStatsLog.PROCESS_START_TIME__TYPE__COLD, - app.getStartTime(), - (int) (bindApplicationTimeMillis - app.getStartTime()), - (int) (SystemClock.elapsedRealtime() - app.getStartTime()), + app.getStartElapsedTime(), + (int) (bindApplicationTimeMillis - app.getStartUptime()), + (int) (SystemClock.uptimeMillis() - app.getStartUptime()), app.getHostingRecord().getType(), (app.getHostingRecord().getName() != null ? app.getHostingRecord().getName() : "")); return true; diff --git a/services/core/java/com/android/server/am/AppBatteryTracker.java b/services/core/java/com/android/server/am/AppBatteryTracker.java index 64ff532b026a..90201a033668 100644 --- a/services/core/java/com/android/server/am/AppBatteryTracker.java +++ b/services/core/java/com/android/server/am/AppBatteryTracker.java @@ -654,7 +654,7 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy> final long start = stats.getStatsStartTimestamp(); final long end = stats.getStatsEndTimestamp(); final double scale = expectedDuration > 0 - ? (expectedDuration * 1.0d) / (end - start) : 1.0d; + ? Math.min((expectedDuration * 1.0d) / (end - start), 1.0d) : 1.0d; final AppBatteryPolicy bgPolicy = mInjector.getPolicy(); for (UidBatteryConsumer uidConsumer : uidConsumers) { // TODO: b/200326767 - as we are not supporting per proc state attribution yet, diff --git a/services/core/java/com/android/server/am/AppRestrictionController.java b/services/core/java/com/android/server/am/AppRestrictionController.java index 798647e04325..635d86c69985 100644 --- a/services/core/java/com/android/server/am/AppRestrictionController.java +++ b/services/core/java/com/android/server/am/AppRestrictionController.java @@ -223,6 +223,11 @@ public final class AppRestrictionController { private static final String ATTR_LEVEL_TS = "levelts"; private static final String ATTR_REASON = "reason"; + private static final String[] ROLES_IN_INTEREST = { + RoleManager.ROLE_DIALER, + RoleManager.ROLE_EMERGENCY, + }; + private final Context mContext; private final HandlerThread mBgHandlerThread; private final BgHandler mBgHandler; @@ -1386,6 +1391,7 @@ public final class AppRestrictionController { initBgRestrictionExemptioFromSysConfig(); initRestrictionStates(); initSystemModuleNames(); + initRolesInInterest(); registerForUidObservers(); registerForSystemBroadcasts(); mNotificationHelper.onSystemReady(); @@ -2666,6 +2672,18 @@ public final class AppRestrictionController { } } + private void initRolesInInterest() { + final int[] allUsers = mInjector.getUserManagerInternal().getUserIds(); + for (String role : ROLES_IN_INTEREST) { + if (mInjector.getRoleManager().isRoleAvailable(role)) { + for (int userId : allUsers) { + final UserHandle user = UserHandle.of(userId); + onRoleHoldersChanged(role, user); + } + } + } + } + private void onRoleHoldersChanged(@NonNull String roleName, @NonNull UserHandle user) { final List<String> rolePkgs = mInjector.getRoleManager().getRoleHoldersAsUser( roleName, user); diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index 0518899fd946..8a7fece5905c 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -61,7 +61,6 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; -import android.os.PowerExemptionManager; import android.os.PowerExemptionManager.ReasonCode; import android.os.PowerExemptionManager.TempAllowListType; import android.os.Process; @@ -1934,9 +1933,6 @@ public final class BroadcastQueue { } private void maybeReportBroadcastDispatchedEventLocked(BroadcastRecord r, int targetUid) { - // STOPSHIP (217251579): Temporarily use temp-allowlist reason to identify - // push messages and record response events. - useTemporaryAllowlistReasonAsSignal(r); if (r.options == null || r.options.getIdForResponseEvent() <= 0) { return; } @@ -1951,17 +1947,6 @@ public final class BroadcastQueue { mService.getUidStateLocked(targetUid)); } - private void useTemporaryAllowlistReasonAsSignal(BroadcastRecord r) { - if (r.options == null || r.options.getIdForResponseEvent() > 0) { - return; - } - final int reasonCode = r.options.getTemporaryAppAllowlistReasonCode(); - if (reasonCode == PowerExemptionManager.REASON_PUSH_MESSAGING - || reasonCode == PowerExemptionManager.REASON_PUSH_MESSAGING_OVER_QUOTA) { - r.options.recordResponseEventWhileInBackground(reasonCode); - } - } - @NonNull private UsageStatsManagerInternal getUsageStatsManagerInternal() { final UsageStatsManagerInternal usageStatsManagerInternal = diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java index 7db99f1da130..be112537274a 100644 --- a/services/core/java/com/android/server/camera/CameraServiceProxy.java +++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java @@ -239,6 +239,8 @@ public class CameraServiceProxy extends SystemService public long mResultErrorCount; public boolean mDeviceError; public List<CameraStreamStats> mStreamStats; + public String mUserTag; + private long mDurationOrStartTimeMs; // Either start time, or duration once completed CameraUsageEvent(String cameraId, int facing, String clientName, int apiLevel, @@ -257,7 +259,7 @@ public class CameraServiceProxy extends SystemService public void markCompleted(int internalReconfigure, long requestCount, long resultErrorCount, boolean deviceError, - List<CameraStreamStats> streamStats) { + List<CameraStreamStats> streamStats, String userTag) { if (mCompleted) { return; } @@ -268,6 +270,7 @@ public class CameraServiceProxy extends SystemService mResultErrorCount = resultErrorCount; mDeviceError = deviceError; mStreamStats = streamStats; + mUserTag = userTag; if (CameraServiceProxy.DEBUG) { Slog.v(TAG, "A camera facing " + cameraFacingToString(mCameraFacing) + " was in use by " + mClientName + " for " + @@ -794,7 +797,8 @@ public class CameraServiceProxy extends SystemService + ", requestCount " + e.mRequestCount + ", resultErrorCount " + e.mResultErrorCount + ", deviceError " + e.mDeviceError - + ", streamCount is " + streamCount); + + ", streamCount is " + streamCount + + ", userTag is " + e.mUserTag); } // Convert from CameraStreamStats to CameraStreamProto CameraStreamProto[] streamProtos = new CameraStreamProto[MAX_STREAM_STATISTICS]; @@ -851,7 +855,8 @@ public class CameraServiceProxy extends SystemService MessageNano.toByteArray(streamProtos[1]), MessageNano.toByteArray(streamProtos[2]), MessageNano.toByteArray(streamProtos[3]), - MessageNano.toByteArray(streamProtos[4])); + MessageNano.toByteArray(streamProtos[4]), + e.mUserTag); } } @@ -1038,6 +1043,7 @@ public class CameraServiceProxy extends SystemService long resultErrorCount = cameraState.getResultErrorCount(); boolean deviceError = cameraState.getDeviceErrorFlag(); List<CameraStreamStats> streamStats = cameraState.getStreamStats(); + String userTag = cameraState.getUserTag(); synchronized(mLock) { // Update active camera list and notify NFC if necessary boolean wasEmpty = mActiveCameraUsage.isEmpty(); @@ -1091,7 +1097,8 @@ public class CameraServiceProxy extends SystemService if (oldEvent != null) { Slog.w(TAG, "Camera " + cameraId + " was already marked as active"); oldEvent.markCompleted(/*internalReconfigure*/0, /*requestCount*/0, - /*resultErrorCount*/0, /*deviceError*/false, streamStats); + /*resultErrorCount*/0, /*deviceError*/false, streamStats, + /*userTag*/""); mCameraUsageHistory.add(oldEvent); } break; @@ -1101,7 +1108,7 @@ public class CameraServiceProxy extends SystemService if (doneEvent != null) { doneEvent.markCompleted(internalReconfigureCount, requestCount, - resultErrorCount, deviceError, streamStats); + resultErrorCount, deviceError, streamStats, userTag); mCameraUsageHistory.add(doneEvent); // Check current active camera IDs to see if this package is still diff --git a/services/core/java/com/android/server/pm/AppsFilterImpl.java b/services/core/java/com/android/server/pm/AppsFilterImpl.java index b4ddda551c76..dff7100e90e3 100644 --- a/services/core/java/com/android/server/pm/AppsFilterImpl.java +++ b/services/core/java/com/android/server/pm/AppsFilterImpl.java @@ -228,15 +228,6 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable * Watchable machinery */ private final WatchableImpl mWatchable = new WatchableImpl(); - /** - * The observer that watches for changes from array members - */ - private final Watcher mObserver = new Watcher() { - @Override - public void onChange(@Nullable Watchable what) { - AppsFilterImpl.this.dispatchChange(what); - } - }; /** * Ensures an observer is in the list, exactly once. The observer cannot be null. The @@ -331,8 +322,6 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable mProtectedBroadcastsSnapshot = new SnapshotCache.Auto<>( mProtectedBroadcasts, mProtectedBroadcasts, "AppsFilter.mProtectedBroadcasts"); - registerObservers(); - Watchable.verifyWatchedAttributes(this, mObserver); mSnapshot = makeCache(); } @@ -375,18 +364,6 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable mSystemReady = true; } - @SuppressWarnings("GuardedBy") - private void registerObservers() { - mImplicitlyQueryable.registerObserver(mObserver); - mRetainedImplicitlyQueryable.registerObserver(mObserver); - mQueriesViaPackage.registerObserver(mObserver); - mQueriesViaComponent.registerObserver(mObserver); - mQueryableViaUsesLibrary.registerObserver(mObserver); - mForceQueryable.registerObserver(mObserver); - mProtectedBroadcasts.registerObserver(mObserver); - mShouldFilterCache.registerObserver(mObserver); - } - /** * Return a snapshot. If the cached snapshot is null, build a new one. The logic in * the function ensures that this function returns a valid snapshot even if a race @@ -773,9 +750,7 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable mShouldFilterCache.put(recipientUid, visibleUid, false); } } - if (changed) { - onChanged(); - } + onChanged(); return changed; } @@ -986,6 +961,7 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable } updateEntireShouldFilterCacheInner(settings, users, userId); }); + onChanged(); } private void updateEntireShouldFilterCacheInner( @@ -1008,8 +984,7 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable private void updateEntireShouldFilterCacheAsync() { mBackgroundExecutor.execute(() -> { final ArrayMap<String, PackageStateInternal> settingsCopy = new ArrayMap<>(); - final Collection<SharedUserSetting> sharedUserSettingsCopy = - new ArraySet<SharedUserSetting>(); + final Collection<SharedUserSetting> sharedUserSettingsCopy = new ArraySet<>(); final ArrayMap<String, AndroidPackage> packagesCache = new ArrayMap<>(); final UserInfo[][] usersRef = new UserInfo[1][]; mStateProvider.runWithState((settings, sharedUserSettings, users) -> { @@ -1049,6 +1024,7 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable } else { updateEntireShouldFilterCacheInner(settingsCopy, usersRef[0], USER_ALL); + onChanged(); } }); } @@ -1058,7 +1034,6 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable return; } updateEntireShouldFilterCache(newUserId); - onChanged(); } public void onUserDeleted(@UserIdInt int userId) { @@ -1078,6 +1053,7 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable settings.get(packageName), settings, users, USER_ALL, settings.size() /*maxIndex*/); }); + onChanged(); } private void updateShouldFilterCacheForPackage( @@ -1389,9 +1365,8 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable } } } - - onChanged(); }); + onChanged(); } private ArraySet<? extends PackageStateInternal> getSharedUserPackages(int sharedUserAppId, @@ -1495,6 +1470,7 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable mStateProvider.runWithState((settings, sharedUserSettings, users) -> callingSharedPkgSettings.addAll(getSharedUserPackages( packageState.getSharedUserAppId(), sharedUserSettings))); + } else { callingPkgSetting = packageState; } @@ -1624,9 +1600,12 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mQueriesViaComponent"); } if (mQueriesViaComponentRequireRecompute) { + final ArrayMap<String, PackageStateInternal> settingsCopy = new ArrayMap<>(); mStateProvider.runWithState((settings, sharedUserSettings, users) -> { - recomputeComponentVisibility(settings); + settingsCopy.putAll(settings); }); + recomputeComponentVisibility(settingsCopy); + onChanged(); } synchronized (mLock) { if (mQueriesViaComponent.contains(callingAppId, targetAppId)) { diff --git a/services/core/java/com/android/server/pm/ChangedPackagesTracker.java b/services/core/java/com/android/server/pm/ChangedPackagesTracker.java index bd12981bfced..380213576b2f 100644 --- a/services/core/java/com/android/server/pm/ChangedPackagesTracker.java +++ b/services/core/java/com/android/server/pm/ChangedPackagesTracker.java @@ -90,25 +90,27 @@ class ChangedPackagesTracker { } void updateSequenceNumber(@NonNull String packageName, int[] userList) { - for (int i = userList.length - 1; i >= 0; --i) { - final int userId = userList[i]; - SparseArray<String> changedPackages = mUserIdToSequenceToPackage.get(userId); - if (changedPackages == null) { - changedPackages = new SparseArray<>(); - mUserIdToSequenceToPackage.put(userId, changedPackages); - } - Map<String, Integer> sequenceNumbers = mChangedPackagesSequenceNumbers.get(userId); - if (sequenceNumbers == null) { - sequenceNumbers = new HashMap<>(); - mChangedPackagesSequenceNumbers.put(userId, sequenceNumbers); - } - final Integer sequenceNumber = sequenceNumbers.get(packageName); - if (sequenceNumber != null) { - changedPackages.remove(sequenceNumber); + synchronized (mLock) { + for (int i = userList.length - 1; i >= 0; --i) { + final int userId = userList[i]; + SparseArray<String> changedPackages = mUserIdToSequenceToPackage.get(userId); + if (changedPackages == null) { + changedPackages = new SparseArray<>(); + mUserIdToSequenceToPackage.put(userId, changedPackages); + } + Map<String, Integer> sequenceNumbers = mChangedPackagesSequenceNumbers.get(userId); + if (sequenceNumbers == null) { + sequenceNumbers = new HashMap<>(); + mChangedPackagesSequenceNumbers.put(userId, sequenceNumbers); + } + final Integer sequenceNumber = sequenceNumbers.get(packageName); + if (sequenceNumber != null) { + changedPackages.remove(sequenceNumber); + } + changedPackages.put(mChangedPackagesSequenceNumber, packageName); + sequenceNumbers.put(packageName, mChangedPackagesSequenceNumber); } - changedPackages.put(mChangedPackagesSequenceNumber, packageName); - sequenceNumbers.put(packageName, mChangedPackagesSequenceNumber); + mChangedPackagesSequenceNumber++; } - mChangedPackagesSequenceNumber++; } } diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java index 7dae22a44cc5..d3d291ea52ac 100644 --- a/services/core/java/com/android/server/pm/DeletePackageHelper.java +++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java @@ -108,7 +108,7 @@ final class DeletePackageHelper { } /** - * This method is an internal method that could be get invoked either + * This method is an internal method that could be invoked either * to delete an installed package or to clean up a failed installation. * After deleting an installed package, a broadcast is sent to notify any * listeners that the package has been removed. For cleaning up a failed @@ -146,6 +146,8 @@ final class DeletePackageHelper { int[] allUsers; final int freezeUser; final SparseArray<TempUserState> priorUserStates; + + final boolean isInstallerPackage; /** enabled state of the uninstalled application */ synchronized (mPm.mLock) { final Computer computer = mPm.snapshotComputer(); @@ -226,6 +228,8 @@ final class DeletePackageHelper { freezeUser = removeUser; priorUserStates = null; } + + isInstallerPackage = mPm.mSettings.isInstallerPackage(packageName); } synchronized (mPm.mInstallLock) { @@ -324,6 +328,12 @@ final class DeletePackageHelper { } } + if (res && isInstallerPackage) { + final PackageInstallerService packageInstallerService = + mPm.mInjector.getPackageInstallerService(); + packageInstallerService.onInstallerPackageDeleted(uninstalledPs.getAppId(), removeUser); + } + return res ? PackageManager.DELETE_SUCCEEDED : PackageManager.DELETE_FAILED_INTERNAL_ERROR; } diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 9b5984e09c8b..e406a1a4bca7 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -861,6 +861,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements synchronized (mSessions) { mSessions.put(sessionId, session); } + mPm.addInstallerPackageName(session.getInstallSource()); mCallbacks.notifySessionCreated(session.sessionId, session.userId); @@ -1735,4 +1736,37 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements .setPackage(sessionInfo.installerPackageName); mContext.sendBroadcastAsUser(sessionUpdatedIntent, UserHandle.of(userId)); } + + /** + * Abandon unfinished sessions if the installer package has been uninstalled. + * @param installerAppId the app ID of the installer package that has been uninstalled. + * @param userId the user that has the installer package uninstalled. + */ + void onInstallerPackageDeleted(int installerAppId, int userId) { + synchronized (mSessions) { + for (int i = 0; i < mSessions.size(); i++) { + final PackageInstallerSession session = mSessions.valueAt(i); + if (!matchesInstaller(session, installerAppId, userId)) { + continue; + } + // Find parent session and only abandon parent session if installer matches + PackageInstallerSession root = !session.hasParentSessionId() + ? session : mSessions.get(session.getParentSessionId()); + if (root != null && matchesInstaller(root, installerAppId, userId) + && !root.isDestroyed()) { + root.abandon(); + } + } + } + } + + private boolean matchesInstaller(PackageInstallerSession session, int installerAppId, + int userId) { + final int installerUid = session.getInstallerUid(); + if (installerAppId == UserHandle.USER_ALL) { + return UserHandle.getAppId(installerUid) == installerAppId; + } else { + return UserHandle.getUid(userId, installerAppId) == installerUid; + } + } } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index ce1ee70ca5ac..ffd924edc7d8 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -1543,6 +1543,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService } // Link watchables to the class + @SuppressWarnings("GuardedBy") private void registerObservers(boolean verify) { // Null check to handle nullable test parameters if (mPackages != null) { @@ -2256,7 +2257,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService @GuardedBy("mLock") void updateInstantAppInstallerLocked(String modifiedPackage) { - // we're only interested in updating the installer appliction when 1) it's not + // we're only interested in updating the installer application when 1) it's not // already set or 2) the modified package is the installer if (mInstantAppInstallerActivity != null && !mInstantAppInstallerActivity.getComponentName().getPackageName() @@ -2740,7 +2741,6 @@ public class PackageManagerService implements PackageSender, TestUtilityService return mModuleInfoProvider.getModuleInfo(packageName, flags); } - @GuardedBy("mLock") void updateSequenceNumberLP(PackageSetting pkgSetting, int[] userList) { mChangedPackagesTracker.updateSequenceNumber(pkgSetting.getPackageName(), userList); } @@ -3074,8 +3074,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService userId); } - private void enforceCanSetDistractingPackageRestrictionsAsUser(@NonNull Computer snapshot, - int callingUid, int userId, String callingMethod) { + private void enforceCanSetDistractingPackageRestrictionsAsUser(int callingUid, int userId, + String callingMethod) { mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS, callingMethod); @@ -3159,6 +3159,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService } } + @SuppressWarnings("GuardedBy") VersionInfo getSettingsVersionForPackage(AndroidPackage pkg) { if (pkg.isExternalStorage()) { if (TextUtils.isEmpty(pkg.getVolumeUuid())) { @@ -3288,6 +3289,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService * Update component enabled settings to {@link PackageManager#COMPONENT_ENABLED_STATE_DEFAULT} * if the resetEnabledSettingsOnAppDataCleared is {@code true}. */ + @GuardedBy("mLock") private void resetComponentEnabledSettingsIfNeededLPw(String packageName, int userId) { final AndroidPackage pkg = packageName != null ? mPackages.get(packageName) : null; if (pkg == null || !pkg.isResetEnabledSettingsOnAppDataCleared()) { @@ -3879,6 +3881,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService } } + @GuardedBy("mLock") private boolean setEnabledSettingInternalLocked(@NonNull Computer computer, PackageSetting pkgSetting, ComponentEnabledSetting setting, @UserIdInt int userId, String callingPackage) { @@ -4554,7 +4557,9 @@ public class PackageManagerService implements PackageSender, TestUtilityService final Computer snapshot = snapshotComputer(); unsuspendForSuspendingPackage(snapshot, packageName, userId); removeAllDistractingPackageRestrictions(snapshot, userId); - flushPackageRestrictionsAsUserInternalLocked(userId); + synchronized (mLock) { + flushPackageRestrictionsAsUserInternalLocked(userId); + } } } if (observer != null) { @@ -4972,6 +4977,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService } @Override + @SuppressWarnings("GuardedBy") public int getRuntimePermissionsVersion(@UserIdInt int userId) { Preconditions.checkArgumentNonnegative(userId); enforceAdjustRuntimePermissionsPolicyOrUpgradeRuntimePermissions( @@ -5576,7 +5582,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService int restrictionFlags, int userId) { final int callingUid = Binder.getCallingUid(); final Computer snapshot = snapshotComputer(); - enforceCanSetDistractingPackageRestrictionsAsUser(snapshot, callingUid, userId, + enforceCanSetDistractingPackageRestrictionsAsUser(callingUid, userId, "setDistractingPackageRestrictionsAsUser"); Objects.requireNonNull(packageNames, "packageNames cannot be null"); return mDistractingPackageHelper.setDistractingPackageRestrictionsAsUser(snapshot, @@ -5845,6 +5851,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService } @Override + @SuppressWarnings("GuardedBy") public void setRuntimePermissionsVersion(int version, @UserIdInt int userId) { Preconditions.checkArgumentNonnegative(version); Preconditions.checkArgumentNonnegative(userId); @@ -6363,6 +6370,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService } @Override + @SuppressWarnings("GuardedBy") public void updateRuntimePermissionsFingerprint(@UserIdInt int userId) { mSettings.updateRuntimePermissionsFingerprint(userId); } @@ -6397,6 +6405,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService } @Override + @SuppressWarnings("GuardedBy") public boolean isPermissionUpgradeNeeded(int userId) { return mSettings.isPermissionUpgradeNeeded(userId); } @@ -7168,4 +7177,10 @@ public class PackageManagerService implements PackageSender, TestUtilityService void notifyInstantAppPackageInstalled(String packageName, int[] newUsers) { mInstantAppRegistry.onPackageInstalled(snapshotComputer(), packageName, newUsers); } + + void addInstallerPackageName(InstallSource installSource) { + synchronized (mLock) { + mSettings.addInstallerPackageNames(installSource); + } + } } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index b53cfc558f6e..e6d59d43ffbe 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -380,8 +380,8 @@ public final class Settings implements Watchable, Snappable { private final SnapshotCache<WatchedArrayMap<String, PackageSetting>> mPackagesSnapshot; /** - * List of packages that were involved in installing other packages, i.e. are listed - * in at least one app's InstallSource. + * List of packages that were involved in installing other packages, i.e. packages that created + * new sessions or are listed in at least one app's InstallSource. */ @Watched private final WatchedArraySet<String> mInstallerPackages; @@ -5923,4 +5923,8 @@ public final class Settings implements Watchable, Snappable { } } } + + boolean isInstallerPackage(@NonNull String packageName) { + return mInstallerPackages.contains(packageName); + } } diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index 056f25542294..9627c4394db7 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -105,6 +105,7 @@ import android.util.TypedXmlSerializer; import android.util.Xml; import android.view.IWindowManager; +import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; @@ -455,6 +456,8 @@ public class ShortcutService extends IShortcutService.Stub { private final boolean mIsAppSearchEnabled; + private ComponentName mChooserActivity; + static class InvalidFileFormatException extends Exception { public InvalidFileFormatException(String message, Throwable cause) { super(message, cause); @@ -1646,6 +1649,26 @@ public class ShortcutService extends IShortcutService.Stub { return callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID; } + @VisibleForTesting + ComponentName injectChooserActivity() { + if (mChooserActivity == null) { + mChooserActivity = ComponentName.unflattenFromString( + mContext.getResources().getString(R.string.config_chooserActivity)); + } + return mChooserActivity; + } + + private boolean isCallerChooserActivity() { + // TODO(b/228975502): Migrate this check to a proper permission or role check + final int callingUid = injectBinderCallingUid(); + ComponentName systemChooser = injectChooserActivity(); + if (systemChooser == null) { + return false; + } + int uid = injectGetPackageUid(systemChooser.getPackageName(), UserHandle.USER_SYSTEM); + return uid == callingUid; + } + private void enforceSystemOrShell() { if (!(isCallerSystem() || isCallerShell())) { throw new SecurityException("Caller must be system or shell"); @@ -2525,7 +2548,9 @@ public class ShortcutService extends IShortcutService.Stub { IntentFilter filter, @UserIdInt int userId) { Preconditions.checkStringNotEmpty(packageName, "packageName"); Objects.requireNonNull(filter, "intentFilter"); - verifyCaller(packageName, userId); + if (!isCallerChooserActivity()) { + verifyCaller(packageName, userId); + } enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_APP_PREDICTIONS, "getShareTargets"); synchronized (mLock) { diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java index aede4b1e2f5d..685b744c8062 100644 --- a/services/core/java/com/android/server/power/Notifier.java +++ b/services/core/java/com/android/server/power/Notifier.java @@ -64,6 +64,8 @@ import com.android.server.policy.WindowManagerPolicy; import com.android.server.statusbar.StatusBarManagerInternal; import java.io.PrintWriter; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; /** * Sends broadcasts about important power state changes. @@ -133,6 +135,7 @@ public class Notifier { private final DisplayManagerInternal mDisplayManagerInternal; private final NotifierHandler mHandler; + private final Executor mBackgroundExecutor; private final Intent mScreenOnIntent; private final Intent mScreenOffIntent; @@ -169,9 +172,12 @@ public class Notifier { // True if a user activity message should be sent. private boolean mUserActivityPending; + private final AtomicBoolean mIsPlayingChargingStartedFeedback = new AtomicBoolean(false); + public Notifier(Looper looper, Context context, IBatteryStats batteryStats, SuspendBlocker suspendBlocker, WindowManagerPolicy policy, - FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector) { + FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector, + Executor backgroundExecutor) { mContext = context; mBatteryStats = batteryStats; mAppOps = mContext.getSystemService(AppOpsManager.class); @@ -188,6 +194,7 @@ public class Notifier { mVibrator = mContext.getSystemService(Vibrator.class); mHandler = new NotifierHandler(looper); + mBackgroundExecutor = backgroundExecutor; mScreenOnIntent = new Intent(Intent.ACTION_SCREEN_ON); mScreenOnIntent.addFlags( Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND @@ -824,25 +831,36 @@ public class Notifier { return; } - // vibrate - final boolean vibrate = Settings.Secure.getIntForUser(mContext.getContentResolver(), - Settings.Secure.CHARGING_VIBRATION_ENABLED, 1, userId) != 0; - if (vibrate) { - mVibrator.vibrate(CHARGING_VIBRATION_EFFECT, HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES); + if (!mIsPlayingChargingStartedFeedback.compareAndSet(false, true)) { + // there's already a charging started feedback Runnable scheduled to run on the + // background thread, so let's not execute another + return; } - // play sound - final String soundPath = Settings.Global.getString(mContext.getContentResolver(), - wireless ? Settings.Global.WIRELESS_CHARGING_STARTED_SOUND - : Settings.Global.CHARGING_STARTED_SOUND); - final Uri soundUri = Uri.parse("file://" + soundPath); - if (soundUri != null) { - final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri); - if (sfx != null) { - sfx.setStreamType(AudioManager.STREAM_SYSTEM); - sfx.play(); + // vibrate & play sound on a background thread + mBackgroundExecutor.execute(() -> { + // vibrate + final boolean vibrate = Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.CHARGING_VIBRATION_ENABLED, 1, userId) != 0; + if (vibrate) { + mVibrator.vibrate(CHARGING_VIBRATION_EFFECT, + HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES); } - } + + // play sound + final String soundPath = Settings.Global.getString(mContext.getContentResolver(), + wireless ? Settings.Global.WIRELESS_CHARGING_STARTED_SOUND + : Settings.Global.CHARGING_STARTED_SOUND); + final Uri soundUri = Uri.parse("file://" + soundPath); + if (soundUri != null) { + final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri); + if (sfx != null) { + sfx.setStreamType(AudioManager.STREAM_SYSTEM); + sfx.play(); + } + } + mIsPlayingChargingStartedFeedback.set(false); + }); } private void showWirelessChargingStarted(int batteryLevel, @UserIdInt int userId) { diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 6e78ecb83005..e0da0e8bdf35 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -140,6 +140,7 @@ import java.util.Arrays; import java.util.List; import java.util.NoSuchElementException; import java.util.Objects; +import java.util.concurrent.Executor; /** * The power manager service is responsible for coordinating power management @@ -905,10 +906,11 @@ public final class PowerManagerService extends SystemService static class Injector { Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats, SuspendBlocker suspendBlocker, WindowManagerPolicy policy, - FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector) { + FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector, + Executor backgroundExecutor) { return new Notifier( looper, context, batteryStats, suspendBlocker, policy, faceDownDetector, - screenUndimDetector); + screenUndimDetector, backgroundExecutor); } SuspendBlocker createSuspendBlocker(PowerManagerService service, String name) { @@ -1227,7 +1229,8 @@ public final class PowerManagerService extends SystemService mBatteryStats = BatteryStatsService.getService(); mNotifier = mInjector.createNotifier(Looper.getMainLooper(), mContext, mBatteryStats, mInjector.createSuspendBlocker(this, "PowerManagerService.Broadcasts"), - mPolicy, mFaceDownDetector, mScreenUndimDetector); + mPolicy, mFaceDownDetector, mScreenUndimDetector, + BackgroundThread.getExecutor()); mPowerGroups.append(Display.DEFAULT_DISPLAY_GROUP, new PowerGroup(WAKEFULNESS_AWAKE, mPowerGroupWakefulnessChangeListener, diff --git a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java index bce1cce0f47f..08344170b02d 100644 --- a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java +++ b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java @@ -560,7 +560,7 @@ public final class SensorPrivacyService extends SystemService { + " sensors"); return; } - mContext.startActivityAsUser(dialogIntent, options.toBundle(), info.mUser); + mContext.startActivityAsUser(dialogIntent, options.toBundle(), UserHandle.SYSTEM); } /** diff --git a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessorImpl.java b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessorImpl.java index ae52912d4821..7173f6008177 100644 --- a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessorImpl.java +++ b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessorImpl.java @@ -59,6 +59,8 @@ public final class ServiceConfigAccessorImpl implements ServiceConfigAccessor { */ private static final Set<String> CONFIGURATION_INTERNAL_SERVER_FLAGS_KEYS_TO_WATCH = Set.of( ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED, + ServerFlags.KEY_PRIMARY_LTZP_MODE_OVERRIDE, + ServerFlags.KEY_SECONDARY_LTZP_MODE_OVERRIDE, ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_RUN_IN_BACKGROUND_ENABLED, ServerFlags.KEY_ENHANCED_METRICS_COLLECTION_ENABLED, ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT, @@ -443,6 +445,9 @@ public final class ServiceConfigAccessorImpl implements ServiceConfigAccessor { mTestPrimaryLocationTimeZoneProviderMode = mTestPrimaryLocationTimeZoneProviderPackageName == null ? PROVIDER_MODE_DISABLED : PROVIDER_MODE_ENABLED; + // Changing this state can affect the content of ConfigurationInternal, so listeners need to + // be informed. + mContext.getMainThreadHandler().post(this::handleConfigurationInternalChangeOnMainThread); } @Override @@ -469,6 +474,9 @@ public final class ServiceConfigAccessorImpl implements ServiceConfigAccessor { mTestSecondaryLocationTimeZoneProviderMode = mTestSecondaryLocationTimeZoneProviderPackageName == null ? PROVIDER_MODE_DISABLED : PROVIDER_MODE_ENABLED; + // Changing this state can affect the content of ConfigurationInternal, so listeners need to + // be informed. + mContext.getMainThreadHandler().post(this::handleConfigurationInternalChangeOnMainThread); } @Override @@ -573,6 +581,10 @@ public final class ServiceConfigAccessorImpl implements ServiceConfigAccessor { mTestSecondaryLocationTimeZoneProviderPackageName = null; mTestSecondaryLocationTimeZoneProviderMode = null; mRecordStateChangesForTests = false; + + // Changing LTZP config can affect the content of ConfigurationInternal, so listeners + // need to be informed. + mContext.getMainThreadHandler().post(this::handleConfigurationInternalChangeOnMainThread); } private boolean isTelephonyFallbackSupported() { diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java index f75608e14539..898d02e212f4 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java @@ -336,13 +336,13 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub boolean isTelephonyTimeZoneDetectionSupported() { enforceManageTimeZoneDetectorPermission(); - return mServiceConfigAccessor.isTelephonyTimeZoneDetectionFeatureSupported(); + return mTimeZoneDetectorStrategy.isTelephonyTimeZoneDetectionSupported(); } boolean isGeoTimeZoneDetectionSupported() { enforceManageTimeZoneDetectorPermission(); - return mServiceConfigAccessor.isGeoTimeZoneDetectionFeatureSupported(); + return mTimeZoneDetectorStrategy.isGeoTimeZoneDetectionSupported(); } /** diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java index 6b04adf7de61..95ebd6803cd0 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java @@ -124,4 +124,10 @@ public interface TimeZoneDetectorStrategy extends Dumpable { /** Generates a state snapshot for metrics. */ @NonNull MetricsTimeZoneDetectorState generateMetricsState(); + + /** Returns {@code true} if the device supports telephony time zone detection. */ + boolean isTelephonyTimeZoneDetectionSupported(); + + /** Returns {@code true} if the device supports geolocation time zone detection. */ + boolean isGeoTimeZoneDetectionSupported(); } diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java index e21d0e45ef0e..66c23f5d01a4 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java @@ -396,6 +396,20 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat getLatestGeolocationSuggestion()); } + @Override + public boolean isTelephonyTimeZoneDetectionSupported() { + synchronized (this) { + return mCurrentConfigurationInternal.isTelephonyDetectionSupported(); + } + } + + @Override + public boolean isGeoTimeZoneDetectionSupported() { + synchronized (this) { + return mCurrentConfigurationInternal.isGeoDetectionSupported(); + } + } + private static int scoreTelephonySuggestion(@NonNull TelephonyTimeZoneSuggestion suggestion) { int score; if (suggestion.getZoneId() == null) { diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index c52a4e499f1f..63619e543a6d 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -446,20 +446,6 @@ final class AccessibilityController { // Not relevant for the window observer. } - MagnificationSpec getMagnificationSpecForWindow(WindowState windowState) { - if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) { - mAccessibilityTracing.logTrace(TAG + ".getMagnificationSpecForWindow", - FLAGS_MAGNIFICATION_CALLBACK, - "windowState={" + windowState + "}"); - } - final int displayId = windowState.getDisplayId(); - final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); - if (displayMagnifier != null) { - return displayMagnifier.getMagnificationSpecForWindow(windowState); - } - return null; - } - boolean hasCallbacks() { if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) { diff --git a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java index ca636b3552e9..6e5011dacd82 100644 --- a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java +++ b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java @@ -596,8 +596,6 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener { * surface flinger to the accessibility framework. */ public static class AccessibilityWindow { - private static final Region TEMP_REGION = new Region(); - private static final RectF TEMP_RECTF = new RectF(); // Data private IWindow mWindow; private int mDisplayId; @@ -615,15 +613,16 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener { private final Region mLetterBoxBounds = new Region(); private WindowInfo mWindowInfo; + /** * Returns the instance after initializing the internal data. * @param service The window manager service. * @param inputWindowHandle The window from the surface flinger. - * @param inverseMatrix The magnification spec inverse matrix. + * @param magnificationInverseMatrix The magnification spec inverse matrix. */ public static AccessibilityWindow initializeData(WindowManagerService service, - InputWindowHandle inputWindowHandle, Matrix inverseMatrix, IBinder pipIBinder, - Matrix displayMatrix) { + InputWindowHandle inputWindowHandle, Matrix magnificationInverseMatrix, + IBinder pipIBinder, Matrix displayMatrix) { final IWindow window = inputWindowHandle.getWindow(); final WindowState windowState = window != null ? service.mWindowMap.get( window.asBinder()) : null; @@ -655,13 +654,35 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener { inputWindowHandle.frameTop, inputWindowHandle.frameRight, inputWindowHandle.frameBottom); getTouchableRegionInWindow(instance.mShouldMagnify, inputWindowHandle.touchableRegion, - instance.mTouchableRegionInWindow, windowFrame, inverseMatrix, displayMatrix); + instance.mTouchableRegionInWindow, windowFrame, magnificationInverseMatrix, + displayMatrix); getUnMagnifiedTouchableRegion(instance.mShouldMagnify, inputWindowHandle.touchableRegion, instance.mTouchableRegionInScreen, - inverseMatrix, displayMatrix); + magnificationInverseMatrix, displayMatrix); instance.mWindowInfo = windowState != null ? windowState.getWindowInfo() : getWindowInfoForWindowlessWindows(instance); + // Compute the transform matrix that will transform bounds from the window + // coordinates to screen coordinates. + final Matrix inverseTransform = new Matrix(); + inputWindowHandle.transform.invert(inverseTransform); + inverseTransform.postConcat(displayMatrix); + inverseTransform.getValues(instance.mWindowInfo.mTransformMatrix); + + // Compute the magnification spec matrix. + final Matrix magnificationSpecMatrix = new Matrix(); + if (instance.shouldMagnify() && magnificationInverseMatrix != null + && !magnificationInverseMatrix.isIdentity()) { + if (magnificationInverseMatrix.invert(magnificationSpecMatrix)) { + magnificationSpecMatrix.getValues(sTempFloats); + final MagnificationSpec spec = instance.mWindowInfo.mMagnificationSpec; + spec.scale = sTempFloats[Matrix.MSCALE_X]; + spec.offsetX = sTempFloats[Matrix.MTRANS_X]; + spec.offsetY = sTempFloats[Matrix.MTRANS_Y]; + } else { + Slog.w(TAG, "can't find spec"); + } + } return instance; } @@ -779,7 +800,7 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener { // for the consistency and match developers expectation. // So we need to make the intersection between the frame and touchable region to // obtain the real touch region in the screen. - Region touchRegion = TEMP_REGION; + Region touchRegion = new Region(); touchRegion.set(inRegion); touchRegion.op(frame, Region.Op.INTERSECT); @@ -807,8 +828,7 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener { forEachRect(inRegion, rect -> { // Move to origin as all transforms are captured by the matrix. - RectF windowFrame = TEMP_RECTF; - windowFrame.set(rect); + RectF windowFrame = new RectF(rect); displayMatrix.mapRect(windowFrame); inverseMatrix.mapRect(windowFrame); diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 36a7c7756a90..75d462131aae 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -2644,14 +2644,16 @@ class ActivityStarter { Slog.w(TAG, "startActivity called from finishing " + mSourceRecord + "; forcing " + "Intent.FLAG_ACTIVITY_NEW_TASK for: " + mIntent); mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK; - mNewTaskInfo = mSourceRecord.info; - // It is not guaranteed that the source record will have a task associated with it. For, - // example, if this method is being called for processing a pending activity launch, it - // is possible that the activity has been removed from the task after the launch was - // enqueued. + // It is not guaranteed that the source record will have a task associated with it. + // For example, if this method is being called for processing a pending activity + // launch, it is possible that the activity has been removed from the task after the + // launch was enqueued. final Task sourceTask = mSourceRecord.getTask(); - mNewTaskIntent = sourceTask != null ? sourceTask.intent : null; + if (sourceTask == null || sourceTask.getTopNonFinishingActivity() == null) { + mNewTaskInfo = mSourceRecord.info; + mNewTaskIntent = sourceTask != null ? sourceTask.intent : null; + } } mSourceRecord = null; mSourceRootTask = null; diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index b37f980ce9a0..0ed671804fe4 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -167,6 +167,23 @@ class BackNavigationController { currentActivity = window.mActivityRecord; currentTask = window.getTask(); callbackInfo = window.getOnBackInvokedCallbackInfo(); + final DisplayContent displayContent = window.getDisplayContent(); + + // When IME is shown, return the more prioritized callback between IME and app. + // Priority ordering follows: OVERLAY, IME, DEFAULT. + if (displayContent != null && displayContent.getImeContainer().isVisible()) { + WindowState imeWindow = displayContent.getImeContainer().getWindow( + windowState -> windowState.getOnBackInvokedCallbackInfo() != null); + if (imeWindow != null) { + OnBackInvokedCallbackInfo imeCallbackInfo = + imeWindow.getOnBackInvokedCallbackInfo(); + if (imeCallbackInfo != null && (callbackInfo == null + || callbackInfo.getPriority() <= imeCallbackInfo.getPriority())) { + callbackInfo = imeCallbackInfo; + } + } + } + if (callbackInfo == null) { Slog.e(TAG, "No callback registered, returning null."); return null; @@ -189,12 +206,10 @@ class BackNavigationController { // If we don't need to set up the animation, we return early. This is the case when // - We have an application callback. // - We don't have any ActivityRecord or Task to animate. - // - The IME is opened, and we just need to close it. // - The home activity is the focused activity. if (backType == BackNavigationInfo.TYPE_CALLBACK || currentActivity == null || currentTask == null - || currentTask.getDisplayContent().getImeContainer().isVisible() || currentActivity.isActivityTypeHome()) { return infoBuilder .setType(backType) diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index a7b37281842a..6162f12b516e 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -289,6 +289,23 @@ class InsetsPolicy { return adjustVisibilityForTransientTypes(originalState); } + /** + * @param type the internal type of the insets. + * @return {@code true} if the given type is controllable, {@code false} otherwise. + */ + static boolean isInsetsTypeControllable(@InternalInsetsType int type) { + switch (type) { + case ITYPE_STATUS_BAR: + case ITYPE_NAVIGATION_BAR: + case ITYPE_IME: + case ITYPE_CLIMATE_BAR: + case ITYPE_EXTRA_NAVIGATION_BAR: + return true; + default: + return false; + } + } + private static @InternalInsetsType int getInsetsTypeForLayoutParams( WindowManager.LayoutParams attrs) { @WindowManager.LayoutParams.WindowType int type = attrs.type; diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java index f3f08b202fed..e04644c564f8 100644 --- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java @@ -16,11 +16,7 @@ package com.android.server.wm; -import static android.view.InsetsState.ITYPE_CLIMATE_BAR; -import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_IME; -import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; -import static android.view.InsetsState.ITYPE_STATUS_BAR; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_INSETS; import static com.android.server.wm.InsetsSourceProviderProto.CAPTURED_LEASH; @@ -128,18 +124,7 @@ abstract class InsetsSourceProvider { mStateController = stateController; mFakeControl = new InsetsSourceControl( source.getType(), null /* leash */, new Point(), Insets.NONE); - - switch (source.getType()) { - case ITYPE_STATUS_BAR: - case ITYPE_NAVIGATION_BAR: - case ITYPE_IME: - case ITYPE_CLIMATE_BAR: - case ITYPE_EXTRA_NAVIGATION_BAR: - mControllable = true; - break; - default: - mControllable = false; - } + mControllable = InsetsPolicy.isInsetsTypeControllable(source.getType()); } InsetsSource getSource() { diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index d46061649676..6f69e0324b0b 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -2006,6 +2006,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // of the activity entering PIP r.getDisplayContent().prepareAppTransition(TRANSIT_NONE); + final TaskFragment organizedTf = r.getOrganizedTaskFragment(); // TODO: Does it make sense to only count non-finishing activities? final boolean singleActivity = task.getActivityCount() == 1; final Task rootTask; @@ -2043,6 +2044,14 @@ class RootWindowContainer extends WindowContainer<DisplayContent> task.clearLastRecentsAnimationTransaction(false /* forceRemoveOverlay */); } + // The organized TaskFragment is becoming empty because this activity is reparented + // to a new PIP Task. In this case, we should notify the organizer about why the + // TaskFragment becomes empty. + if (organizedTf != null && organizedTf.getNonFinishingActivityCount() == 1 + && organizedTf.getTopNonFinishingActivity() == r) { + organizedTf.mClearedTaskFragmentForPip = true; + } + // There are multiple activities in the task and moving the top activity should // reveal/leave the other activities in their original task. // On the other hand, ActivityRecord#onParentChanged takes care of setting the @@ -2107,6 +2116,13 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // Reset the state that indicates it can enter PiP while pausing after we've moved it // to the root pinned task r.supportsEnterPipOnTaskSwitch = false; + + if (organizedTf != null && organizedTf.mClearedTaskFragmentForPip) { + // Dispatch the pending info to TaskFragmentOrganizer before PIP animation. + // Otherwise, it will keep waiting for the empty TaskFragment to be non-empty. + mService.mTaskFragmentOrganizerController.dispatchPendingInfoChangedEvent( + organizedTf); + } } finally { mService.continueWindowLayout(); } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index ac048434c060..bd078d880980 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -2043,7 +2043,7 @@ class Task extends TaskFragment { Rect outOverrideBounds = getResolvedOverrideConfiguration().windowConfiguration.getBounds(); if (windowingMode == WINDOWING_MODE_FULLSCREEN) { - if (!mCreatedByOrganizer) { + if (!isOrganized()) { // Use empty bounds to indicate "fill parent". outOverrideBounds.setEmpty(); } @@ -2788,9 +2788,11 @@ class Task extends TaskFragment { } final Rect visibleFrame = sTmpBounds; + final WindowManager.LayoutParams attrs = win.mAttrs; visibleFrame.set(win.getFrame()); visibleFrame.inset(win.getInsetsStateWithVisibilityOverride().calculateVisibleInsets( - visibleFrame, win.mAttrs.softInputMode)); + visibleFrame, attrs.type, win.getWindowingMode(), attrs.softInputMode, + attrs.flags)); out.union(visibleFrame); } diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index 83bd979a8e2f..4e0d84c67d03 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -193,6 +193,12 @@ class TaskFragment extends WindowContainer<WindowContainer> { boolean mClearedTaskForReuse; /** + * The last running activity of the TaskFragment was reparented to a different Task because it + * is entering PiP. + */ + boolean mClearedTaskFragmentForPip; + + /** * When we are in the process of pausing an activity, before starting the * next one, this variable holds the activity that is currently being paused. * @@ -847,6 +853,16 @@ class TaskFragment extends WindowContainer<WindowContainer> { return getActivity((r) -> r.canBeTopRunning() && r.getTask() == this.getTask()); } + int getNonFinishingActivityCount() { + final int[] runningActivityCount = new int[1]; + forAllActivities(a -> { + if (!a.finishing) { + runningActivityCount[0]++; + } + }); + return runningActivityCount[0]; + } + boolean isTopActivityFocusable() { final ActivityRecord r = topRunningActivity(); return r != null ? r.isFocusable() @@ -1709,6 +1725,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { void addChild(WindowContainer child, int index) { ActivityRecord r = topRunningActivity(); mClearedTaskForReuse = false; + mClearedTaskFragmentForPip = false; boolean isAddingActivity = child.asActivityRecord() != null; final Task task = isAddingActivity ? getTask() : null; @@ -2253,22 +2270,16 @@ class TaskFragment extends WindowContainer<WindowContainer> { } final Point positionInParent = new Point(); getRelativePosition(positionInParent); - final int[] runningActivityCount = new int[1]; - forAllActivities(a -> { - if (!a.finishing) { - runningActivityCount[0]++; - } - }); return new TaskFragmentInfo( mFragmentToken, mRemoteToken.toWindowContainerToken(), getConfiguration(), - runningActivityCount[0] == 0, - runningActivityCount[0], + getNonFinishingActivityCount(), isVisible(), childActivities, positionInParent, - mClearedTaskForReuse); + mClearedTaskForReuse, + mClearedTaskFragmentForPip); } @Nullable diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index 8a0ae65267a3..9bf988f16090 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -38,6 +38,7 @@ import android.os.Parcel; import android.os.RemoteException; import android.util.Slog; import android.util.proto.ProtoOutputStream; +import android.view.Display; import android.view.SurfaceControl; import android.window.ITaskOrganizer; import android.window.ITaskOrganizerController; @@ -526,17 +527,17 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } final StartingWindowRemovalInfo removalInfo = new StartingWindowRemovalInfo(); removalInfo.taskId = task.mTaskId; - removalInfo.playRevealAnimation = prepareAnimation; + removalInfo.playRevealAnimation = prepareAnimation + && task.getDisplayInfo().state == Display.STATE_ON; final boolean playShiftUpAnimation = !task.inMultiWindowMode(); final ActivityRecord topActivity = task.topActivityContainsStartingWindow(); if (topActivity != null) { removalInfo.deferRemoveForIme = topActivity.mDisplayContent .mayImeShowOnLaunchingActivity(topActivity); - if (prepareAnimation && playShiftUpAnimation) { + if (removalInfo.playRevealAnimation && playShiftUpAnimation) { final WindowState mainWindow = topActivity.findMainWindow(false/* includeStartingApp */); if (mainWindow != null) { - final SurfaceControl.Transaction t = mainWindow.getPendingTransaction(); removalInfo.windowAnimationLeash = applyStartingWindowAnimation(mainWindow); removalInfo.mainFrame = mainWindow.getRelativeFrame(); } diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index be745966ce30..42fad6d14fb9 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -409,21 +409,6 @@ public abstract class WindowManagerInternal { public abstract void getMagnificationRegion(int displayId, @NonNull Region magnificationRegion); /** - * Gets the magnification and translation applied to a window given its token. - * Not all windows are magnified and the window manager policy determines which - * windows are magnified. The returned result also takes into account the compat - * scale if necessary. - * - * @param windowToken The window's token. - * - * @return The magnification spec for the window. - * - * @see #setMagnificationCallbacks(int, MagnificationCallbacks) - */ - public abstract MagnificationSpec getCompatibleMagnificationSpecForWindow( - IBinder windowToken); - - /** * Sets a callback for observing which windows are touchable for the purposes * of accessibility on specified display. * diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 4d262efd03d0..2f4dc515fd11 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -1976,8 +1976,10 @@ public class WindowManagerService extends IWindowManager.Stub // We use the visible frame, because we want the animation to morph the window from what // was visible to the user to the final destination of the new window. final Rect frame = new Rect(replacedWindow.getFrame()); + final WindowManager.LayoutParams attrs = replacedWindow.mAttrs; frame.inset(replacedWindow.getInsetsStateWithVisibilityOverride().calculateVisibleInsets( - frame, replacedWindow.mAttrs.softInputMode)); + frame, attrs.type, replacedWindow.getWindowingMode(), attrs.softInputMode, + attrs.flags)); // We treat this as if this activity was opening, so we can trigger the app transition // animation and piggy-back on existing transition animation infrastructure. final DisplayContent dc = activity.getDisplayContent(); @@ -7621,29 +7623,6 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public MagnificationSpec getCompatibleMagnificationSpecForWindow(IBinder windowToken) { - synchronized (mGlobalLock) { - WindowState windowState = mWindowMap.get(windowToken); - if (windowState == null) { - return null; - } - MagnificationSpec spec = null; - if (mAccessibilityController.hasCallbacks()) { - spec = mAccessibilityController.getMagnificationSpecForWindow(windowState); - } - if ((spec == null || spec.isNop()) && windowState.mGlobalScale == 1.0f) { - return null; - } - MagnificationSpec result = new MagnificationSpec(); - if (spec != null) { - result.setTo(spec); - } - result.scale *= windowState.mGlobalScale; - return result; - } - } - - @Override public boolean setMagnificationCallbacks(int displayId, @Nullable MagnificationCallbacks callbacks) { synchronized (mGlobalLock) { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index e7d4877ce514..238f96ffd1e1 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -1851,7 +1851,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP bounds.set(mWindowFrames.mFrame); bounds.inset(getInsetsStateWithVisibilityOverride().calculateVisibleInsets( - bounds, mAttrs.softInputMode)); + bounds, mAttrs.type, getWindowingMode(), mAttrs.softInputMode, mAttrs.flags)); if (intersectWithRootTaskBounds) { bounds.intersect(mTmpRect); } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceManagementResourcesProvider.java b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceManagementResourcesProvider.java index 82fe8b9362b0..6aef90c79705 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceManagementResourcesProvider.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceManagementResourcesProvider.java @@ -55,8 +55,6 @@ class DeviceManagementResourcesProvider { private static final String TAG_ROOT = "root"; private static final String TAG_DRAWABLE_STYLE_ENTRY = "drawable-style-entry"; private static final String TAG_DRAWABLE_SOURCE_ENTRY = "drawable-source-entry"; - private static final String ATTR_DRAWABLE_STYLE_SIZE = "drawable-style-size"; - private static final String ATTR_DRAWABLE_SOURCE_SIZE = "drawable-source-size"; private static final String ATTR_DRAWABLE_STYLE = "drawable-style"; private static final String ATTR_DRAWABLE_SOURCE = "drawable-source"; private static final String ATTR_DRAWABLE_ID = "drawable-id"; @@ -70,9 +68,9 @@ class DeviceManagementResourcesProvider { mUpdatedDrawablesForStyle = new HashMap<>(); /** - * Map of <drawable_id, <source_id, resource_value>> + * Map of <drawable_id, <source_id, <style_id, resource_value>>> */ - private final Map<String, Map<String, ParcelableResource>> + private final Map<String, Map<String, Map<String, ParcelableResource>>> mUpdatedDrawablesForSource = new HashMap<>(); /** @@ -110,7 +108,8 @@ class DeviceManagementResourcesProvider { if (DevicePolicyResources.UNDEFINED.equals(drawableSource)) { updated |= updateDrawable(drawableId, drawableStyle, resource); } else { - updated |= updateDrawableForSource(drawableId, drawableSource, resource); + updated |= updateDrawableForSource( + drawableId, drawableSource, drawableStyle, resource); } } if (!updated) { @@ -138,19 +137,23 @@ class DeviceManagementResourcesProvider { } } - // TODO(b/214576716): change this to respect style private boolean updateDrawableForSource( - String drawableId, String drawableSource, ParcelableResource updatableResource) { + String drawableId, String drawableSource, String drawableStyle, + ParcelableResource updatableResource) { synchronized (mLock) { + Map<String, Map<String, ParcelableResource>> drawablesForId = + mUpdatedDrawablesForSource.get(drawableId); if (!mUpdatedDrawablesForSource.containsKey(drawableId)) { mUpdatedDrawablesForSource.put(drawableId, new HashMap<>()); } - ParcelableResource current = mUpdatedDrawablesForSource.get(drawableId).get( - drawableSource); + if (!drawablesForId.containsKey(drawableSource)) { + mUpdatedDrawablesForSource.get(drawableId).put(drawableSource, new HashMap<>()); + } + ParcelableResource current = drawablesForId.get(drawableSource).get(drawableStyle); if (updatableResource.equals(current)) { return false; } - mUpdatedDrawablesForSource.get(drawableId).put(drawableSource, updatableResource); + drawablesForId.get(drawableSource).put(drawableStyle, updatableResource); return true; } } @@ -175,23 +178,30 @@ class DeviceManagementResourcesProvider { } @Nullable - ParcelableResource getDrawable( - String drawableId, String drawableStyle, String drawableSource) { + ParcelableResource getDrawable(String drawableId, String drawableStyle, String drawableSource) { synchronized (mLock) { - if (mUpdatedDrawablesForSource.containsKey(drawableId) - && mUpdatedDrawablesForSource.get(drawableId).containsKey(drawableSource)) { - return mUpdatedDrawablesForSource.get(drawableId).get(drawableSource); + ParcelableResource resource = getDrawableForSourceLocked( + drawableId, drawableStyle, drawableSource); + if (resource != null) { + return resource; } if (!mUpdatedDrawablesForStyle.containsKey(drawableId)) { - Log.d(TAG, "No updated drawable found for drawable id " + drawableId); return null; } - if (mUpdatedDrawablesForStyle.get(drawableId).containsKey(drawableStyle)) { - return mUpdatedDrawablesForStyle.get(drawableId).get(drawableStyle); - } + return mUpdatedDrawablesForStyle.get(drawableId).get(drawableStyle); + } + } + + @Nullable + ParcelableResource getDrawableForSourceLocked( + String drawableId, String drawableStyle, String drawableSource) { + if (!mUpdatedDrawablesForSource.containsKey(drawableId)) { + return null; + } + if (!mUpdatedDrawablesForSource.get(drawableId).containsKey(drawableSource)) { + return null; } - Log.d(TAG, "No updated drawable found for drawable id " + drawableId); - return null; + return mUpdatedDrawablesForSource.get(drawableId).get(drawableSource).get(drawableStyle); } /** @@ -249,12 +259,8 @@ class DeviceManagementResourcesProvider { @Nullable ParcelableResource getString(String stringId) { synchronized (mLock) { - if (mUpdatedStrings.containsKey(stringId)) { - return mUpdatedStrings.get(stringId); - } + return mUpdatedStrings.get(stringId); } - Log.d(TAG, "No updated string found for string id " + stringId); - return null; } private void write() { @@ -359,50 +365,55 @@ class DeviceManagementResourcesProvider { } void writeInner(TypedXmlSerializer out) throws IOException { + writeDrawablesForStylesInner(out); + writeDrawablesForSourcesInner(out); + writeStringsInner(out); + } + + private void writeDrawablesForStylesInner(TypedXmlSerializer out) throws IOException { if (mUpdatedDrawablesForStyle != null && !mUpdatedDrawablesForStyle.isEmpty()) { for (Map.Entry<String, Map<String, ParcelableResource>> drawableEntry : mUpdatedDrawablesForStyle.entrySet()) { - out.startTag(/* namespace= */ null, TAG_DRAWABLE_STYLE_ENTRY); - out.attribute( - /* namespace= */ null, ATTR_DRAWABLE_ID, drawableEntry.getKey()); - out.attributeInt( - /* namespace= */ null, - ATTR_DRAWABLE_STYLE_SIZE, - drawableEntry.getValue().size()); - int counter = 0; for (Map.Entry<String, ParcelableResource> styleEntry : drawableEntry.getValue().entrySet()) { + out.startTag(/* namespace= */ null, TAG_DRAWABLE_STYLE_ENTRY); + out.attribute( + /* namespace= */ null, ATTR_DRAWABLE_ID, drawableEntry.getKey()); out.attribute( /* namespace= */ null, - ATTR_DRAWABLE_STYLE + (counter++), + ATTR_DRAWABLE_STYLE, styleEntry.getKey()); styleEntry.getValue().writeToXmlFile(out); + out.endTag(/* namespace= */ null, TAG_DRAWABLE_STYLE_ENTRY); } - out.endTag(/* namespace= */ null, TAG_DRAWABLE_STYLE_ENTRY); } } + } + + private void writeDrawablesForSourcesInner(TypedXmlSerializer out) throws IOException { if (mUpdatedDrawablesForSource != null && !mUpdatedDrawablesForSource.isEmpty()) { - for (Map.Entry<String, Map<String, ParcelableResource>> drawableEntry + for (Map.Entry<String, Map<String, Map<String, ParcelableResource>>> drawableEntry : mUpdatedDrawablesForSource.entrySet()) { - out.startTag(/* namespace= */ null, TAG_DRAWABLE_SOURCE_ENTRY); - out.attribute( - /* namespace= */ null, ATTR_DRAWABLE_ID, drawableEntry.getKey()); - out.attributeInt( - /* namespace= */ null, - ATTR_DRAWABLE_SOURCE_SIZE, - drawableEntry.getValue().size()); - int counter = 0; - for (Map.Entry<String, ParcelableResource> sourceEntry + for (Map.Entry<String, Map<String, ParcelableResource>> sourceEntry : drawableEntry.getValue().entrySet()) { - out.attribute( - /* namespace= */ null, - ATTR_DRAWABLE_SOURCE + (counter++), - sourceEntry.getKey()); - sourceEntry.getValue().writeToXmlFile(out); + for (Map.Entry<String, ParcelableResource> styleEntry + : sourceEntry.getValue().entrySet()) { + out.startTag(/* namespace= */ null, TAG_DRAWABLE_SOURCE_ENTRY); + out.attribute(/* namespace= */ null, ATTR_DRAWABLE_ID, + drawableEntry.getKey()); + out.attribute(/* namespace= */ null, ATTR_DRAWABLE_SOURCE, + sourceEntry.getKey()); + out.attribute(/* namespace= */ null, ATTR_DRAWABLE_STYLE, + styleEntry.getKey()); + styleEntry.getValue().writeToXmlFile(out); + out.endTag(/* namespace= */ null, TAG_DRAWABLE_SOURCE_ENTRY); + } } - out.endTag(/* namespace= */ null, TAG_DRAWABLE_SOURCE_ENTRY); } } + } + + private void writeStringsInner(TypedXmlSerializer out) throws IOException { if (mUpdatedStrings != null && !mUpdatedStrings.isEmpty()) { for (Map.Entry<String, ParcelableResource> entry : mUpdatedStrings.entrySet()) { @@ -417,52 +428,48 @@ class DeviceManagementResourcesProvider { } } - private boolean readInner( - TypedXmlPullParser parser, int depth, String tag) + private boolean readInner(TypedXmlPullParser parser, int depth, String tag) throws XmlPullParserException, IOException { if (depth > 2) { return true; // Ignore } switch (tag) { - case TAG_DRAWABLE_STYLE_ENTRY: - String drawableId = parser.getAttributeValue( - /* namespace= */ null, ATTR_DRAWABLE_ID); - mUpdatedDrawablesForStyle.put( - drawableId, - new HashMap<>()); - int size = parser.getAttributeInt( - /* namespace= */ null, ATTR_DRAWABLE_STYLE_SIZE); - for (int i = 0; i < size; i++) { - String style = parser.getAttributeValue( - /* namespace= */ null, ATTR_DRAWABLE_STYLE + i); - mUpdatedDrawablesForStyle.get(drawableId).put( - style, - ParcelableResource.createFromXml(parser)); + case TAG_DRAWABLE_STYLE_ENTRY: { + String id = parser.getAttributeValue(/* namespace= */ null, ATTR_DRAWABLE_ID); + String style = parser.getAttributeValue( + /* namespace= */ null, ATTR_DRAWABLE_STYLE); + ParcelableResource resource = ParcelableResource.createFromXml(parser); + if (!mUpdatedDrawablesForStyle.containsKey(id)) { + mUpdatedDrawablesForStyle.put(id, new HashMap<>()); } + mUpdatedDrawablesForStyle.get(id).put(style, resource); break; - case TAG_DRAWABLE_SOURCE_ENTRY: - drawableId = parser.getAttributeValue( - /* namespace= */ null, ATTR_DRAWABLE_ID); - mUpdatedDrawablesForSource.put(drawableId, new HashMap<>()); - size = parser.getAttributeInt( - /* namespace= */ null, ATTR_DRAWABLE_SOURCE_SIZE); - for (int i = 0; i < size; i++) { - String source = parser.getAttributeValue( - /* namespace= */ null, ATTR_DRAWABLE_SOURCE + i); - mUpdatedDrawablesForSource.get(drawableId).put( - source, - ParcelableResource.createFromXml(parser)); + } + case TAG_DRAWABLE_SOURCE_ENTRY: { + String id = parser.getAttributeValue(/* namespace= */ null, ATTR_DRAWABLE_ID); + String source = parser.getAttributeValue( + /* namespace= */ null, ATTR_DRAWABLE_SOURCE); + String style = parser.getAttributeValue( + /* namespace= */ null, ATTR_DRAWABLE_STYLE); + ParcelableResource resource = ParcelableResource.createFromXml(parser); + if (!mUpdatedDrawablesForSource.containsKey(id)) { + mUpdatedDrawablesForSource.put(id, new HashMap<>()); + } + if (!mUpdatedDrawablesForSource.get(id).containsKey(source)) { + mUpdatedDrawablesForSource.get(id).put(source, new HashMap<>()); } + mUpdatedDrawablesForSource.get(id).get(source).put(style, resource); break; - case TAG_STRING_ENTRY: - String sourceId = parser.getAttributeValue( - /* namespace= */ null, ATTR_SOURCE_ID); - mUpdatedStrings.put( - sourceId, ParcelableResource.createFromXml(parser)); + } + case TAG_STRING_ENTRY: { + String id = parser.getAttributeValue(/* namespace= */ null, ATTR_SOURCE_ID); + mUpdatedStrings.put(id, ParcelableResource.createFromXml(parser)); break; - default: + } + default: { Log.e(TAG, "Unexpected tag: " + tag); return false; + } } return true; } diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java index 6a27ecc7f094..1753fc7a61e4 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java @@ -737,6 +737,101 @@ public class JobSchedulerServiceTest { assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); } + @Test + public void testGetRescheduleJobForPeriodic_outsideWindow_flex_failedJob_longPeriod() { + JobStatus job = createJobStatus( + "testGetRescheduleJobForPeriodic_outsideWindow_flex_failedJob_longPeriod", + createJobInfo().setPeriodic(7 * DAY_IN_MILLIS, 9 * HOUR_IN_MILLIS)); + JobStatus failedJob = mService.getRescheduleJobForFailureLocked(job); + // First window starts 6.625 days from now. + advanceElapsedClock(6 * DAY_IN_MILLIS + 15 * HOUR_IN_MILLIS); + long now = sElapsedRealtimeClock.millis(); + long nextWindowStartTime = now + 7 * DAY_IN_MILLIS; + long nextWindowEndTime = nextWindowStartTime + 9 * HOUR_IN_MILLIS; + + advanceElapsedClock(6 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS); + // Say the job ran at the very end of its previous window. The intended JSS behavior is to + // have consistent windows, so the new window should start as soon as the previous window + // ended and end PERIOD time after the previous window ended. + JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob); + assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); + assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); + + advanceElapsedClock(DAY_IN_MILLIS); + // Say the job ran a day late. Since the period is massive compared to the flex, JSS should + // put the rescheduled job in the original window. + rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob); + assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); + assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); + + // 1 day before the start of the next window. Given the large period, respect the original + // next window. + advanceElapsedClock(nextWindowStartTime - sElapsedRealtimeClock.millis() - DAY_IN_MILLIS); + rescheduledJob = mService.getRescheduleJobForPeriodic(job); + assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); + assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); + + // 1 hour before the start of the next window. It's too close to the next window, so the + // returned job should be for the window after. + long oneHourBeforeNextWindow = + nextWindowStartTime - sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS; + long fiveMinsBeforeNextWindow = + nextWindowStartTime - sElapsedRealtimeClock.millis() - 5 * MINUTE_IN_MILLIS; + advanceElapsedClock(oneHourBeforeNextWindow); + nextWindowStartTime += 7 * DAY_IN_MILLIS; + nextWindowEndTime += 7 * DAY_IN_MILLIS; + rescheduledJob = mService.getRescheduleJobForPeriodic(job); + assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); + assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); + + // 5 minutes before the start of the next window. It's too close to the next window, so the + // returned job should be for the window after. + advanceElapsedClock(fiveMinsBeforeNextWindow); + rescheduledJob = mService.getRescheduleJobForPeriodic(job); + assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); + assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); + + advanceElapsedClock(14 * DAY_IN_MILLIS); + // Say that the job ran at this point, probably because the phone was off the entire time. + // The next window should be consistent (start and end at the time it would have had the job + // run normally in previous windows). + nextWindowStartTime += 14 * DAY_IN_MILLIS; + nextWindowEndTime += 14 * DAY_IN_MILLIS; + + rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob); + assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); + assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); + + // Test original job again but with a huge delay from the original execution window + + // 1 day before the start of the next window. Given the large period, respect the original + // next window. + advanceElapsedClock(nextWindowStartTime - sElapsedRealtimeClock.millis() - DAY_IN_MILLIS); + rescheduledJob = mService.getRescheduleJobForPeriodic(job); + assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); + assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); + + // 1 hour before the start of the next window. It's too close to the next window, so the + // returned job should be for the window after. + oneHourBeforeNextWindow = + nextWindowStartTime - sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS; + fiveMinsBeforeNextWindow = + nextWindowStartTime - sElapsedRealtimeClock.millis() - 5 * MINUTE_IN_MILLIS; + advanceElapsedClock(oneHourBeforeNextWindow); + nextWindowStartTime += 7 * DAY_IN_MILLIS; + nextWindowEndTime += 7 * DAY_IN_MILLIS; + rescheduledJob = mService.getRescheduleJobForPeriodic(job); + assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); + assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); + + // 5 minutes before the start of the next window. It's too close to the next window, so the + // returned job should be for the window after. + advanceElapsedClock(fiveMinsBeforeNextWindow); + rescheduledJob = mService.getRescheduleJobForPeriodic(job); + assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); + assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); + } + /** Tests that rare job batching works as expected. */ @Test public void testRareJobBatching() { diff --git a/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java b/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java index 9cf6c0325024..5c4657fb0027 100644 --- a/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java @@ -67,6 +67,8 @@ import com.android.server.power.batterysaver.BatterySaverStateMachine; import com.android.server.power.batterysaver.BatterySavingStats; import com.android.server.testutils.OffsettableClock; +import java.util.concurrent.Executor; + import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -164,7 +166,8 @@ public class PowerManagerServiceMockingTest { @Override Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats, SuspendBlocker suspendBlocker, WindowManagerPolicy policy, - FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector) { + FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector, + Executor executor) { return mNotifierMock; } diff --git a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java index 3d3c1abb3e91..4e059b4b7432 100644 --- a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java @@ -16,6 +16,9 @@ package com.android.server; +import static com.android.server.GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS; +import static com.android.server.GestureLauncherService.EMERGENCY_GESTURE_TAP_DETECTION_MIN_TIME_MS; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -248,7 +251,7 @@ public class GestureLauncherServiceTest { mGestureLauncherService.updateCameraDoubleTapPowerEnabled(); long eventTime = INITIAL_EVENT_TIME_MILLIS + - GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, IGNORED_REPEAT); boolean interactive = true; @@ -296,7 +299,7 @@ public class GestureLauncherServiceTest { assertFalse(intercepted); assertFalse(outLaunched.value); - final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; eventTime += interval; keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, IGNORED_REPEAT); @@ -341,7 +344,7 @@ public class GestureLauncherServiceTest { assertFalse(intercepted); assertFalse(outLaunched.value); - final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS; + final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS; eventTime += interval; keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, IGNORED_REPEAT); @@ -435,7 +438,7 @@ public class GestureLauncherServiceTest { assertFalse(intercepted); assertFalse(outLaunched.value); - final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; eventTime += interval; keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, IGNORED_REPEAT); @@ -489,7 +492,7 @@ public class GestureLauncherServiceTest { assertFalse(intercepted); assertFalse(outLaunched.value); - final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; // 2nd button triggers camera eventTime += interval; @@ -578,7 +581,7 @@ public class GestureLauncherServiceTest { assertFalse(intercepted); assertFalse(outLaunched.value); - final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; // 3 more button presses which should not trigger any gesture (camera gesture disabled) for (int i = 0; i < 3; i++) { eventTime += interval; @@ -632,7 +635,7 @@ public class GestureLauncherServiceTest { assertFalse(intercepted); assertFalse(outLaunched.value); - final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; // 3 more button presses which should not trigger any gesture, but intercepts action. for (int i = 0; i < 3; i++) { eventTime += interval; @@ -735,7 +738,7 @@ public class GestureLauncherServiceTest { interactive, outLaunched); assertTrue(intercepted); assertFalse(outLaunched.value); - interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; eventTime += interval; } } @@ -763,12 +766,33 @@ public class GestureLauncherServiceTest { interactive, outLaunched); assertTrue(intercepted); assertFalse(outLaunched.value); - interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; eventTime += interval; } } @Test + public void testInterceptPowerKeyDown_triggerEmergency_fiveFastTaps_gestureIgnored() { + // Trigger emergency by tapping button 5 times + long eventTime = triggerEmergencyGesture(/* tapIntervalMs= */ 1); + + // Add 1 more millisecond and send the event again. + eventTime += 1; + + // Subsequent long press is intercepted, but should not trigger any gesture + KeyEvent 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); + MutableBoolean outLaunched = new MutableBoolean(true); + boolean intercepted = mGestureLauncherService.interceptPowerKeyDown( + keyEvent, + /* interactive= */ true, + outLaunched); + assertFalse(intercepted); + assertFalse(outLaunched.value); + } + + @Test public void testInterceptPowerKeyDown_triggerEmergency_longPress_cooldownTriggered() { // Enable power button cooldown withEmergencyGesturePowerButtonCooldownPeriodMsValue(3000); @@ -890,7 +914,7 @@ public class GestureLauncherServiceTest { assertFalse(intercepted); assertFalse(outLaunched.value); - final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + final long interval = 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, @@ -936,7 +960,7 @@ public class GestureLauncherServiceTest { assertFalse(intercepted); assertFalse(outLaunched.value); - final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; eventTime += interval; keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, IGNORED_REPEAT); @@ -983,7 +1007,7 @@ public class GestureLauncherServiceTest { assertFalse(intercepted); assertFalse(outLaunched.value); - final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS; + final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS; eventTime += interval; keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, IGNORED_REPEAT); @@ -1075,7 +1099,7 @@ public class GestureLauncherServiceTest { assertFalse(intercepted); assertFalse(outLaunched.value); - final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; eventTime += interval; keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, IGNORED_REPEAT); @@ -1120,7 +1144,7 @@ public class GestureLauncherServiceTest { assertFalse(intercepted); assertFalse(outLaunched.value); - final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS; + final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS; eventTime += interval; keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, IGNORED_REPEAT); @@ -1212,7 +1236,7 @@ public class GestureLauncherServiceTest { assertFalse(intercepted); assertFalse(outLaunched.value); - final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; eventTime += interval; keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, IGNORED_REPEAT); @@ -1261,7 +1285,7 @@ public class GestureLauncherServiceTest { assertFalse(intercepted); assertFalse(outLaunched.value); - final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; eventTime += interval; keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, IGNORED_REPEAT); @@ -1306,7 +1330,7 @@ public class GestureLauncherServiceTest { assertFalse(intercepted); assertFalse(outLaunched.value); - final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS; + final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS; eventTime += interval; keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, IGNORED_REPEAT); @@ -1384,9 +1408,20 @@ public class GestureLauncherServiceTest { /** * Helper method to trigger emergency gesture by pressing button for 5 times. + * * @return last event time. */ private long triggerEmergencyGesture() { + return triggerEmergencyGesture(CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1); + } + + /** + * Helper method to trigger emergency gesture by pressing button for 5 times with + * specified interval between each tap + * + * @return last event time. + */ + private long triggerEmergencyGesture(long tapIntervalMs) { // Enable emergency power gesture withEmergencyGestureEnabledConfigValue(true); withEmergencyGestureEnabledSettingValue(true); @@ -1402,8 +1437,7 @@ public class GestureLauncherServiceTest { keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, IGNORED_REPEAT); mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive, outLaunched); - final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; - eventTime += interval; + eventTime += tapIntervalMs; } // 5th button press should trigger the emergency flow @@ -1412,12 +1446,22 @@ public class GestureLauncherServiceTest { outLaunched.value = false; boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive, outLaunched); - assertTrue(outLaunched.value); + long emergencyGestureTapDetectionMinTimeMs = Settings.Global.getInt( + mContext.getContentResolver(), + Settings.Global.EMERGENCY_GESTURE_TAP_DETECTION_MIN_TIME_MS, + EMERGENCY_GESTURE_TAP_DETECTION_MIN_TIME_MS); assertTrue(intercepted); - verify(mUiEventLogger, times(1)) - .log(GestureLauncherService.GestureLauncherEvent.GESTURE_EMERGENCY_TAP_POWER); - verify(mStatusBarManagerInternal).onEmergencyActionLaunchGestureDetected(); - + if (tapIntervalMs * 4 > emergencyGestureTapDetectionMinTimeMs) { + assertTrue(outLaunched.value); + verify(mUiEventLogger, times(1)) + .log(GestureLauncherService.GestureLauncherEvent.GESTURE_EMERGENCY_TAP_POWER); + verify(mStatusBarManagerInternal).onEmergencyActionLaunchGestureDetected(); + } else { + assertFalse(outLaunched.value); + verify(mUiEventLogger, never()) + .log(GestureLauncherService.GestureLauncherEvent.GESTURE_EMERGENCY_TAP_POWER); + verify(mStatusBarManagerInternal, never()).onEmergencyActionLaunchGestureDetected(); + } return eventTime; } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java index 97ebdd4ad7fa..08df4381ad62 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java @@ -58,6 +58,7 @@ import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; @@ -435,7 +436,7 @@ public class AbstractAccessibilityServiceConnectionTest { VIEWID_RESOURCE_NAME, INTERACTION_ID, mMockCallback, TID); verify(mMockIA11yInteractionConnection).findAccessibilityNodeInfosByViewId( eq(ROOT_NODE_ID), eq(VIEWID_RESOURCE_NAME), any(), eq(INTERACTION_ID), - captor.capture(), anyInt(), eq(PID), eq(TID), any()); + captor.capture(), anyInt(), eq(PID), eq(TID), any(), nullable(float[].class)); verify(mMockSecurityPolicy).computeValidReportedPackages(any(), anyInt()); verifyReplaceActions(captor.getValue()); } @@ -449,7 +450,7 @@ public class AbstractAccessibilityServiceConnectionTest { VIEW_TEXT, INTERACTION_ID, mMockCallback, TID); verify(mMockIA11yInteractionConnection).findAccessibilityNodeInfosByText( eq(ROOT_NODE_ID), eq(VIEW_TEXT), any(), eq(INTERACTION_ID), - captor.capture(), anyInt(), eq(PID), eq(TID), any()); + captor.capture(), anyInt(), eq(PID), eq(TID), any(), nullable(float[].class)); verify(mMockSecurityPolicy).computeValidReportedPackages(any(), anyInt()); verifyReplaceActions(captor.getValue()); } @@ -463,7 +464,7 @@ public class AbstractAccessibilityServiceConnectionTest { INTERACTION_ID, mMockCallback, 0, TID, null); verify(mMockIA11yInteractionConnection).findAccessibilityNodeInfoByAccessibilityId( eq(ROOT_NODE_ID), any(), eq(INTERACTION_ID), captor.capture(), anyInt(), - eq(PID), eq(TID), any(), any()); + eq(PID), eq(TID), any(), nullable(float[].class), any()); verify(mMockSecurityPolicy).computeValidReportedPackages(any(), anyInt()); verifyReplaceActions(captor.getValue()); } @@ -476,7 +477,8 @@ public class AbstractAccessibilityServiceConnectionTest { mServiceConnection.findFocus(PIP_WINDOWID, ROOT_NODE_ID, FOCUS_INPUT, INTERACTION_ID, mMockCallback, TID); verify(mMockIA11yInteractionConnection).findFocus(eq(ROOT_NODE_ID), eq(FOCUS_INPUT), - any(), eq(INTERACTION_ID), captor.capture(), anyInt(), eq(PID), eq(TID), any()); + any(), eq(INTERACTION_ID), captor.capture(), anyInt(), eq(PID), eq(TID), any(), + nullable(float[].class)); verify(mMockSecurityPolicy).computeValidReportedPackages(any(), anyInt()); verifyReplaceActions(captor.getValue()); } @@ -489,7 +491,8 @@ public class AbstractAccessibilityServiceConnectionTest { mServiceConnection.focusSearch(PIP_WINDOWID, ROOT_NODE_ID, FOCUS_DOWN, INTERACTION_ID, mMockCallback, TID); verify(mMockIA11yInteractionConnection).focusSearch(eq(ROOT_NODE_ID), eq(FOCUS_DOWN), - any(), eq(INTERACTION_ID), captor.capture(), anyInt(), eq(PID), eq(TID), any()); + any(), eq(INTERACTION_ID), captor.capture(), anyInt(), eq(PID), eq(TID), any(), + nullable(float[].class)); verify(mMockSecurityPolicy).computeValidReportedPackages(any(), anyInt()); verifyReplaceActions(captor.getValue()); } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInteractionControllerNodeRequestsTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInteractionControllerNodeRequestsTest.java index 022c137491df..842b23c91e41 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInteractionControllerNodeRequestsTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInteractionControllerNodeRequestsTest.java @@ -842,7 +842,7 @@ public class AccessibilityInteractionControllerNodeRequestsTest { null, interactionId, callback, prefetchFlags, processAndThreadId, - processAndThreadId, null, null); + processAndThreadId, null, null, null); } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/ActionReplacingCallbackTest.java b/services/tests/servicestests/src/com/android/server/accessibility/ActionReplacingCallbackTest.java index 72820f15315e..c16f3d3fa1ab 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/ActionReplacingCallbackTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/ActionReplacingCallbackTest.java @@ -16,6 +16,27 @@ package com.android.server.accessibility; +import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction.ACTION_ACCESSIBILITY_FOCUS; +import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction.ACTION_CLEAR_ACCESSIBILITY_FOCUS; +import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK; +import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction.ACTION_COLLAPSE; +import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction.ACTION_CONTEXT_CLICK; +import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND; + +import static junit.framework.TestCase.assertTrue; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertThat; +import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyObject; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.MockitoAnnotations.initMocks; + import android.graphics.Region; import android.os.RemoteException; import android.view.MagnificationSpec; @@ -35,28 +56,8 @@ import org.mockito.Captor; import org.mockito.Mock; import java.util.Arrays; -import java.util.HashSet; import java.util.List; -import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction.ACTION_ACCESSIBILITY_FOCUS; -import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction.ACTION_CLEAR_ACCESSIBILITY_FOCUS; -import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK; -import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction.ACTION_COLLAPSE; -import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND; -import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction.ACTION_CONTEXT_CLICK; -import static junit.framework.TestCase.assertTrue; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertThat; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyObject; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.MockitoAnnotations.initMocks; - /** * Tests for ActionReplacingCallback */ @@ -130,8 +131,8 @@ public class ActionReplacingCallbackTest { verify(mMockReplacerConnection).findAccessibilityNodeInfoByAccessibilityId( eq(AccessibilityNodeInfo.ROOT_NODE_ID), (Region) anyObject(), mInteractionIdCaptor.capture(), eq(mActionReplacingCallback), eq(0), - eq(INTERROGATING_PID), eq(INTERROGATING_TID), (MagnificationSpec) anyObject(), - eq(null)); + eq(INTERROGATING_PID), eq(INTERROGATING_TID), nullable(MagnificationSpec.class), + nullable(float[].class), eq(null)); mReplacerInteractionId = mInteractionIdCaptor.getValue().intValue(); } @@ -247,7 +248,7 @@ public class ActionReplacingCallbackTest { .findAccessibilityNodeInfoByAccessibilityId(eq(AccessibilityNodeInfo.ROOT_NODE_ID), (Region) anyObject(), anyInt(), (ActionReplacingCallback) anyObject(), eq(0), eq(INTERROGATING_PID), eq(INTERROGATING_TID), - (MagnificationSpec) anyObject(), eq(null)); + (MagnificationSpec) anyObject(), nullable(float[].class), eq(null)); ActionReplacingCallback actionReplacingCallback = new ActionReplacingCallback( mMockServiceCallback, mMockReplacerConnection, INTERACTION_ID, INTERROGATING_PID, INTERROGATING_TID); 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 a79a52c1c198..e4ee4d064724 100644 --- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java @@ -538,6 +538,11 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { } @Override + ComponentName injectChooserActivity() { + return mInjectedChooserActivity; + } + + @Override void wtf(String message, Throwable th) { // During tests, WTF is fatal. fail(message + " exception: " + th + "\n" + Log.getStackTraceString(th)); @@ -678,6 +683,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { protected int mInjectedCallingUid; protected String mInjectedClientPackage; + protected ComponentName mInjectedChooserActivity; protected Map<String, PackageInfo> mInjectedPackages; @@ -723,6 +729,9 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { protected static final String LAUNCHER_4 = "com.android.launcher.4"; protected static final int LAUNCHER_UID_4 = 10014; + protected static final String CHOOSER_ACTIVITY_PACKAGE = "com.android.intentresolver"; + protected static final int CHOOSER_ACTIVITY_UID = 10015; + protected static final int USER_0 = UserHandle.USER_SYSTEM; protected static final int USER_10 = 10; protected static final int USER_11 = 11; diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java index a350dfbad662..867890f938ba 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java @@ -6901,6 +6901,9 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { } public void testGetShareTargets_permission() { + addPackage(CHOOSER_ACTIVITY_PACKAGE, CHOOSER_ACTIVITY_UID, 10, "sig1"); + mInjectedChooserActivity = + ComponentName.createRelative(CHOOSER_ACTIVITY_PACKAGE, ".ChooserActivity"); IntentFilter filter = new IntentFilter(); assertExpectException(SecurityException.class, "Missing permission", () -> @@ -6909,6 +6912,11 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { // Has permission, now it should pass. mCallerPermissions.add(permission.MANAGE_APP_PREDICTIONS); mManager.getShareTargets(filter); + + runWithCaller(CHOOSER_ACTIVITY_PACKAGE, USER_0, () -> { + // Access is allowed when called from the configured system ChooserActivity + mManager.getShareTargets(filter); + }); } public void testHasShareTargets_permission() { diff --git a/services/tests/servicestests/src/com/android/server/power/NotifierTest.java b/services/tests/servicestests/src/com/android/server/power/NotifierTest.java index a3223d6d2b7b..8f5f0e6fe131 100644 --- a/services/tests/servicestests/src/com/android/server/power/NotifierTest.java +++ b/services/tests/servicestests/src/com/android/server/power/NotifierTest.java @@ -16,6 +16,8 @@ package com.android.server.power; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; @@ -56,6 +58,8 @@ import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.concurrent.Executor; + /** * Tests for {@link com.android.server.power.Notifier} */ @@ -79,6 +83,7 @@ public class NotifierTest { private Context mContextSpy; private Resources mResourcesSpy; private TestLooper mTestLooper = new TestLooper(); + private FakeExecutor mTestExecutor = new FakeExecutor(); private Notifier mNotifier; @Before @@ -107,6 +112,7 @@ public class NotifierTest { // WHEN wired charging starts mNotifier.onWiredChargingStarted(USER_ID); mTestLooper.dispatchAll(); + mTestExecutor.simulateAsyncExecutionOfLastCommand(); // THEN the device vibrates once verify(mVibrator, times(1)).vibrate(any(), any(VibrationAttributes.class)); @@ -122,6 +128,7 @@ public class NotifierTest { // WHEN wired charging starts mNotifier.onWiredChargingStarted(USER_ID); mTestLooper.dispatchAll(); + mTestExecutor.simulateAsyncExecutionOfLastCommand(); // THEN the device doesn't vibrate verify(mVibrator, never()).vibrate(any(), any(VibrationAttributes.class)); @@ -137,6 +144,7 @@ public class NotifierTest { // WHEN wireless charging starts mNotifier.onWirelessChargingStarted(5, USER_ID); mTestLooper.dispatchAll(); + mTestExecutor.simulateAsyncExecutionOfLastCommand(); // THEN the device vibrates once verify(mVibrator, times(1)).vibrate(any(), any(VibrationAttributes.class)); @@ -152,6 +160,7 @@ public class NotifierTest { // WHEN wireless charging starts mNotifier.onWirelessChargingStarted(5, USER_ID); mTestLooper.dispatchAll(); + mTestExecutor.simulateAsyncExecutionOfLastCommand(); // THEN the device doesn't vibrate verify(mVibrator, never()).vibrate(any(), any(VibrationAttributes.class)); @@ -170,6 +179,7 @@ public class NotifierTest { // WHEN wired charging starts mNotifier.onWiredChargingStarted(USER_ID); mTestLooper.dispatchAll(); + mTestExecutor.simulateAsyncExecutionOfLastCommand(); // THEN the device doesn't vibrate verify(mVibrator, never()).vibrate(any(), any(VibrationAttributes.class)); @@ -186,6 +196,7 @@ public class NotifierTest { // WHEN wireless charging starts mNotifier.onWirelessChargingStarted(5, USER_ID); mTestLooper.dispatchAll(); + mTestExecutor.simulateAsyncExecutionOfLastCommand(); // THEN the charging animation is triggered verify(mStatusBarManagerInternal, times(1)).showChargingAnimation(5); @@ -202,6 +213,7 @@ public class NotifierTest { // WHEN wireless charging starts mNotifier.onWirelessChargingStarted(5, USER_ID); mTestLooper.dispatchAll(); + mTestExecutor.simulateAsyncExecutionOfLastCommand(); // THEN the charging animation never gets called verify(mStatusBarManagerInternal, never()).showChargingAnimation(anyInt()); @@ -211,7 +223,8 @@ public class NotifierTest { @Override Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats, SuspendBlocker suspendBlocker, WindowManagerPolicy policy, - FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector) { + FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector, + Executor backgroundExecutor) { return mNotifierMock; } @@ -300,6 +313,32 @@ public class NotifierTest { mInjector.createSuspendBlocker(mService, "testBlocker"), null, null, - null); + null, + mTestExecutor); + } + + private static class FakeExecutor implements Executor { + private Runnable mLastCommand; + + @Override + public void execute(Runnable command) { + assertNull(mLastCommand); + assertNotNull(command); + mLastCommand = command; + } + + public Runnable getAndResetLastCommand() { + Runnable toReturn = mLastCommand; + mLastCommand = null; + return toReturn; + } + + public void simulateAsyncExecutionOfLastCommand() { + Runnable toRun = getAndResetLastCommand(); + if (toRun != null) { + toRun.run(); + } + } } + } diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java index c9721dbecdb4..fbcad62988be 100644 --- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java @@ -109,6 +109,7 @@ import org.mockito.stubbing.Answer; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicReference; /** @@ -220,7 +221,8 @@ public class PowerManagerServiceTest { @Override Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats, SuspendBlocker suspendBlocker, WindowManagerPolicy policy, - FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector) { + FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector, + Executor executor) { return mNotifierMock; } diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java index 2d0dca25465b..c9fc033e9086 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java @@ -61,6 +61,16 @@ class FakeTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy { } @Override + public boolean isTelephonyTimeZoneDetectionSupported() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isGeoTimeZoneDetectionSupported() { + throw new UnsupportedOperationException(); + } + + @Override public void dump(IndentingPrintWriter pw, String[] args) { mDumpCalled = true; } diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java index 49cd343ef4af..82e54110b971 100644 --- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java @@ -20,6 +20,8 @@ import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_EXT_ENABLE_ON_BACK import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.window.BackNavigationInfo.typeToString; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; + import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; @@ -81,8 +83,8 @@ public class BackNavigationControllerTests extends WindowTestsBase { @Test public void backNavInfo_HomeWhenBackToLauncher() { - Task task = createTopTaskWithActivity(); - IOnBackInvokedCallback callback = withSystemCallback(task); + IOnBackInvokedCallback callback = + withCallback(createTopTaskWithActivity(), OnBackInvokedDispatcher.PRIORITY_SYSTEM); SurfaceControl.Transaction tx = mock(SurfaceControl.Transaction.class); BackNavigationInfo backNavigationInfo = mBackNavigationController.startBackNavigation(mWm, @@ -103,7 +105,7 @@ public class BackNavigationControllerTests extends WindowTestsBase { public void backTypeCrossTaskWhenBackToPreviousTask() { Task taskA = createTask(mDefaultDisplay); createActivityRecord(taskA); - withSystemCallback(createTopTaskWithActivity()); + withCallback(createTopTaskWithActivity(), OnBackInvokedDispatcher.PRIORITY_SYSTEM); BackNavigationInfo backNavigationInfo = startBackNavigation(); assertWithMessage("BackNavigationInfo").that(backNavigationInfo).isNotNull(); assertThat(typeToString(backNavigationInfo.getType())) @@ -155,7 +157,7 @@ public class BackNavigationControllerTests extends WindowTestsBase { @Test public void preparesForBackToHome() { Task task = createTopTaskWithActivity(); - withSystemCallback(task); + withCallback(task, OnBackInvokedDispatcher.PRIORITY_SYSTEM); BackNavigationInfo backNavigationInfo = startBackNavigation(); assertThat(typeToString(backNavigationInfo.getType())) @@ -165,7 +167,8 @@ public class BackNavigationControllerTests extends WindowTestsBase { @Test public void backTypeCallback() { Task task = createTopTaskWithActivity(); - IOnBackInvokedCallback appCallback = withAppCallback(task); + IOnBackInvokedCallback appCallback = + withCallback(task, OnBackInvokedDispatcher.PRIORITY_DEFAULT); BackNavigationInfo backNavigationInfo = startBackNavigation(); assertThat(typeToString(backNavigationInfo.getType())) @@ -226,20 +229,63 @@ public class BackNavigationControllerTests extends WindowTestsBase { 1, appLatch.getCount()); } - private IOnBackInvokedCallback withSystemCallback(Task task) { - IOnBackInvokedCallback callback = createOnBackInvokedCallback(); - task.getTopMostActivity().getTopChild().setOnBackInvokedCallbackInfo( - new OnBackInvokedCallbackInfo(callback, OnBackInvokedDispatcher.PRIORITY_SYSTEM)); - return callback; + @Test + public void returnsImeCallback_imeVisible() { + // Set up a top activity with a default priority callback. + IOnBackInvokedCallback appCallback = + withCallback(createTopTaskWithActivity(), OnBackInvokedDispatcher.PRIORITY_DEFAULT); + IOnBackInvokedCallback imeCallback = createOnBackInvokedCallback(); + + // Set up an IME window with also a default priority callback. + final DisplayArea.Tokens imeContainer = mDisplayContent.getImeContainer(); + final WindowState imeWindow = createImeWindow(); + imeWindow.setOnBackInvokedCallbackInfo( + new OnBackInvokedCallbackInfo( + imeCallback, OnBackInvokedDispatcher.PRIORITY_DEFAULT)); + spyOn(imeContainer); + // Simulate IME becoming visible. + doReturn(true).when(imeContainer).isVisible(); + doReturn(imeWindow).when(imeContainer).getWindow(any()); + BackNavigationInfo backNavigationInfo = startBackNavigation(); + + // Expect the IME callback to be selected. + assertThat(backNavigationInfo.getOnBackInvokedCallback()).isEqualTo(imeCallback); } - private IOnBackInvokedCallback withAppCallback(Task task) { + @Test + public void returnsAppOverlayCallback_imeVisible() { + // Set up a top activity with an overlay priority callback. + IOnBackInvokedCallback appCallback = + withCallback(createTopTaskWithActivity(), OnBackInvokedDispatcher.PRIORITY_OVERLAY); + IOnBackInvokedCallback imeCallback = createOnBackInvokedCallback(); + + // Set up an IME window with a default priority callback. + final DisplayArea.Tokens imeContainer = mDisplayContent.getImeContainer(); + final WindowState imeWindow = createImeWindow(); + imeWindow.setOnBackInvokedCallbackInfo( + new OnBackInvokedCallbackInfo( + imeCallback, OnBackInvokedDispatcher.PRIORITY_DEFAULT)); + spyOn(imeContainer); + // Simulate IME becoming visible. + doReturn(true).when(imeContainer).isVisible(); + doReturn(imeWindow).when(imeContainer).getWindow(any()); + BackNavigationInfo backNavigationInfo = startBackNavigation(); + + // Expect the app callback to be selected. + assertThat(backNavigationInfo.getOnBackInvokedCallback()).isEqualTo(appCallback); + } + + private IOnBackInvokedCallback withCallback(Task task, int priority) { IOnBackInvokedCallback callback = createOnBackInvokedCallback(); task.getTopMostActivity().getTopChild().setOnBackInvokedCallbackInfo( - new OnBackInvokedCallbackInfo(callback, OnBackInvokedDispatcher.PRIORITY_DEFAULT)); + new OnBackInvokedCallbackInfo(callback, priority)); return callback; } + private WindowState createImeWindow() { + return createWindow(null, W_INPUT_METHOD, "mImeWindow", 12345 /* fake ime uide */); + } + @Nullable private BackNavigationInfo startBackNavigation() { return mBackNavigationController.startBackNavigation(mWm, new StubTransaction()); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java index 30c2413eb6f7..54fa4e4bf7ec 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java @@ -209,7 +209,7 @@ public class TaskFragmentTest extends WindowTestsBase { } @Test - public void testEmbeddedTaskFragmentEnterPip_resetOrganizerOverrideConfig() { + public void testEmbeddedTaskFragmentEnterPip_singleActivity_resetOrganizerOverrideConfig() { final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm) .setOrganizer(mOrganizer) .setFragmentToken(new Binder()) @@ -242,6 +242,38 @@ public class TaskFragmentTest extends WindowTestsBase { } @Test + public void testEmbeddedTaskFragmentEnterPip_multiActivities_notifyOrganizer() { + final Task task = createTask(mDisplayContent); + final TaskFragment taskFragment0 = new TaskFragmentBuilder(mAtm) + .setParentTask(task) + .setOrganizer(mOrganizer) + .setFragmentToken(new Binder()) + .createActivityCount(1) + .build(); + final TaskFragment taskFragment1 = new TaskFragmentBuilder(mAtm) + .setParentTask(task) + .setOrganizer(mOrganizer) + .setFragmentToken(new Binder()) + .createActivityCount(1) + .build(); + final ActivityRecord activity0 = taskFragment0.getTopMostActivity(); + spyOn(mAtm.mTaskFragmentOrganizerController); + + // Move activity to pinned. + mRootWindowContainer.moveActivityToPinnedRootTask(activity0, + null /* launchIntoPipHostActivity */, "test"); + + // Ensure taskFragment requested config is reset. + assertTrue(taskFragment0.mClearedTaskFragmentForPip); + assertFalse(taskFragment1.mClearedTaskFragmentForPip); + final TaskFragmentInfo info = taskFragment0.getTaskFragmentInfo(); + assertTrue(info.isTaskFragmentClearedForPip()); + assertTrue(info.isEmpty()); + verify(mAtm.mTaskFragmentOrganizerController) + .dispatchPendingInfoChangedEvent(taskFragment0); + } + + @Test public void testActivityHasOverlayOverUntrustedModeEmbedded() { final Task rootTask = createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl index 07e18d524701..37403a806daf 100644 --- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl +++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl @@ -355,6 +355,11 @@ interface ITelecomService { void setTestDefaultCallRedirectionApp(String packageName); + /** + * @see TelecomServiceImpl#requestLogMark + */ + void requestLogMark(in String message); + void setTestPhoneAcctSuggestionComponent(String flattenedComponentName); void setTestDefaultCallScreeningApp(String packageName); diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index a6a7c841c5ec..9c886aada728 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -832,6 +832,17 @@ public class CarrierConfigManager { "dial_string_replace_string_array"; /** + * Specifies a map from dialstrings to replacements for international roaming network service + * numbers which cannot be replaced on the carrier side. + * <p> + * Individual entries have the format: + * [dialstring to replace]:[replacement] + * @hide + */ + public static final String KEY_INTERNATIONAL_ROAMING_DIAL_STRING_REPLACE_STRING_ARRAY = + "international_roaming_dial_string_replace_string_array"; + + /** * Flag specifying whether WFC over IMS supports the "wifi only" option. If false, the wifi * calling settings will not include an option for "wifi only". If true, the wifi calling * settings will include an option for "wifi only" @@ -8713,6 +8724,7 @@ public class CarrierConfigManager { sDefaults.putStringArray(KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY, null); sDefaults.putStringArray(KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY, null); sDefaults.putStringArray(KEY_DIAL_STRING_REPLACE_STRING_ARRAY, null); + sDefaults.putStringArray(KEY_INTERNATIONAL_ROAMING_DIAL_STRING_REPLACE_STRING_ARRAY, null); sDefaults.putBoolean(KEY_FORCE_HOME_NETWORK_BOOL, false); sDefaults.putInt(KEY_GSM_DTMF_TONE_DELAY_INT, 0); sDefaults.putInt(KEY_IMS_DTMF_TONE_DELAY_INT, 0); diff --git a/telephony/java/android/telephony/PhysicalChannelConfig.java b/telephony/java/android/telephony/PhysicalChannelConfig.java index d91134e33ef3..d978f5701eca 100644 --- a/telephony/java/android/telephony/PhysicalChannelConfig.java +++ b/telephony/java/android/telephony/PhysicalChannelConfig.java @@ -23,16 +23,12 @@ import android.os.Parcel; import android.os.Parcelable; import android.telephony.Annotation.NetworkType; -import com.android.telephony.Rlog; - import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Arrays; import java.util.Objects; public final class PhysicalChannelConfig implements Parcelable { - static final String TAG = "PhysicalChannelConfig"; - // TODO(b/72993578) consolidate these enums in a central location. /** @hide */ @Retention(RetentionPolicy.SOURCE) @@ -571,21 +567,19 @@ public final class PhysicalChannelConfig implements Parcelable { public @NonNull Builder setNetworkType(@NetworkType int networkType) { if (!TelephonyManager.isNetworkTypeValid(networkType)) { - Rlog.e(TAG, "Builder.setNetworkType: Network type " + networkType + " is invalid."); - } else { - mNetworkType = networkType; + throw new IllegalArgumentException("Network type " + networkType + " is invalid."); } + mNetworkType = networkType; return this; } public @NonNull Builder setFrequencyRange(int frequencyRange) { if (!ServiceState.isFrequencyRangeValid(frequencyRange) && frequencyRange != ServiceState.FREQUENCY_RANGE_UNKNOWN) { - Rlog.e(TAG, "Builder.setFrequencyRange: Frequency range " + frequencyRange + throw new IllegalArgumentException("Frequency range " + frequencyRange + " is invalid."); - } else { - mFrequencyRange = frequencyRange; } + mFrequencyRange = frequencyRange; return this; } @@ -601,21 +595,19 @@ public final class PhysicalChannelConfig implements Parcelable { public @NonNull Builder setCellBandwidthDownlinkKhz(int cellBandwidthDownlinkKhz) { if (cellBandwidthDownlinkKhz < CELL_BANDWIDTH_UNKNOWN) { - Rlog.e(TAG, "Builder.setCellBandwidthDownlinkKhz: Cell downlink bandwidth(kHz) " + throw new IllegalArgumentException("Cell downlink bandwidth(kHz) " + cellBandwidthDownlinkKhz + " is invalid."); - } else { - mCellBandwidthDownlinkKhz = cellBandwidthDownlinkKhz; } + mCellBandwidthDownlinkKhz = cellBandwidthDownlinkKhz; return this; } public @NonNull Builder setCellBandwidthUplinkKhz(int cellBandwidthUplinkKhz) { if (cellBandwidthUplinkKhz < CELL_BANDWIDTH_UNKNOWN) { - Rlog.e(TAG, "Builder.setCellBandwidthUplinkKhz: Cell uplink bandwidth(kHz) " + throw new IllegalArgumentException("Cell uplink bandwidth(kHz) " + cellBandwidthUplinkKhz + " is invalid."); - } else { - mCellBandwidthUplinkKhz = cellBandwidthUplinkKhz; } + mCellBandwidthUplinkKhz = cellBandwidthUplinkKhz; return this; } @@ -632,20 +624,18 @@ public final class PhysicalChannelConfig implements Parcelable { public @NonNull Builder setPhysicalCellId(int physicalCellId) { if (physicalCellId > PHYSICAL_CELL_ID_MAXIMUM_VALUE) { - Rlog.e(TAG, "Builder.setPhysicalCellId: Physical cell ID " + physicalCellId + throw new IllegalArgumentException("Physical cell ID " + physicalCellId + " is over limit."); - } else { - mPhysicalCellId = physicalCellId; } + mPhysicalCellId = physicalCellId; return this; } public @NonNull Builder setBand(int band) { if (band <= BAND_UNKNOWN) { - Rlog.e(TAG, "Builder.setBand: Band " + band + " is invalid."); - } else { - mBand = band; + throw new IllegalArgumentException("Band " + band + " is invalid."); } + mBand = band; return this; } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt index 4cddd8506df7..aaa2db768792 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt @@ -34,8 +34,6 @@ import com.android.server.wm.flicker.statusBarLayerIsVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsVisible import com.android.server.wm.flicker.replacesLayer -import com.android.server.wm.flicker.rules.WMFlickerServiceRuleForTestSpec -import org.junit.Rule import org.junit.Test /** @@ -45,9 +43,6 @@ abstract class CloseAppTransition(protected val testSpec: FlickerTestParameter) protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() protected open val testApp: StandardAppHelper = SimpleAppHelper(instrumentation) - @get:Rule - val flickerRule = WMFlickerServiceRuleForTestSpec(testSpec) - /** * Specification of the test transition to execute */ diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt index 04fdda425edf..2f546b56f145 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt @@ -63,8 +63,12 @@ import org.junit.runners.Parameterized @FixMethodOrder(MethodSorters.NAME_ASCENDING) @Group4 class TaskTransitionTest(val testSpec: FlickerTestParameter) { - val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() private val mTestApp: NewTasksAppHelper = NewTasksAppHelper(instrumentation) + private val mWallpaper by lazy { + getWallpaperPackage(InstrumentationRegistry.getInstrumentation()) + ?: error("Unable to obtain wallpaper") + } @FlickerBuilderProvider fun buildFlicker(): FlickerBuilder { @@ -97,7 +101,7 @@ class TaskTransitionTest(val testSpec: FlickerTestParameter) { @Test fun wallpaperWindowIsNeverVisible() { testSpec.assertWm { - this.isNonAppWindowInvisible(WALLPAPER) + this.isNonAppWindowInvisible(mWallpaper) } } @@ -109,7 +113,7 @@ class TaskTransitionTest(val testSpec: FlickerTestParameter) { @Test fun wallpaperLayerIsNeverVisible() { testSpec.assertLayers { - this.isInvisible(WALLPAPER) + this.isInvisible(mWallpaper) this.isInvisible(WALLPAPER_BBQ_WRAPPER) } } @@ -229,15 +233,14 @@ class TaskTransitionTest(val testSpec: FlickerTestParameter) { fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible() companion object { - private val WALLPAPER = getWallpaperPackage(InstrumentationRegistry.getInstrumentation()) private val LAUNCH_NEW_TASK_ACTIVITY = LAUNCH_NEW_TASK_ACTIVITY_COMPONENT_NAME.toFlickerComponent() private val SIMPLE_ACTIVITY = SIMPLE_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME.toFlickerComponent() - private fun getWallpaperPackage(instrumentation: Instrumentation): FlickerComponentName { + private fun getWallpaperPackage(instrumentation: Instrumentation): FlickerComponentName? { val wallpaperManager = WallpaperManager.getInstance(instrumentation.targetContext) - return wallpaperManager.wallpaperInfo.component.toFlickerComponent() + return wallpaperManager.wallpaperInfo?.component?.toFlickerComponent() } @Parameterized.Parameters(name = "{0}") diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt index 8b851e5cfc2b..f0d16f3edb11 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt @@ -25,13 +25,11 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.annotation.Group3 import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.SimpleAppHelper -import com.android.server.wm.flicker.rules.WMFlickerServiceRuleForTestSpec import com.android.server.wm.flicker.statusBarLayerIsVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsVisible import com.android.server.wm.traces.common.FlickerComponentName import org.junit.FixMethodOrder -import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -80,9 +78,6 @@ import org.junit.runners.Parameterized class ChangeAppRotationTest( testSpec: FlickerTestParameter ) : RotationTransition(testSpec) { - @get:Rule - val flickerRule = WMFlickerServiceRuleForTestSpec(testSpec) - override val testApp = SimpleAppHelper(instrumentation) override val transition: FlickerBuilder.() -> Unit get() = { diff --git a/tools/aapt/SdkConstants.h b/tools/aapt/SdkConstants.h index f57c4ee9d5b6..a146466402f6 100644 --- a/tools/aapt/SdkConstants.h +++ b/tools/aapt/SdkConstants.h @@ -48,6 +48,7 @@ enum { SDK_R = 30, SDK_S = 31, SDK_S_V2 = 32, + SDK_TIRAMISU = 33, SDK_CUR_DEVELOPMENT = 10000, }; diff --git a/tools/aapt2/SdkConstants.h b/tools/aapt2/SdkConstants.h index f2aaaf5ddcf0..0bd61c04f2b2 100644 --- a/tools/aapt2/SdkConstants.h +++ b/tools/aapt2/SdkConstants.h @@ -58,6 +58,7 @@ enum : ApiVersion { SDK_R = 30, SDK_S = 31, SDK_S_V2 = 32, + SDK_TIRAMISU = 33, SDK_CUR_DEVELOPMENT = 10000, }; |